Frameworks PHP : choisir entre Symfony et Laravel

Publié: 2022-03-11

Aujourd'hui, lors du démarrage d'un nouveau projet, l'une des décisions clés est de choisir le bon cadre. Il est devenu difficile d'imaginer créer une application Web complexe à partir de zéro de nos jours sans une seule.

De nombreux langages populaires pour le développement Web ont leur framework « par défaut », comme Ruby on Rails pour Ruby ou Django pour Python. Cependant, PHP n'a pas une telle valeur par défaut et propose plusieurs options populaires parmi lesquelles choisir.

Selon Google Trends et GitHub, les frameworks PHP les plus populaires sont Symfony avec 13,7k étoiles et Laravel avec 29k étoiles (au moment de la rédaction de cet article).

Dans cet article, je vais comparer ces deux frameworks et vous montrer comment implémenter des fonctionnalités simples et quotidiennes avec chacun. De cette façon, vous pouvez comparer le code d'exemples réels côte à côte.

Cet article suppose de solides compétences en PHP et une compréhension du paradigme architectural MVC, mais aucune expérience préalable avec Symfony ou Laravel n'est requise.

Les prétendants

Laravel

Lorsque nous parlons de Laravel, nous faisons référence à Laravel version 4 et au-delà. Laravel 4 est sorti en 2013 et représentait une réécriture complète du framework. La fonctionnalité du framework a été découplée en composants séparés, qui ont été gérés avec Composer, au lieu que tout soit dans un seul et même énorme référentiel de code.

Laravel se déclare comme un cadre de développement rapide avec une syntaxe simple et belle, facile à apprendre, à lire et à entretenir. C'est le framework le plus populaire en 2016. Selon les tendances de Google, il est trois fois plus populaire que les autres frameworks, et sur GitHub, il compte deux fois plus d'étoiles que ses concurrents.

SymfonyName

Symfony 2 est sorti en 2011, mais il ne faut pas le confondre avec Symfony 1, qui était un framework totalement différent avec des principes sous-jacents différents. Fabien Potencier a créé Symfony 2, et la version actuelle est la 3.2, qui est une version incrémentale de Symfony 2. Par conséquent, ils sont souvent appelés simplement Symfony2/3.

Comme Laravel 4, Symfony 2 est conçu comme un ensemble de composants découplés. Il y a deux avantages ici : nous pouvons remplacer n'importe quel composant dans un projet Symfony, et nous pouvons prendre et utiliser n'importe quel composant Symfony dans un projet non-Symfony. Les composants Symfony peuvent servir d'excellents exemples de code et ils sont utilisés dans de nombreux projets open source tels que Drupal, phpBB et Codeception. En fait, Laravel lui-même utilise pas moins de 14 composants Symfony. Comprendre Symfony vous offre donc de nombreux avantages lorsque vous travaillez avec d'autres projets.

Installations de cadre

Les deux frameworks sont livrés avec des programmes d'installation et des wrappers disponibles via le serveur Web intégré PHP.

Installation de Symfony

L'installation de Symfony est aussi simple que ceci :

 # 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

C'est ça! Votre installation Symfony est disponible sur l'URL http://localhost:8000 .

Installation de Laravel

Le processus d'installation de Laravel est presque le même et aussi simple que celui de Symfony ; la seule différence est que vous installez le programme d'installation de Laravel via 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

Vous pouvez maintenant visiter http://localhost:8000 et vérifier votre installation Laravel.

Remarque : Laravel et Symfony fonctionnent tous deux sur le même port localhost (8000) par défaut, vous ne pouvez donc pas exécuter simultanément ces instances par défaut. N'oubliez pas d'arrêter le serveur Symfony en exécutant php bin/console server:stop avant de lancer le serveur Laravel.

À propos de l'installation du framework

Ce sont des exemples d'installation de base. Pour des exemples d'utilisation plus avancés, comme la possibilité de configurer des projets avec des domaines locaux ou d'exécuter plusieurs projets à la fois, les deux frameworks fournissent des boîtes Vagrant :

  • Ferme de Laravel,
  • Ferme Symfony.

Configurations de cadre de base

Configuration de base de Symfony

Symfony utilise YAML comme syntaxe pour spécifier sa configuration. La configuration par défaut se trouve dans le fichier app/config/config.yml et ressemble à l'exemple suivant :

 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%' # ...

