¿Qué es un Controlador?

Un controlador es una función PHP que se encarga de obtener la información de una petición HTTP(Request) y de generar y devolver la respuesta HTTP(Response).

El controlador contiene toda la lógica que la aplicación necesita para generar el contenido de la página, y la respuesta puede ser una página HTML, un documento XML, un array JSON serializado, una cabecera de error, etc.

Cuando creamos un controlador, hay que guardarlo en el directorio designado para almacenar nuestro código, /src , dentro de la carpeta AppBundle/Controller . El nombre lleva como sufijo la palabra Controller . Ej: BlogController .

AppBundle contiene todo el código de nuestra aplicación, a excepción de las vistas, como veremos más adelante.

Los bundles son la parte más importante de Symfony2. Conceptualmente se puede decir que son similares a los plugins que encontramos otras aplicaciones. La diferencia es que en Symfony todo es un bundle , incluída la aplicación que vamos a desarrollar en este tutorial. El nombre lleva como sufijo la palabra Bundle . Ej: UploadFilesBundle .

Más adelante entraremos en detalles sobre este concepto.

¿Cómo funciona?

El siguiente esquema refleja el flujo de una petición HTTP en Symfony.

enter image description here

Imagen obtenida de http://symfony.com/doc/current/_images/request-flow.png

El componente Routing es el encargado de asociar un patrón de URL y un controlador. El enrutado de una aplicación Symfony se define en el archivo routing.yml .

# /app/config/routing.yml

app:
    resource: "@AppBundle/Controller/"
    type:     annotation


Vemos que hay configurado un enrutamiento con el nombre identificador app , cuyos controladores residen en AppBundle/Controller/ , siendo del tipo annotation (configuración por anotaciones).


Al instalar Symfony, vemos que por defecto hay creado un controlador llamado DefaultController .

# /src/AppBundle/Controller/DefaultController

<?php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

class DefaultController extends Controller
{
    /**
     * @Route("/", name="homepage")
     */
    public function indexAction(Request $request)
    {
        return $this->render('default/index.html.twig', array(
            'base_dir' => realpath($this->container->getParameter('kernel.root_dir').'/..'),
        ));
    }
}

Symfony2 utiliza los namespaces de PHP 5.3 para asignar un “espacio de nombres” completo a la clase del controlador.

DefaultController extiende de la clase Controller , la cual se está importando más arriba mediante un use statement de Symfony:

use Symfony\Bundle\FrameworkBundle\Controller\Controller;


Esta clase contiene varias utilidades para las tareas más comunes de los controladores y también incluye un acceso a cualquier otro recurso que necesite la clase del controlador. Extender la clase base
Controller en Symfony es opcional. Aunque muchos programadores lo hacen para utilizar los atajos, no es obligatorio su uso.


Hay definida una función indexAction , que recibe como parámetro el objeto Request con toda la información que contiene la petición realizada:

use Symfony\Component\HttpFoundation\Request;


El nombre de cada action de un controlador lleva como sufijo la palabra Action . Sirven para referenciar una ruta determinada en la aplicación. En este caso / , que es la ruta principal, a la que se accede desde:

http://localhost/my_blog/web/app_dev.php


Esto está definido en las anotaciones del método:

 /**
 * @Route("/", name="homepage")
 */


Las anotaciones tienen la misma sintaxis que los comentarios en PHP, sólo que a direferencia de éstos, sí son interpretadas como parte del código.

Mediante el objeto Route establece como primer parámetro la ruta asociada a dicho action , en el segundo le asigna un nombre homepage que podremos utilizar más adelante en nuestro código para refefenciar esta acción.


A través del método render de la clase Controller , indexAction devuelve como primer parámetro una vista default/index.html.twig . El segundo parámetro es un array de valores que contiene una variable base_dir , cuyo valor es la ruta absoluta de nuestro equipo en la que reside la aplicación, en mi caso: /var/www/my_blog/ .

Algunos ejemplos

Devolver una página HTML simple.

# Muestra Hola mundo en un HTML
<?php

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;

/**
 * @Route("/", name="homepage")
 */
public function indexAction()
{
    return new Response('<html><body>Hola Mundo!</body></html>');
}


