20 trucos para Symfony 4

20 trucos para Symfony 4

En este artículo quiero recoger algunos de esos trucos o atajos que podemos usar en Symfony y que, aunque estén bien documentados, es posible que no os suenen si no habéis buceado por su documentación. Así que vamos a ello! Espero descubriros alguno que no conozcáis!

1. Crear excepciones HTTP directamente desde el controlador

Symfony permite crear excepciones relacionadas con algunos de los errores HTTP más típicos directamente desde el controlador sin tener que escribir new Tipo de Excepción :

throw $this->createNotFoundException('message'); // 404
throw $this->createAccessDeniedException('message'); // 403

2. Bindear parámetros por su nombre

Con la llegada del autowiring a Symfony, inyectar servicios pasó a resultar muy sencillo gracias a la posibilidad de referirnos a ellos por su clase o interfaz que implementan en el constructor de la clase donde queremos inyectarlos.

Sin embargo, también es posible decirle a Symfony que cuando encuentre en el constructor de un servicio un parámetro nombrado de forma específica, lo bindee con lo que nosotros le digamos:

services:
  _defaults:
    autowire: true
    autoconfigure: true
    bind:
      $clientSecret: '%clientSecret%'

$myAwesomeService: '@my_awersome_service'

3. Autoinvocar controladores

Si queremos que nuestros controladores sean first-class es posible recurrir al método mágico__invoke de PHP para que en la definición de la ruta no sea necesario especificar el método del controlador asociado.

Es decir, podemos definir una ruta del siguiente modo:

a_route:
  path: /some-path
  controller:  App\Controller\AnyController

De modo que en nuestro controlador solo sea necesario escribir un método:

class AnyController extends AbstractController {
  public function __invoke( // services ) {

// code

De este modo nuestros controladores siguen el principio de responsabilidad única y escribir tests sobre ellos se vuelve bastante más limpio (pues solo hay que comprobar una funcionalidad).

4. Inyectar repositorios

Pese a que estamos acostumbrados a inyectar en nuestros servicios directamente la interfaz EntityManagerInterface y de ahí pedir el repositorio de nuestra clase mediante getRepository , recordad que en Symfony todas las clases que definamos son servicios (salvo las que especifiquemos que no), por lo que también podemos inyectarlos directamente:

construct(AnyRepository $anyRepository) {
// constructor
}

Esto permite hacer cosas tan interesantes como decorar repositorios:

5. Ordenar consultas a Doctrine

Otra de esas cosas que suelen resultar bastante útiles es la posibilidad de especificar el orderBy y el limit al emplear el método findBy de nuestros repositorios de Doctrine, lo cual nos evitará tener que escribir la consulta:

$repository->findBy(
[ 'field' => 'value'],
[ 'fieldToOrder' => 'ASC'],
$limit,
$offset
);

6. Definir un commando más rápido (aún)

Ahora es posible prescindir del método configure cuando escribimos comandos para definir el nombre asociado al comando y que emplearemos cuando queramos ejecutarlo desde consola.

Tan solo basta con definir la propiedad estática:

protected static $defaultName = 'app:command-name';

y ya podremos ejecutar nuestro comando.

7. Inyectar servicios en comandos

También, gracias al autowiring y a que los comandos también son definidos como servicios, es posible inyectar otros servicios en ellos directamente a través del constructor sin necesidad de extender la clase ContainerAwareCommand :

public function __construct(ServiceInterface $aService) {
  $this->aService = $aService;
  parent::__construct();
}

Nota. Recordad que ahora los comandos deben extender de la clase:

use Symfony\Component\Console\Command\Command;

8. Incluir archivos generados por webpack

La nueva versión de Webpack Encore trae entre sus principales novedades una nueva forma de incluir los archivos que generemos.

A partir de ahora, en vez de tener que escribir

<link rel="stylesheet" href="{{ asset('build/file.css') }}">
<script src="{{ asset('build/file.js') }}"></script>

para incluir los css / js generados, basta con:

{{ encore_entry_link_tags('entryName') }}
{{ encore_entry_script_tags('entryName') }}

Como veis, ¡mucho más sencillo!

9. Inicialización de los tests

Aunque probablemente esto ya lo conozcáis, no está de más recordar la forma en que podemos realizar ciertas acciones antes de la ejecución de cada test así como deshacerlas una vez finalizadas (por ejemplo, igual queremos situar la base de datos en un cierto estado para ese test en concreto). Para ello basta con implementar los métodos:

protected function setUp() {}
protected function tearDown() {}

El primero se ejecutará antes de cada test definido dentro de la clase mientras que el segundo lo hará cada vez que finalice la ejecución de uno.

Realmente muy útil para ciertas pruebas.

10. Comando make para generar entidades

Esto fue un amor a primera vista cuando me encontré con él. A partir de ahora generar entidades es tan sencillo como ejecutar el comando:

bin/console make:entity

Con él podremos definir las propiedades y relaciones de nuestras entidades por línea de comandos, lo cual generará directamente tanto la clase como su repositorio. ¡Perfecto para ahorrar tiempo!

11. Comando make para generar administradores

Y si el anterior sirve para ahorra tiempo generando entidades, si estamos usando SonataAdmin para generar el panel de administración de nuestra aplicación, podemos ejecutar:

bin/console make:sonata:admin

para generar directamente el servicio y la clase asociada para la entidad cuyo administrador queremos generar.

12. Enumerados

Esto no es un truco propio de Symfony pero quería dejaros este boilerplate que uso habitualmente para definir tipos enumerados en mi aplicación (perfecto para definir opciones estáticas dentro de la aplicación):

<?php
namespace App\Enum;
class SimpleEnum {
  private static $types = [
'a' => self::A,
'b' => self::B
];
  const A = 1;
const B = 2;
  public static function getValues(){
return self::$types;
}
  public static function fromString($index){
return self::$types[$index];
}
  public static function toString($value){
return array_search($value, self::$types);
}
}

13. Traducir las rutas

Esta ha sido una de las novedades que más he agradecido, ya que añade soporte nativo para traducir las rutas de nuestra aplicación.

Basta con definir la ruta del siguiente modo:

route_name:
controller: App\Controller\AnyController::actionName
path:
es: /path
en: /translated-path

De modo que cuando generemos la ruta empleando la función path , ésta será generada teniendo en cuenta el valor del locale actual.

14. Doctrine migrations

Aunque ya es el sistema recomendado por la documentación de Symfony cuando trabajamos con Doctrine, no está de más recordar que las Doctrine Migrations nos permiten tener una versionado de nuestra base de datos, de modo que podamos navegar por las distintas versiones que hemos ido generando de forma bastante sencilla.

Para ello basta con emplear los comandos:

bin/console make:migration
bin/console doctrine:migrations:migrate

Lo cual irá generando los correspondientes archivos de migración en la carpeta Migrations de nuestro proyecto así como actualizará la tabla migration_versions para llevar el control de en qué versión nos encontramos.

15. Devolver un código de error HTTP al renderear un archivo twig

Cuando queramos devolver un código de error a la vez que devolvemos el HTML asociado a un archivo twig basta con devolver lo siguiente:

return new Response(
  $this->renderView(
    'error.html.twig',
    ['error' => $e->getMessage()],
    Response::HTTP_BAD_REQUEST
  )
);

Por cierto, echad un ojo a las constantes definidas por la clase Response para devolver los errores y no tenerlos hardcodeados.

16. Comprobar si un usuario ha iniciado sesión

Recordad que para comprobar en un controlador si el usuario que ha realizado la petición ha iniciado sesión no sirve con:

$user = $this->getUser();
if ($user) {
}

sino que tendremos que recurrir al método:

$this->isGranted('IS_AUTHENTICATED_FULLY')

con el fin de asegurarnos de que el usuario posee una sesión.

Además, tenemos el atajo

$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');

para lanzar una excepción 403 cuando el usuario no posea el rol que estamos comprobando.

17. Property Accessor

Una de los componentes más útiles y que más suelen pasar desapercibidos es el conocido como PropertyAcccess , un componente que nos permite abstraer la lógica a la hora de acceder a propiedades de objetos y arrays de modo que determinadas partes de nuestro código queden mucho más limpias.

De hecho con la recién salida del horno versión 4.3 de Symfony su rendimiento ha mejorado aún más por lo que os recomiendo que le echéis un ojo porque siempre viene bien conocer este tipo de herramientas.

18. Doctrine event listener

Los Doctrine Event Listener nos permiten realizar acciones durante el ciclo de vida de las entidades controladas por Doctrine. Para ello existen numerosos eventos (como por ejemplo prePersist , preUpdate u onFlush a los que podemos engancharnos mediante servicios para realizar en ellos las operaciones que necesitemos.

De hecho, en estos eventos tendremos acceso a un argumento que nos proveerá de un EntityManager así como podremos acceder a los cambios realizados en las entidades mediante las UnitOfWork tal y y como os conté en el artículo donde os explicaba la forma de detectar cambios en una entidad:

Os aconsejo que le echéis un ojo a la documentación oficial de Doctrine para saber qué operaciones podéis realizar en cada evento (ya que, por ejemplo, modificar relaciones no está permitido en casi ninguno de ellos).

19. Traducir fechas en twig

Este es uno de mis favoritos, ya que por defecto el filtro date de Twig no traduce las fechas al idioma que hemos escogido, por lo que es necesario recurrir a las Twig Extensions para obtener dicha funcionalidad.

Si no las tenéis instaladas podéis hacerlo mediante composer :

composer require twig/extensions

y generar un archivo twig_extensions.yaml en la ruta config/packages con el siguiente contenido:

services:
_defaults:
  public: false
  autowire: true
  autoconfigure: true
#Twig\Extensions\ArrayExtension: ~
Twig\Extensions\DateExtension:
  arguments: ['@translator']
Twig\Extensions\IntlExtension: ~
#Twig\Extensions\TextExtension: ~

Como podéis ver he activado dos extensiones:

  • DateExtension , la cual permite usar el filtro time_diff y así obtener la diferencia entre la fecha actual y la que le pasemos. Necesita como argumento una interfaz Translator y que en la carpeta translations de nuestro proyecto generemos un archivo date.LANG.yaml con las traducciones.
  • IntlExtension , la cual permite traducir fechas mediante el filtro localizeddate y cuyo uso podéis ver en el siguiente enlace:

20. Los CacheAdapters

Finalmente, otro de los componentes más útiles que Symfony trae de gratis son los CacheAdapter de los cuales ya os hablé en este artículo:

Y que simplifican el uso de los distintos tipo de caché que podemos instalar en el servidor al proveer una misma interfaz para ellos. Muy muy útiles.

Final

Espero que estos trucos os hayan servido bien para descubrir algo nuevo bien para refrescar cómo se hace algo. Si tenéis alguno que queráis compartir no dudéis en dejarlo en los comentarios!