PHP 7.4 MySQL 5.7 Symfony 5.3.10
- Проект докеризирован для более простых тестирования и разработки
- Запускаем Docker - docker-compose up -d --build (для PhpStorm можно настроить конфигурацию - Run/Debug Configurations - конфигурация Docker-compose)
- После запуска контейнеров, в контейнере app запускаются консольные команды
- Композер - composer install
- Миграции - php bin/console doctrine:migrations:migrate
- Очистка кэша - php bin/console cache:clear
- Загрузка фикстур - php bin/console doctrine:fixtures:load
- Проект доступен по адресу localhost
- Настройка Xdebug в PhpStorm
- PHP - Настроить CLI Interpreter из Docker Compose
- PHP - Debug - Настроить порт Xdebug равным 9003
- PHP - Servers - Добавить сервер с именем _, хост - localhost и Project files = /app
- БД доступна по адресу - localhost, данные для подключения - в .env (т.к. задание тестовое)
- В докере поднята только конфигурация для разработки, для прода можно запилить отдельную
- Запросы к АПИ заворачиваются в ДТО и валидируются до попадания в экшен контроллера
- Все ошибки отлавливаются и отдаются в формате JSON с соответствующим кодом HTTP
- Критические ошибки дополнительно логируются в файл
- При изменении данных автомобиля дополнительно фиксируется история изменений
- Марка автомобиля вынесена в отдельную таблицу для простоты генерации UI и контроля за вводимыми данными. Также можно использовать для связанных справочных данных (например: Производитель - модель)
- В справочниках (марка и опции) в качестве данных для выпадающих списков используется поле код - оно уникально и не зависит от ИД
- Для ИД используется UUID для простоты генерации сущностей и избавления от race-condition
- Для хранения цены используется тип DECIMAL (т.к. у нас только хранение). Для полноценной работы с деньгами и валютами я бы использовал https://www.moneyphp.org
Реализуем простую систему подбора автомобиля. В нашем салоне есть новые и подержанные автомобили разных марок, годов выпуска, с разной ценой, и так далее.
- Марка (в интерфейсе это выбор из списка)
- Новый или подержанный
- Год выпуска (возможность выбрать от и до, как вместе, так и по отдельности)
- Цена (возможность выбрать от и до, как вместе, так и по отдельности)
- Наличие датчика дождя
GET /api/v1/vehicles?filter[brand]=123&filter[isNew]=1
Структура запроса и ответа на усмотрение автора, формат обмена данными -- json.
Образец полного запроса (исходя из данных фикстур):
GET /api/v1/vehicles?filter[brand]=bmv&filter[isNew]=1&filter[yearFrom]=2010&filter[yearTo]=2020&filter[priceFrom]=100000&filter[priceTo]=2000000&filter[option][]=rain_sensor
GET параметры помещаются в ДТО, валидируются. В случае ошибок - отдается Json ответ с описанием ошибки и соответствующим Http-кодом. Далее происходит генерация запроса, выборка из БД и генерация форматированного ответа с нужными полями в Json с использованием компонента Symfony/Serializer.
Наш автосалон иногда креативно подходит к продажам, иногда требуется уменьшить пробег автомобилей. Для этого нам нужна консольная команда, которая всем машинам с пробегом более 150000 км уменьшит его на 30%.
Консольная команда
app:vehicle:drop-mileage-percent
Есть возможность задания необязательных параметров:
- mileage - Пробег, больше которого надо уже уменьшать
- percent - Процент уменьшения
Если заданы параметры, то происходит их проверка на валидность. В случае их отсутствия используются параметры по умолчанию: пробег 150000 и 30%. Далее запрашивается общее количество автомобилей, подходящих под условие, выводится прогресс-бар и происходит изменение пробега. Автомобили берутся пачками по константе PAGE_SIZE - для предотвращения ситуации с очень большим количеством подходящих автомобилей и превышения объема памяти. Сохранение происходит также, порциями по PAGE_SIZE штук.
В принципе, эту команду можно было сделать массовым аналогом запроса из задачи 3, но руки не дошли, да и время уже много прошло.
POST запрос по адресу /api/v1/vehicles/{id}/drop-mileage { "type": "miles" | "percent", "value": 15000 } Пост запрос по указанному адресу где {id} -- идентификатор машины, с пэйлоадом в json с двумя полями:
- type -- тип значения, может быть "miles" - скручиваем мили, "percent" - скручивает проценты
- value -- количество процентов/миль зависимости от значения type
Валидация:
- Проценты -- целое число больше нуля
- Мили -- целое число больше нуля
- Нельзя скрутить больше 95% текущего пробега авто ни процентами ни милями
- Если пробег машины стал меньше 5000, ставим флаг, что она новая
Запрос отвечает 200 -- все окей 404 -- машина по айди не найдена 400 -- ошибка валидации
В качестве ИД используется Uuid из таблицы vehicles. Обработка данных запроса происходит аналогично задаче 1. Далее, находится подходящий автомобиль и обрабатывается сервисом из задачи 2.
Примечание С точки зрения построения Restful API, я бы использовал такой ендпойнт для такого запроса (согласно https://restfulapi.net):
PUT /api/v1/vehicles/{id}/mileage с JSON пэйлоадом