За основу взят реальный рабочий проект - каталог вырубных штампов для полиграфической продукции. В рамках данной работы он воссоздан с нуля по шагам с использованием (по возможности) правильных подходов.
Разработка ведётся в ветке репозитория master. Для каждого нового блока логики создаётся отдельная ветка, которая потом сливается в основную.
Для реализации API созданы стандартные ресурсные контроллеры, однако стандартные маршруты не используются. Вместо них создан более наглядный унифицированный синтаксис, где параметры передаются в теле запроса, а не в URI.
Для каждой сущности в API существует пять методов, связанных с соответствующими методами контроллеров
Метод API | Метод контроллера | Параметры | Возвращаемые значения |
---|---|---|---|
get | show | id | Запись с заданным ID (ассоциативный массив) |
list | index | - | Все записи (ассоциативный массив) |
add | store | value | true/false |
update | update | id, value | true/false |
delete | destroy | id | true/false |
Основной объект, описываемый в данном проекте, - вырубной штамп (модель Punch). Штамп используется для изготовления одного или нескольких видов продукции (модель Product, модель Punch связана с ней отношением "многие-ко-многим") из одного или нескольких видов материалов (модель Material, модель Punch связана с ней отношением "многие-ко-многим") и может использоваться на одном или нескольких станках (модель Machines, модель Punch связана с ней отношением "многие-ко-многим"). Все вышеупомянутые модели связаны через связующие таблицы (machine_punch, material_punch, product_punch). Кроме того, с каждым штампом связаны одно или несколько изображений (фото готовой продукции, крой и т.п; модель Pic, модель Punch связана с ней отношением "один-ко-многим").
Модели созданы командой Artisan make:model (папка app/Models).
Для создания таблиц в БД используются миграции (команда make:migration, папка database/migrations). Таблицы свойств (products, machines, materials) имеют общую структуру (колонки id и value). Таблица pics помимо этого содержит колонку punch_id, связанную с колонкой id таблицы punches. Связующие таблицы содержат ключи, связанные с таблицей punches и соответствующей таблицей свойств. Таблица punches содержит колонки для свойств, которые характеризуют конкретный штамп, поэтому их нерационально выносить в отдельные таблицы.
Для таблиц свойств созданы заполнители (команда make:seeder, папка database/seeders), чтобы не было необходимости заполнять их после установки приложения.
Для каждой модели свойств и для главной модели Punch создаются ресурсные контроллеры (команда make:controller с ключом --resource).
Поскольку контроллеры для моделей свойств абсолютно идентичны и отличаются только используемой моделью, весь их функционал вынесен в абстрактный класс PropertyController, от которого наследуются контроллеры конкретных свойств. В контроллерах свойств остался только конструктор, записывающий в экземпляр контроллера нужную модель и название таблицы (каждый раз дёргать название таблицы из модели нерационально).
Для моделей свойств создан валидатор PropertyRequest - один для всех методов. В методе rules задаются правила для валидации полей id и value, а также производится проверка используемого метода для применения соответствующих правил (чтобы не плодить отдельные классы для каждого метода). Заданы понятные сообщения для ошибок валидации, а также переопределён метод failedValidation для возвращения текста ошибки вместо перенаправления.
Для модели Punch создан аналогичный валидатор PunchRequest. Основное отличие его от предыдущего - в правилах валидации для полей (их больше и они разнообразнее) и их модификации под разные методы.
Также в этой ветке создан контроллер PunchController: созданы методы index, show, update, destroy, create. Метод update пока не обновляет привязку к свойствам (продукты, машины, материалы) и картинки (доделаю, если успею).
В качестве пакета аутентификации и авторизации выбран Passport (настроен по документации).
Регистрация, аутентификация и выход реализованы в контроллере AuthController. В качестве имени пользователя выбрано поле name, e-mail не требуется, подтверждение пароля отключено.
Все маршруты "обёрнуты" в посредника, проверяющего авторизацию, и перенаправляющего пользователя на страницу входа.
За аутентификацию отвечает класс AuthController. Авторизация действий пользователя реализована в классах FavoriteRequest, PropertyRequest, PunchRequest: в методах authorize проверяется роль пользователя и разрешается либо запрещается выполнение действия.
На шлюзы и охранников переделать авторизацию не успел.
Данные в таблицах materials, products, properties обновляются очень редко, поэтому разумно их закэшировать, чтобы снизить нагрузку на базу данных. Для кэша выбран драйвер file, поскольку объём данных небольшой и более производительные решения не требуются (к тому же в этом случае проект не зависит от настроек сервера).
В методах index и show контроллера PropertyController в первую очередь проверяется наличие данных в кэше по ключу, совпадающему с названием таблицы соответствующей модели (свойство tableName). При наличии данных в кэше возвращаются они, обращения к базе не происходит. Если данных в кэше нет, то они извлекаются из базы и записываются в кэш.
В методах add, update и destroy в случае успешного обновления данных в базе кэш очищается.
Таким образом контроллер всегда отдаёт актуальные данные.
Не совсем корректно с точки зрения версионного контроля. Здесь же подправлена выдача методов контроллеров: сейчас все данные отдаются в поле "result" ассоциативного массива. Кроме того, сделано приведение результатов обновления и удаления к логическому типу.
Избранное представляет собой связующую таблицу favorites с колонками user_id и punch_id.
Ресурсный контроллер FavoriteController содержит три метода:
- index - для вывода всего избранного текущего пользователя (возвращает массив штампов);
- store - для добавления штампа в избранное;
- destroy - для удаления штампа из избранного.
Все методы доступны только для аутентифицированных пользователей.
При добавлении в избранное проверяется, чтобы штамп присутствовал в базе и отсутствовал в избранном текущего пользователя. При удалении проверяется, чтобы штамп присутствовал в избранном текущего пользователя.
Попутно доделана авторизация: в таблицу users добавлено поле role_id, связанное с таблицей roles. Для простоты сделано две роли - user (может только читать данные) и admin (может всё). В классах валидации дополнен метод authorize, куда добавлена проверка роли пользователя (через политики и шлюзы было решено не делать).
Сравнение в этом проекте не требуется, и поскольку его структура точно такая же, как у Избранного, было решено его не реализовывать.