Frameworks de PHP: elegir entre Symfony y Laravel
Publicado: 2022-03-11Hoy, al iniciar un nuevo proyecto, una de las decisiones clave es elegir el marco adecuado. Se ha vuelto difícil imaginar la creación de una aplicación web compleja desde cero hoy en día sin una.
Muchos lenguajes populares para el desarrollo web tienen su marco "predeterminado", como Ruby on Rails para Ruby o Django para Python. Sin embargo, PHP no tiene un único valor predeterminado y tiene múltiples opciones populares para elegir.
Según las tendencias de Google y GitHub, los marcos PHP más populares son Symfony con 13,7k estrellas y Laravel con 29k estrellas (al momento de escribir este artículo).
En este artículo, compararé estos dos marcos y le mostraré cómo implementar características simples y cotidianas con cada uno. De esta manera, puede comparar el código de ejemplos de la vida real uno al lado del otro.
Este artículo presupone fuertes habilidades de PHP y una comprensión del paradigma arquitectónico MVC, pero no se requiere experiencia previa con Symfony o Laravel.
los contendientes
Laravel
Cuando hablamos de Laravel, nos referimos a Laravel versión 4 y posteriores. Laravel 4 se lanzó en 2013 y representó una reescritura completa del marco. La funcionalidad del marco se desacoplaba en componentes separados, que se administraban con Composer, en lugar de que todo estuviera en un solo repositorio de código enorme.
Laravel se declara a sí mismo como un marco para el desarrollo rápido con una sintaxis simple y hermosa que es fácil de aprender, leer y mantener. Es el framework más popular en 2016. Según las tendencias de Google, es tres veces más popular que otros frameworks, y en GitHub, tiene dos veces más estrellas que la competencia.
Symfony
Symfony 2 se lanzó en 2011, pero no debe confundirse con Symfony 1, que era un marco totalmente diferente con diferentes principios subyacentes. Fabien Potencier creó Symfony 2, y la versión actual es 3.2, que es una versión incremental de Symfony 2. Por lo tanto, a menudo se les llama simplemente Symfony2/3.
Al igual que Laravel 4, Symfony 2 está diseñado como un conjunto de componentes desacoplados. Hay dos beneficios aquí: podemos reemplazar cualquier componente en un proyecto de Symfony, y podemos tomar y usar cualquier componente de Symfony en un proyecto que no sea de Symfony. Los componentes de Symfony pueden servir como excelentes ejemplos de código y se utilizan en muchos proyectos de código abierto como Drupal, phpBB y Codeception. De hecho, el propio Laravel utiliza nada menos que 14 componentes de Symfony. Comprender Symfony te brinda muchos beneficios cuando trabajas con otros proyectos.
Instalaciones de marcos
Ambos marcos vienen con instaladores y contenedores disponibles a través del servidor web incorporado de PHP.
Instalación de Symfony
La instalación de Symfony es tan simple como lo siguiente:
# Downloading Symfony installer sudo curl -LsS https://symfony.com/installer -o /usr/local/bin/symfony # Granting permissions to execute installer sudo chmod a+x /usr/local/bin/symfony # Creating new Symfony project symfony new symfony_project # Launching built-in server cd symfony_project/ && php bin/console server:start ¡Eso es todo! Tu instalación de Symfony está disponible en la URL http://localhost:8000 .
Instalación de Laravel
El proceso de instalación de Laravel es casi el mismo y tan simple como el de Symfony; la única diferencia es que instalas el instalador de Laravel a través de Composer:
# Downloading Laravel installer using Composer composer global require "laravel/installer" # Creating new Laravel project laravel new laravel_project # Launching built-in server cd laravel_project/ && php artisan serve Ahora puede visitar http://localhost:8000 y verificar su instalación de Laravel.
Nota: tanto Laravel como Symfony se ejecutan en el mismo puerto localhost (8000) de forma predeterminada, por lo que no puedes tener estas instancias predeterminadas ejecutándose simultáneamente. No olvides detener el servidor Symfony ejecutando php bin/console server:stop antes de iniciar el servidor Laravel.
Acerca de la instalación del marco
Estos son ejemplos de una instalación básica. Para ejemplos de uso más avanzados, como poder configurar proyectos con dominios locales o ejecutar varios proyectos a la vez, ambos marcos proporcionan cuadros de Vagrant:
- hacienda laravel,
- Granja de Symfony.
Configuraciones básicas del marco
Configuración básica de Symfony
Symfony usa YAML como sintaxis para especificar su configuración. La configuración predeterminada se encuentra en el archivo app/config/config.yml y se parece al siguiente ejemplo:
imports: - { resource: parameters.yml } - { resource: security.yml } - { resource: services.yml } framework: secret: '%secret%' router: { resource: '%kernel.root_dir%/config/routing.yml' } # ... # Twig Configuration twig: debug: '%kernel.debug%' strict_variables: '%kernel.debug%' # ... Para crear una configuración específica del entorno, cree el archivo app/config/config_ENV.yml que contiene los parámetros de configuración básicos. Este es un ejemplo de un archivo config_dev.yml para el entorno de desarrollo:
imports: - { resource: config.yml } # ... web_profiler: toolbar: true # ... Este ejemplo activa la herramienta web_profiler de Symfony solo para el entorno de desarrollo. Esta herramienta lo ayuda a depurar y perfilar su aplicación directamente en la ventana del navegador.
En los archivos de configuración, también puede notar construcciones %secret% . Nos permiten poner variables específicas del entorno en el archivo de parameters.yml separado. Este archivo puede ser único en cada máquina y no se almacena bajo control de versiones. Para el control de versiones, tenemos un archivo parameters.yml.dist especial que es la plantilla para el archivo parameters.yml .
Aquí hay un ejemplo del archivo de parameters.yml :
parameters: database_host: 127.0.0.1 database_port: null database_name: symfony database_user: root database_password: null secret: f6b16aea89dc8e4bec811dea7c22d9f0e55593afConfiguración básica de Laravel
La configuración de Laravel se ve muy diferente a la de Symfony. Lo único que tienen en común es que ambos usan archivos que no están almacenados bajo control de versiones ( .env en el caso de Laravel) y una plantilla para generar este archivo ( .env.example ). Este archivo tiene una lista de claves y valores, como el siguiente ejemplo:
APP_ENV=local APP_KEY=base64:Qm8mIaur5AygPDoOrU+IKecMLWgmcfOjKJItb7Im3Jk= APP_DEBUG=true APP_LOG_LEVEL=debug APP_URL=http://localhost Al igual que el archivo YAML de Symfony, este para Laravel también es legible por humanos y se ve limpio. Además, puede crear un archivo .env.testing que se usará cuando se ejecuten las pruebas de PHPUnit.
La configuración de la aplicación se almacena en archivos .php en el directorio de config . La configuración básica se almacena en el archivo app.php y la configuración específica del componente se almacena en archivos <component>.php (por ejemplo, cache.php o mail.php ). Aquí hay un ejemplo de un archivo config/app.php :
<?php return [ 'name' => 'Laravel', 'env' => env('APP_ENV', 'production'), 'debug' => env('APP_DEBUG', false), 'url' => env('APP_URL', 'http://localhost'), 'timezone' => 'UTC', 'locale' => 'en', // ... ];Configuración del Framework: Symfony vs. Laravel
Los mecanismos de configuración de aplicaciones de Symfony te permiten crear diferentes archivos para diferentes entornos. Además, evita que inyecte lógica PHP compleja en la configuración de YAML.
Sin embargo, es posible que se sienta más cómodo con la sintaxis de configuración predeterminada de PHP que Laravel está usando y no tiene que aprender la sintaxis de YAML.
Enrutamiento y controlador
En general, una aplicación web de back-end tiene una responsabilidad principal: leer cada solicitud y crear una respuesta según el contenido de la solicitud. El controlador es una clase responsable de transformar la solicitud en la respuesta llamando a los métodos de la aplicación, mientras que el enrutador es un mecanismo que lo ayuda a detectar qué clase de controlador y método debe ejecutar para una solicitud en particular.
Vamos a crear un controlador que muestre una página de publicación de blog solicitada desde la ruta /posts/{id} .
Enrutamiento y controlador en Laravel
Controlador
<?php namespace App\Http\Controllers; use App\Post; class BlogController extends Controller { /** * Show the blog post * @param int $id * @return \Illuminate\Http\Response */ public function show($id) { return view('post', ['post' => Post::findOrFail($id)]); } }enrutador
Route::get('/posts/{id}', 'BlogController@show'); Hemos definido la ruta para las solicitudes GET . Todas las solicitudes con el URI que coincida con /posts/{id} ejecutarán el método show the del controlador BlogController y pasarán el id del parámetro a ese método. En el controlador, estamos tratando de encontrar el objeto del modelo POST con la id pasada, y llamamos al ayudante de Laravel view() para representar la página.
Enrutamiento y controlador en Symfony
En Symfony, exampleController es un poco más grande:
<?php namespace BlogBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; class PostController extends Controller { /** * @Route("/posts/{id}") * @param int $id * @return \Symfony\Component\HttpFoundation\Response */ public function indexAction($id) { $repository = $this->getDoctrine()->getRepository('BlogBundle:Post'); $post = $repository->find($id); if ($post === null) { throw $this->createNotFoundException(); } return $this->render('BlogBundle:Post:show.html.twig', ['post'=>$post]); } } Puede ver que ya hemos incluido @Route("/posts/{id}”) en la anotación, por lo que solo necesitamos incluir el controlador en el archivo de configuración routing.yml :
blog: resource: "@BlogBundle/Controller/" type: annotation prefix: /La lógica paso a paso es la misma que en el caso de Laravel.
Enrutamiento y controlador: Symfony vs. Laravel
En esta etapa, podrías pensar que Laravel es mucho mejor que Symfony. Esto es cierto, al principio. Se ve mucho mejor y más fácil de empezar. Sin embargo, en las aplicaciones de la vida real, no deberías llamar a Doctrine desde el controlador. En su lugar, debe llamar a un servicio que intentará encontrar la publicación o lanzar una excepción HTTP 404 .
Plantillas
Laravel se envía con un motor de plantillas llamado Blade y Symfony se envía con Twig. Ambos motores de plantillas implementan dos características principales:
- Herencia de plantilla
- Bloques o secciones
Ambas funciones le permiten definir una plantilla básica con secciones reemplazables y plantillas secundarias que completan los valores de esas secciones.
Consideremos nuevamente el ejemplo de una página de publicación de blog.
Motor de plantillas de cuchillas de Laravel
// base.blade.php <html> <head> <style></style> <title>@section('page-title') Welcome to blog! @show </title> </head> <body> <div class="container"> <h1>@yield('title')</h1> <div class="row"> @yield('content') </div> </div> </body> </html> // post.blade.php @extends('base') @section('page-title')Post {{ $post->title }} - read this and more in our blog.@endsection @section('title'){{ $post->title }}@endsection @section('content') {{ $post->content }} @endsection Ahora puede decirle a Laravel en su controlador que represente la plantilla post.blade.php . ¿Recuerdas nuestra llamada view('post', …) en el ejemplo anterior de Controller? No necesita saber en su código que se hereda de alguna otra plantilla. Todo está definido solo en sus plantillas, en el nivel de vista.
Motor de plantillas Symfony Twig
// base.html.twig <html> <head> <style></style> <title>{% block page_title %} Welcome to blog! {% endblock %} </title> </head> <body> <div class="container"> <h1>{% block title %}{% endblock %}</h1> <div class="row"> {% block content %}{% endblock %} </div> </div> </body> </html> // show.html.twig {% extends '@Blog/base.html.twig' %} {% block page_title %}Post {{ post.title }} - read this and more in our blog.{% endblock %} {% block title %}{{ post.title }}{% endblock %} {% block content %} {{ post.content }} {% endblock %}Plantillas: Symfony vs Laravel
Estructuralmente, las plantillas Blade y Twig son bastante similares. Ambos generan plantillas en código PHP y funcionan rápido, y ambos implementan estructuras de control, como sentencias if y bucles. La característica más importante que tienen ambos motores es que escapan de la salida de forma predeterminada, lo que ayuda a prevenir ataques XSS.
Aparte de la sintaxis, la principal diferencia es que Blade le permite inyectar código PHP directamente en sus plantillas y Twig no. En cambio, Twig te permite usar filtros.
Por ejemplo, si desea poner en mayúscula una cadena, en Blade debe especificar lo siguiente:
{{ ucfirst('welcome friend') }}En Twig, por otro lado, harías lo siguiente:
{{ 'welcome friend'|capitalize }}En Blade, es más fácil extender algunas funciones, pero Twig no permite ningún código PHP directo en las plantillas.
Inyección de dependencia
Las aplicaciones tienen muchos servicios y componentes diferentes, con varias interdependencias. Debe almacenar toda la información sobre los objetos creados y sus dependencias de alguna manera.
Aquí viene nuestro siguiente componente: Contenedor de servicios. Es un objeto PHP que crea servicios solicitados y almacena información sobre los objetos creados y sus dependencias.
Consideremos el siguiente ejemplo: está creando una clase PostService para implementar un método que es responsable de crear una nueva publicación de blog. Esta clase depende de otros dos servicios: PostRepository , que se encarga de almacenar información en la base de datos, y SubscriberNotifier , que se encarga de notificar a los usuarios suscritos sobre la nueva publicación. Para que funcione, debe pasar estos dos servicios como argumentos del constructor de PostService o, en otras palabras, debe inyectar estas dependencias.
Ejemplo de inyección de dependencia de Symfony
Primero, definamos nuestros servicios de muestra:
<?php // src/BlogBundle/Repository/PostRepository.php namespace BlogBundle\Repository; use BlogBundle\Entity\Post; use Doctrine\ORM\EntityRepository; class PostRepository extends EntityRepository { public function persist(Post $post) { // Perform save to db } } <?php // src/BlogBundle/Service/SubscriberNotifier.php namespace BlogBundle\Service; use BlogBundle\Entity\Post; class SubscriberNotifier { public function notifyCreate(Post $post) { // Notify subscribers } } <?php // src/BlogBundle/Service/PostService namespace BlogBundle\Service; use BlogBundle\Entity\Post; use BlogBundle\Repository\PostRepository; class PostService { /** @var PostRepository */ private $repository; /** @var SubscriberNotifier */ private $notifier; function __construct(PostRepository $repository, SubscriberNotifier $notifier) { $this->repository = $repository; $this->notifier = $notifier; } public function create(Post $post) { $this->repository->persist($post); $this->notifier->notifyCreate($post); } }La siguiente es la configuración de inyección de dependencia:
# src/BlogBundle/Resources/config/services.yml services: # Our main service blog.post_service: class: BlogBundle\Service\PostService arguments: ['@blog.post_repository', '@blog.subscriber_notifier'] # SubscriberNotifier service. It could also have its own dependencies, for example, mailer class. blog.subscriber_notifier: class: BlogBundle\Service\SubscriberNotifier # Repository. Don't dive deep into it's configuration, it is not a subject now blog.post_repository: class: BlogBundle\Repository\PostRepository factory: 'doctrine.orm.default_entity_manager:getRepository' arguments: - BlogBundle\Entity\PostAhora puede solicitar su servicio de publicación en cualquier parte del código desde su objeto Service Container. Por ejemplo, en el controlador podría ser algo como esto:
// Controller file. $post variable defined below $this->get('blog.post_service')->create($post);Service Container es un gran componente y ayuda a construir su aplicación siguiendo los principios de diseño SOLID.
Ejemplo de inyección de dependencia de Laravel
Es mucho más fácil administrar dependencias en Laravel. Consideremos el mismo ejemplo:
<?php // app/Repository/PostRepository.php namespace App\Repository; use App\Post; class PostRepository { public function persist(Post $post) { // Perform save to db } } <?php // app/Service/SubscriberNotifier.php namespace App\Service; use App\Post; class SubscriberNotifier { public function notifyCreate(Post $post) { // Notify subscribers } } <?php // app/Service/PostService.php namespace App\Service; use App\Post; use App\Repository\PostRepository; class PostService { /** @var PostRepository */ private $repository; /** @var SubscriberNotifier */ private $notifier; public function __construct(PostRepository $repository, SubscriberNotifier $notifier) { $this->repository = $repository; $this->notifier = $notifier; } public function create(Post $post) { $this->repository->persist($post); $this->notifier->notifyCreate($post); } } Aquí viene la belleza de Laravel: no necesita crear configuraciones de dependencia . Laravel escanea automáticamente las dependencias de PostService en los tipos de argumentos de su constructor y las resuelve automáticamente.

También puede usar la inyección en su método de controlador para usar PostService al "escribirlo" en los argumentos del método:
<?php namespace App\Http\Controllers; use App\Post; use App\Service\PostService; class BlogController extends Controller { public function create(PostService $service) { $post = new Post(['title' => 'Title', 'content' => 'Content']); $service->create($post); return redirect('/posts/'.$post->id); } }Inyección de dependencia: Symfony vs. Laravel
La detección automática de Laravel funciona muy bien. Symfony tiene una capacidad similar llamada "autowire" que está desactivada de forma predeterminada y podría activarse agregando autowire: true a tu configuración de dependencia, pero requiere alguna configuración. La forma de Laravel es más simple.
Mapeo relacional de objetos (ORM)
Para trabajar con bases de datos, ambos marcos vienen con funciones de mapeo relacional de objetos (ORM). ORM asigna registros de la base de datos a objetos en el código. Para hacer esto, debe crear modelos para cada tipo de registro (o cada tabla) en su base de datos.
Symfony usa un proyecto de terceros, Doctrine, para interactuar con la base de datos, mientras que Laravel usa su propia biblioteca Eloquent.
El ORM Eloquent implementa el patrón ActiveRecord para trabajar con la base de datos. En este patrón, cada modelo es consciente de la conexión con la base de datos y puede interactuar con ella. Por ejemplo, puede guardar datos en la base de datos, actualizar o eliminar un registro.
Doctrine implementa el patrón Data Mapper, donde los modelos no saben nada sobre la base de datos; solo son conscientes de los datos en sí. Una capa separada especial, EntityManager , almacena toda la información sobre la interacción entre modelos y bases de datos, y maneja todas las operaciones.
Tomemos un ejemplo para entender la diferencia. Digamos que su modelo tiene una clave de id principal, título, contenido y autor. La tabla Publicaciones almacena solo la id del autor, por lo que debe crear una relación con la tabla Usuarios .
Doctrina
Empecemos definiendo los modelos:
<?php // src/BlogBundle/Entity/User.php namespace BlogBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * User * * @ORM\Table(name="user") * @ORM\Entity */ class User { /** * @var int * * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @var string * * @ORM\Column(name="name", type="string", length=255) */ private $name; } <?php // src/BlogBundle/Entity/Post.php namespace BlogBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * Post * * @ORM\Table(name="post") * @ORM\Entity(repositoryClass="BlogBundle\Repository\PostRepository") */ class Post { /** * @var int * * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ protected $id; /** * @var string * * @ORM\Column(name="title", type="string", length=255) */ protected $title; /** * @var string * * @ORM\Column(name="content", type="text") */ protected $content; /** * @var User * * @ORM\ManyToOne(targetEntity="BlogBundle\Entity\User") * @ORM\JoinColumn(name="author_id", referencedColumnName="id") */ protected $author;Aquí, creamos información de mapeo del modelo y ahora podemos usar un asistente para generar los apéndices del método:
php bin/console doctrine:generate:entities BlogBundleA continuación, definimos los métodos de repositorio de publicaciones:
<?php // src/BlobBundle/Repository/PostRepository.php namespace BlogBundle\Repository; use BlogBundle\Entity\Post; use Doctrine\ORM\EntityRepository; class PostRepository extends EntityRepository { /** * Store post to database * * @param Post $post */ public function persist(Post $post) { $this->getEntityManager()->persist($post); $this->getEntityManager()->flush(); } /** * Search posts with given author's name * * @param string $name * @return array */ public function findByAuthorName($name) { return $this->createQueryBuilder('posts') ->select('posts') ->join('posts.author', 'author') ->where('author.name = :name') ->setParameter('name', $name) ->getQuery() ->getResult(); } } Ahora puedes llamar a estos métodos desde el servicio o, por ejemplo, desde PostController :
// To search for posts $posts = $this->getDoctrine()->getRepository('BlogBundle:Post')->findByAuthorName('Karim'); // To save new post in database $this->getDoctrine()->getRepository('BlogBundle:Post')->persist($post);Elocuente
El modelo de usuario se envía con Laravel y está definido de forma predeterminada, por lo que solo necesita definir un modelo para la publicación .
<?php // app/Post.php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { public function author() { return $this->belongsTo('App\User', 'author_id'); } } Eso es todo para los modelos. En Eloquent, no tiene que definir las propiedades del modelo, ya que las crea dinámicamente en función de la estructura de la tabla de la base de datos. Para almacenar una nueva publicación $post en la base de datos, debe realizar esta llamada (desde el controlador, por ejemplo):
$post->save();Para encontrar todas las publicaciones de un autor con un nombre dado, el mejor enfoque sería buscar un usuario con su nombre y solicitar todas las publicaciones de los usuarios:
$posts = Post::whereHas('author', function ($q) { $q->where('name', 'Karim'); })->get();ORM: Symfony frente a Laravel
Con respecto a ORM, Eloquent parece mucho más amigable para los desarrolladores de PHP y más fácil de aprender que Doctrine.
Despachador de eventos vs. Middleware
Una de las cosas más importantes que hay que entender sobre un framework es su ciclo de vida.
Symfony y despachador de eventos
Para convertir una solicitud en una respuesta, Symfony usa EventDispatcher. En consecuencia, activa diferentes eventos del ciclo de vida y detectores de eventos especiales para manejar estos eventos. Al principio, envía el evento kernel.request que incluye información de solicitud. El detector predeterminado principal de este evento es RouterListener , que invoca el componente del enrutador para encontrar una regla de ruta adecuada para la solicitud actual. Después de esto, se ejecutan otros eventos paso a paso. Los detectores de eventos típicos son una comprobación de seguridad, una verificación de token CSRF y un proceso de registro. Si desea agregar alguna funcionalidad en el ciclo de vida de la solicitud, debe crear un EventListener personalizado y suscribirlo al evento necesario.
Laravel y Middleware
Laravel usa una solución diferente: middleware. Me gusta comparar el middleware con una cebolla: su aplicación tiene ciertas capas y una solicitud pasa a través de estas capas en el camino hacia el controlador y de regreso. Por lo tanto, si desea ampliar la lógica de su aplicación y agregar alguna funcionalidad en el ciclo de vida de la solicitud, debe agregar una capa adicional a su lista de middleware y Laravel la ejecutará.
API REST
Intentemos crear un ejemplo CRUD básico para administrar una publicación de blog:
- Crear -
POST /posts/ - Leer -
GET /posts/{id} - Actualización -
PATCH /posts/{id} - Eliminar -
DELETE /posts/{id}
API REST en Symfony
Symfony no tiene una solución lista para usar fácil para la creación rápida de API REST, pero tiene excelentes paquetes de terceros FOSRestBundle y JMSSerializerBundle .
Consideremos el ejemplo de trabajo mínimo con FOSRestBundle y JMSSerializerBundle . Después de instalarlos y activarlos en AppKernel , puede establecer en la configuración del paquete que usará el formato JSON y que esto no tiene que incluirse en las solicitudes de URL:
#app/config/config.yml fos_rest: routing_loader: default_format: json include_format: falseEn la configuración de enrutamiento, debe especificar que este controlador implementará un recurso REST:
#app/config/routing.yml blog: resource: BlogBundle\Controller\PostController type: restImplementó un método persistente en el repositorio en el ejemplo anterior; ahora necesita agregar un método de eliminación:
// src/BlogBundle/Repository/PostRepository.php public function delete(Post $post) { $this->getEntityManager()->remove($post); $this->getEntityManager()->flush(); }A continuación, debe crear una clase de formulario para aceptar solicitudes de entrada y asignarlas al modelo. Puede hacerlo utilizando un asistente CLI:
php bin/console doctrine:generate:form BlogBundle:PostRecibirá un tipo de formulario generado con el siguiente código:
<?php // src/BlogBundle/Form/PostType.php namespace BlogBundle\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; class PostType extends AbstractType { /** * {@inheritdoc} */ public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('title')->add('content'); } /** * {@inheritdoc} */ public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => 'BlogBundle\Entity\Post', 'csrf_protection' => false ]); } /** * {@inheritdoc} */ public function getBlockPrefix() { return 'post'; } }Ahora implementemos nuestro controlador.
Nota: el código que les mostraré no es perfecto. Viola algunos principios de diseño, pero podría refactorizarse fácilmente. El objetivo principal es mostrarle cómo implementar cada método, paso a paso.
<?php // src/BlogBundle/Controller/PostController.php namespace BlogBundle\Controller; use BlogBundle\Entity\Post; use BlogBundle\Form\PostType; use FOS\RestBundle\Controller\FOSRestController; use FOS\RestBundle\View\View; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; class PostController extends FOSRestController { /** * @param $id * @return Response */ public function getPostAction($id) { $view = new View(); $post = $this->getDoctrine()->getRepository('BlogBundle:Post')->find($id); if ($post === null) { $view->setStatusCode(Response::HTTP_NOT_FOUND); } else { $view->setData(['post' => $post]); } return $this->handleView($view); } /** * @param Request $request * @return Response */ public function postPostAction(Request $request) { $view = new View(null, Response::HTTP_BAD_REQUEST); $post = new Post; $form = $this->createForm(PostType::class, $post, ['method' => $request->getMethod()]); $form->handleRequest($request); if ($form->isValid()) { $this->getDoctrine()->getRepository('BlogBundle:Post')->persist($post); $view->setStatusCode(Response::HTTP_CREATED); $postUrl = $this->generateUrl('get_post', ['id' => $post->getId()], UrlGeneratorInterface::ABSOLUTE_URL); $view->setHeader('Location', $postUrl); } else { $view->setData($form->getErrors()); } return $this->handleView($view); } /** * @param $id * @param Request $request * @return Response */ public function patchPostAction($id, Request $request) { $view = new View(null, Response::HTTP_BAD_REQUEST); $post = $this->getDoctrine()->getRepository('BlogBundle:Post')->find($id); if ($post === null) { $view->setStatusCode(Response::HTTP_NOT_FOUND); } else { $form = $this->createForm(PostType::class, $post, ['method' => $request->getMethod()]); $form->handleRequest($request); if ($form->isValid()) { $this->getDoctrine()->getRepository('BlogBundle:Post')->persist($post); $view->setStatusCode(Response::HTTP_NO_CONTENT); $postUrl = $this->generateUrl('get_post', ['id' => $post->getId()], UrlGeneratorInterface::ABSOLUTE_URL); $view->setHeader('Location', $postUrl); } else { $view->setData($form->getErrors()); } } return $this->handleView($view); } /** * @param $id * @return Response */ public function deletePostAction($id) { $view = new View(null, Response::HTTP_NOT_FOUND); $post = $this->getDoctrine()->getRepository('BlogBundle:Post')->find($id); if ($post !== null) { $this->getDoctrine()->getRepository('BlogBundle:Post')->delete($post); $view->setStatusCode(Response::HTTP_NO_CONTENT); } return $this->handleView($view); } } Con FOSRestBundle , no necesita declarar una ruta para cada método; simplemente siga la convención con los nombres de los métodos del controlador, y JMSSerializerBundle convertirá automáticamente sus modelos a JSON.
API REST en Laravel
En primer lugar, debe definir las rutas. Puede hacer esto en la sección API de las reglas de ruta para desactivar algunos componentes de middleware predeterminados y activar otros. La sección API se encuentra en el archivo routes/api.php .
<?php // routes/api.php Route::resource('/posts', 'BlogController'); En el modelo, debe definir la propiedad $fillable para pasar variables en los métodos de creación y actualización del modelo:
<?php // app/Post.php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { protected $fillable = ['title', 'content']; // …Ahora vamos a definir el controlador:
<?php // app/Http/Controllers/BlogController.php namespace App\Http\Controllers; use App\Post; use Illuminate\Http\Request; use Illuminate\Http\Response; class BlogController extends Controller { public function show(Post $post) { return $post; } public function store(Request $request) { $post = Post::create($request->get('post')); return response(null, Response::HTTP_CREATED, ['Location'=>'/posts/'.$post->id]); } public function update(Post $post, Request $request) { $post->update($request->get('post')); return response(null, Response::HTTP_NO_CONTENT, ['Location'=>'/posts/'.$post->id]); } public function destroy(Post $post) { $post->delete(); return response(null, Response::HTTP_NO_CONTENT); } } En Symfony, estás usando FosRestBundle , que envuelve los errores en JSON. En Laravel, debes hacerlo tú mismo. Debe actualizar el método de procesamiento en el controlador de excepciones para devolver errores JSON para esperar solicitudes JSON:
<?php // app/Exceptions/Handler.php namespace App\Exceptions; use Exception; use Illuminate\Auth\AuthenticationException; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; class Handler extends ExceptionHandler { /** * Render an exception into an HTTP response. * * @param \Illuminate\Http\Request $request * @param \Exception $exception * @return \Illuminate\Http\Response */ public function render($request, Exception $exception) { if ($request->expectsJson()) { $status = 400; if ($this->isHttpException($exception)) { $status = $exception->getStatusCode(); } elseif ($exception instanceof ModelNotFoundException) { $status = 404; } $response = ['message' => $exception->getMessage(), 'code' => $exception->getCode()]; return response()->json($response, $status); } return parent::render($request, $exception); } // ... }API REST: Symfony frente a Laravel
Como puedes ver, para una API REST típica, Laravel es mucho más simple que Symfony.
Elegir al ganador: ¿Symfony o Laravel?
No hay un ganador claro entre Laravel y Symfony, ya que todo depende de tu objetivo final.
Laravel es una mejor opción si:
- Esta es su primera experiencia con el marco, ya que es fácil de aprender y tiene una sintaxis más simple y mejores materiales de aprendizaje.
- Está creando un producto de inicio y verificando su hipótesis, ya que es bueno para el desarrollo rápido de aplicaciones y los desarrolladores de Laravel son fáciles de encontrar.
Symfony es la mejor opción si:
- Está creando una aplicación empresarial compleja, ya que es muy escalable, fácil de mantener y está bien estructurada.
- Estás construyendo una migración de un gran proyecto a largo plazo, ya que Symfony tiene planes de lanzamiento predecibles para los próximos seis años, por lo que es menos probable que haya sorpresas.
