Контроллеры Vesp
Маршрутизация
<?php
use Slim\Routing\RouteCollectorProxy;
// Все маршруты начинаются с /api/...
$group = $app->group(
'/api',
function (RouteCollectorProxy $group) {
// А дальше простое перечисление кто за что отвечает
$group->any('/security/login', App\Controllers\Security\Login::class);
$group->any('/security/logout', App\Controllers\Security\Logout::class);
$group->any('/user/profile', App\Controllers\User\Profile::class);
// Контроллер вывода загруженных изображений
$group->get('/image/{id}', App\Controllers\Image::class);
// Контроллеры админки - группа admin внутри группы api
$group->group(
'/admin',
static function (RouteCollectorProxy $group) {
$group->any('/users[/{id}]', App\Controllers\Admin\Users::class);
$group->any('/user-roles[/{id}]', App\Controllers\Admin\UserRoles::class);
}
);
}
);
$group->any('/users[/{id}]', App\Controllers\Admin\Users::class);
Немного практики
<?php
// Наше новое пространство имён
namespace App\Controllers\Web;
use App\Models\User;
use Vesp\Controllers\ModelGetController;
// Контроллер должен расширять контроллеры из Vesp
class Users extends ModelGetController
{
// Указываем с какой моделью контроллер работает
protected $model = User::class;
}
$group->group(
'/web',
static function (RouteCollectorProxy $group) {
// Вот и наш контроллер, отвечает только на GET запросы
$group->get('/users[/{id}]', App\Controllers\Web\Users::class);
}
);
Основной котроллер Vesp
- scope - строка с требуемым разрешением на запуск, по умолчанию пустая
- getProperty() - получает переданные параметры по ключу, например $this->getProperty('id') или $this->getProperty('limit')
- setProperty() - устанавливает параметр, можно заменять присланные параметры пользователя своими
- unsetProperty() - удаляет установленный параметр, например если юзеру нельзя указывать limit
- getProperties() - возвращает весь массив с параметрами контроллера
- setProperties() - заменяет все параметры своим массивом
- response() - базовый метод ответа на запрос пользователю в формате JSON
- success() - ответ об успешной операции, использует response() с заранее указанным кодом ответа 200
- failure() - тоже сокращённый вызов response() с ответом о неудачном запросе, код по умолчанию 422.
- checkScope() - метод для проверки прав пользователя
- __invoke() - основной метод работы контроллера
- protected $route - текущий маршрут, наследник интерфейса Slim\Interfaces\RouteInterface
- protected $request - объект с данными запроса пользователя, наследник Psr\Http\Message\RequestInterface
- protected $response - наследник Psr\Http\Message\ResponseInterface, используется в методе response() для вывода JSON ответа
- protected $user - класс с текущим пользователем App\Models\User, который сделал запрос. Если юзер не авторизовался, то будет null
public function options(): ResponseInterface
{
$response = $this->success();
if (getenv('CORS')) {
$response = $response
->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Authorization')
->withHeader('Access-Control-Allow-Methods', 'POST, GET, HEAD, OPTIONS, DELETE, PUT, PATCH, UPDATE');
}
return $response;
}
Расширяющие контроллеры
- protected $model - обязательная модель этого контроллера, с которой он работает
- protected $primaryKey = 'id' - первичный ключ модели
- protected $maxLimit = 1000 - максимальное количество выбираемых строк через GET, можно отключить, указав 0 - нет лимита
- put() - создание новой модели, то есть сохранение новой записи в её таблиц базы данных
- patch() - изменение модели по её первичному ключу
- delete() - удаление модели по ключу
- get() - получение одной или нескольких моделей для просмотра, если количество результатов больше $this->maxLimit, то ограничивает их до этого числа
-
beforeSave() - проверка свойств модели перед сохранением. Здесь можно вернуть ошибку, типа "такое-то поле не заполнено" через $this->failure() или null, если всё в порядке.
-
afterSave() - модификация уже сохранённой модели, можно довыбирать к ней какие-то данные перед возвращением юзеру. Возвращает модель, никаких ошибок тут уже нет.
-
beforeGet() - метод модификации выборки одной модели по первиному ключу
-
beforeCount() - метод модификации выборки коллекции моделей, когда первичного ключа не указано. Вызывается перед подсчётом итогового количества выбираемых записей, поэтому именно здесь нужно указывать условия where(), join() и т.д.
-
afterCount() - метод для добавления в выборку сортировки, связей моделей и прочего, что не влияет на количество выбираемых моделей.
-
prepareRow() - метод, через который проходит каждая модель перед выводом наружу пользователю. Превращает класс модели в массив, используется и в put() и в patch(), и конечно же, в get(). В этом методе вы можете стандартизировать что именно вернётся юзеру в ответ на любой запрос.
-
beforeDelete() - проверка модели перед удалением, можно вернуть null если всё ок, или $this->failure() - если нет.
Еще немного практики
<?php
namespace App\Controllers\Web;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Vesp\Controllers\ModelGetController;
class Users extends ModelGetController
{
protected $model = User::class;
// Меняем уловия перед подсчётом результатов
protected function beforeCount(Builder $c): Builder
{
// Выбираем только активных пользователей
$c->where('active', true);
// Если прислали параметр query - это поиск
if ($query = $this->getProperty('query')) {
// Добавляем вложенное условие для фильтрации по username и fullname
$c->where(static function (Builder $c) use ($query) {
$c->where('username', 'LIKE', "%$query%");
$c->orWhere('fullname', 'LIKE', "%$query%");
});
}
// И возвращаем модифицированный запрос
return $c;
}
// Меняем запрос после подсчёта результатов
protected function afterCount(Builder $c): Builder
{
// Если не указана сортировка - сортируем по username
if (!$this->getProperty('sort')) {
$c->orderBy('username');
}
return $c;
}
// Ну и подготовка ответа
public function prepareRow(Model $object): array
{
/** @var User $object */
// Здесь выбираем только 3 колонки из всей модели
$array = [
'id' => $object->id,
'username' => $object->username,
'fullname' => $object->fullname,
];
// И возвращаем массив
return $array;
}
}
- запрос http://vesp-shop.test/api/web/users выдаёт список пользователей с тремя колонками, отсортированный по username.
- запрос http://vesp-shop.test/api/web/users/1 тоже выведет всего 3 колонки
- теперь ищем всех с user в username или fullname - http://vesp-shop.test/api/web/users?query=user
- или сортируем created_at - http://vesp-shop.test/api/web/users?sort=created_at&dir=desc