Cómo actualizar las aplicaciones de Symfony 2.0 a Symfony 2.1

Actualizar a Symfony 2.1 una aplicación desarollada con Symfony 2.0 es un proceso necesariamente manual y que requiere de varios pasos que debes realizar en el orden adecuado.

El único recurso oficial que existe sobre la actualización es el famoso documento UPGRADE que explica detalladamente todos los cambios que tienes que hacer en el código de una aplicación Symfony 2.0 para que funcione en Symfony 2.1.

El problema es que actualizar la aplicación tal y como explica ese documento no es suficiente, ya que por ejemplo no menciona cómo pasar del viejo archivo deps al archivo composer.json que utiliza Symfony 2.1 para gestionar los vendors.

Recientemente hemos actualizado a Symfony 2.1 la aplicación Cupon que se programó para Symfony 2.0. Este artículo es el resumen de todo lo aprendido sobre cómo actualizar una aplicación Symfony 2.0 a 2.1. Aunque este artículo sea un poco largo, no te asustes porque el proceso de actualización no es tan complicado.

Primeros pasos

Si utilizas Git para controlar el código de tu aplicación, asegúrate de estar en la rama de la versión 2.0 y crea a partir de ella la nueva rama para Symfony 2.1. En nuestro caso, esto fue tan sencillo como ejecutar el siguiente comando:

$ git checkout -b 2.1

Convertir el archivo deps en composer.json

Symfony incluye decenas de librerías y herramientas creadas por terceros. Para gestionar esas dependencias, Symfony 2.0 utiliza un sistema propio basado en un archivo de configuración llamado deps. Por su parte, Symfony 2.1 emplea una solución mucho más sencilla, completa y profesional basada en la herramienta Composer.

Lamentablemente no existe una utilidad para crear automáticamente el archivo de configuración composer.json a partir del archivo deps. Así que crea un nuevo archivo llamado composer.json en la raíz de tu proyecto con el siguiente contenido:

{
"name": "javiereguiluz/cupon",
"description": "Aplicación de prueba para aprender a programar con Symfony 2.1",
"autoload": {
    "psr-0": { "": "src/" }
},
"require": {
    "symfony/framework-standard-edition": "2.1.*"
},
"scripts": {
    "post-install-cmd": [
       "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
       "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
       "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
       "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile"
    ],
    "post-update-cmd": [
       "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
       "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
       "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
       "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile"
    ]
},
"minimum-stability": "dev"
}

Cambia el valor de las opciones name y description por algo más adecuado para tu proyecto. Las demás partes no las modifiques, por muy raras que te parezcan. La opción scripts es la que hace que después de cada actualización de Symfony2 se regenere el archivo bootstrap.php, se borre la caché y se generen todos los assets (archivos CSS y JavaScript).

La opción require es la más importante, ya que indica qué paquetes deben instalarse en tu aplicación:

"require": {
    "symfony/framework-standard-edition": "2.1.*"
},

La configuración anterior hace que Symfony siempre se actualice a la versión más reciente que exista en la rama 2.1. Si eres un programador precavido, quizás prefieras indicar una versión exacta de Symfony 2.1:

"require": {
    "symfony/framework-standard-edition": "2.1.1"
},

De esta manera, cuando salga una nueva versión de Symfony 2.1 podrás probarla tranquilamente en tu entorno de desarrollo sabiendo que todavía no se ha actualizado en la aplicación de producción

Declarar las dependencias propias

Tu aplicación seguramente utiliza varios bundles de Symfony2 desarrollados por terceros y otras librerías y herramientas no incluidas en Symfony2. En la aplicación Cupon por ejemplo se utilizan los fixtures de Doctrine (a través de DoctrineFixturesBundle) y un paginador (a través de IdeupSimplePaginatorBundle). Por eso el archivo deps incluye estas dependencias propias:

[doctrine-fixtures]
    git=http://github.com/doctrine/data-fixtures.git
 
[DoctrineFixturesBundle]
    git=http://github.com/doctrine/DoctrineFixturesBundle.git
    target=/bundles/Symfony/Bundle/DoctrineFixturesBundle
    version=origin/2.0
 
[doctrine-extensions]
    git=https://github.com/beberlei/DoctrineExtensions.git
 
[IdeupSimplePaginatorBundle]
    git=https://github.com/javiacei/IdeupSimplePaginatorBundle
    target=/bundles/Ideup/SimplePaginatorBundle

Lo que debes hacer es transformar esas dependencias en requerimientos de Composer. El truco para hacerlo está en que el archivo deps es tan torpe que hay que indicar una por una todas las dependencias de cada bundle. Sin embargo, Composer es tan avanzado que sólo tienes que decirle el bundle que quieres instalar y el se encarga de instalar también todas las librerías y dependencias que necesite.

Así que la configuración anterior se transforma en lo siguiente:

"require": {
    "symfony/framework-standard-edition": "2.1.*",
    "doctrine/doctrine-fixtures-bundle": "dev-master",
    "ideup/simple-paginator-bundle": "dev-master"
},

