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:

/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

/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:

Igonrar determinados directorios

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)

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
php symfony propel:build-model

Luego hay que limpiar la caché:

php symfony cache:clear
php symfony propel:build-sql

Genera el siguiente archivo:

/mi_proyecto/data/sql/lib.model.sql
php symfony plugin:publish-assets

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

php symfony doctrine:data-load

TODOS los comandos anteriores para PROPEL

php symfony doctrine:build-all-reload --no-confirmation
php symfony cc
php symfony app:routes frontend
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':

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