PHP-фреймворки: выбор между Symfony и Laravel

Опубликовано: 2022-03-11

Сегодня при запуске нового проекта одним из ключевых решений является выбор правильного фреймворка. В настоящее время трудно представить создание сложного веб-приложения с нуля без него.

Многие популярные языки для веб-разработки имеют свою среду «по умолчанию», например, Ruby on Rails для Ruby или Django для Python. Однако в PHP нет такого единственного значения по умолчанию и есть несколько популярных опций на выбор.

Согласно тенденциям Google и GitHub, самыми популярными PHP-фреймворками являются Symfony с 13,7 тыс. звезд и Laravel с 29 тыс. звезд (на момент написания этой статьи).

В этой статье я собираюсь сравнить эти две платформы и показать вам, как реализовать простые повседневные функции с каждой из них. Таким образом, вы можете сравнить код реальных примеров бок о бок.

Эта статья предполагает хорошие навыки PHP и понимание архитектурной парадигмы MVC, но никакого предыдущего опыта работы с Symfony или Laravel не требуется.

Претенденты

Ларавель

Говоря о Laravel, мы имеем в виду Laravel версии 4 и выше. Laravel 4 был выпущен в 2013 году и представлял собой полную переработку фреймворка. Функциональность фреймворка была разделена на отдельные компоненты, которые управлялись с помощью Composer, вместо того, чтобы все было в одном огромном репозитории кода.

Laravel позиционирует себя как фреймворк для быстрой разработки с простым и красивым синтаксисом, который легко изучать, читать и поддерживать. Это самый популярный фреймворк 2016 года. По трендам Google он в три раза популярнее других фреймворков, а на GitHub у него в два раза больше звезд, чем у конкурентов.

Симфони

Symfony 2 был выпущен в 2011 году, но его не следует путать с Symfony 1, который был совершенно другим фреймворком с другими базовыми принципами. Фабьен Потенсье создал Symfony 2, и текущая версия — 3.2, которая является инкрементной версией Symfony 2. Поэтому их часто называют просто Symfony2/3.

Как и Laravel 4, Symfony 2 разработан как набор несвязанных компонентов. Здесь есть два преимущества: мы можем заменить любой компонент в проекте Symfony, и мы можем взять и использовать любой компонент Symfony в проекте, отличном от Symfony. Компоненты Symfony могут служить прекрасными примерами кода и используются во многих проектах с открытым исходным кодом, таких как Drupal, phpBB и Codeception. На самом деле в самом Laravel используется не менее 14 компонентов Symfony. Таким образом, понимание Symfony дает вам много преимуществ при работе с другими проектами.

Установки фреймворка

Обе платформы поставляются с установщиками и оболочками, доступными через встроенный веб-сервер PHP.

Установка Симфони

Установка Symfony проста:

 # 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

Вот и все! Ваша установка Symfony доступна по адресу http://localhost:8000 .

Установка Laravel

Процесс установки Laravel почти такой же и простой, как и для Symfony; единственная разница в том, что вы устанавливаете установщик Laravel через 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

Теперь вы можете посетить http://localhost:8000 и проверить установку Laravel.

Примечание. И Laravel, и Symfony по умолчанию работают с одним и тем же локальным портом (8000), поэтому вы не можете запускать эти экземпляры по умолчанию одновременно. Не забудьте остановить сервер Symfony, запустив php bin/console server:stop перед запуском сервера Laravel.

Об установке фреймворка

Это примеры базовой установки. Для более сложных примеров использования, таких как возможность настройки проектов с локальными доменами или одновременного запуска нескольких проектов, обе платформы предоставляют поля Vagrant:

  • Усадьба Ларавеля,
  • Усадьба Симфони.

Базовые конфигурации фреймворка

Базовая конфигурация Symfony

Symfony использует YAML в качестве синтаксиса для указания своей конфигурации. Конфигурация по умолчанию находится в файле app/config/config.yml и выглядит следующим образом:

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

Чтобы создать конфигурацию для конкретной среды, создайте файл app/config/config_ENV.yml , содержащий основные параметры конфигурации. Вот пример файла config_dev.yml для среды разработки:

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

Этот пример включает инструмент Symfony web_profiler только для среды разработки. Этот инструмент помогает вам отлаживать и профилировать ваше приложение прямо в окне браузера.

В конфигурационных файлах также можно заметить конструкции %secret% . Они позволяют нам помещать переменные среды в отдельный файл parameters.yml . Этот файл может быть уникальным на каждой машине и не хранится под контролем версий. Для контроля версий у нас есть специальный файл parameters.yml.dist , который является шаблоном для файла parameters.yml .