Pour créer une configuration spécifique à l'environnement, créez le fichier app/config/config_ENV.yml contenant les paramètres de configuration de base. Voici un exemple de fichier config_dev.yml pour l'environnement de développement :

 imports: - { resource: config.yml } # ... web_profiler: toolbar: true # ...

Cet exemple active l'outil web_profiler Symfony uniquement pour l'environnement de développement. Cet outil vous aide à déboguer et à profiler votre application directement dans la fenêtre du navigateur.

Dans les fichiers de configuration, vous pouvez également remarquer des constructions %secret% . Ils nous permettent de mettre des variables spécifiques à l'environnement dans le fichier parameters.yml séparé. Ce fichier peut être unique sur chaque machine et n'est pas stocké sous contrôle de version. Pour le contrôle de version, nous avons un fichier parameters.yml.dist spécial qui est le modèle du fichier parameters.yml .

Voici un exemple de fichier parameters.yml :

 parameters: database_host: 127.0.0.1 database_port: null database_name: symfony database_user: root database_password: null secret: f6b16aea89dc8e4bec811dea7c22d9f0e55593af

Configuration de base de Laravel

La configuration de Laravel est très différente de celle de Symfony. La seule chose qu'ils ont en commun est qu'ils utilisent tous les deux des fichiers qui ne sont pas stockés sous contrôle de version ( .env dans le cas de Laravel) et un modèle pour générer ce fichier ( .env.example ). Ce fichier contient une liste de clés et de valeurs, comme dans l'exemple suivant :

 APP_ENV=local APP_KEY=base64:Qm8mIaur5AygPDoOrU+IKecMLWgmcfOjKJItb7Im3Jk= APP_DEBUG=true APP_LOG_LEVEL=debug APP_URL=http://localhost

Comme le fichier Symfony YAML, celui-ci pour Laravel est également lisible par l'homme et semble propre. Vous pouvez également créer un fichier .env.testing qui sera utilisé lors de l'exécution des tests PHPUnit.

La configuration de l'application est stockée dans des fichiers .php dans le répertoire config . La configuration de base est stockée dans le fichier app.php et la configuration spécifique au composant est stockée dans les fichiers <component>.php (par exemple, cache.php ou mail.php ). Voici un exemple de fichier 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', // ... ];

Configuration du framework : Symfony contre Laravel

Les mécanismes de configuration des applications de Symfony vous permettent de créer différents fichiers pour différents environnements. De plus, cela vous empêche d'injecter une logique PHP complexe dans la configuration YAML.

Cependant, vous vous sentirez peut-être plus à l'aise avec la syntaxe de configuration PHP par défaut utilisée par Laravel et vous n'aurez pas à apprendre la syntaxe YAML.

Routage et contrôleur

En général, une application Web back-end a une responsabilité principale : lire chaque requête et créer une réponse en fonction du contenu de la requête. Le contrôleur est une classe chargée de transformer la requête en réponse en appelant des méthodes d'application, tandis que le routeur est un mécanisme qui vous aide à détecter quelle classe et méthode de contrôleur vous devez exécuter pour une requête particulière.

Créons un contrôleur qui affichera une page de publication de blog demandée à partir de la route /posts/{id} .

Routage et contrôleur à Laravel

Manette

 <?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)]); } }

Routeur

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

Nous avons défini la route pour les requêtes GET . Toutes les requêtes dont l'URI correspond à /posts/{id} exécuteront la méthode show the du contrôleur BlogController et transmettront le paramètre id à cette méthode. Dans le contrôleur, nous essayons de trouver l'objet du modèle POST avec l' id passé et appelons Laravel helper view() pour rendre la page.

Routage et contrôleur dans Symfony

Dans Symfony, exampleController est un peu plus gros :

 <?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]); } }

