Сортировка моделей и генерация 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
👍
👎
❤️
🔥
😮
😢
😀
😡
140
22.08.2023, 05:44:12
Комментарии
bezumkin.ru
Личный сайт Василия Наумкина
Прямой эфир
Александр Наумов
23.07.2024, 00:20:37
Василий, спасибо большое!!
Vesp 3.0
101
Василий Наумкин
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
russel gal
09.03.2024, 20:17:18
> А этот стоило написать хотя бы затем, чтобы получить комментарий от юзера, который ничего не писал...
Релиз @vesp/nuxt-fontawesome
3
Александр Наумов
27.01.2024, 03:06:18
Василий, спасибо!
Извини, тупанул.
Новая структура таблиц магазина
15
Василий Наумкин
22.01.2024, 07:43:20
Давай-давай!
Начинаем новый курс
4
Василий Наумкин
24.12.2023, 14:26:13
Спасибо!
Запуск в продакшн с помощью Docker
20