Вот пример файла parameters.yml :

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

Базовая конфигурация Laravel

Конфигурация Laravel сильно отличается от конфигурации Symfony. Единственное, что у них общего, это то, что они оба используют файлы, которые не хранятся под контролем версий ( .env в случае Laravel), и шаблон для генерации этого файла ( .env.example ). Этот файл содержит список ключей и значений, как в следующем примере:

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

Как и файл Symfony YAML, этот файл для Laravel также удобочитаем и выглядит чистым. Вы можете дополнительно создать файл .env.testing , который будет использоваться при запуске тестов PHPUnit.

Конфигурация приложения хранится в файлах .php в каталоге config . Базовая конфигурация хранится в файле app.php , а конфигурация конкретного компонента хранится в файлах <component>.php (например, cache.php или mail.php ). Вот пример файла 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', // ... ];

Конфигурация фреймворка: Symfony против Laravel

Механизмы конфигурации приложений Symfony позволяют создавать разные файлы для разных сред. Кроме того, это предотвращает внедрение сложной логики PHP в конфигурацию YAML.

Однако вы можете чувствовать себя более комфортно с синтаксисом конфигурации PHP по умолчанию, который использует Laravel, и вам не нужно изучать синтаксис YAML.

Маршрутизация и контроллер

В общем, у серверного веб-приложения есть одна основная обязанность: читать каждый запрос и создавать ответ в зависимости от содержимого запроса. Контроллер — это класс, отвечающий за преобразование запроса в ответ путем вызова методов приложения, а маршрутизатор — это механизм, помогающий определить, какой класс и метод контроллера следует выполнять для конкретного запроса.

Давайте создадим контроллер, который будет отображать страницу сообщения в блоге, запрашиваемую из маршрута /posts/{id} .

Маршрутизация и контроллер в Laravel

Контроллер

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

Маршрутизатор

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

Мы определили маршрут для запросов GET . Все запросы с URI, совпадающим с /posts/{id} , будут выполнять метод show контроллера BlogController и передавать id параметра этому методу. В контроллере мы пытаемся найти объект модели POST с переданным id и вызвать Laravel helper view() для рендеринга страницы.

Маршрутизация и контроллер в Symfony

В Symfony exampleController немного больше:

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

Как видите, мы уже включили @Route("/posts/{id}”) в аннотацию, поэтому нам просто нужно включить контроллер в файл конфигурации routing.yml :

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

Пошаговая логика такая же, как и в случае с Laravel.

Маршрутизация и контроллер: Symfony против Laravel

На этом этапе вы можете подумать, что Laravel намного лучше, чем Symfony. Это правда, в начале. Это выглядит намного лучше и легче начать. Однако в реальных приложениях не следует вызывать Doctrine из контроллера. Вместо этого вам следует вызвать службу, которая попытается найти сообщение или выдать исключение HTTP 404 Exception .

Шаблоны

Laravel поставляется с механизмом шаблонов под названием Blade, а Symfony поставляется с Twig. Оба механизма шаблонов реализуют две основные функции:

  1. Наследование шаблонов
  2. Блоки или секции

Обе функции позволяют определить базовый шаблон с переопределяемыми разделами и дочерними шаблонами, которые заполняют значения этих разделов.

Давайте снова рассмотрим пример страницы поста в блоге.

Шаблонизатор Laravel Blade

 // 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

Теперь вы можете сказать Laravel в своем контроллере, чтобы отображался шаблон post.blade.php . Помните наш вызов view('post', …) в предыдущем примере с контроллером? Вам не нужно знать в своем коде, что он унаследован от какого-то другого шаблона. Это все определяется только в ваших шаблонах, на уровне просмотра.

Шаблонизатор 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 %}

Шаблоны: Symfony против Laravel

Структурно шаблоны Blade и Twig очень похожи. Оба генерируют шаблоны в код PHP и работают быстро, а также реализуют управляющие структуры, такие как операторы if и циклы. Самая важная особенность обоих движков заключается в том, что они по умолчанию избегают вывода, что помогает предотвратить атаки XSS.

Помимо синтаксиса, основное отличие заключается в том, что Blade позволяет вам внедрять PHP-код непосредственно в ваши шаблоны, а Twig — нет. Вместо этого Twig позволяет вам использовать фильтры.

Например, если вы хотите сделать строку заглавной, в Blade вы должны указать следующее:

 {{ ucfirst('welcome friend') }}