Vous pouvez voir que nous avons déjà inclus @Route("/posts/{id}”) dans l'annotation, il nous suffit donc d'inclure le contrôleur dans le fichier de configuration routing.yml :

 blog: resource: "@BlogBundle/Controller/" type: annotation prefix: /

La logique étape par étape est la même que dans le cas Laravel.

Routage et contrôleur : Symfony contre Laravel

À ce stade, vous pourriez penser que Laravel est beaucoup plus agréable que Symfony. C'est vrai, au début. Il a l'air bien meilleur et plus facile à démarrer. Cependant, dans les applications réelles, vous ne devriez pas appeler Doctrine depuis le contrôleur. Au lieu de cela, vous devez appeler un service qui essaiera de trouver le message ou lancera HTTP 404 Exception .

Modèles

Laravel est livré avec un moteur de template appelé Blade et Symfony est livré avec Twig. Les deux moteurs de modèles implémentent deux fonctionnalités principales :

  1. Héritage de modèle
  2. Blocs ou sections

Les deux fonctionnalités vous permettent de définir un modèle de base avec des sections remplaçables et des modèles enfants qui remplissent les valeurs de ces sections.

Reprenons l'exemple d'une page d'article de blog.

Moteur de modèle de lame 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

Vous pouvez maintenant dire à Laravel dans votre contrôleur de rendre le modèle post.blade.php . Vous souvenez-vous de notre appel view('post', …) dans l'exemple précédent de Controller ? Vous n'avez pas besoin de savoir dans votre code qu'il est hérité d'un autre modèle. Tout est défini uniquement dans vos modèles, au niveau de la vue.

Moteur de template 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 %}

Modèles : Symfony contre Laravel

Structurellement, les modèles Blade et Twig sont assez similaires. Les deux génèrent des modèles dans le code PHP et fonctionnent rapidement, et les deux implémentent des structures de contrôle, telles que des instructions if et des boucles. La caractéristique la plus importante des deux moteurs est qu'ils échappent à la sortie par défaut, ce qui aide à prévenir les attaques XSS.

Outre la syntaxe, la principale différence est que Blade vous permet d'injecter du code PHP directement dans vos modèles, contrairement à Twig. Au lieu de cela, Twig vous permet d'utiliser des filtres.

Par exemple, si vous souhaitez mettre une chaîne en majuscule, dans Blade, vous devez spécifier ce qui suit :

 {{ ucfirst('welcome friend') }}

Dans Twig, en revanche, vous feriez ce qui suit :

 {{ 'welcome friend'|capitalize }}

Dans Blade, il est plus facile d'étendre certaines fonctionnalités, mais Twig n'autorise aucun code PHP direct dans les modèles.

Injection de dépendance

Les applications comportent de nombreux services et composants différents, avec diverses interdépendances. Vous devez stocker toutes les informations sur les objets créés et leurs dépendances d'une manière ou d'une autre.

Voici notre prochain composant - Service Container. C'est un objet PHP qui crée les services demandés et stocke des informations sur les objets créés et leurs dépendances.

Considérons l'exemple suivant : Vous créez une classe PostService pour implémenter une méthode responsable de la création d'un nouveau billet de blog. Cette classe dépend de deux autres services : PostRepository , qui est responsable du stockage des informations dans la base de données, et SubscriberNotifier , qui est chargé d'informer les utilisateurs abonnés de la nouvelle publication. Pour que cela fonctionne, vous devez transmettre ces deux services en tant qu'arguments du constructeur de PostService ou, en d'autres termes, vous devez injecter ces dépendances.

Exemple d'injection de dépendance Symfony

Tout d'abord, définissons nos exemples de services :

 <?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); } }

Vient ensuite la configuration de l'injection de dépendance :

 # 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\Post

Vous pouvez maintenant demander votre service de publication n'importe où dans le code à partir de votre objet Service Container. Par exemple, dans le contrôleur, cela pourrait ressembler à ceci :

 // Controller file. $post variable defined below $this->get('blog.post_service')->create($post);

Service Container est un excellent composant, et il aide à créer votre application en suivant les principes de conception SOLID.

Connexe : Injection de véritable dépendance avec les composants Symfony

Exemple d'injection de dépendance Laravel

Il est beaucoup plus facile de gérer les dépendances dans Laravel. Prenons le même exemple :

 <?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); } }

Voici la beauté de Laravel - vous n'avez pas besoin de créer des configurations de dépendance . Laravel analyse automatiquement les dépendances de PostService dans ses types d'arguments de constructeur et les résout automatiquement.

Vous pouvez également utiliser l'injection dans votre méthode de contrôleur pour utiliser PostService en le "typant" dans les arguments de la méthode :

 <?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); } }

