Hola Amigos! На связи Евгений Шмулевский, PHP-разработчик из Amiga. Рассказываю в статье, как организую свой код в проектах, использующих Laravel. Статья адресована начинающим разработчикам.
Hola Amigos! На связи Евгений Шмулевский, PHP-разработчик из Amiga. Рассказываю в статье, как организую свой код в проектах, использующих Laravel. Статья адресована начинающим разработчикам.
Начал заниматься программированием с 2001 года, привет Basic и Express/Turbo Pascal. Веб-разработкой — с 2011 года, а профессионально в вебе с 2013 года. Работал продолжительное время с Битрикс, а с 2018 начал осваивать Laravel.
Решил немного структурировать, с чем удалось познакомиться после перехода в мир фреймворков из мира чудного (ударение можете сами поставить) Битрикс. Многие вещи стали для меня открытием и особенно переоткрыл для себя ООП.
Давайте посмотрим, какие есть подходы к организации кода.
Когда я начал изучать Laravel (до этого еще знакомился с Zend/Yii/Phalcon), то писал всю бизнес логику в контроллерах. Так сделал пару своих пет-проектов (своя LMS, и CRM для HR) и особенно плохого в этом ничего не видел.
Действительно, организация кода в контроллерах является наиболее быстрым решением, но имеет ряд недостатков:
Почему же этот подход остается популярным? Первая причина — это то, что во время изучения фреймворка во многих курсах преподается данный подход, т.к. его легче усваивать. И далее уже разработчик идет по накатанной. Вторая причина — то, что данный подход прост в реализации и подходит для небольших приложений.
Далее познакомился с подходом с использованием команд. Команда — выполнение некоторого ОДНОГО действия. В таком случае бизнес-логика выносится в отдельный класс-команду.
В некоторых проектах сталкивался с тем, что весь код был вынесен из контроллера в команду просто через copy-paste. На входе у такого action объект Request. Считаю, что при использовании команд разработчику следует отвязаться от Request, либо через параметры, либо через DTO (о DTO напишу чуть ниже). Это позволяет использовать action вне контроллера и не зависеть от Request.
Второй момент, класс команды должен быть разбит на небольшие методы. Это позволит легче поддерживать код в будущем. Примеры наименования команд:
Ниже пример использования Action в упрощенном варианте (далее посмотрим, как сделать лучше):
Дальнейшее изучение вопроса навело меня на Сервисы. С понятием «сервиса» я познакомился, изучая курс от Povilas Korop «How to Structure Laravel Projects».
Сервис, в отличии от action, может иметь несколько методов и является более крупным строительным блоком. Маттиас Нобак в книге «Объекты. Стильное ООП» приводит такое определение: сервисы — объекты, которые либо выполняют задачу, либо предоставляют информацию. Также автор отмечает, что сервисы не хранят свое состояние, т.к. для повторного использования сервиса придется заново его инициализировать, сбрасывая состояние.
Также сервисы могут реализовывать интерфейсы и внедряться через интерфейс. Сервис также как и команда должен иметь единственную ответственность.
Ниже схема использования сервисов:
Если нам необходима просто выборка, то можно не использовать сервисы, а обращаться напрямую к классу репозитория.
С репозиториями также познакомился на курсе от Povilas Korop. Паттерн репозиторий служит цели отделить логику работы с БД от бизнес-логики приложения. Лично для себя выделяю основной плюс в переиспользовании методов выборки. Примерами методов репозитория могут быть такие названия методов как:
Также репозиторий может использоваться для create/update/delete операций. Я предпочитаю в репозиторий выносить только все операции выборки, а для операций изменяющих БД использую сервисы.
Плюсы паттерна:
Еще один кирпичик это DTO, те специальный тип объекта предназначенный для передачи данных между другими объектами. Чаще всего DTO имеют набор публичных полей и методы для своего создания.
В чем плюсы использования DTO:
DTO могут создаваться через обычный конструктор, либо через вызова метода который возвращает сам DTO. Примерами может служить вызов:
Начиная с php 8.0, можно задавать свойства прямо в конструкторе, определяя область видимости. Также для облегчения создания DTO есть пакет от spatie.
Теперь посмотрим, как в приложении используются все вышеперечисленные блоки. Ниже примерная схема взаимодействия (читается справа налево).
Немного слов о структуре. Поначалу пользовался штатным подходом, как изначально рекомендует нам документация Laravel. Проект разбивается на папки, которым соответствует функционал хранящихся в них классов. Типичная структура Laravel проекта:
Данный подход хорош для быстрой разработки небольших приложений. В противовес этому подходу существует подход в организации классов, используя модули.
В идеальном случае, модуль — это независимая часть бизнес-логики. При модульной организации кода структура у нас получается примерно следующей:
Код разбивается, исходя из логики принадлежности к Домену. Есть также упрощенный вариант, когда Controllers не выносится в папку с модулями, а остается в изначальной директории app/Http/Controllers.
Плюсами данного подхода является:
Минусы:
Вот основная логика организации проекта. Для примера создал на github репозиторий с примером простого проекта (авторизация + выборка сущностей) организованный по модулям. Его можно использовать в качестве шаблона для новых проектов. Успехов!