Parámetros de una ruta como argumentos del controlador.

# Parámetros de ruta como argumentos del controlador

<?php

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;

/**
 * @Route("/hola/{name}", name="hello")
 */
public function helloAction($name)
{
    return new Response('<html><body>Hola '.$name.'!</body></html>');
}


Acceso a parámetros mediante el objeto Request .

# Acceso a parámetros

<?php

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

/**
 * @Route("/blog/post/{id}", name="blog-post")
 */
public function postAction(Request $request)
{
  $request->get('id'); // $_GET
  $request->get('slug'); // $_POST
}


Crear una respuesta de tipo JSON con código de estado HTTP 200.

# Creación respuesta JSON

<?php

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;

/**
 * @Route("/hola/{name}", name="hello")
 */
public function helloAction($name)
{
  $response = new Response(json_encode(array('name' => $name)));
  $response->headers->set('Content-Type', 'application/json');
}


Redirecciones. Métodos propios de la clase Controller .

# Redirecciones

<?php

public function indexAction()
{
  return $this->redirect($this->generateUrl('homepage'), 301); // redirección permanente a una ruta de la aplicación.

  return $this->redirectToRoute('homepage'); // simplifica el método anterior. Código de estatus por defecto 302.

  return $this->redirect('http://symfony.com/doc'); // redirigir a una url

  return $this->render('hello/index.html.twig', array('name' => $name)); // redirigir a una vista devolviendo un *array* de valores.
}


Acceder a otros servicios.

# Acceso a otros servicios

<?php

public function indexAction()
{
  $templating = $this->get('templating');

  $router = $this->get('router');

  $mailer = $this->get('mailer');
}


Podemos ver en los ejemplos cómo se hace uso de los componentes del framework mediante los use statement , a través de la palabra reservada use , seguido del namespace de la clase.

Suele ser muy útil disponer en el controlador del objeto Request asociado a la petición del usuario, especialmente cuando se trabaja con formularios.

Importar componentes no necesarios en un controlador, servicio, etc. afecta negativamente al rendimiento de la aplicación.

Para listar todas las rutas disponibles en la aplicación podemos ejecutar por consola el siguiente comando:

$ php app/console debug:router

Enrutamiento para la aplicación de ejemplo my_blog

Routing

# /app/config/routing.yml

# Utilizamos la configuración por defecto.

app:
    resource: "@AppBundle/Controller/"
    type:     annotation

Controller

# Controller

<?php

# /src/AppBundle/Controller/PublicController.php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;


/**
 * @Route("/")
 */
class PublicController extends Controller
{
    /**
     * @Route("/", name="home")
     */
    public function homeAction(Request $request){//...}

    /**
     * @Route("/post/{id}/{slug}", name="blog-post")
     */
    public function postAction(Request $request){//...}

    /**
     * @Route("/archivo/categoria/{slug}", name="blog-archive-category")
     */
    public function archiveCategoryAction(Request $request){//...}

    /**
     * @Route("/archivo/etiqueta/{slug}", name="blog-archive-tag")
     */
    public function archiveTagAction(Request $request){//...}
}


Este controlador PublicController contendrá los métodos necesarios para:

  • Mostrar la página de inicio con un listado de los últimos post publicados.
  • Devolver la información correspondiente a un post en concreto.
  • Obtener un listado de post ordenados por categoría.
  • Obtener un listado de post ordenados por etiquetas.

# Public Controller

<?php

# /src/AppBundle/Controller/CommentsController.php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;


/**
 * @Route("/comentarios")
 */
class CommentsController extends Controller
{

    /**
     * @Route("/nuevo", name="comments-new")
     */
    public function newAction(Request $request){//...}

    /**
     * @Route("/aprobar/{id}", name="comments-approve")
     */
    public function approveAction(Request $request){//...}

}


CommentsController
nos servirá para:

  • Crear un nuevo comentrario, a través de un formulario en la página detalle de un post
  • Marcar un comentario como “Aprobado” y que de esta forma así a ser visible a todos los usuarios.

Este post ha sido producido gracias a la colaboración de
Sopinet Software