Injection de dépendance : Symfony contre Laravel

La détection automatique de Laravel fonctionne très bien. Symfony a une capacité similaire appelée « autowire » qui est désactivée par défaut et pourrait être activée en ajoutant autowire: true à votre configuration de dépendance, mais cela nécessite une certaine configuration. La méthode Laravel est plus simple.

Mappage objet-relationnel (ORM)

Pour travailler avec des bases de données, les deux frameworks sont livrés avec des fonctionnalités de mappage objet-relationnel (ORM). ORM mappe les enregistrements de la base de données aux objets du code. Pour cela, vous devez créer des modèles pour chaque type d'enregistrement (ou chaque table) de votre base de données.

Symfony utilise un projet tiers Doctrine pour interagir avec la base de données, tandis que Laravel utilise sa propre bibliothèque Eloquent.

L'ORM Eloquent implémente le modèle ActiveRecord pour travailler avec la base de données. Dans ce modèle, chaque modèle est conscient de la connexion à la base de données et peut interagir avec elle. Par exemple, il peut enregistrer des données dans la base de données, mettre à jour ou supprimer un enregistrement.

Doctrine implémente le modèle Data Mapper, où les modèles ne savent rien de la base de données ; ils ne sont conscients que des données elles-mêmes. Une couche séparée spéciale, EntityManager , stocke toutes les informations sur l'interaction entre les modèles et les bases de données, et gère toutes les opérations.

Prenons un exemple pour comprendre la différence. Supposons que votre modèle ait une clé d' id principale, un titre, un contenu et un auteur. La table Posts stocke uniquement l' id de l'auteur , vous devez donc créer une relation avec la table Utilisateurs .

Doctrine

Commençons par définir les modèles :

 <?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;

Ici, nous avons créé des informations de mappage de modèle et pouvons maintenant utiliser un assistant pour générer les stubs de méthode :

 php bin/console doctrine:generate:entities BlogBundle

Ensuite, nous définissons les méthodes post-repository :

 <?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(); } }

Vous pouvez maintenant appeler ces méthodes depuis le service ou, par exemple, depuis 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);

Éloquent

Le modèle User est livré avec Laravel et il est défini par défaut, vous n'avez donc besoin de définir qu'un seul modèle pour le 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'); } }

C'est tout pour les modèles. Dans Eloquent, vous n'avez pas à définir les propriétés du modèle, car il les construit dynamiquement en fonction de la structure de la table de la base de données. Pour stocker un nouveau message $post dans la base de données, vous devez effectuer cet appel (depuis le contrôleur, par exemple) :

 $post->save();

Pour trouver tous les messages d'un auteur avec un nom donné, la meilleure approche serait de trouver un utilisateur avec son nom et de demander tous les messages des utilisateurs :

 $posts = Post::whereHas('author', function ($q) { $q->where('name', 'Karim'); })->get();

ORM : Symfony contre Laravel

En ce qui concerne ORM, Eloquent semble beaucoup plus convivial pour les développeurs PHP et plus facile à apprendre que Doctrine.

Répartiteur d'événements vs middleware

Cycle de vie Symfony vs Laravel

L'une des choses les plus importantes à comprendre à propos d'un framework est son cycle de vie.

Symfony et répartiteur d'événements

Pour convertir une requête en réponse, Symfony utilise EventDispatcher. Il déclenche par conséquent différents événements de cycle de vie et des écouteurs d'événements spéciaux pour gérer ces événements. Au début, il distribue l'événement kernel.request qui inclut les informations de requête. Le principal écouteur par défaut de cet événement est RouterListener , qui appelle le composant routeur pour trouver une règle de routage appropriée pour la demande actuelle. Après cela, d'autres événements sont exécutés étape par étape. Les écouteurs d'événement typiques sont un contrôle de sécurité, une vérification de jeton CSRF et un processus de journalisation. Si vous souhaitez ajouter des fonctionnalités dans le cycle de vie de la demande, vous devez créer un EventListener personnalisé et l'abonner à l'événement nécessaire.

Laravel et middleware

Laravel utilise une solution différente : le middleware. J'aime comparer le middleware à un oignon : votre application comporte certaines couches et une requête passe par ces couches sur le chemin du contrôleur et en revient. Ainsi, si vous souhaitez étendre la logique de votre application et ajouter des fonctionnalités dans le cycle de vie de la demande, vous devez ajouter une couche supplémentaire à votre liste de middleware, et Laravel l'exécutera.

