Laravel vs Symfony

¿Qué framework es mejor?

Escrito por Matías Navarro Carter hace 4 semanas
En: PHP / Frameworks
Lectura de 15 minuto(s).

Uno de los debates más candentes entre los entusiastas de PHP y una de las preguntas más comunes entre aquellos que están buscando un framework PHP para iniciarse es: ¿Qué framework es mejor? ¿Symfony o Laravel? La verdad, como en todas las herramientas de desarrollo, la respuesta siempre depende del uso que se le pretende dar a la a la misma.

Hay mucho dogmatismo binario con respecto al desarrollo de Sotfware. Siempre se nos presentan dos alternativas como si éstas fueran excluyentes. Y la verdad, lo que realmente hace crecer a un programador no es el quedarse con una forma exclusiva de hacer las cosas, y "casarse" con una sola herramienta. Lo que hace bueno a un programador es la capacidad de utilizar X o Y herramienta de la manera correcta y de acuerdo a los diferentes escenarios que enfrenta.

En mi experiencia con Laravel y Symfony, he podido ver dónde cada uno es fuerte y es débil, y he decidido realizar un pequeño sumario para guíar a aquellos a tomar la mejor decisión posible respecto de qué framework elegir para sus proyectos, por las razones correctas.

Filosofía

Lo primero que debemos entender es que los mantenedores de ambos frameworks tienen filosofías muy diferentes en cuánto a cómo debería desarrollarse una pieza de software. En el lado de Symfony, por ejemplo, se pone muchísimo énfasis en los principios SOLID, en no violar la Ley de Demeter, o en DRY. Symfony es muchísimo más dogmático, y sus desarrolladores toman mucho prestado de la escuela de Java y del Gang of Four. En Symfony, lo que prima es la calidad de tu código y la mantenibilidad de tu aplicación.

Esto no quiere decir que en Laravel no se preocupen de los principios de programación en absoluto, pero sí muchas veces han sacrificado algunos principios en pro de hacer que Laravel sea fácil y cómodo de usar para el desarrollador. En esto, no hay duda: el enfoque principal y la filosofía de Laravel consisten en hacer del desarollo una experiencia más placentera para el desarrollador y en proveerle de herramientas que le harán su trabajo más sencillo. En esto, toman muchísimo de la escuela de Ruby y muchas de sus implementaciones y liberías se le asemejan.

Arquitectura y Service Container

En términos de arquitectura ambos frameworks se parecen mucho. Ambos poseen una arquitectura orientada a servicios, con un DI Container, y una filosofía de Controladores pequeños y Modelos grandes. Además, ambos frameworks cuentan con un Event Dispatcher y la posibilidad de hookear casi cada uno de los procedimientos relevantes que ocurren dentro del Request Cycle.

Sin embargo, la forma en que Symfony organiza sus componentes es mucho más desacoplada que en Laravel. En Symfony, tus componentes literalmente "no saben nada" de los otros componentes, lo que te permite reemplazarlos sin mayor complicación por otras implementaciones de la misma funcionalidad siempre y cuando se conformen a la interfaz correspondiente. Laravel, por el contrario, tiene una arquitectura mucho más acoplada, y las diferentes clases y funcionalidades saben demasiado de lo que los rodea (una violación a la Ley de Demeter). Esto significa que si un componente de Laravel no satisface tus necesidades y quieres reemplazarlo por algo más personalizado y complejo, tendrás unos cuantos dolores de cabeza en el proceso.

La forma en que cada Framework registra y llama sus servicios dentro del Container varía un poco. Symfony sigue la filosofía más tradicional de injectar el container o servicios específicos de forma un poco más explícita, dentro del constructor de la clase. Sin embargo, además de contar con esa misma funcionalidad, en Laravel se hace uso de la técnica conocida como Facades, una especie de alias de una clase que se llama con un use Statement, que sirve a modo de proxy para obtener un servicio desde cualquier lugar de tu aplicación, sin necesidad de inyectarlo en el constructor.

El injectar y modificar servicios y dependencias es verdaderamente sencillo en Laravel. Puedes ir con la forma difícil y lenta de registrar tus servicios uno por uno, y luego declararlos en el constructor, o puedes utilizar Facades. En este sentido, Laravel ofrece un poco más de opciones al desarrollador, mientras que Symfony es un poco más rígido. Con todo, Symfony ha hecho esfuerzos por hacer del registro de servicios una tarea un poco más amena con su "nuevo" autowire feature (>2.8).

Persistencia (Bases de Datos)

Ambos frameworks hacen uso de componentes ORM's y DBAL's. Sin embargo, varían grandemente en filosofías. El ORM de Laravel utiliza la famosa implementación de Active Record, hecha popular por Ruby on Rails, mientras que Symfony utiliza Data Mapper.

Active Record

