Aprende Symfony2 (Parte 6): Conclusión

5 de octubre de 2014

Este es el quinto artículo de la serie para aprender sobre el framework Symfony2. En los anteriores artículos desarrollamos una aplicación sencilla que muestra información sobre empresas y que contiene los siguientes archivos:

.
├── app
│   ├── AppKernel.php
│   ├── cache
│   │   └── .gitkeep
│   ├── config
│   │   ├── config_test.yml
│   │   ├── config.yml
│   │   └── routing.yml
│   ├── logs
│   │   └── .gitkeep
│   └── phpunit.xml.dist
├── composer.json
├── composer.lock
├── src
│    └── AppBundle
│        ├── Controller
│        │   └── ApiController.php
│        ├── AppBundle.php
│        └── Tests
│            └── Controller
│                └── ApiControllerTest.php
├── .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. En este artículo es un resumen general de todo lo visto en la serie.

Composer

Composer te ayuda a instalar y actualizar librerías de terceros. Descárgalo una sola vez e instálalo globalmente en tu ordenador:

$ curl -sS https://getcomposer.org/installer | php
$ sudo mv ./composer.phar /usr/local/bin/composer

Deberías poder ejecutarlo así: composer

  • instalar una librería de terceros: composer require [--dev] <vendor/name:version>
  • descargar las librerías de terceros del proyecto: composer install
  • actualizar las librerías de terceros del proyecto: composer update

Las librerías de terceros disponibles se pueden encontrar en Packagist.

Aquí tienes un artículo sobre cómo indicar las versiones de las dependencias en Composer.

En estos artículos hemos creado un proyecto desde cero, pero la manera recomendada de comenzar una aplicación Symfony2 es usar el comando de inicialización de Composer: composer create-project <vendor/name> <path-to-install>

Puedes usar la edición estándar de Symfony cuya dependencia se llama symfony/framework-standard-edition, o cualquier otra distribución.

Si lo prefieres y eres experto en Symfony, puedes utilizar una distribución vacía como Symfony Empty Edition:

$ composer create-project gnugat/symfony-framework-empty-edition <path-to-install>

[tip] En el servidor de producción, usa este comando para instalar las dependencias del proyecto (las librerías de terceros):

$ composer install --no-dev --optimize

[/tip]

Bundles

Integran tu código con el framework. Más concretamente, configuran el contenedor de inyección de dependencias del kernel.

El único bundle que necesitas crear es el AppBundle, donde estará todo tu código. Así es como se hace:

  1. crea su directorio: mkdir -p src/AppBundle
  2. crea su clase: $EDITOR src/AppBundle/AppBundle.php
  3. regístralo en el kernel: $EDITOR app/AppKernel.php

Una clase Bundle tiene esta pinta:

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

namespace AppBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class AppBundle extends Bundle
{
}

Aplicación

En tu aplicación, hay muy pocos archivos relacionados con el framework Symfony2. Esta es la lista de los que editarás normalmente:

El kernel de la aplicación

El archivo app/AppKernel.php es donde se registran los bundles y donde se carga la configuración. Sólo necesitarás editarlo cuando instales un bundle nuevo.

Manera de proceder: primero instala el bundle vía Composer:

$ composer require [--dev] <vendor/name:version>

Después regístralo en el kernel de la aplicación:

<?php
// app/AppKernel.php

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

class AppKernel extends Kernel
{
    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\AsseticBundle\AsseticBundle(),
            new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
            new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),

            // Añade aquí tus bundles
        );

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

            // O aquí, si quieres que sólo estén disponibles en el entorno de testing
        }

        return $bundles;
    }

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

La configuración del enrutamiento

El archivo app/config/routing.yml es donde asocias la acción de un controlador a una URL. Ejemplo:

# app/config/routing.yml
cotizacion:
    path: /api/{empresa}
    methods:
        - GET
    defaults:
        _controller: AppBundle:Api:cotizacion

informacion:
    path: /api/informacion
    methods:
        - POST
    defaults:
        _controller: AppBundle:Api:informacion

Como ves, puedes definir las rutas usando placeholders o variables, que después estarán disponibles en el controlador vía el objeto Request:

$request->query->get('empresa');

Controladores, tu punto de entrada

Cada ruta se asocia a la acción de un controlador. Un controlador es una clase que se encuentra en src/AppBundle/Controller, con el sufijo Controller.

Una acción es un método público de un controlador, con el sufijo Action, que recibe un parámetro Request $request y debe devolver una instancia del objeto Response:

<?php
// src/AppBundle/Controller/ApiController.php

namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;

class ApiController extends Controller
{
    public function informacionAction(Request $request)
    {
        $contenidoDeLaPeticion = $request->getContent();
        $contenidoEnviado = json_decode($contenidoDeLaPeticion, true);

        if (!isset($contenidoEnviado['empresa'])
            || 'ACME' !== $contenidoEnviado['empresa']) {
            $respuesta['mensaje'] = 'ERROR - Empresa desconocida';
            $codigoEstado = Response::HTTP_UNPROCESSABLE_ENTITY;
        } else {
            $respuesta['mensaje'] = 'OK';
            $respuesta['empresa'] = array(...);
            $codigoEstado = Response::HTTP_OK;
        }

        return new JsonResponse($respuesta, $codigoEstado);
    }
}

NOTA Puedes crear sub-directorios en src/AppBundle/Controller, permitiéndote así categorizar tus controladores. En la definición de tus rutas, se vería así: AppBundle:Subdirectorio\Controller:action.

Tests funcionales

Puedes usar cualquier framework de testing en un proyecto Symfony2. PHPUnit es uno de ellos, y muy popular, por lo que es el que usamos en nuestros ejemplos.

Los tests funcionales replican los controladores y comprueban si el código de estado es correcto. Si estás construyendo una API, puedes comprobar en mayor profundidad si el código de estado es el esperado:

<?php
// src/AppBundle/Tests/Controller/ApiControllerTest.php

namespace AppBundle\Tests\Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\Response;

class ApiControllerTest extends WebTestCase
{
    private function post($uri, array $data)
    {
        $content = json_encode($data);
        $client = static::createClient();
        $client->request('POST', $uri, array(), array(), array(), $content);

        return $client->getResponse();
    }

    public function testInformacionSobreEmpresaExistente()
    {
        $response = $this->post('/api/informacion', array('empresa' => 'ACME'));

        $this->assertSame(Response::HTTP_OK , $response->getStatusCode());
    }

    public function testInformacionSobreEmpresaInexistente()
    {
        $response = $this->post('/api/informacion', array('empresa' => 'NO_EXISTE'));

        $this->assertSame(Response::HTTP_UNPROCESSABLE_ENTITY , $response->getStatusCode());
    }
}

La clase WebTestCase nos la proporciona el framework: crea una aplicación (como hacemos nosotros en web/app.php), por lo que puedes enviar peticiones y testear las respuestas.

Dónde poner tu propio código

Puedes poner tu código en cualquier punto de src/AppBundle.

¿Quién dijo que debes desacoplar tu código de Symfony2? ¡Puedes escribirlo desacoplado directamente!

La convención es crear directorios con nombres relativos a los objetos que contienen. Por ejemplo, el directorio Controller contiene clases controladoras (a las que se añade el sufijo Controller). No tienes por qué seguir estas convenciones (salvo para los controladores y los comandos): ¡Organízate como prefieras!

Conclusión

Symfony2 no se entromete en el desarrollo de tus aplicaciones. Las únicas clases del framework que necesitamos usar son el controlador, la petición y la respuesta.

El flujo de trabajo es realmente simple:

  1. Symfony2 convierte la petición HTTP en el objeto Request
  2. El componente de routing permite que se ejecute el controlador relacionado con la URL actual
  3. El controlador recibe el objeto Request como parámetro, y debe devolver un objeto Response
  4. Symfony2 convierte el objeto Response en la respuesta HTTP

¿Qué debería hacer a continuación?

Practicar.

Sabes lo estrictamente necesario sobre Symfony2, y la única manera de aprender más es practicar, encontrar nuevos casos de uso, encontrar respuestas en la documentación y preguntar en StackOverflow (si nadie lo ha preguntado antes) o en el foro de este sitio web.

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