Para encontrar el nombre completo de la dependencia que corresponde a cada bundle, puedes acceder a la sección de bundles de este sitio y buscar el bundle que utilizas. Después, sólo tienes que consultar las instrucciones de instalación en Symfony 2.1 o directamente el valor de la opción name de su archivo composer.json.

Después de este proceso, el archivo composer.json completo resultante es el siguiente:

{
"name": "javiereguiluz/cupon",
"description": "Aplicación de prueba para aprender a programar con Symfony 2.1",
"autoload": {
    "psr-0": { "": "src/" }
},
"require": {
    "symfony/framework-standard-edition": "2.1.*",
    "doctrine/doctrine-fixtures-bundle": "dev-master",
    "ideup/simple-paginator-bundle": "dev-master"
},
"scripts": {
    "post-install-cmd": [
       "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
       "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
       "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
       "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile"
    ],
    "post-update-cmd": [
       "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
       "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
       "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
       "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile"
    ]
},
"minimum-stability": "dev"
}

Instalación de los nuevos vendors

El siguiente paso consiste en borrar el actual directorio vendor/ de tu proyecto. Borra el directorio entero, no sólo sus contenidos. Y no te preocupes por perder cosas, ya que en el directorio vendor/ no deberías haber hecho ningún cambio local ni deberías haber añadido ningún código.

Después, instala los nuevos vendors ejecutando el siguiente comando desde el directorio raíz de tu proyecto:

En Windows:

> php composer.phar install

En Linux y Mac OS X:

$ composer install

Para que los comandos anteriores te funcionen bien, Composer debe estar instalado globalmente en tu ordenador. Consulta la guía de instalación de Composer para saber cómo hacerlo.

Actualizar los archivos de Symfony 2.1

En el directorio app/ de tu aplicación existen varios archivos importantes de Symfony2. Seguramente el más importante es app/autoload.php, que hace que las clases PHP se carguen de forma automática.

En Symfony 2.0 este archivo tenía decenas de líneas. En Symfony 2.1 simplemente tiene los siguientes contenidos:

use Doctrine\Common\Annotations\AnnotationRegistry;
 
$loader = require __DIR__.'/../vendor/autoload.php';
 
// intl
if (!function_exists('intl_get_error_code')) {
    require_once __DIR__.'/../vendor/symfony/symfony/src/Symfony/Component/Locale/Resources/stubs/functions.php';
 
    $loader->add('', __DIR__.'/../vendor/symfony/symfony/src/Symfony/Component/Locale/Resources/stubs');
}
 
AnnotationRegistry::registerLoader(array($loader, 'loadClass'));
 
return $loader;

A continuación, te indicamos la lista de archivos que debes actualizar y un enlace a los nuevos contenidos de cada uno:

Obviamente, antes de machacar el contenido de tus archivos piensa si has hecho algún cambio en ellos (sobre todo en app.php y app_dev.php). El archivo app/autoload.php seguramente contiene modificaciones tuyas, pero puedes borrarlas con seguridad, ya que Composer hace que no sea necesario registrar los namespaces a mano.

Mención aparte merece el archivo app/AppKernel.php. Symfony2 no realiza ningún cambio en este archivo, pero puede ser necesario que tu realices algunas modificaciones. Concretamente, algún bundle puede dejar de funcionar porque su namespace ha cambiado para Symfony 2.1 y ya no se puede registrar de la misma manera que en Symfony 2.0. El ejemplo más claro son los fixtures de Doctrine2:

Registrar el bundle de fixtures en Symfony 2.0:

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            // ...
            new Symfony\Bundle\DoctrineFixturesBundle\DoctrineFixturesBundle(),
        );
 
        // ...
    }
}

Registrar el bundle de fixtures en Symfony 2.1:

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            // ...
            new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(),
        );
 
        // ...
    }
}

Nuestro consejo es que no pierdas el tiempo comprobando si los bundles han cambiado sus namespaces. Ejecuta la aplicación y si ves algún mensaje de error que indica que no se ha podido encontrar o registrar algún bundle, actualiza su namespace. Para encontrar el namespace correcto en Symfony 2.1, puedes consultar de nuevo la sección de los bundles de este sitio.

Actualizar las opciones de configuración

El cambio más importante relacionado con la configuración es que en Symfony 2.1 el locale ya no depende de la sesión sino de la petición. Además, la opción auto_start de las sesiones ya no existe porque se gestiona automáticamente:

Configuración original de 2.0:

# app/config/config.yml
framework:
    esi:             { enabled: true }
    translator:      { fallback: es }
    secret:          %secret%
    router:          { resource: "%kernel.root_dir%/config/routing.yml" }
    form:            true
    csrf_protection: true
    validation:      { enable_annotations: true }
    templating:      { engines: ['twig'] }
    session:
        default_locale: %locale%
        auto_start:     true

Nueva configuración de 2.1:

# app/config/config.yml
framework:
    default_locale:  %locale%
    esi:             { enabled: true }
    translator:      { fallback: es }
    secret:          %secret%
    router:          { resource: "%kernel.root_dir%/config/routing.yml" }
    form:            true
    csrf_protection: true
    validation:      { enable_annotations: true }
    templating:      { engines: ['twig'] }
    session:         ~