С другой стороны, в Twig вы должны сделать следующее:

 {{ 'welcome friend'|capitalize }}

В Blade проще расширить некоторый функционал, но Twig не допускает прямого PHP-кода в шаблонах.

Внедрение зависимости

Приложения имеют множество различных служб и компонентов с различными взаимозависимостями. Всю информацию о созданных объектах и ​​их зависимостях нужно как-то хранить.

А вот и наш следующий компонент — Service Container. Это объект PHP, который создает запрошенные службы и хранит информацию о созданных объектах и ​​их зависимостях.

Давайте рассмотрим следующий пример: вы создаете класс PostService для реализации метода, отвечающего за создание новой записи в блоге. Этот класс зависит от двух других сервисов: PostRepository , который отвечает за хранение информации в базе данных, и SubscriberNotifier , который отвечает за уведомление подписанных пользователей о новом сообщении. Чтобы заставить его работать, вам нужно передать эти две службы в качестве аргументов конструктора PostService или, другими словами, вам нужно внедрить эти зависимости.

Пример внедрения зависимостей Symfony

Во-первых, давайте определим наши примеры сервисов:

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

Далее идет конфигурация внедрения зависимостей:

 # 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

Теперь вы можете запросить свою почтовую службу в любом месте кода из объекта Service Container. Например, в контроллере это может быть примерно так:

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

Service Container — отличный компонент, который помогает создавать приложения в соответствии с принципами проектирования SOLID.

Связанный: Внедрение истинной зависимости с компонентами Symfony

Пример внедрения зависимостей Laravel

В Laravel намного проще управлять зависимостями. Рассмотрим тот же пример:

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

В этом вся прелесть Laravel — вам не нужно создавать конфигурации зависимостей . Laravel автоматически сканирует зависимости для PostService в своих типах аргументов конструктора и автоматически разрешает их.

Вы также можете использовать инъекцию в своем методе контроллера, чтобы использовать PostService , «подсказывая тип» в аргументах метода:

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

Внедрение зависимостей: Symfony против Laravel

Автоопределение Laravel отлично работает. Symfony имеет аналогичную возможность под названием «autowire», которая по умолчанию отключена и может быть включена путем добавления autowire: true к вашей конфигурации зависимостей, но она требует некоторой настройки. Путь Laravel проще.

Реляционное сопоставление объектов (ORM)

Для работы с базами данных обе платформы поставляются с функциями объектно-реляционного сопоставления (ORM). ORM сопоставляет записи из базы данных с объектами в коде. Для этого вы должны создать модели для каждого типа записи (или каждой таблицы) в вашей базе данных.

Symfony использует сторонний проект Doctrine для взаимодействия с базой данных, а Laravel использует собственную библиотеку Eloquent.

Eloquent ORM реализует шаблон ActiveRecord для работы с базой данных. В этом шаблоне каждая модель знает о подключении к базе данных и может взаимодействовать с ней. Например, он может сохранять данные в базу данных, обновлять или удалять записи.

Doctrine реализует шаблон Data Mapper, в котором модели ничего не знают о базе данных; они знают только о самих данных. Специальный отдельный слой EntityManager хранит всю информацию о взаимодействии между моделями и базами данных и обрабатывает все операции.

Давайте возьмем пример, чтобы понять разницу. Допустим, у вашей модели есть первичный ключ id , заголовок, контент и автор. В таблице сообщений хранится только id автора, поэтому вам необходимо создать связь с таблицей пользователей .

Доктрина

Начнем с определения моделей:

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

Здесь мы создали информацию об отображении модели и теперь можем использовать помощника для создания заглушек методов:

 php bin/console doctrine:generate:entities BlogBundle

Далее мы определяем методы пост-репозитория:

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

Теперь вы можете вызывать эти методы из сервиса или, например, из 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);

Красноречивый

Модель User поставляется с Laravel и определена по умолчанию, поэтому вам нужно определить только одну модель для 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'); } }

Это все для моделей. В Eloquent вам не нужно определять свойства модели, поскольку он создает их динамически на основе структуры таблицы базы данных. Чтобы сохранить новый пост $post в базу данных, вам нужно сделать этот вызов (например, из контроллера):

 $post->save();

Чтобы найти все сообщения автора с заданным именем, лучше всего найти пользователя по его имени и запросить сообщения всех пользователей:

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

ORM: Symfony против Laravel

Что касается ORM, Eloquent выглядит гораздо более дружелюбным для PHP-разработчиков и более простым в освоении, чем Doctrine.

Диспетчер событий и ПО промежуточного слоя

Symfony против Laravel Жизненный цикл