En Active Record (Eloquent) tus Modelos son una representación directa de una fila en una de tus tablas de tu base de datos. Además, tienen la capacidad de obtenerse a sí mismos (como registros en la base de datos) y de persistirse a sí mismos también. Esto hace que la lógica de tus modelos esté fuertemente ligada a tu base de datos. Los defensores de Active Record plantean que, aunque esto viola el principio de Responsabilidad Única, es una violación que tiene sentido, porque no tendríamos por qué pretender que detrás de nuestros Modelos no hay una base de datos, ya que es la razón principal para tener modelos en primer lugar.

El resultado de esta implementación es que es muy sencillo el obtener y persistir datos utilizando Eloquent. Algo así como esto:

<?php 

// Me devuelve un objeto EloquentCollection, un Arrayable que contiene muchas instancias de Post.
$posts = \App\Post::all();

// Puedo tomar el primero
$post = array_shift($posts);

// Modificarlo
$post->title = 'He cambiado el título';

// Y persistirlo en la base de datos
$post->save();

Manipular registros de base de datos con Active Record se vuelve entonces una tarea bastante sencilla: cambiar propiedades y hacer que el objeto se persista a sí mismo en la base de datos.

Data Mapper

Uno de los principios más importantes de la programación es el principio de Responsabilidad Única. Básicamente dice que una clase sólo debería tener una sola responsabilidad, una sola razón de ser. La crítica a Active Record sería entonces, en estas líneas: tus Modelos representan datos en la base de datos, pero otra clase debería ser la encargada de persistir esa representación en la base de datos, o de instanciar una representación consumiendo datos de la base de datos. Por esta razón, en el mundo de Symfony se prefiere la ORM llamada Doctrine.

Con Doctrine, la idea es tener una capa intermedia entre tu base de datos y la lógica de negocio de tu aplicación. Se hace todo esto mediante un objeto llamado Entity Manager. Él es el encargado de realizar queries a la base de datos y de persistir tus modelos. Esto da muchas ventajas, como la posibilidad de hacer que diferentes Entidades estén localizadas en diferentes Bases de Datos, y la posibilidad de agrupar múltiples operaciones de persistencia en una sola transacción, lo que hace que los tiempos de escritura se reduzcan considerablemente.

Estas ventajas vienen a costa de una mayor verbosidad realizando consultas a la base de datos, aunque no es nada tan terrible:

<?php

// Primero llamamos al Entity Manager, que sabe todo acerca de nuestra base de datos.
$em = $this->getDoctrine()->getManager();

// Luego hacemos una consulta.
$posts = $em->getRepository('AppBundle:Post')->findAll();

// Podemos actualizar nuestros datos con getters and setters
$post = array_shift($posts);
$post->setTitle('Tengo un nuevo título');

// Persistimos los cambios en la base de datos
$em->persist($post);
$em->flush(); // Realiza las operaciones de persistencia en una sola transacción

¿Cuál es mejor?

Hay todo un debate religioso en el mundo del desarollo de Software sobre si Data Mapper o Active Record es la mejor alternativa. Por mi parte, la respuesta es sencilla. En proyectos de mediano a pequeño tamaño, Active Record es probablemente la solución. Cuando se trata de aplicaciones más complejas (como por ejemplo, las Entidades de tu aplicacón viviendo en bases de datos diferentes o alta frecuencia de persistencia) quizás Data Mapper sea la alternativa correcta. Con todo, ambas son sólidas opciones.

Configuración, Bundles y Packages

Un buen framework es aquel que nos ofrece flexibilidad tanto en su infraestructura como en su configuración. Por ello, tanto Symfony como Laravel cuentan con un robusto sistema de configuración tanto de funcionalidad interna, como de paquetes de terceros.

Laravel posee una configuración puramente escrita en PHP, salvo por las variables de entorno que utiliza. Symfony, por su parte, heredando de la escuela de Java, se enreda en archivos de configuración en formatos XML y YML, los cuales parsea y coloca en una caché.

Ambos frameworks cuentan con un sistema para integrar funcionalidad reutilizable en forma de Paquetes (Laravel) o Bundles (Symfony). Éstos consisten en un montón de clases y archivos de configuración, cargados por una clase maestra que se registra en el Kernel de tu aplicación, y que proveen variada funcionalidad. Algunos paquetes o bundles se encargan de cosas complejas como proveer de una base para desarrollar RESTful Api's hasta de cosas básicas como pequeños listeners que realizan tareas interesantes.

La diferencia más grande es en la forma en que la configuración de estos paquetes se modifica. Al final, ambos frameworks tratan de resolver el problema de cómo poder ofrecer flexibilidad en librerías que viven fuera del proyecto, en tu carpeta vendor.

Symfony utiliza una especie de namespaces dentro del archivo de configuración principal, y luego múltiples llaves y opciones. En Laravel, por su parte, puedes "exportar" la configuración y otros elementos de un paquete de vendor a la carpeta config de tu proyecto y sobrescribirlo allí, quedando perfectamente versionados.

