Выбор PHP фреймворка: Laravel глазами Symfony разработчика

Фреймворк — это некий скелет структуры проекта, предоставляющий реализованный функционал «из коробки». Данный функционал позволяет решать наиболее типичные задачи при разработке. Архитектура фреймворка может как реализовывать некий собственный набор паттернов, так и представлять собой реализацию отдельных общеизвестных паттернов.

Архитектура Laravel строится вокруг популярного в последнее время принципа Inversion of Control >> Dependency Injection >> Service Container. Этот принцип хорошо знаком разработчикам Symfony, т.к. является краеугольным камнем всей Symfony разработки.

Service Container Laravel очень напоминает Symfony с незначительными отличиями. Это неудивительно, ведь он реализует тот же паттерн, хоть и немного иначе. В целом же надо отметить, что именно Service Container обделен вниманием в документации Laravel. Вероятно, разработчики фреймворка не считают данный компонент центральным в его построении. Хотя, возможно, подробное описание опущено, чтобы не усложнять документацию и предоставить разработчику возможность ориентироваться в работе с компонентом по ситуации.

Более подробно о принципе Inversion of Control и паттернах его реализующих (DependencyInjection >> Service Container; и (анти)паттерне ServiceLocator) можно посмотреть здесь:

Laravel использует часть компонентов Symfony. Это не означает, что компоненты внедряются “копипастом” и, соответственно, работают по принципу «достаточно только прочитать документацию оригинала и можно использовать». Скорее компоненты удачно вплетены в структуру фреймворка, где-то немного изменены (обернуты), где-то дополнены и подстроены для соответствия «экосистеме» фреймворка. На самом деле, это достаточно логично, т.к. многие компоненты Sf поставляются как готовый к использованию код и хорошо решают задачи, которые ставятся перед фреймворками на общем уровне.

Компоненты фреймворков: сравниваем Laravel и Symfony

Так как я симфонист и смотрю на все глазами симфониста, то попытаюсь сравнить решения фреймворков Laravel и Symfony.

HTTP fundamentals & MVC pattern (Controllers, Routing, Views)

Итак, давайте вернемся к корням веб-разработки, в частности к протоколу HTTP. Что бы не делал ваш серверный код, в конечном итоге он должен получить какой-то запрос, обработать его и вернуть определенный ответ. Если использовать plain PHP, можно довольно легко запутаться, например, с телом ответа и возвратом заголовков ответа. Если вы уже давно в разработке, то стоит упомянуть «Headers already sent» и вы сразу всё поймёте.

Чтобы избежать проблем подобного рода, можно сэмулировать работу с запросом и ответом как работу с парой соответствующих классов (объектов). Компонент HTTPFoundation Symfony служит именно этой цели. Он же используется в Laravel и позволяет эмулировать работу на уровне HTTP протокола. Прост и удобен в использовании. За примерами использования welcome в документацию.

Паттерн MVС всем хорошо известен, поэтому достаточно сказать, что в Laravel он реализован. Реализован весьма стандартно: есть привычные контроллеры с actions, отдельный уровень с представлением, с использованием шаблонизатора и всё, что понимается под моделью.

Простой экшен в контроллере может выглядеть примерно так:

public function postResetPassword(Request $request)
{
        $user = Auth::user();
        if(!$user){
            return redirect()
			->action('AuthController@getLogin');
        }
        $user->changed_by_user = true;
        $user->password = $request->input('password');
        $user->save();
        return redirect()
			->action('UserController@getInfo', array());
}

Здесь мы проверяем, авторизован ли пользователь, и меняем его пароль. Стоит обратить внимание, что экшен принимает в качестве аргумента объект класса Request, который в свою очередь унаследован от класса Request, идущего в составе HTTPFoundation Symfony:

use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
class Request extends SymfonyRequest implements Arrayable, ArrayAccess
ORM Eloquent vs. Doctrine ORM

Довольно стандартной задачей при веб-разработке является сохранение объектов в базу данных и чтение из базы данных. Для работы с БД используются так называемые Модели. В Laravel процесс работы с БД осуществляется посредством ORM Eloquent.

