Frameworks PHP: Escolhendo entre Symfony e Laravel
Publicados: 2022-03-11Hoje, ao iniciar um novo projeto, uma das principais decisões é escolher a estrutura certa. Tornou-se difícil imaginar construir um aplicativo web complexo do zero hoje em dia sem um.
Muitas linguagens populares para desenvolvimento web têm seu framework “padrão”, como Ruby on Rails para Ruby ou Django para Python. No entanto, o PHP não tem um padrão único e tem várias opções populares para escolher.
De acordo com as tendências do Google e GitHub, os frameworks PHP mais populares são o Symfony com 13,7k estrelas e o Laravel com 29k estrelas (no momento da redação deste artigo).
Neste artigo, vou comparar esses dois frameworks e mostrar como implementar recursos simples e cotidianos com cada um. Dessa forma, você pode comparar o código de exemplos da vida real lado a lado.
Este artigo pressupõe fortes habilidades em PHP e uma compreensão do paradigma de arquitetura MVC, mas nenhuma experiência anterior com Symfony ou Laravel é necessária.
Os contendores
Laravel
Ao falar sobre o Laravel, estamos nos referindo ao Laravel versão 4 e posteriores. O Laravel 4 foi lançado em 2013 e representou uma reescrita completa do framework. A funcionalidade do framework foi desacoplada em componentes separados, que foram gerenciados com o Composer, em vez de tudo estar em um único repositório de código enorme.
O Laravel se declara como um framework para desenvolvimento rápido com uma sintaxe simples e bonita que é fácil de aprender, ler e manter. É o framework mais popular em 2016. De acordo com as tendências do Google, é três vezes mais popular que outros frameworks e, no GitHub, tem duas vezes mais estrelas que os concorrentes.
Symfony
O Symfony 2 foi lançado em 2011, mas não deve ser confundido com o Symfony 1, que era um framework totalmente diferente com princípios subjacentes diferentes. Fabien Potencier criou o Symfony 2, e a versão atual é 3.2, que é uma versão incremental do Symfony 2. Portanto, eles são frequentemente chamados simplesmente de Symfony2/3.
Assim como o Laravel 4, o Symfony 2 é projetado como um conjunto de componentes desacoplados. Existem dois benefícios aqui: Podemos substituir qualquer componente em um projeto Symfony, e podemos pegar e usar qualquer componente Symfony em um projeto não Symfony. Os componentes do Symfony podem servir como ótimos exemplos de código e são usados em muitos projetos de código aberto, como Drupal, phpBB e Codeception. Na verdade, o próprio Laravel usa nada menos que 14 componentes Symfony. Entender o Symfony oferece muitos benefícios ao trabalhar com outros projetos.
Instalações da Estrutura
Ambos os frameworks vêm com instaladores e wrappers disponíveis através do servidor web embutido do PHP.
Instalação do Symfony
A instalação do Symfony é tão simples quanto o seguinte:
# 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 É isso! Sua instalação do Symfony está disponível na URL http://localhost:8000 .
Instalação do Laravel
O processo de instalação do Laravel é quase o mesmo e tão simples quanto o do Symfony; a única diferença é que você instala o instalador do Laravel através do 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 Agora você pode visitar http://localhost:8000 e verificar sua instalação do Laravel.
Nota: Tanto o Laravel quanto o Symfony rodam na mesma porta localhost (8000) por padrão, então você não pode ter essas instâncias padrão rodando simultaneamente. Não se esqueça de parar o servidor Symfony executando php bin/console server:stop antes de iniciar o servidor Laravel.
Sobre a instalação da estrutura
Estes são exemplos de uma instalação básica. Para exemplos de uso mais avançados, como configurar projetos com domínios locais ou executar vários projetos ao mesmo tempo, ambos os frameworks fornecem caixas Vagrant:
- Laravel Homestead,
- Symfony Homestead.
Configurações básicas da estrutura
Configuração Básica do Symfony
O Symfony usa YAML como sintaxe para especificar sua configuração. A configuração padrão está localizada no arquivo app/config/config.yml e se parece com o exemplo a seguir:
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 criar uma configuração específica do ambiente, crie o arquivo app/config/config_ENV.yml contendo os parâmetros básicos de configuração. Aqui está um exemplo de um arquivo config_dev.yml para o ambiente de desenvolvimento:
imports: - { resource: config.yml } # ... web_profiler: toolbar: true # ... Este exemplo ativa a ferramenta web_profiler Symfony apenas para o ambiente de desenvolvimento. Essa ferramenta ajuda você a depurar e criar o perfil do seu aplicativo diretamente na janela do navegador.
Nos arquivos de configuração, você também pode notar construções %secret% . Eles nos permitem colocar variáveis específicas do ambiente no arquivo parameters.yml separado. Este arquivo pode ser único em cada máquina e não é armazenado sob controle de versão. Para controle de versão, temos um arquivo parameters.yml.dist especial que é o modelo para o arquivo parameters.yml .
Aqui está um exemplo do arquivo parameters.yml :
parameters: database_host: 127.0.0.1 database_port: null database_name: symfony database_user: root database_password: null secret: f6b16aea89dc8e4bec811dea7c22d9f0e55593afConfiguração Básica do Laravel
A configuração do Laravel parece muito diferente daquela do Symfony. A única coisa que eles têm em comum é que ambos usam arquivos que não são armazenados sob controle de versão ( .env no caso Laravel) e um template para gerar este arquivo ( .env.example ). Este arquivo tem uma lista de chaves e valores, como no exemplo a seguir:
APP_ENV=local APP_KEY=base64:Qm8mIaur5AygPDoOrU+IKecMLWgmcfOjKJItb7Im3Jk= APP_DEBUG=true APP_LOG_LEVEL=debug APP_URL=http://localhost Assim como o arquivo YAML do Symfony, este para Laravel também é legível por humanos e parece limpo. Você também pode criar o arquivo .env.testing que será usado ao executar testes PHPUnit.
A configuração do aplicativo é armazenada em arquivos .php no diretório config . A configuração básica é armazenada no arquivo app.php e a configuração específica do componente é armazenada em arquivos <component>.php (por exemplo, cache.php ou mail.php ). Aqui está um exemplo de um 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', // ... ];Configuração do Framework: Symfony vs. Laravel
Os mecanismos de configuração de aplicativos do Symfony permitem que você crie arquivos diferentes para ambientes diferentes. Além disso, impede que você injete lógica PHP complexa na configuração YAML.
No entanto, você pode se sentir mais confortável com a sintaxe de configuração PHP padrão que o Laravel está usando e não precisa aprender a sintaxe YAML.
Roteamento e controlador
Em geral, um aplicativo da Web de back-end tem uma responsabilidade principal: ler cada solicitação e criar uma resposta dependendo do conteúdo da solicitação. O controlador é uma classe responsável por transformar a solicitação em resposta chamando os métodos da aplicação, enquanto o roteador é um mecanismo que ajuda a detectar qual classe de controlador e método você deve executar para uma determinada solicitação.
Vamos criar um controlador que mostrará uma página de postagem de blog solicitada da rota /posts/{id} .
Roteamento e controlador em 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)]); } }Roteador
Route::get('/posts/{id}', 'BlogController@show'); Definimos a rota para solicitações GET . Todas as solicitações com o URI correspondente ao /posts/{id} executarão o método do controlador BlogController show e passarão o id do parâmetro para esse método. No controller, estamos tentando encontrar o objeto do model POST com o id passado, e chamar o helper do Laravel view() para renderizar a página.
Roteamento e controlador no Symfony
No Symfony, exampleController é um pouco maior:
<?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]); } } Você pode ver que já incluímos @Route("/posts/{id}”) na anotação, então só precisamos incluir o controlador no arquivo de configuração routing.yml :
blog: resource: "@BlogBundle/Controller/" type: annotation prefix: /A lógica passo a passo é a mesma do caso Laravel.
Roteamento e controlador: Symfony vs. Laravel
Nesta fase, você pode pensar que o Laravel é muito melhor que o Symfony. Isso é verdade, no começo. Parece muito melhor e mais fácil de começar. No entanto, nas aplicações da vida real, você não deve chamar o Doctrine do controlador. Em vez disso, você deve chamar um serviço que tentará encontrar a postagem ou lançar HTTP 404 Exception .
Modelos
Laravel vem com um motor de template chamado Blade e Symfony vem com Twig. Ambos os mecanismos de modelo implementam dois recursos principais:
- Herança de modelo
- Blocos ou seções
Ambos os recursos permitem definir um modelo básico com seções substituíveis e modelos filho que preenchem os valores dessas seções.
Vamos considerar o exemplo de uma página de postagem de blog novamente.
Mecanismo de modelo de lâmina 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 Agora você pode dizer ao Laravel em seu Controller para renderizar o template post.blade.php . Você se lembra da nossa chamada view('post', …) no exemplo anterior do Controller? Você não precisa saber em seu código que ele é herdado de algum outro modelo. Tudo é definido apenas em seus modelos, no nível de visualização.
Mecanismo de modelo do 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 %}Modelos: Symfony vs. Laravel
Estruturalmente, os modelos Blade e Twig são bastante semelhantes. Ambos geram modelos em código PHP e trabalham rápido, e ambos implementam estruturas de controle, como instruções if e loops. O recurso mais importante que ambos os mecanismos têm é que eles escapam da saída por padrão, o que ajuda a evitar ataques XSS.
Além da sintaxe, a principal diferença é que o Blade permite que você injete código PHP diretamente em seus templates e o Twig não. Em vez disso, o Twig permite que você use filtros.
Por exemplo, se você quiser colocar uma string em maiúscula, no Blade você deve especificar o seguinte:
{{ ucfirst('welcome friend') }}No Twig, por outro lado, você faria o seguinte:
{{ 'welcome friend'|capitalize }}No Blade, é mais fácil estender algumas funcionalidades, mas o Twig não permite nenhum código PHP direto nos templates.
Injeção de dependência
Os aplicativos têm muitos serviços e componentes diferentes, com várias interdependências. Você precisa armazenar todas as informações sobre os objetos criados e suas dependências de alguma forma.
Aí vem nosso próximo componente - Service Container. É um objeto PHP que cria serviços solicitados e armazena informações sobre os objetos criados e suas dependências.
Vamos considerar o seguinte exemplo: Você está criando uma classe PostService para implementar um método que é responsável por criar uma nova postagem no blog. Essa classe depende de dois outros serviços: PostRepository , que é responsável por armazenar as informações no banco de dados, e SubscriberNotifier , que é responsável por notificar os usuários inscritos sobre a nova postagem. Para que funcione, você precisa passar esses dois serviços como os argumentos construtores de PostService ou, em outras palavras, você precisa injetar essas dependências.
Exemplo de injeção de dependência do Symfony
Primeiro, vamos definir nossos serviços de amostra:
<?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); } }A seguir está a configuração de injeção de dependência:
# 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\PostAgora você pode solicitar seu serviço de postagem em qualquer lugar do código do seu objeto Service Container. Por exemplo, no controlador poderia ser algo assim:
// Controller file. $post variable defined below $this->get('blog.post_service')->create($post);O Service Container é um ótimo componente e ajuda a construir seu aplicativo seguindo os princípios de design SOLID.
Exemplo de injeção de dependência de Laravel
É muito mais fácil gerenciar dependências no Laravel. Vamos considerar o mesmo exemplo:
<?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); } } Aqui vem a beleza do Laravel - você não precisa criar configurações de dependência . O Laravel verifica automaticamente as dependências de PostService em seus tipos de argumentos de construtor e as resolve automaticamente.