El otro cambio necesario consiste en eliminar la opción secure_controllers del servicio jms_security_extra:

Configuración original de 2.0:

# app/config/config.yml
jms_security_extra:
    secure_controllers:  false
    secure_all_services: false

Nueva configuración de 2.1:

# app/config/config.yml
jms_security_extra:
    secure_all_services: false

Si después de estos cambios se sigue produciendo algún error, busca en el documento UPGRADE la opción que te está causando problemas y consulta también la documentación de todos los bundles que utilices.

Actualizar las entidades

Los dos componentes de Symfony 2.1 que más cambios han sufrido desde la versión 2.0 son los formularios y el validador. Como las entidades de Doctrine casi siempre incluyen información de validación, comprueba si utilizas alguna de las siguientes validaciones que han cambiado:

Validación 2.0                      Validación 2.1
---------------------------------   ----------------------------------
`@Assert\Size(min = 2, max = 16)`   `@Assert\Range(min = 2, max = 16)`
`@Assert\Min(2)`                    `@Assert\Range(min = 2)`
`@Assert\Max(2)`                    `@Assert\Range(max = 2)`
`@Assert\MinLength(8)`              `@Assert\Length(min = 8)`
`@Assert\MaxLength(8)`              `@Assert\Length(max = 8)`

Si la entidad representa a un usuario, seguramente emplearás la interfaz UserInterface para integrarla fácilmente con el componente de seguridad de Symfony2. Esta interfaz se ha simplificado en Symfony 2.1, por lo que puedes eliminar el siguiente método equals():

public function equals(UserInterface $usuario)
{
    return $this->getEmail() == $usuario->getEmail();
}

Actualizar los formularios

Si en tu aplicación haces un uso muy avanzado de los formularios, vas a tener que dedicar mucho tiempo a su actualización a Symfony 2.1. Además, es posible que algunos cambios ni siquiera estén bien documentados en el documento UPGRADE.

Por el contrario, si haces un uso normal de los formularios de Symfony 2.0, no te costará apenas esfuerzo actualizarlos a Symfony 2.1. El primer cambio importante se refiere a los argumentos del método buildForm():

Formularios Symfony 2.0:

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
 
class UsuarioType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        // ...
    }
}

Formularios Symfony 2.1:

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
 
class UsuarioType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // ...
    }
}

Si el formulario está relacionado con una entidad de Doctrine, ahora es obligatorio definir la opción data_class que indica el namespace del objeto que se manipula a través del formulario:

// ...
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
 
class UsuarioType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // ...
    }
 
    public function getName()
    {
        // ...
    }
 
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Cupon\UsuarioBundle\Entity\Usuario',
        ));
    }
}

Por último, también ha cambiado el método más importante de los que se utilizan al procesar el formulario en un controlador: bindRequest() ahora se llama bind():

Formularios Symfony 2.0:

class DefaultController extends Controller
{
    public function registroAction()
    {
        // ...
 
        if ($peticion->getMethod() == 'POST') {
            $formulario->bindRequest($this->getRequest());
 
            // ...
        }
 
        // ...
    }
}

Formularios Symfony 2.1:

class DefaultController extends Controller
{
    public function registroAction()
    {
        // ...
 
        if ($peticion->getMethod() == 'POST') {
            $formulario->bind($this->getRequest());
 
            // ...
        }
 
        // ...
    }
}

Para todos los demás cambios relacionados con los formularios, no olvides consultar el documento UPGRADE.

Actualizar los tests

Los tests unitarios y funcionales no han sufrido ningún cambio, pero lógicamente deben adaptarse a los cambios del resto de componentes de Symfony2. Si por ejemplo tus tests hacen uso del validador, ha cambiado la forma de obtenerlo:

Obtener el validador en los tests de Symfony 2.0:

use Symfony\Component\Validator\ValidatorFactory;
 
$this->validator = ValidatorFactory::buildDefault()
    ->getValidator();

Obtener el validador en los tests de Symfony 2.1:

use Symfony\Component\Validator\Validation;
 
$this->validator = Validation::createValidatorBuilder()
    ->enableAnnotationMapping()
    ->getValidator();

Lamentablemente no es posible crear una guía de actualización de los tests, así que tendrás que ir corrigiendo poco a poco todos los errores que se muestren al ejecutar phpunit -c app hasta que por fin veas la barra verde que significa que ya no hay errores.

Otros cambios

Los cambios explicados en este artículo se pueden considerar como obligatorios ya que es casi seguro que todas las aplicaciones Symfony 2.0 deben hacerlos. No obstante, en tu aplicación seguramente tendrás que hacer algunos cambios más. Para ello, no olvides tener siempre a mano el documento UPGRADE.

Compartir en

¿Has visto algún error?

Avísanos en [email protected] para que podamos corregirlo. Gracias.

Proyectos Symfony destacados

La forma más sencilla de generar el backend de tus aplicaciones Symfony. Ver más

Síguenos en @symfony_es para acceder a las últimas noticias.