Eloquent использует паттерн Active Record, который значительно проще паттернов DataMapper, Unit of Work и Identity Map. Последние используются в ORM Doctrine, которая является базовой ORM для Symfony. Работа с Eloquent за счет простоты имеет множество преимуществ. Пожалуй, самые главные: немногословность при описании моделей и простота использования в коде. При создании модели вовсе не обязательно описывать каждый аксессор, достаточно обратиться к свойству по названию поля в БД.

Пример работы с Eloquent (из официальной документации):

$user = new User;
$user->name = 'John';
$user->save();
$user = User::find(1);
$user->email = '[email protected]';
$user->save();

Простота имеет и обратную сторону – по умолчанию, подсказок в IDE не стоит ждать из-за отсутствия описанных методов/свойств. Эту проблему помогает решить laravel-ide-helper, который генерирует комментарии к моделям.

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

Artisan vs. Console Commands

Как и практически в любом современном фреймворке, в Laravel есть мощный инструмент для работы с консолью. В Laravel этот компонент называется Artisan (ремесленник, мастер). Он предоставляет широкие возможности по выполнению различных рутинных операций. В основном там сосредоточены генераторы кода (миграции, модели, контроллеры, слушатели и другие). С помощью artisan можно управлять очередями задач, выполнять операции по расписанию, очищать кеши, и много что ещё. Помимо этого, очень легко писать свои команды. Для этого надо создать класс команды и зарегистрировать его. Также можно создавать цепочки из команд, вызывая их друг из друга.

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

$ php artisan make:model User

Artisan немногим отличается от стандартных консольных компонентов других фреймворков. Важно знать, что он есть и он работает. Моя любимая команда php artisan inspire :).

Service Container

Как я уже упоминал ранее, архитектура Laravel построена на базе паттерна Service Container. В целом, его реализация очень похожа на реализацию в Symfony, хотя есть небольшие отличия.

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

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

Templating: Blade vs. Twig

Представление реализовано на базе шаблонизатора Blade. Это обычный шаблонизатор. Присутствуют встроенные операторы (вроде if), возможно переопределение частей шаблонов, есть удобные генераторы для получения html блоков, можно провести доступ до нужных сервисов, можно даже написать собственную директиву.

Blade показался показался мне менее удобным, чем Twig. Хотя фреймворк Laravel в целом значительно проще Symfony, именно шаблонизатор для Symfony, на мой взгляд, выглядит проще для освоения.

Debug and error handling

В Laravel присутствует режим разработки, в котором разработчику становится доступна специальная панель на каждой странице. В дебаг панели можно посмотреть, что происходило на стороне фреймворка для формирования ответа сервера. Например, там можно увидеть список выполнявшихся запросов к БД, сработавшие исключения, отправленные е-мэйлы и другое. Панель очень сильно похожа на дебаг панель Symfony, но при этом она значительно проигрывает в мощности.

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

Выводы

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

Для установки фреймворка и работы со сторонними библиотеками используется composer, так что установка и обновление внешних библиотек не представляет особых трудностей. Отдельно стоит обратить внимание на производительность Laravel. Фреймворк несложный и, соответственно, по большей части быстрый. Каких-то специальных бенчмарков я не использовал, но по ощущениям и беглым взглядам на дебаг панель (ах да, там ещё можно увидеть общее время выполнения скрипта) выигрыш в скорости очевиден.

Немного разочаровала документация. Хотя она и представлена, но в очень ограниченном виде. Думаю, когда её составляли, разработчики руководствовались тем же принципом минимализма, что и при построении фреймворка. Она охватывает только основной функционал, а остальные чудеса фреймворка нужно исследовать самому, либо искать в Интернете. Хорошо, что Laravel весьма распространён, и у него большое комьюнити – практически любую проблему, с которой вы столкнетесь, давно решил кто-то другой.

Напоследок хочу добавить, что фреймворк (надо отдать честь его разработчикам) на практике оказывается ровно таким, каким его позиционируют. Это простой и удобный инструмент для «мастеров» в тех случаях, когда им не нужно ударяться во все тяжкие энтерпрайза.