Table of Contents
php php5 symfony framework
Symfony
Marco de trabajo para desarrollar en php. Actualmente la versión 1.2.
Se trata de una colección de archivos php que una vez descomprimidos, permiten automatizar las tareas más frecuentes en el desarrollo de cualquier aplicación web.
Para que se entienda vamos a ver con un ejemplo, que seguiremos a lo largo de este manual, las posibilidades de instalar symfony.
Existen dos posibilidades:
- Instalación compartida de symfony
/tmp/symfony | Directorio de instalación de symfony |
/tmp/proyectos_symfony/proyecto_1 | Directorio donde residirá el proyecto 1 |
/tmp/proyectos_symfony/proyecto_2 | Directorio donde residirá el proyecto 2 |
Luego habrá que enlazar /tmp/symfony para que sea accesible para los dos proyectos
- Instalación individual de symfony
/tmp/proyectos_symfony/proyecto_1 | Directorio donde residirá el proyecto 1 |
/tmp/proyectos_symfony/proyecto_1/symfony_1_2_8 | Directorio donde residirá symfony solo para el proyecto 1 |
/tmp/proyectos_symfony/proyecto_2 | Directorio donde residirá el proyecto 2 |
/tmp/proyectos_symfony/proyecto_2/symfony_1_2_8 | Directorio donde residirá symfony solo para el proyecto 2 |
Me decanto por esta opción
Instalación
1. Descargar las fuentes
http://www.symfony-project.org/installation/1_2 wget -c --tries=0 http://www.symfony-project.org/get/symfony-1.2.8.tgz
2. Descomprimir:
tar xvfz symfony-1.2.8.tgz
3. Descargar un guión para comprobar dependencias:
wget -c --tries=0 http://sf-to.org/1.2/check.php
4. Ejecutar el guión desde una web, pues el php.ini de php-cli (cuando ejecutamos php desde linea de comandos) es distinto de cuando ejecutamos php como módulo de php):
http://localhost/mi_ruta/check_configuration.php
En mi caso el resultado fue:
******************************** * * * symfony requirements check * * * ******************************** php.ini used by PHP: /etc/php5/cli/php.ini ** WARNING ** * The PHP CLI can use a different php.ini file * than the one used with your web server. * If this is the case, please launch this * utility from your web server. ** WARNING ** ** Mandatory requirements ** OK PHP version is at least 5.2.4 ** Optional checks ** OK PDO is installed [[WARNING]] PDO has some drivers installed: : FAILED *** Install PDO drivers (mandatory for Propel and Doctrine) *** OK PHP-XML module is installed [[WARNING]] XSL module is installed: FAILED *** Install the XSL module (recommended for Propel) *** OK The token_get_all() function is available OK The mb_strlen() function is available OK The iconv() function is available OK The utf8_decode() is available [[WARNING]] A PHP accelerator is installed: FAILED *** Install a PHP accelerator like APC (highly recommended) *** [[WARNING]] php.ini has short_open_tag set to off: FAILED *** Set it to off in php.ini *** [[WARNING]] php.ini has magic_quotes_gpc set to off: FAILED *** Set it to off in php.ini *** OK php.ini has register_globals set to off OK php.ini has session.auto_start set to off OK PHP version is not 5.2.9
Por partes:
PDO
Si hago un:
php -m
Obtengo:
... pcre PDO posix ...
Por lo que entiendo que SI está habilitado
XLS
aptitude install php5-xsl
Acelerador
aptitude install php-apc
short_open_tag
Editar el fichero de configuración de php:
nano /etc/php5/apache2/php.ini
Y dejar la linea tal que así:
short_open_tag = Off
magic_quotes_gpc
Editar el fichero de configuración de php:
nano /etc/php5/apache2/php.ini
Y dejar la linea tal que así:
magic_quotes_gpc = Off
Tras los cambios en php.ini:
apache2ctl restart
Como yo me decanto por la instalación individual de symfony, lo copiamos a su ubicación:
cp -Rv /ruta/fuente/symfony /tmp/proyectos_symfony/proyecto_1/symfony_1_2_8
Crear proyecto
Como me he decantado por la instalación individual de symfony, tenemos la siguiente estructura:
/tmp/proyectos_symfony/proyecto_1 | Directorio donde residirá el proyecto 1 |
/tmp/proyectos_symfony/proyecto_1/symfony_1_2_8 | Directorio donde residirá symfony solo para el proyecto 1 |
De esa forma, tanto 'proyecto_1' como 'proyecto_2' comparten la version 1.2 de symfony:
mkdir /tmp/proyectos_symfony/proyecto_1 /tmp/proyectos_symfony/proyecto_2 cp -Rv /ruta/fuente/symfony/* /tmp/symfony/symfony_1_2_8/
Y por último creamos 'proyecto_1':
cd /tmp/proyectos_symfony/proyecto_1 php symfony_1_2_8/data/bin/symfony generate:project proyecto_1
Ha creado la siguiente estructura dentro de '/tmp/proyectos_symfony/proyecto_1':
drwxr-xr-x 2 root root 4096 sep 11 01:47 apps drwxrwxrwx 2 root root 4096 sep 11 01:47 cache drwxr-xr-x 2 root root 4096 sep 11 01:47 config drwxr-xr-x 3 root root 4096 sep 11 01:47 data drwxr-xr-x 2 root root 4096 sep 11 01:47 doc drwxr-xr-x 2 root root 4096 sep 11 01:47 lib drwxrwxrwx 2 root root 4096 sep 11 01:47 log drwxr-xr-x 2 root root 4096 sep 11 01:47 plugins -rwxrwxrwx 1 root root 446 sep 11 01:47 symfony drwxr-xr-x 7 root root 4096 sep 11 01:45 symfony_1_2_8 drwxr-xr-x 5 root root 4096 sep 11 01:47 test drwxr-xr-x 6 root root 4096 sep 11 01:47 web
Para conocer donde está el 'core' de symfony de ese proyecto, una vez en el directorio del proyecto:
php symfony -V symfony version 1.2.8 (/tmp/proyectos_symfony/proyecto_1/symfony_1_2_8/lib)
Crear frontend
Nos aseguramos estar dentro de '/tmp/proyectos_symfony/proyecto_1':
cd /tmp/proyectos_symfony/proyecto_1 php symfony generate:app --escaping-strategy=on --csrf-secret=UniqueSecret frontend
Esto ha creado la siguiente estructura, dentro de '/tmp/proyectos_symfony/proyecto_1/apps/frontend/':
drwxr-xr-x 2 root root 4096 sep 14 10:37 config drwxr-xr-x 2 root root 4096 sep 14 10:37 i18n drwxr-xr-x 2 root root 4096 sep 14 10:37 lib drwxr-xr-x 2 root root 4096 sep 14 10:37 modules drwxr-xr-x 2 root root 4096 sep 14 10:37 templates
Crear módulo
Nos aseguramos estar dentro de '/tmp/proyectos_symfony/proyecto_1':
cd /tmp/proyectos_symfony/proyecto_1 php symfony generate:module frontend modulo_1
Esto ha creado la siguiente estructura, dentro de '/tmp/proyectos_symfony/proyecto_1/apps/frontend/modules/modulo_1':
drwxr-xr-x 2 javi javi 4096 oct 6 18:31 actions drwxr-xr-x 2 javi javi 4096 oct 6 18:31 templates
Archivo | Url |
---|---|
/tmp/proyectos_symfony/proyecto_1/apps/frontend/modules/modulo_1/actions | http://localhost/proyecto_1/web/frontend_dev.php/modulo_1/index |
Permisos
cd /tmp/proyectos_symfony/proyecto_1 chmod 777 cache/ log/
Si se usa subversion conviene eludir estos directorios a la hora de exportar:
Ruta
Si en lugar de dejarse el núcleo de symfony en un directorio 'estático' se hubiese incluido dentro del proyecto, para mover el proyecto a cualquier otro directorio:
1. Editar el fichero:
nano /tmp/proyectos_symfony/proyecto_1/config/ProjectConfiguration.class.php
2. Dejar el fichero así (siguiendo el ejemplo):
require_once dirname(__FILE__).'/../symfony_1_2_8/lib/autoload/sfCoreAutoload.class.php'; sfCoreAutoload::register(); class ProjectConfiguration extends sfProjectConfiguration { public function setup() { // for compatibility / remove and enable only the plugins you want $this->enableAllPluginsExcept(array('sfDoctrinePlugin', 'sfCompat10Plugin')); } }
3. Crear un enlace simbólico:
cd /tmp/proyectos_symfony/proyecto_1/web ln -s ../symfony_1_2_8/data/web/sf/ .
De esta forma hemos optado por la instalación individual de symfony: moviendo el directorio '/tmp/proyectos_symfony/proyecto_1' tenemos nuestro proyecto entero, al coste de que pesa, vacío, 25 Mb
Configurar la bd
En este ejemplo vamos a usar mysql. Desde dentro del proyecto:
ORM | SGBD | Comando |
---|---|---|
Propel | MySQL | php symfony configure:database "mysql:host=localhost;dbname=blog" root mYsEcret |
Propel | Sqlite | php symfony configure:database "sqlite://%SF_DATA_DIR%/sqlite/bd_sqlite.bd" |
Doctrine | MySQL | php symfony configure:database --name=doctrine --class=sfDoctrineDatabase "mysql:host=localhost;dbname=jobeet" root mYsEcret |
Doctrine | Sqlite | php symfony configure:database --name=doctrine --class=sfDoctrineDatabase "sqlite:///%SF_DATA_DIR%/sqlite/bd_sqlite.bd" |
Configurar el servidor de páginas web
Ejemplo de configuración para este ejemplo en el caso de que optemos por una instalación compartida de symfony, que no es el caso:
<VirtualHost *> ServerName mi_pagina.mine.nu DocumentRoot /tmp/proyectos_symfony/proyecto_1/web <Directory "/tmp/proyectos_symfony/proyecto_1/web"> AllowOverride All Allow from All </Directory> Alias /sf /tmp/symfony/data/web/sf <Directory "/tmp/symfony/data/web/sf"> AllowOverride All Allow from All </Directory> </VirtualHost>
Ahora la web (ojo, con estilos e imagen de fondo) debería verse desde:
http://mi_pagina.mine.nu
Teóricamente también deberíamos ver el frontend en desarrollo:
http://mi_pagina.mine.nu/frontend_dev.php
Pero a mí me sale el siguiente mensaje:
You are not allowed to access this file. Check frontend_dev.php for more information.
El motivo es que por defecto está configurado para que solo se pueda acceder a ese entorno desde la misma máquina (127.0.0.1) que aloja symfony. Para hacer accesible ese entorno a cualquiera:
1. Editar el fichero:
nano /tmp/proyectos_symfony/proyecto_1/web/frontend_dev.php
2. Y dejarlo tal que así:
<?php // this check prevents access to debug front controllers that are deployed by accident to production servers. // feel free to remove this, extend it or make something more sophisticated. /* if (!in_array(@$_SERVER['REMOTE_ADDR'], array('127.0.0.1', '::1'))) { die('You are not allowed to access this file. Check '.basename(__FILE__).' for more information.'); } */ require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php'); $configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'dev', true); sfContext::createInstance($configuration)->dispatch();
Diagramas
Flujo de trabajo de symfony
Organización de código
Directorios
apps/ frontend/ backend/ cache/ config/ data/ sql/ doc/ lib/ model/ log/ plugins/ test/ bootstrap/ unit/ functional/ web/ css/ images/ js/ uploads/
Directorio 'Applications'
apps/ [application name]/ modules/ [module name]/ actions/ actions.class.php config/ lib/ templates/ indexSuccess.php
Capa vista
Conceptos
Parameter holder | $request->getParameterHolder()->set('foo', 'bar'); echo $request->getParameterHolder()->get('foo'); // The 'foobar' parameter is not defined, so the getter returns an empty value echo $request->getParameter('foobar'); => null // A default value can be used by putting the getter in a condition if ($request->hasParameter('foobar')) { echo $request->getParameter('foobar'); } else { echo 'default'; } => default // But it is much faster to use the second getter argument for that echo $request->getParameter('foobar', 'default'); => default // Uso de namespaces $user->setAttribute('foo', 'bar1'); $user->setAttribute('foo', 'bar2', 'my/name/space'); echo $user->getAttribute('foo'); => 'bar1' echo $user->getAttribute('foo', null, 'my/name/space'); => 'bar2' // Añadir Parameter Holder a una clase class MyClass { protected $parameterHolder = null; public function initialize($parameters = array()) { $this->parameterHolder = new sfParameterHolder(); $this->parameterHolder->add($parameters); } public function getParameterHolder() { return $this->parameterHolder; } } |
Constantes | // Instead of PHP constants, define('FOO', 'bar'); echo FOO; // symfony uses the sfConfig object sfConfig::set('foo', 'bar'); echo sfConfig::get('foo'); |
Autocarga de clases | $myObject = new MyClass(); |
Helper | Listing 4-11 - The link_to(), and url_for() Helpers <p>Hello, world!</p> <?php if ($hour >= 18): ?> <p>Or should I say good evening? It is already <?php echo $hour ?>.</p> <?php endif; ?> <form method="post" action="<?php echo url_for('content/update') ?>"> <label for="name">What is your name?</label> <input type="text" name="name" id="name" value="" /> <input type="submit" value="Ok" /> <?php echo link_to('I never say my name','content/update?name=anonymous') ?> </form> |
Partial | |
Componente | |
Slot | |
Component slot | http://www.symfony-project.org/book/1_2/07-Inside-the-View-Layer |
Comandos
NOTA supongo que antes de lanzar cada comando, es conveniente limpiar la caché (teóricamente en entornos 'dev'… la caché se genera cada vez)
- Limpiar la caché
php symfony cc * Configurar la conexión a la base de datos con PROPEL**
# Ejemplo MySQL php symfony configure:database "mysql:host=localhost;dbname=blog" root mYsEcret
# Ejemplo Sqlite php symfony configure:database "sqlite://%SF_DATA_DIR%/sqlite/bd_sqlite.bd"
O editar el archivo:
/mi_proyecto/config/databases.yml
- Crear el modelo de objetos con PROPEL
php symfony propel:build-model
Luego hay que limpiar la caché:
php symfony cache:clear
- Crear el diagrama entidad-relación a partir del modelo .yml con PROPEL
php symfony propel:build-sql
Genera el siguiente archivo:
/mi_proyecto/data/sql/lib.model.sql
- Habilitar los activos (assets) asociados a un plugin que acaba de ser habilitado
php symfony plugin:publish-assets
- Crear la bd a partir del archivo '/mi_proyecto/data/sql/schema.sql' con PROPEL
php symfony propel:insert-sql –no-confirmation
NOTA Todo lo marcado con 'con PROPEL' se puede hacer con doctrine sustituyendo en el comando 'propel' por 'doctrine' y viceversa
- Cargar los datos a partir de '/mi_proyecto/data/fixtures/'fixtures con DOCTRINE
php symfony doctrine:data-load
TODOS los comandos anteriores para PROPEL
php symfony doctrine:build-all-reload --no-confirmation php symfony cc
- Listar rutas de una aplicación
php symfony app:routes frontend
- Mostrar el detalle de una ruta
php symfony app:routes frontend job_edit
Errores
Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 71 bytes) in /mnt/disco_1/datos/www/privado/inmobiliaria2/symfony_1_2_8/lib/plugins/sfPropelPlugin/lib/vendor/phing/util/StringHelper.php on line 136
Al intentar crear el modelo de datos:
php symfony propel:build-all
Aparece el siguiente error:
Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 71 bytes) in /mnt/disco_1/datos/www/privado/inmobiliaria2/symfony_1_2_8/lib/plugins/sfPropelPlugin/lib/vendor/phing/util/StringHelper.php on line 136
SOLUCION
1) (Todo como root) Incrementar el máximo de memoria RAM que puede consumir un guión php (es decir, ejecutado desde su CLI):
nano /etc/php5/cli/php.ini
Y dejar el siguiente valor, por ejemplo, así:
memory_limit = 40M ; Maximum amount of memory a script may consume (32MB)
2) Grabar y salir
SQLSTATE[HY000]: General error: 26 file is encrypted or is not a database
El problema es que la bd sqlite se creó con sqlite 2.x. Para comprobar que es así:
cat /mi_proyecto/data/mi_bd_sqlite.db
La primera linea tiene que ser parecida a:
This file contains an SQLite 2.1 database...
Solución
1. (Como root) Instalar sqlite3:
aptitude update && aptitude install sqlite3
2. Volver a crear la bd con el comando:
sqlite3 /mi_proyecto/data/mi_bd_sqlite.db
3. (No sé si es necesario si la ubicación y nombre de la bd es exacta) Volver a crear el modelo… (desde la raíz del proyecto):
php symfony cache:clear php symfony configure:database "sqlite://%SF_DATA_DIR%/mi_bd_sqlite.db" php symfony propel:build-model
Fatal error: Class 'sfProjectConfiguration' not found in /mnt/disco_1/datos/proyectos_symfony/jobeet/config/ProjectConfiguration.class.php on line 10
En el archivo '/mi_proyecto/config/ProjectConfiguration.class.php':
- No se ha modificado la ruta para que incluya el archivo correctamente
- Se ha comentado por error la linea 'sfCoreAutoload::register();'
Fatal error: Class 'sfPropelDatabase' not found in /mnt/disco_1/datos/proyectos_symfony/jobeet/cache/frontend/dev/config/config_databases.yml.php on line 6
Se está intentando crear las sentencias sql con doctrine:
php symfony doctrine:build-sql
Pero en el archivo de configuración:
/mi_proyecto/apps/frontend/config/databases.yml
Todavía quedan residuos de propel que hay que eliminar:
dev: propel: param: classname: DebugPDO test: propel: param: classname: DebugPDO all: propel: class: sfPropelDatabase param: classname: PropelPDO dsn: 'mysql:dbname=jobeet;host=localhost' username: root password: null encoding: utf8 persistent: true pooling: true doctrine: class: sfDoctrineDatabase param: dsn: 'sqlite:///%SF_DATA_DIR%/sqlite/bd_sqlite.bd' username: root password: null
Debería quedar tal que así:
all: doctrine: class: sfDoctrineDatabase param: dsn: 'sqlite:///%SF_DATA_DIR%/sqlite/bd_sqlite.bd' username: root password: null