Prefiero la forma en como Laravel maneja la configuración. Se siente muchísmo más natural y menos invasiva, sobretodo cuando la configuración requerida de algunos paquetes de Symfony provoca errores si no la has añadido antes de que el paquete de composer termine de instalarse.

Sin embargo, Symfony ha dado buenos pasos en esta dirección con Symfony 4. El uso de Flex y Recipes hacen que instalar paquetes de terceros y montar su configuración sea un proceso mucho más agradable ahora. De hecho, el uso de parámetros se ha reemplazado por el de variables de entorno.

Enrutamiento y Modelamiento de Base de Datos

Con respecto al enrutamiento, Laravel maneja un archivo de rutas para Web, Api y otras cosas. Allí, registras rutas utilizando el Facade Route, y definiendo el método, la ruta y el controlador que apunta allí, de la siguiente manera:

<?php

Route::get('/posts', 'PostController@index');
Route::get('/posts/{id}', 'PostController@show');

En Symfony, el procedimiento es mucho mejor para mi gusto. El archivo de rutas de Laravel termina siendo largo y muchas veces se hace difícil encontrar elementos. En Symfony, por el contrario, las rutas se registran en el mismo controlador, utilizando nuevamente algo muy propio de Java: anotaciones de Code Blocks.

<?php

class PostController {

    /**
     * @Route("/posts")
     */
    public function indexAction()
    {
        // Lógica de tu controlador
    }

    /**
     * @Route("/posts/{id}")
     */
    public function showAction($id)
    {
        // Lógica
    }
}

Estas anotaciones son leídas por un parser mediante el uso de \Reflection, que guarda todos los datos de las clases con anotaciones en un objeto llamado ClassMetadata, que luego puedes reutilizar en tu aplicación para realizar otras acciones. Esta es una poderosa característica.

Las anotaciones también resultan convenientes a la hora de definir la estructura de nuestra base de datos. Mientras en Laravel hacemos uso de Migrations, que básicamente es una especie de control de versión de tu base de datos, en Symfony aquello no es necesario: toda la definición de tu base de datos vive dentro de la misma clase que representa una tabla.

Así, no tenemos que ir cambiando entre Controladores y archivos de definición de rutas, o entre Modelos/Entidades y archivos de migración. Hace mucho sentido que las mismas anotaciones de una clase o un método definan su funcionalidad dentro de ellas mismas, y vivan allí como una suerte de documentación integrada. En esto, para mi gusto, Symfony es mejor.

Debugging

En Debugging lamentablemente Laravel deja mucho que desear. A pesar de que se están haciendo esfuerzos desde Laravel 5.5 por mejorar la interfaz de debugging colocando nuevamente la librería Whoops!, no llega más lejos que ser un Stack Trace con un poco de estilo.

Debuggear tu aplicación en Symfony, por otro lado, es una maravilla gracias al profiler. El profiler guarda información de cada request que llega al front controller de desarrollo, le asigna un código único, y guarda toda su información: toda la información de la request y el response, los eventos disparados, el usuario autenticado, los formularios procesados, las llamadas a base de datos y caché, las entradas de log, etcétera. Es una de las mejores funcionalidades que ofrece Symfony, y una de mis favoritas por lejos.

Motores de Plantillas

Ambos frameworks utilizan motores de plantillas bastante potentes. Laravel utiliza Blade y Symfony usa Twig. Ambos poseen casi la misma funcionalidad y pueden ser extendidos. Quizás en donde varían mucho es en la sintaxis. Blade es casi como PHP pero con algunas directivas que comienzan con @. Sin embargo, la sintaxis de Twig se parece mucho a la de Javascript. ¿Podríamos llamar a esto un empate?

Popularidad y Facilidad de Uso

No es ningún secreto que en los últimos tres años Laravel ha sobrepasado a Symfony como el framework de PHP más popular. Así lo muestra Google Trends, resultados de búqueda en Stack Overflow, y la creciente demanda laboral por desarrolladores Laravel en el mercado.

Aunque la popularidad no es el factor principal a la hora de decidir por un framework, si dice mucho del estado de la comunidad y de la facilidad de adopción del mismo. Para los que se están iniciando en POO, Laravel resulta una excelente herramienta muy fácil de aprender. Para aquellos que tienen un conocimiento más avanzado de Patrones de Diseño y POO, Symfony resulta una alternativa excelente, porque no les obliga a estructurar su código a la manera del Framework y les ofrece mucha flexibilidad en cuanto a la organización de sus clases.

Si tuviera que elegir uno sería difícil. Ambos me gustan y ambos son muy buenos. Laravel quizás es más sencillo de utilizar para proyectos pequeños y medianos y para el desarrollador no tan experimentado. Además, tiene pequeños treats que harán del desarrollo de tu código una delicia. Si utilizas Symfony, será más complejo, pero aprenderás programación orientada a objetos a una mayor profundidad y no tendrás mucha del azúcar sintáctica que Laravel ofrece, pero te obligará a ser creativo e idear soluciones por ti mismo.