La arquitectura

¡Eres mi héroe! ¿Quién habría pensado que todavía estarías aquí después de las tres primeras partes? Tu esfuerzo pronto será bien recompensado. En las tres primeras partes no vimos en demasiada profundidad la arquitectura de la plataforma. Porque esta hace que Symfony2 esté al margen de la multitud de plataformas, ahora vamos a profundizar en la arquitectura.

Comprendiendo la estructura de directorios

La estructura de directorios de una aplicación Symfony2 es bastante flexible, pero la estructura de directorios de la distribución de la edición estándar refleja la estructura típica y recomendada de una aplicación Symfony2:

  • app/: Configuración de la aplicación:
  • src/: El código PHP del proyecto;
  • vendor/: Las dependencias de terceros;
  • web/: El directorio raíz del servidor web.

El Directorio web/

El directorio web raíz, es el hogar de todos los archivos públicos y estáticos tales como imágenes, hojas de estilo y archivos JavaScript. También es el lugar donde vive cada controlador frontal:

// web/app.php
require_once __DIR__.'/../app/bootstrap.php.cache';
require_once __DIR__.'/../app/AppKernel.php';

use Symfony\Component\HttpFoundation\Request;

$kernel = new AppKernel('prod', false);
$kernel->loadClassCache();
$kernel->handle(Request::createFromGlobals())->send();

El núcleo requiere en primer lugar el archivo bootstrap.php.cache, el cual arranca la plataforma y registra el cargador automático (ve más abajo).

Al igual que cualquier controlador frontal, app.php utiliza una clase del núcleo, AppKernel, para arrancar la aplicación.

El directorio app/

La clase AppKernel es el punto de entrada principal para la configuración de la aplicación y, como tal, se almacena en el directorio app/.

Esta clase debe implementar dos métodos:

  • registerBundles() debe devolver un arreglo de todos los paquetes necesarios para ejecutar la aplicación;
  • registerContainerConfiguration() carga la configuración de la aplicación (más sobre esto más adelante).

La autocarga es manejada automáticamente vía Composer, ¡lo cual significa que puedes utilizar cualquier clase PHP sin hacer nada en absoluto! Si necesitas más flexibilidad, puedes extender el autocargador en el archivo app/autoload.php. Todas las dependencias están almacenadas bajo el directorio vendor/, pero esto sólo es una convención. Las puedes almacenar en cualquier lugar que gustes, globalmente en tu servidor o localmente en tus proyectos.

Nota

Si quieres aprender más sobre el autocargador de Composer, consulta Composer-Autoloader. Symfony también tiene un componente autoloading — consulta «El componente ClassLoader».

Comprendiendo el sistema de paquetes

Esta sección introduce una de las más importantes y poderosas características de Symfony2, el sistema de paquetes.

Un paquete es un poco como un complemento en otros programas. Así que ¿por qué se llama paquete y no complemento? Esto se debe a que en Symfony2 todo es un paquete, desde las características del núcleo de la plataforma hasta el código que escribes para tu aplicación. Los paquetes son ciudadanos de primera clase en Symfony2. Esto te proporciona la flexibilidad para utilizar las características preconstruidas envasadas en paquetes de terceros o para distribuir tus propios paquetes. Además, facilita la selección y elección de las características por habilitar en tu aplicación y optimizarlas en la forma que desees. Y al final del día, el código de tu aplicación es tan importante como el mismo núcleo de la plataforma.

Registrando un paquete

Una aplicación se compone de paquetes tal como está definido en el método registerBundles() de la clase AppKernel. Cada paquete vive en un directorio que contiene una única clase Paquete que lo describe:

// app/AppKernel.php
public function registerBundles()
{
    $bundles = array(
        new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
        new Symfony\Bundle\SecurityBundle\SecurityBundle(),
        new Symfony\Bundle\TwigBundle\TwigBundle(),
        new Symfony\Bundle\MonologBundle\MonologBundle(),
        new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
        new Symfony\Bundle\DoctrineBundle\DoctrineBundle(),
        new Symfony\Bundle\AsseticBundle\AsseticBundle(),
        new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
        new JMS\SecurityExtraBundle\JMSSecurityExtraBundle(),
    );

    if (in_array($this->getEnvironment(), array('dev', 'test'))) {
        $bundles[] = new Acme\DemoBundle\AcmeDemoBundle();
        $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
        $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
        $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
    }

    return $bundles;
}

Además del AcmeDemoBundle del cual ya te hemos hablado, observa que el núcleo también habilita otros paquetes como FrameworkBundle, DoctrineBundle, SwiftmailerBundle y AsseticBundle. Todos ellos son parte del núcleo de la plataforma.

Configurando un paquete

Cada paquete se puede personalizar a través de archivos de configuración escritos en YAML, XML o PHP. Échale un vistazo a la configuración predeterminada:

# app/config/config.yml
imports:
    - { resource: parameters.yml }
    - { resource: security.yml }

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

# Configuración de Twig
twig:
    debug:            "%kernel.debug%"
    strict_variables: "%kernel.debug%"

# Configuración de Assetic
assetic:
    debug:          "%kernel.debug%"
    use_controller: false
    bundles:        [ ]
    #java: /usr/bin/java
    filters:
        cssrewrite: ~
        #closure:
        #    jar: "%kernel.root_dir%/Resources/java/compiler.jar"
        #yui_css:
        #    jar: "%kernel.root_dir%/Resources/java/yuicompressor-2.4.7.jar"

# Configuración de Doctrine
doctrine:
    dbal:
        driver:   "%database_driver%"
        host:     "%database_host%"
        port:     "%database_port%"
        dbname:   "%database_name%"
        user:     "%database_user%"
        password: "%database_password%"
        charset:  UTF8

    orm:
        auto_generate_proxy_classes: "%kernel.debug%"
        auto_mapping: true

# Configuración de Swiftmailer
swiftmailer:
    transport: "%mailer_transport%"
    host:      "%mailer_host%"
    username:  "%mailer_user%"
    password:  "%mailer_password%"
    spool:     { type: memory }

Cada entrada —como framework— define la configuración de un paquete específico. Por ejemplo, framework configura el FrameworkBundle mientras que swiftmailer configura el SwiftmailerBundle.

Cada entorno puede reemplazar la configuración predeterminada proporcionando un archivo de configuración específico. Por ejemplo, el entorno dev carga el archivo config_dev.yml, el cual carga la configuración principal (es decir, config.yml) y luego la modifica agregando algunas herramientas de depuración:

# app/config/config_dev.yml
imports:
    - { resource: config.yml }

framework:
    router:   { resource: "%kernel.root_dir%/config/routing_dev.yml" }
    profiler: { only_exceptions: false }

web_profiler:
    toolbar: true
    intercept_redirects: false

monolog:
    handlers:
        main:
            type:  stream
            path:  "%kernel.logs_dir%/%kernel.environment%.log"
            level: debug
        firephp:
            type:  firephp
            level: info

assetic:
    use_controller: true

Extendiendo un paquete

Además de ser una buena manera de organizar y configurar tu código, un paquete puede extender otro paquete. La herencia de paquetes te permite sustituir cualquier paquete existente con el fin de personalizar sus controladores, plantillas, o cualquiera de sus archivos. Aquí es donde son útiles los nombres lógicos (por ejemplo, @AcmeDemoBundle/Controller/SecuredController.php): estos abstraen en dónde se almacena realmente el recurso.

Nombres lógicos de archivo

Cuando quieras hacer referencia a un archivo de un paquete, utiliza esta notación: @NOMBRE_PAQUETE/ruta/al/archivo; Symfony2 resolverá @NOMBRE_PAQUETE a la ruta real del paquete. Por ejemplo, la ruta lógica @AcmeDemoBundle/Controller/DemoController.php se convierte en src/Acme/DemoBundle/Controller/DemoController.php, ya que Symfony conoce la ubicación del AcmeDemoBundle .

