Пишем приложения на Laravel, которые легко поддерживать.

maintainable Laravel applications

Это расшифровка моего доклада с Laracon AU.

Создание BaseCode и Shift дало мне уникальное понимание написания приложений на Laravel. Я объединил свои 20 лет программирования и 20 000 апгрейдов Ларавел в 10 советов по созданию легко обслуживаемых приложений.

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

Иди в ногу со временем

Начнем с самого главного — будь актуальным. Да, вы слышите это от создателя Shift. Но тем не менее это так.

Слишком много приложений, по разными причинам, решили остаться устаревшими. Я обязан вам сказать, что LTS (Долгосрочная поддержка) — это ловушка, форки фреймворка — наивно, а изменения в папке vendor — отложенная катастрофа.

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

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

Принимай стандарты

Сейчас я хочу обратить ваше внимание на стандарты оформления кода. Вы можете не соглашаться со всеми стандартами. Мне, например мне не нравится пробел в (оператор отрицания). Или нехватка пробелов для . (оператор конкатенации). Но, когда я пишу на Laravel, я стараюсь им следовать.

Тем не менее, многие разработчики создают свои собственные стандарты. По факту, я все еще получаю тикеты, о том, что Shift непригоден для использования, так как он переформатировал их код. Я знаю, что стиль кода, это как стиль одежды — личная, идентификационная черта.

В конце концов, это нормально, если вы хотите настроить его под себя и автоматизировать. В идеале используя PHP CS Fixer. Shift использует файл .php_cs в вашем проекте для форматировании кода.

Тщеславные пространства имен

Никогда не меняйте пространство имен App. Тейлор и Джеффри Уэй уже отказались ото подобного. Даже artisan-команда app:name была удалена. Несмотря на все это, я все еще вижу проекты, делающие это.

Меняя его, вы вынуждены следить за ним в нескольких местах. Что увеличивает накладные расходы на техническое обслуживание и создает барьер для входа в проект. Рассмотрим файл HTTP/Kernel. Он содержит 23 ссылки на пространство имен App. Это 23 дополнительные точки, которые нужно будет обслуживать.

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

Независимо от настройки пространства имен, я всегда рекомендую использовать morphMap для любых полиморфных или динамических связей модели, сохраненных в базе данных. Это отделит ваш код от БД.

Дефолтная структура

Подобно поддержке стандартов, я рекомендую вам максимально придерживаться дефолтной структуре папок Laravel. Я вижу приложения, создающие свои собственные структуры в папке app. Часто для модульности или разделения кода по доменам.

Тем не менее, в этих папках все равно воссоздаются дефолтные структуры. Controllers, Models, Events. Это создает еще большие накладные расходы, что, в конечном итоге, начинает конкурировать с дефолтной структурой. Это приводит к параллельным иерархиям наследования, что является сигналом Кода с душком.

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

К чему все приведет

Еще один распространенный вопрос, связанный со структурой, к чему все это приведет?

Опять таки, Laravel в своей изначальной структуре предоставляет множество папок на выбор. Я полагаю, что большинство классов могут быть организованы в одной из них. А если это не подходит, то размещайте его в папке Services.

В этой папке будут находиться классы, которые самоорганизуются со временем. Как только они достигнут критической массы, реструктурируйте их в свою собственную папку верхнего уровня, например: Facades, Clients, Contracts, Traits и т.д.

Это может показаться способом для ленивых, но это поможет избежать неправильной абстракции. Я обсуждаю этого в Правиле Трёх из BaseCode. Предпосылка будущего — ты всегда умнее, чем ты сейчас. Поэтому, если вы сможете сейчас отложить принятие решения, то потом найдете лучшую абстракцию.

Управление пакетами

Composer позволяет очень просто использовать пакеты и управлять ими. До такой степени, что мы даже не задумываемся об их поддержке. Но, любой пакет, который мы вносим в наше приложение, является кодом, которым мы должны управлять. Код, с которым связан код нашего приложения.

Это то, что мы должны помнить перед добавлением пакета. Всегда проверяйте, что ваши пакеты правильно зарегистрированы. Метрики от Shift показывают, что многие приложения неправильно требуют зависимости в режиме development. То есть код, который не «требуется» на продакшне. Например, пакет barryvdh/laravel-debugbar.

Многие простые пакеты раздуты и быстро устаревают или забрасываются своими авторами. Вспомним некогда популярный пакет laracasts/Commander. Сейчас он давно заброшен и заменен кодом в ядре.

Тем не менее, мы все еще можем использовать этот пакет в качестве примера. Он содержит 7 файлов для выполнения одной простой вещи — выполнения обработчика команды. Весь этот код может быть одним трейтом с одним методом execute. Сделайте так и не нужно будет заботится об этом пакете вообще.