Você também pode usar a injeção em seu método do controlador para usar o PostService “digitando-o” nos argumentos do 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); } }Injeção de dependência: Symfony vs. Laravel
A autodetecção do Laravel funciona muito bem. O Symfony tem um recurso semelhante chamado “autowire” que é desativado por padrão e pode ser ativado adicionando autowire: true à sua configuração de dependência, mas requer alguma configuração. A maneira Laravel é mais simples.
Mapeamento Relacional de Objetos (ORM)
Para trabalhar com bancos de dados, ambas as estruturas vêm com recursos de Mapeamento Relacional de Objeto (ORM). O ORM mapeia registros do banco de dados para objetos no código. Para fazer isso, você deve criar modelos para cada tipo de registro (ou cada tabela) em seu banco de dados.
O Symfony usa um projeto de terceiros Doctrine para interagir com o banco de dados, enquanto o Laravel usa sua própria biblioteca Eloquent.
O Eloquent ORM implementa o padrão ActiveRecord para trabalhar com o banco de dados. Nesse padrão, cada modelo está ciente da conexão com o banco de dados e pode interagir com ele. Por exemplo, ele pode salvar dados no banco de dados, atualizar ou excluir um registro.
O Doctrine implementa o padrão Data Mapper, onde os modelos não sabem nada sobre o banco de dados; eles estão cientes apenas dos dados em si. Uma camada especial separada, EntityManager , armazena todas as informações sobre a interação entre modelos e bancos de dados e trata de todas as operações.
Vamos dar um exemplo para entender a diferença. Digamos que seu modelo tenha uma chave de id primária, título, conteúdo e autor. A tabela Posts armazena apenas o id do autor , portanto, você precisa criar uma relação com a tabela Users .
Doutrina
Vamos começar definindo os 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;Aqui, criamos informações de mapeamento de modelo e agora podemos usar um auxiliar para gerar os stubs de método:
php bin/console doctrine:generate:entities BlogBundleEm seguida, definimos os métodos do repositório de postagem:
<?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(); } } Agora você pode chamar esses métodos do serviço ou, por exemplo, do 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);Eloquente
O modelo User vem com o Laravel e é definido por padrão, então você só precisa definir um modelo para o Post .
<?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'); } } Isso é tudo para os modelos. No Eloquent, você não precisa definir as propriedades do modelo, pois ele as constrói dinamicamente com base na estrutura da tabela do banco de dados. Para armazenar um novo post $post no banco de dados, você precisa fazer esta chamada (do controlador, por exemplo):
$post->save();Para encontrar todas as postagens de um autor com um determinado nome, a melhor abordagem seria encontrar um usuário com seu nome e solicitar as postagens de todos os usuários:
$posts = Post::whereHas('author', function ($q) { $q->where('name', 'Karim'); })->get();ORM: Symfony vs. Laravel
Com relação ao ORM, o Eloquent parece muito mais amigável para desenvolvedores PHP e mais fácil de aprender do que o Doctrine.
Event Dispatcher vs. Middleware
Uma das coisas mais importantes para entender sobre um framework é seu ciclo de vida.
Symfony e Event Dispatcher
Para converter uma requisição em uma resposta, o Symfony usa EventDispatcher. Conseqüentemente, ele dispara diferentes eventos de ciclo de vida e ouvintes de eventos especiais para lidar com esses eventos. No início, ele despacha o evento kernel.request que inclui informações de solicitação. O principal ouvinte padrão desse evento é RouterListener , que chama o componente roteador para encontrar uma regra de rota adequada para a solicitação atual. Depois disso, outros eventos são executados passo a passo. Os ouvintes de eventos típicos são uma verificação de segurança, verificação de token CSRF e um processo de registro. Se você quiser adicionar alguma funcionalidade no ciclo de vida da solicitação, precisará criar um EventListener personalizado e assiná-lo no evento necessário.
Laravel e Middleware
O Laravel usa uma solução diferente: middleware. Eu gosto de comparar o middleware a uma cebola: seu aplicativo tem certas camadas e uma solicitação passa por essas camadas no caminho para o controlador e vice-versa. Então, se você quiser estender a lógica da sua aplicação e adicionar alguma funcionalidade no ciclo de vida da requisição, você precisa adicionar uma camada adicional à sua lista de middleware, e o Laravel irá executá-la.
API REST
Vamos tentar criar um exemplo básico de CRUD para gerenciar uma postagem no blog:
- Criar -
POST /posts/ - Ler -
GET /posts/{id} - Atualização -
PATCH /posts/{id} - Excluir -
DELETE /posts/{id}
API REST no Symfony
O Symfony não possui uma solução fácil e pronta para uso para criação rápida de API REST, mas possui ótimos pacotes de terceiros FOSRestBundle e JMSSerializerBundle .
Vamos considerar o exemplo de trabalho mínimo com FOSRestBundle e JMSSerializerBundle . Depois de instalá-los e ativá-los no AppKernel , você pode definir na configuração do pacote que usará o formato JSON e que isso não precisa ser incluído nas solicitações de URL:
#app/config/config.yml fos_rest: routing_loader: default_format: json include_format: falseNa configuração de roteamento, você deve especificar que este controlador implementará um recurso REST:
#app/config/routing.yml blog: resource: BlogBundle\Controller\PostController type: restVocê implementou um método persist no repositório no exemplo anterior; agora você precisa adicionar um método de exclusão:
// src/BlogBundle/Repository/PostRepository.php public function delete(Post $post) { $this->getEntityManager()->remove($post); $this->getEntityManager()->flush(); }Em seguida, você precisa criar uma classe de formulário para aceitar solicitações de entrada e mapeá-las para o modelo. Você pode fazer isso usando um ajudante CLI:
php bin/console doctrine:generate:form BlogBundle:PostVocê receberá um tipo de formulário gerado com o seguinte 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'; } }Agora vamos implementar nosso controller.
Nota: o código que vou mostrar não é perfeito. Ele viola alguns princípios de design, mas pode ser facilmente refatorado. O objetivo principal é mostrar como implementar cada método, passo a passo.
<?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); } } Com FOSRestBundle , você não precisa declarar uma rota para cada método; basta seguir a convenção com os nomes dos métodos do controlador e o JMSSerializerBundle converterá automaticamente seus modelos em JSON.
API REST em Laravel
Primeiro, você precisa definir rotas. Você pode fazer isso na seção API das regras de rota para desativar alguns componentes de middleware padrão e ativar outros. A seção API está localizada no arquivo routes/api.php .
<?php // routes/api.php Route::resource('/posts', 'BlogController'); No modelo, você deve definir a propriedade $fillable para passar variáveis nos métodos create e update do modelo:
<?php // app/Post.php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { protected $fillable = ['title', 'content']; // …Agora vamos definir o 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); } } No Symfony, você está usando FosRestBundle , que envolvia erros em JSON. No Laravel, você precisa fazer isso sozinho. Você precisa atualizar o método de renderização no manipulador de exceção para retornar erros JSON para esperar solicitações 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 vs. Laravel
Como você pode ver, para uma API REST típica, o Laravel é muito mais simples que o Symfony.
Escolhendo o vencedor: Symfony ou Laravel?
Não há um vencedor claro entre Laravel e Symfony, pois tudo depende do seu objetivo final.
Laravel é uma escolha melhor se:
- Esta é sua primeira experiência com o framework, pois é fácil de aprender e possui uma sintaxe mais simples e melhores materiais de aprendizagem.
- Você está construindo um produto de inicialização e verificando sua hipótese, pois é bom para o desenvolvimento rápido de aplicativos e os desenvolvedores de Laravel são fáceis de encontrar.
Symfony é a melhor opção se:
- Você está construindo um aplicativo empresarial complexo, pois é muito escalável, sustentável e bem estruturado.
- Você está construindo uma migração de um grande projeto de longo prazo, já que o Symfony tem planos de lançamento previsíveis para os próximos seis anos, então é menos provável que haja surpresas.
