Aprende Symfony2 (Parte 3): Bundles

2 de octubre de 2014

Este es el tercer artículo de la serie para aprender sobre el framework Symfony2. En los anteriores artículos empezamos creando nuestro proyecto vacío con los siguientes archivos:

.
├── app
│   ├── AppKernel.php
│   ├── cache
│   │   └── .gitkeep
│   ├── config
│   │   └── config.yml
│   └── logs
│       └── .gitkeep
├── composer.json
├── composer.lock
├── .gitignore
└── web
    └── app.php

Ejecutar el comando composer install debería crear el directorio vendor/, que hemos ignorado en Git. Si lo necesitas, echa un vistazo al repositorio de código público en el que estamos desarrollando la aplicación. Vamos a ver qué es un bundle.

Creando el bundle de la aplicación

Para que el código que mostremos tenga algún sentido, vamos a desarrollar una aplicación imaginaria de bolsa que muestre la cotización de algunas empresas. Vamos a definir el primer bundle de nuestra aplicación, para tener así un lugar en el que poner nuestro código. Para ello necesitamos crear su directorio:

$ mkdir -p src/AppBundle

Después, crea una clase llamada AppBundle que extienda de Symfony\Component\HttpKernel\Bundle\Bundle y tenga el siguiente contenido:

<?php
// src/AppBundle/AppBundle.php

namespace AppBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class AppBundle extends Bundle
{
    // ...
}

Finalmente registramos el bundle en nuestra aplicación:

<?php
// app/AppKernel.php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        return array(
            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
            new AppBundle\AppBundle(), // <-- ¡Añade esta línea!
        );
    }

    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        $loader->load(__DIR__.'/config/config.yml');
    }
}

Hagamos un commit al repositorio:

$ git add -A
$ git commit -m 'Creado el bundle de la aplicación'

Los Bundles te permiten extender el contenedor de servicios de la aplicación

La clase AppBundle creada anteriormente extiende de la siguiente otra clase (sólo se muestran aquellas partes de código relevantes para este tutorial):

<?php

namespace Symfony\Component\HttpKernel\Bundle;

use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\Console\Application;

abstract class Bundle extends ContainerAware implements BundleInterface
{
    public function getContainerExtension();
    public function registerCommands(Application $application);
}

Estos dos métodos hacen que el bundle sea capaz de autodescubrir sus comandos y su extensión del Contenedor de Inyección de Dependencias (DIC), siempre que se utilice la siguiente estructura de directorios (el único archivo obligatorio es AppBundle.php):

.
├── Command
│   └── *Command.php
├── DependencyInjection
│   └── AppExtension.php
└── AppBundle.php

El nombre de un bundle (en nuestro ejemplo AppBundle) se puede componer de tres partes:

  • el nombre del vendor, que normalmente es el nombre de la empresa o persona que ha desarrollado el bundle (ejemplo: AcmeAppBundle). A veces se utiliza el nombre del proyecto (ejemplo: ProyectoDemoBundle).
  • el nombre real del bundle, que en nuestro caso es simplemente App.
  • el sufijo Bundle, que es obligatorio para todos los bundles.

Por tu bien, elige una palabra corta como nombre del vendor y del bundle, ya que así se simplificará mucho el código de tu aplicación.

La clase AppExtension te permite manipular el contenedor de servicios. Esto normalmente se hace cargando un archivo de configuración localizado en Resources/config/services.xml.

Y precisamente ese es el propósito de los bundles: registrar servicios en el contenedor de servicios de la aplicación.

NOTA Los servicios, la inyección de dependencias y los comandos de Symfony no se explican con detalle en esta serie de tutoriales por ser conceptos demasiado avanzados. Si quieres profundizar en ellos, consulta la documentación de Symfony.

Tipos de bundles

Hay dos tipos de bundles:

  • de integración, que permite utilizar librerías de terceros en tu aplicación. Son reusables y se comparten entre distintas aplicaciones.
  • de aplicación, que no se reutilizan y son totalmente específicos para tu lógica de negocio.

Considera por ejemplo la librería snappy de KnpLabs que permite generar un PDF a partir de una página HTML y puede ser usado en cualquier aplicación (aplicaciones que no utilizan Symfony e incluso aplicaciones sin framework).

La clase que permite esta generación de PDFs es Knp\Bundle\SnappyBundle\Snappy\LoggableGenerator y crearla cuesta bastante. Para que sea más fácil de utilizar, podemos definir su construcción en el contenedor de servicios. Afortunadamente ya hay un bundle que hace esto por nosotros: KnpSnappyBundle.

Ahí tenemos un buen ejemplo de los bundles del primer tipo.

Sobre el segundo tipo de bundle: antes o después, necesitaremos integrar nuestro propio código en nuestra aplicación Symfony2. Podríamos elegir la opción difícil y tediosa escribiendo un montón de inicializaciones y configuraciones, ¡o podríamos usar un bundle que haga el trabajo por nosotros automáticamente!

A veces, encontraremos aplicaciones que tienen muchos bundles, para así poder categorizar las funcionalidades en módulos. Esto no es necesario, y es un poco tedioso, si se me permite: podemos simplemente crear carpetas en un único bundle para categorizar nuestros módulos.

La creación de varios bundles nencesita algunos pasos manuales adicionales. Además no tiene mucho sentido, ya que un bundle es, teóricamente, una unidad desacoplada: si creamos un UserBundle, un FrontendBundle, un BlogBundle y un ForumBundle, nos encontraremos con bundles dependientes unos de otros, a menudo con dependencias cíclicas, y perderemos el tiempo decidiendo dónde poner nuevas clases (que puede que necesiten a los otros tres bundles).

Mi consejo: crea un único bundle para tu aplicación. Si más tarde descubres que has creado un conjunto de clases que tendría sentido reusar en otros proyectos (sean proyectos Symfony2 o no), entonces quizá puedas extraerlas para crear una librería de terceros. Y después podrás finalmente crear un bundle para integrar esa librería en aplicaciones Symfony2.

Conclusión

Los bundles son una manera de extender el Contenedor de Inyección de Dependencias: forman la capa que une tu código y las aplicaciones Symfony2.

Recursos

Aquí os dejo un buen artículo sobre cómo crear bundles reusables:

¿No te gusta seguir las convenciones y no te importa tener que escribir un montón de código de inicialización y configuración? Entonces, aunque no te lo recomiendo, puedes leer estos artículos:

Incluyo estos links únicamente porque me gusta cómo explican el funcionamiento de Symfony2 por dentro, pero no aplicaría lo que dicen en una aplicación real, ya que incluye una complejidad que no compensa (de todos modos, esta es simplemente mi opinión).

Sobre el autor

Este artículo fue publicado originalmente por Loïc Chardonnet y ha sido traducido con permiso por Manuel Gómez.

Artículos de la serie Aprende Symfony