API REST

Essayons de créer un exemple CRUD de base pour gérer un article de blog :

  • Créer - POST /posts/
  • Lire - GET /posts/{id}
  • Mise à jour - PATCH /posts/{id}
  • Supprimer - DELETE /posts/{id}

API REST dans Symfony

Symfony n'a pas de solution prête à l'emploi pour la création rapide d'API REST, mais il a d'excellents bundles tiers FOSRestBundle et JMSSerializerBundle .

Considérons l'exemple de travail minimal avec FOSRestBundle et JMSSerializerBundle . Après les avoir installés et activés dans AppKernel , vous pouvez définir dans la configuration du bundle que vous utiliserez le format JSON et que cela n'a pas à être inclus dans les requêtes d'URL :

 #app/config/config.yml fos_rest: routing_loader: default_format: json include_format: false

Dans la configuration du routage, vous devez spécifier que ce contrôleur implémentera une ressource REST :

 #app/config/routing.yml blog: resource: BlogBundle\Controller\PostController type: rest

Vous avez implémenté une méthode persist dans le référentiel dans l'exemple précédent ; maintenant vous devez ajouter une méthode de suppression :

 // src/BlogBundle/Repository/PostRepository.php public function delete(Post $post) { $this->getEntityManager()->remove($post); $this->getEntityManager()->flush(); }

Ensuite, vous devez créer une classe de formulaire pour accepter les demandes d'entrée et les mapper au modèle. Vous pouvez le faire en utilisant un assistant CLI :

 php bin/console doctrine:generate:form BlogBundle:Post

Vous recevrez un type de formulaire généré avec le code suivant :

 <?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'; } }

Implémentons maintenant notre contrôleur.

Remarque : le code que je vais vous montrer n'est pas parfait. Il viole certains principes de conception mais pourrait être facilement refactorisé. L'objectif principal est de vous montrer comment mettre en œuvre chaque méthode, étape par étape.

 <?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); } }

Avec FOSRestBundle , vous n'avez pas besoin de déclarer une route pour chaque méthode ; suivez simplement la convention avec les noms de méthodes de contrôleur, et JMSSerializerBundle convertira automatiquement vos modèles en JSON.

API REST dans Laravel

Tout d'abord, vous devez définir des itinéraires. Vous pouvez le faire dans la section API des règles de routage pour désactiver certains composants middleware par défaut et en activer d'autres. La section API se trouve dans le fichier routes/api.php .

 <?php // routes/api.php Route::resource('/posts', 'BlogController');

Dans le modèle, vous devez définir la propriété $fillable pour transmettre des variables dans les méthodes de création et de mise à jour du modèle :

 <?php // app/Post.php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { protected $fillable = ['title', 'content']; // …

Définissons maintenant le contrôleur :

 <?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); } }

Dans Symfony, vous utilisez FosRestBundle , qui a enveloppé les erreurs dans JSON. Dans Laravel, vous devez le faire vous-même. Vous devez mettre à jour la méthode de rendu dans le gestionnaire d'exceptions pour renvoyer des erreurs JSON pour les requêtes JSON attendues :

 <?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 contre Laravel

Comme vous pouvez le voir, pour une API REST typique, Laravel est beaucoup plus simple que Symfony.

Choisir le gagnant : Symfony ou Laravel ?

Il n'y a pas de gagnant clair entre Laravel et Symfony, car tout dépend de votre objectif final.

Laravel est un meilleur choix si :

  • Il s'agit de votre première expérience avec le framework, car il est facile à apprendre et a une syntaxe plus simple et de meilleurs supports d'apprentissage.
  • Vous construisez un produit de démarrage et vérifiez votre hypothèse, car c'est bon pour le développement rapide d'applications et les développeurs Laravel sont faciles à trouver.

Symfony est la meilleure option si :

  • Vous construisez une application d'entreprise complexe, car elle est très évolutive, maintenable et bien structurée.
  • Vous construisez une migration d'un grand projet à long terme, car Symfony a des plans de publication prévisibles pour les six prochaines années, il est donc moins probable qu'il y ait des surprises.