Сортировка моделей и генерация uri
Возможно вы обратили внимание, что при создании категории в нашей прошлой заметке, она получалась с rank = 0, и выводилась вверху дерева.
Это потому, что у модели категории, как и у прочих моделей, не прописана логика выставления этого самого rank. И сейчас мы её пропишем.
А потом подумаем, как обновлять uri категорий и товаров при перемещении в дереве.
Сортировочный трейт
Согласно нашей схеме БД, прямо сейчас у нас есть колонка rank в следующих моделях:
- Language
- Category
- Product
- ProductCategory
- ProductFile
- ProductLink
В общем, много где. Хотелось бы как-то управлять этим централизованно, да?
Поэтому пишем новый трейт в Models/Traits/RankedModel.php:
<?php
namespace App\Models\Traits;
trait RankedModel
{
public static function bootRankedModel(): void
{
static::creating(
static function (self $record) {
if (!$record->rank) {
$record->rank = $record->getCurrentRank();
}
}
);
}
protected function getCurrentRank(): int
{
return $this->newQuery()->max('rank') + 1;
}
}
Добавляем трейт в модели и теперь при создании новой записи, в rank будет проставляться такое значение, чтобы она оказалась в конце.
Это отлично работает для обычных моделей, но со вложенными категориями будут проблемы, потому что у них rank зависит от родителя.
В таком случае, мы просто перезаписываем метод их трейта собственным, в модели Category:
protected function getCurrentRank(): int
{
$c = $this->newQuery();
if ($this->parent_id) {
$c->where('parent_id', $this->parent_id);
} else {
$c->whereNull('parent_id');
}
return $c->max('rank') + 1;
}
Теперь значение зависит от родительской категории.
Примерно так же прописываем получение rank и у товаров:
protected function getCurrentRank(): int
{
return $this->newQuery()->where('category_id', $this->category_id)->max('rank') + 1;
}
Товаров без категории быть не может, так что здесь условие попроще.
Обновление uri
Мы уже прописали сохранение полного uri в моделях Category и Product, но это работает только при сохранении непосредственно модели, и никак не касается её связей.
А при перетаскивании категории в дереве нужно обновлять всё в неё вложенное. Как это лучше сделать?
Я предлагаю обновить booted функцию модели Category:
<?php
namespace App\Models;
// ...
class Category extends Model
{
// ...
// Индикатор необходимости обновления uri категории
public static bool $updateUri = false;
protected static function booted(): void
{
static::saving(static function (self $model) {
$uri = [$model->alias];
if ($model->parent) {
array_unshift($uri, $model->parent->uri);
}
$model->uri = implode('/', $uri);
// Если индикатор не выставлен, пробуем выставить автоматически
if (!self::$updateUri) {
self::$updateUri = $model->exists && ($model->isDirty('parent_id') || $model->isDirty('alias'));
}
});
static::saved(static function (self $model) {
// Проверяем необходимость обновления uri
if (self::$updateUri) {
// Обновляем дочерние товары
foreach ($model->products()->cursor() as $product) {
$product->save();
}
// Рекурсивно обновляем дочерние категории
foreach ($model->children()->cursor() as $category) {
$category::$updateUri = true;
$category->save();
}
}
});
}
// ...
}
Здесь продолжается наша рекурсивная магия. Модель Category получает
свойство updateUri, которое управляет обновлением uri дочерних товаров и категорий.
При обновлении дочерних категорий, это свойство выставляется им, и так будет до последнего уровня вложения, рекурсивно.
Обновление uri включается автоматичеси для уже существующих моделей, у которых изменён родитель или alias.
На мой вгляд, вполне себе симпатичное решение, о котором больше не нужно думать - все uri обновляются автоматически при сохранении модели.
Чтобы проверить, как это работает, я добавил генерацию события на обновления таблицы товаров из дерева, при перемещении категории.
Для загрузки товаров используется адрес API admin/products, поэтому событие для обновления таблицы будет 'app::admin-products::update':
<script>
export default {
// ...
methods: {
async sortNodes({to, from, item, oldIndex, newIndex}) {
// ...
if (oldParent !== newParent || oldIndex !== newIndex) {
// ...
this.$root.$emit(`app::admin-products::update`)
}
this.$root.$emit(`app::categories-tree::sort`)
},
}
}
</script>
Проверяем работу:
Сортировка товаров
Хоть у нас и есть колонка rank у товаров, использовать её мы пока не будем. Честно говоря, я просто не представляю как это лучше сделать, ведь таскать товары по таблице с постраничной навигацией просто не получится.
Да и сама таблица может сортироваться по разным колонкам, например по артукулу или цене. То есть, порядок отображения строк не будет соответствовать rank.
Единственное боле-менее рабочее, что могу предложить, это добавить колонку сортировки по rank и кнопочки типа таких:
Типа, двинуть запись выше-ниже.
Но, по моему, это пустой труд. В публичной части всё равно будет вывод каталога с фильтрами и сортировкой по цене, так что и в админке ничего специально сортировать не будем. Но колонка rank на всякий случай пусть останется.
Заключение
Сегодня небольшая заметка, с доработкой мелочей, оставшихся с прошлого раза.
Дальше займёмся карточкой товара - в неё нужно добавить управление связями с другими товарами и подумать, как нам сделать работу с мультикатегориями.
Итоговый коммит с текущими изменениями.
0
👍
👎
❤️
🔥
😮
😢
😀
😡
149
22.08.2023, 05:44:12
Комментарии
Вы должны авторизоваться, чтобы писать комментарии.
bezumkin.ru
Личный сайт Василия Наумкина
Прямой эфир
Василий Наумкин
23.12.2024, 05:33:00
В MODX сначала создали проблему, автоматически генерируя адреса, а потом "решили" заморозкой.
Так ч...
Вывод товаров на сайте
21
Дмитрий
14.12.2024, 09:10:38
Василий, прошу прощения, тупанул, не разобрался сразу. Фреймворк отличный! "Чистый лист" на vue, рис...
Начинаем новый курс!
14
Василий Наумкин
05.12.2024, 20:01:14
В итоге основная ошибка была в неправильном общем root в Nginx, из-за чего запросы не улетали на фай...
Запуск в продакшн
55
Василий Наумкин
22.11.2024, 03:33:54
Спасибо!
День рождения 42
5
inna
06.11.2024, 15:47:13
Да. Все работает. Спасибо.
Vesp 3.0
108
Василий Наумкин
01.07.2024, 11:56:41
Да, верно, именно так.
А в контроллере, скорее всего, ловить данные методом post.
Оплата заказа
2
Василий Наумкин
26.06.2024, 09:38:15
О, точно, вылезает если не залогинен.
Спасибо, исправил!
Обновление проекта
2
Василий Наумкин
09.04.2024, 04:45:01
> Ошибка 500
Это не похоже на ошибку Nginx, это скорее всего ошибка PHP - надо смотреть его логи.
...
Создание нового проекта
63
Василий Наумкин
20.03.2024, 21:21:52
Volledig!
Поездка в Швейцарию
8
Андрей
14.03.2024, 13:47:10
Василий! Как всегда очень круто! Моё почтение!
День рождения 41
6
Уровни подписки
Спасибо!
500 ₽ в месяц
Эта подписка ничего не даёт, просто возможность сказать спасибо за мои заметки. Подписчики отмечаются зелёненьким цветом в комментариях.
Большое спасибо!
1 000 ₽ в месяц
И эта подписка не даёт ничего, кроме оранжевого цвета в комментариях и возможности сказать спасибо, но уже большое!