Конечно, нет необходимости так поступать с каждым пакетом. Но простой функционал может быть ассимилирован в ваш проект для удобства дальнейшего сопровождения.

Плавные биндинги

Laravel обеспечивает много динамики, что может привести к путанице относительно того, что и где находится. А биндинги самые большие путаники.

Для борьбы с этим зарегистрируйте их в одном месте. В идеале в AppServiceProvider. По возможности используйте свойства $bindings и $singletons, чтобы их было легко идентифицировать.

В Ларавеле есть и другие бинлдинги, которым может быть полезна данная практика, например: Events, Policies, Commands, Broadcasts и Routes.

Расписывая свои маршруты, помните, что есть как API, так и веб-маршруты. Слишком часто я вижу, что все подряд размещается в веб-маршрутах, даже если это маршурт API.

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

Конфигурирование Конфигов

Это правда — файлы конфигурации можно настраивать и обновлять. Также верно, что эти файлы являются наиболее часто изменяемыми в Ларавел. Есть удобные способы управления ими и поддержки. Я уже рассказывал об этом в моей предыдущей статье «Ведение конфигурационных файлов в Ларавел».

Избегайте переопределения

Подобно форматированию и структуре, многие приложения вводят собственные абстракции для основных компонентов. Например, создают что-то вроде BaseModel. Эта BaseModel задает некую общую логику, например, незащищенные атрибуты, или иногда, полностью переопределяет основные методы, чтобы немного изменить их работу.

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

Вместо внедрения слоя наследования рассмотрите возможность использования трейтов. В данном случае Unguarded трейта. Я расскажу об этом подробнее в следующем совете. А пока давайте посмотрим на другой пример, касающийся переопределения основных методов.

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

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

Laravel предоставляет все виды методов и свойств обратных вызовов. Например, Form Request имеет свойство для изменения редиректа. Вы можете добавить методы render или report к кастомным исключениям, для лучшей обработки ошибок.

Вы можете подключиться к основной функциональности через события. Laravel запускает события для всех видов основного поведения. Вы можете зарегистрировать слушателя событий для запуска кастомного кода без необходимости переопределения низкоуровневых данных. Эти события запускаются для всех видов компонентов и поведений, например: Auth, Jobs, Notifications, Commands и даже Migrations.

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

Иначе говоря, используйте то, что предлагает фреймворк. Подражайте его паттернам и практикам. Будьте командным игроком. Не будьте изгоем.

Понимание может принимать разные формы. Одним из этих самых простых способов это директивы Blade. Laravel предоставляет десятки замечательных директив. К сожалению, многие приложения используют только стандартную директиву @if. Но есть @isset , @empty, @auth и @guest, которые могут упростить ваши шаблоны и улучшить взаимодействие.

Понимание фреймворка побуждает нас писать код как в Laravel (Laravel Way), что делает его более «домашним», доступным и, следовательно, обслуживаемым.

Другой пример — использование фасадов. Заглядывая вперед — автоматических фасадов. Фасады могут показаться сложными, поскольку приходится создавать не только базовый класс, но и принадлежность, регистрировать его в провайдере, создавать псевдоним.

Но не автоматические фасады. На самом деле мы можем использовать базовый класс в качестве фасада, просто импортируя его под динамическим пространством имен Facades. Laravel понимает класс автоматически. Это означает, что мы можем получить все преимущества фасада, включая его тестируемость.

Уважайте архитектуру MVC

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

Как правило, в MVC при поступлении запроса контроллер выполняет посредническую функцию между моделью и представлением. Это означает ограниченную логику представления. Никакой перекрестной связи между моделью и представлением или ответом.

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

Вместо этого мы можем уважать MVC и Laravel, следуя очень простому шаблону — внедрение зависимостей. Laravel внедряет объект запроса в любой метод контроллера с тайпхинтом Request. Мы также можем использовать тайпхинт объекта Form Request, если у нас есть объект, если мы хотим проверить.

Теперь мы можем передать объект запроса в модель. При этом мы уменьшаем связь только с объектом запроса. Сейчас вы можете подумать, «что в лоб, что по лбу». Возможно, но именно здесь, использованием Form Request, может свести все это вместе. Он служит контрактом, передавая данные, которым мы ожидаем от этого объекта запроса.

Пишите тесты

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

Laravel делает тестирование доступным и простым, независимо от того, какой стиль тестов вы пишете. Есть HTTP-тесты для быстрой отправки запросов в ваше приложение и проверки ответов. Есть Dusk для взаимодействия с вашим приложением через браузер. Все построено на основе PHPUnit, поэтому написание модульных тестов тоже возможно.

Автор: Jason McCreary
Перевод: Алексей Широков

Наш Телеграм-канал — следите за новостями о Laravel.