Nombres lógicos de Controlador

Para los controladores, necesitas hacer referencia a los nombres de método usando el formato NOMBRE_PAQUETE:NOMBRE_CONTROLADOR:NOMBRE_ACCIÓN. Por ejemplo, AcmeDemoBundle:Welcome:index representa al método indexAction de la clase Acme\DemoBundle\Controller\WelcomeController.

Nombres lógicos de plantilla

Para las plantillas, el nombre lógico AcmeDemoBundle:Welcome:index.html.twig se convierte en la ruta del archivo src/Acme/DemoBundle/Resources/views/Welcome/index.html.twig. Incluso las plantillas son más interesantes cuando te das cuenta que no es necesario almacenarlas en el sistema de archivos. Puedes guardarlas fácilmente en una tabla de la base de datos, por ejemplo.

Extendiendo paquetes

Si sigues estas convenciones, entonces puedes utilizar herencia de paquetes para «redefinir» archivos, controladores o plantillas. Por ejemplo, puedes crear un paquete —AcmeNewBundle— y especificar que este sustituye a AcmeDemoBundle. Cuando Symfony carga el controlador AcmeDemoBundle:Welcome:index, primero busca la clase WelcomeController en AcmeNewBundle y luego mirará en AcmeDemoBundle. Esto significa que, ¡un paquete puede redefinir casi cualquier parte de otro paquete!

¿Entiendes ahora por qué Symfony2 es tan flexible? Comparte tus paquetes entre aplicaciones, guárdalas local o globalmente, tú eliges.

Usando vendors

Lo más probable es que tu aplicación dependerá de bibliotecas de terceros. Estas se deberían guardar en el directorio vendor/. Este directorio ya contiene las bibliotecas Symfony2, la biblioteca SwiftMailer, el ORM de Doctrine, el sistema de plantillas Twig y algunas otras bibliotecas y paquetes de terceros.

Comprendiendo la caché y los registros

Symfony2 probablemente es una de las plataformas más rápidas hoy día. Pero ¿cómo puede ser tan rápida si analiza e interpreta decenas de archivos YAML y XML por cada petición? La velocidad, en parte, se debe a su sistema de caché. La configuración de la aplicación sólo se analiza en la primer petición y luego se compila hasta código PHP simple y se guarda en el directorio app/cache/. En el entorno de desarrollo, Symfony2 es lo suficientemente inteligente como para vaciar la caché cuando cambias un archivo. Pero en el entorno de producción, es tu responsabilidad borrar la caché cuando actualizas o cambias tu código o configuración.

Al desarrollar una aplicación web, las cosas pueden salir mal de muchas formas. Los archivos de registro en el directorio app/logs/ dicen todo acerca de las peticiones y ayudan a solucionar rápidamente el problema.

Usando la interfaz de línea de ordenes

Cada aplicación incluye una herramienta de interfaz de línea de ordenes (app/console) que te ayuda a mantener la aplicación. Esta proporciona ordenes que aumentan tu productividad automatizando tediosas y repetitivas tareas.

Ejecútala sin argumentos para obtener más información sobre sus posibilidades:

$ php app/console

La opción --help te ayuda a descubrir el uso de una orden:

$ php app/console router:debug --help

Consideraciones finales

Llámame loco, pero después de leer esta parte, debes sentirte cómodo moviendo cosas y haciendo que Symfony2 trabaje por ti. Todo en Symfony2 está diseñado para allanar tu camino. Por lo tanto, no dudes en renombrar y mover directorios como mejor te parezca.

Y eso es todo para el inicio rápido. Desde probar hasta enviar mensajes de correo electrónico, todavía tienes que aprender mucho para convertirte en gurú de Symfony2. ¿Listo para zambullirte en estos temas ahora? No busques más — ve al Libro oficial y elije cualquier tema que desees.

Bifúrcame en GitHub