Одна из самых важных вещей, которые нужно понимать о фреймворке, — это его жизненный цикл.

Symfony и диспетчер событий

Чтобы преобразовать запрос в ответ, Symfony использует EventDispatcher. Следовательно, он запускает различные события жизненного цикла и специальные прослушиватели событий для обработки этих событий. Вначале он отправляет событие kernel.request , которое включает информацию о запросе. Основным прослушивателем этого события по умолчанию является RouterListener , который вызывает компонент маршрутизатора для поиска подходящего правила маршрутизации для текущего запроса. После этого пошагово выполняются другие события. Типичными прослушивателями событий являются проверка безопасности, проверка токена CSRF и процесс регистрации. Если вы хотите добавить какой-то функционал в жизненный цикл запроса, вам нужно создать собственный EventListener и подписаться на нужное событие.

Laravel и промежуточное ПО

Laravel использует другое решение: промежуточное ПО. Мне нравится сравнивать промежуточное ПО с луковицей: ваше приложение имеет определенные слои, и запрос проходит через эти слои на пути к контроллеру и обратно. Итак, если вы хотите расширить логику своего приложения и добавить некоторые функции в жизненный цикл запроса, вам нужно добавить дополнительный уровень в список промежуточного программного обеспечения, и Laravel выполнит его.

ОТДЫХА API

Давайте попробуем создать базовый пример CRUD для управления записью в блоге:

  • Создать - POST /posts/
  • Чтение – GET /posts/{id}
  • Обновление — PATCH /posts/{id}
  • Удалить - DELETE /posts/{id}

REST API в Symfony

У Symfony нет простого готового решения для быстрого создания REST API, но есть отличные сторонние пакеты FOSRestBundle и JMSSerializerBundle .

Рассмотрим минимальный рабочий пример с FOSRestBundle и JMSSerializerBundle . После того, как вы их установили и включили в AppKernel , вы можете указать в конфигурации пакета, что вы будете использовать формат JSON и что его не нужно включать в запросы URL:

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

В конфигурации маршрутизации следует указать, что данный контроллер будет реализовывать REST-ресурс:

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

В предыдущем примере вы реализовали метод persist в репозитории; теперь вам нужно добавить метод удаления:

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

Затем вам нужно создать класс формы для приема входных запросов и сопоставления их с моделью. Вы можете сделать это с помощью помощника CLI:

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

Вы получите сгенерированный тип формы со следующим кодом:

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

Теперь давайте реализуем наш контроллер.

Примечание: код, который я вам покажу, не идеален. Он нарушает некоторые принципы дизайна, но может быть легко рефакторинг. Основная цель — показать вам, как реализовать каждый метод, шаг за шагом.

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

С FOSRestBundle вам не нужно объявлять маршрут для каждого метода; просто следуйте соглашению с именами методов контроллера, и JMSSerializerBundle автоматически преобразует ваши модели в JSON.

REST API в Laravel

Во-первых, вам нужно определить маршруты. Вы можете сделать это в разделе API правил маршрутизации, чтобы отключить некоторые компоненты промежуточного программного обеспечения по умолчанию и включить другие. Раздел API находится в файле routes/api.php .

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

В модели вы должны определить свойство $fillable для передачи переменных в методы создания и обновления модели:

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

Теперь давайте определим контроллер:

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

В Symfony вы используете FosRestBundle , который заворачивает ошибки в JSON. В Laravel вам нужно сделать это самостоятельно. Вам необходимо обновить метод рендеринга в обработчике исключений, чтобы он возвращал ошибки JSON при ожидании запросов 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); } // ... }

REST API: Symfony против Laravel

Как видите, для типичного REST API Laravel намного проще, чем Symfony.

Выбор победителя: Symfony или Laravel?

Нет явного победителя между Laravel и Symfony, так как все зависит от вашей конечной цели.

Laravel — лучший выбор, если:

  • Это ваш первый опыт работы с фреймворком, так как он прост в освоении, имеет более простой синтаксис и лучшие учебные материалы.
  • Вы создаете продукт для запуска и проверяете свою гипотезу, так как это хорошо для быстрой разработки приложений, а разработчиков Laravel легко найти.

Symfony — лучший вариант, если:

  • Вы создаете сложное корпоративное приложение, поскольку оно очень масштабируемо, удобно в сопровождении и хорошо структурировано.
  • Вы создаете миграцию большого долгосрочного проекта, поскольку Symfony имеет предсказуемые планы выпуска на следующие шесть лет, так что маловероятно, что будут какие-либо сюрпризы.