Отладка SQL запросов контроллеров
Возможно вы обратили внимание, что на прошлом уроке мы добавили в таблицы категорий и товаров строку поиска, но она не работает?
Это потому, что никто не написал сам функционал поиска в контроллерах. Мы просто расширили базовый ModelController и больше ничего не меняли.
Помимо этого в категориях мы указали колонку для вывода общего количества товаров - и она тоже пуста, по той же причине.
Предлагаю сегодня доработать наши контроллеры и добавить недостающие функции.
Отладка SQL запросов
В качестве dev-зависимости Vesp устанавливает очень полезный пакет для отладки запросов - Clockwork, вам нужно только установить его расширение для браузера.
Мы видим всего 3 запроса:
- загрузку группы пользователя для проверки его разрешений
- подсчёт общего количества результатов
- и собственно выборка данных с заданным лимитом
Куда же делась загрузка самого пользователя и его токена, спросите вы? Они не выводятся по умолчанию, потому что слежение Clockwork включено только для маршрутов, а пользователь загружается перед контроллером, в middleware.
Но если хотите, можно изменить файл core/routes.php и заменить
- $group->add(Vesp\Middlewares\Clockwork::class);
- на $app->add(Vesp\Middlewares\Clockwork::class);
Тогда вы будете видеть вообще все запросы:
Вот теперь, давайте посмотрим, что происходит во время изменения строки поиска? Правильно, запросы уходят на сервер, но результаты не меняется - ведь наш контроллер их просто не обрабатывает.
Поиск в котроллере
Так как поиск меняет количество результатов, мы должны обрабатывать его в методе beforeCount, чтобы выдавалось верное общее количество результатов:
protected function beforeCount(Builder $c): Builder
{
// Если указан параметр query
if ($query = $this->getProperty('query')) {
// Делаем вложенный запрос
$c->where(static function (Builder $c) use ($query) {
// В котором ищем по title
$c->where('title', 'LIKE', "%$query%");
// Или description
$c->orWhere('description', 'LIKE', "%$query%");
});
}
return $c;
}
Как видите, вложенный запрос помещается в скобки, так что если вы добавите потом еще одно отдельное условие, например $c->where('active', true); всё будет работать корректно.
Вывод количества товаров
С поиском понятно, а как выводить количество товаров, привязанных к категории? Тут Eloquent предлагает очень удобную функцию withCount(), которы мы используем в :
core/src/Controllers/Admin/Categories.php:
protected function afterCount(Builder $c): Builder
{
// Подсчёт количества товаров категории
$c->withCount('products');
return $c;
}
withCount()требует указать имя связанных моделей, а мы прописывали товары категории в модели Product, помните? Вот через эту связь всё и работает.
Вывод категории товара
Давайте теперь примерно так же выведем и категорию у товара, тоже через связь. Для этого используется следующая функция в котроллере core/src/Controllers/Admin/Products.php:
protected function afterCount(Builder $c): Builder
{
// Присоединение 2х колонок категории товара
$c->with('category:id,title');
return $c;
}
Eloquent не очень любит использовать join таблиц и вместо этого предлагает довольно хитрую штуку - он сначала выбирает наши товары, потом смотрит в их category_id, выбирает отдельным запросом только нужные категории и затем проходит циклом по товарам, добавляя в них родительскую категорию.
И вот, что мы видим в ответе - вложенный массив с категорией, который содержит всего 2 запрошенных колонки:
Согласитесь, очень кратко и удобно. Главное, не забывать просписывать связи в моделях и выбирать соответствующие колонки в запросе. Если бы я не указал id в списке колонок категории, Eloquent бы не знал, к каким товарам её присоединить.
Вообще, таких присоединений можно делать довольно много, и выборки не начинают тормозить при большом количестве результатов, в отличие от join. Минус только один - по таким таблицам нельзя делать сортировку, для этого необходим всё-таки join.
Так что, если нужно просто выбрать данные - используем with(), если нужны и данные и сортировка - то join().
Теперь осталось только вывести новую колонку в таблице с товарами в админке. Редактируем src/admin/pages/products.vue
fields() {
return [
{key: 'id', label: this.$t('components.table.columns.id'), sortable: true},
{key: 'sku', label: this.$t('models.product.sku'), sortable: true},
// Через точку можно указывать вложенные ключи в массиве!
{key: 'category.title', label: this.$t('models.product.category')},
// ...
Пересобираем фронт и любуемся на результат:
Заключение
Вот такой небольшой, но очень полезный урок по отладке наших контроллеров.
Теперь вы всегда будете видеть, как именно проходит запрос и почему контроллер возвращает не то, что вам нужно.
Все изменения можно посмотреть на Github. Я там нашёл и поправил опечатку с указанием даты изменения в таблице товаров.
На следующем уроке освоим библиотеку Faker, нагенерируем кучу товаров с категориями и выведем их на фронтенде.
0
👍
👎
❤️
🔥
😮
😢
😀
😡
320
18.06.2022, 11:35:07
7 комментариев
05.07.2022, 16:03:07
У меня магия так и не сработала, так и не вывелось название категории:
Перепробовал различные варианты. Не понял, почему category.title, а не например categories.title - это хотя бы название привязанной таблицы. Магия с category_id у меня не завелась.
Незначительные опечатки:
src/admin/products.vue
должно быть:
src/admin/pages/products.vue
{key: 'category.title', label: this.$t('models.products.category')}
должно быть:
{key: 'category.title', label: this.$t('models.product.category')}
05.07.2022, 19:24:24
Разобрался, откуда данные должны подтягиваться в category.title - берутся из json. Но все равно Категория без данных.
06.07.2022, 01:55:05
Посмотрел через DevTools откуда подгружается json и он грузится по ссылке: http://vesp-shop.test/api/admin/products где отсутствует category вот в этом и проблема:
а по ссылке http://vesp-shop.test/api/web/products все в порядке:
Осталось понять как формируется json?
06.07.2022, 09:23:27
Все контроллеры Vesp выдают JSON по умолчанию, а результат формируется из условий запроса.
Думаю, ты поторопился и пропустил в этой заметке раздел с выборкой категории, где рассказано, что в контроллере core/src/Controllers/Admin/Products.php должна быть вот такая функция:
Немного доработал заметку, чтобы было понятнее.
06.07.2022, 14:17:46
Василий, спасибо большое, за такие развернутые ответы!
Как всегда у тебя, все классно запроектировано, спасибо, что делишься опытом - узнаю много нового!
06.07.2022, 14:19:08
Спасибо, что читаешь!
06.07.2022, 09:30:26
Потому что мы используем название связи, а не привязанной таблицы. В модели Product прописан метод category() - вот она и используется для работы.
Связь указывается между моделями, а в какой таблице модель хранит данные никого не интересует.
Спасибо, поправил!
bezumkin.ru
Личный сайт Василия Наумкина
Прямой эфир
Василий Наумкин
01.07.2024, 11:56:41
Да, верно, именно так.
А в контроллере, скорее всего, ловить данные методом post.
Василий Наумкин
26.06.2024, 09:38:15
О, точно, вылезает если не залогинен.
Спасибо, исправил!
Василий Наумкин
09.04.2024, 04:45:01
> Ошибка 500
Это не похоже на ошибку Nginx, это скорее всего ошибка PHP - надо смотреть его логи.
...
russel gal
09.03.2024, 20:17:18
> А этот стоило написать хотя бы затем, чтобы получить комментарий от юзера, который ничего не писал...
Александр Наумов
27.01.2024, 03:06:18
Василий, спасибо!
Извини, тупанул.