Модули
Типы
Версия: 1.0
Дата: 2025-11-20
Статус: Реализован
Общее описание
Текущая реализация
Права доступа
Описание полей
UI/UX
Бизнес-логика
Выявленные проблемы
Рекомендации
Модуль "Типы" предназначен для управления справочником типов товаров . Это классификация товаров по основному назначению (Септик, Погреб, Кессон и т.д.).
Основные функции:
Создание и редактирование типов товаров
Привязка типа к товарам
Массовое удаление типов
Модуль Связь Товары product.type — тип товара (строковое поле)Каталог Фильтрация по типу Заказы Тип товара отображается в составе
Метрика Значение Всего типов 4
ID Название Товаров 1 Септик 69 2 Погреб 7 3 Кессон 6 4 Другое 63
┌─────────────────────────────────────────────────────────────────────────────┐
│ МОДУЛЬ "ТИПЫ" │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ РОУТЫ (routes/web.php) │
│ └── GET /types → TypeController@index → Список │
│ │
│ КОНТРОЛЛЕР (TypeController.php) │
│ └── Проверка: только group_id = 1 (администраторы) │
│ │
│ LIVEWIRE КОМПОНЕНТЫ │
│ ├── Type/Add.php — Модальное создание │
│ └── Type/Listing.php — Таблица с inline-редактированием │
│ │
│ МОДЕЛЬ │
│ └── Type — Тип товара │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Поле Тип Описание idbigint PK indexint Порядок сортировки (не используется) namestring Название типа created_attimestamp — updated_attimestamp —
⚠️ Особенность: Связь с товарами через строковое поле product.type, а не FK.
app/
├── Http/Controllers/
│ └── TypeController.php
├── Livewire/Type/
│ ├── Add.php — Модальное создание
│ └── Listing.php — Таблица с inline-редактированием
└── Models/
└── Type.php
database/migrations/
└── 2024_04_14_113519_create_types_table.php
// TypeController.php — ТОЛЬКО АДМИНИСТРАТОРЫ
public function index ( Request $request)
{
$user = $request -> user ();
if ( ! $user or $user -> group_id != 1 ) // Жёсткая проверка group_id
abort ( 403 );
return view ( 'templates.types.table' );
}
⚠️ Особенность: Проверка по group_id, а не через политики!
// Listing.php
$disableEdit = $this -> authUser -> check_access ( 'type_can_edit' );
$canDelete = $this -> authUser -> check_access ( 'type_can_delete' );
// В getActions()
$canDelete = ( $this -> authUser -> check_access ( 'type_can_delete' )
or $this -> authUser -> check_access ( 'type_manager_can_delete' ));
Политика Описание type_can_editРедактирование типов type_can_deleteУдаление типов type_manager_can_deleteУдаление для менеджеров
Роль Просмотр URL Редактирование Удаление Администратор ✅ ✅* ✅* Менеджер ❌ ❌ ❌ Производство ❌ ❌ ❌ Дилер ❌ ❌ ❌
*Зависит от индивидуальных политик
Поле Тип Обязательное Описание nameTextInput ✅ Название типа
Поле Тип Описание nameTextInput Название типа
Колонка Описание Особенности nameИмя Поиск
┌─────────────────────────────────────────────────────────────────────────────┐
│ Типы [+ Создать] │
├─────────────────────────────────────────────────────────────────────────────┤
│ [Поиск...] [Столбцы ▾] │
├────────────────────────────────────────────────────┬────────────────────────┤
│ ☐ Имя │ │
├────────────────────────────────────────────────────┼────────────────────────┤
│ ☐ Другое │ [Изменить] [Удалить] │
│ ☐ Кессон │ [Изменить] [Удалить] │
│ ☐ Погреб │ [Изменить] [Удалить] │
│ ☐ Септик │ [Изменить] [Удалить] │
└────────────────────────────────────────────────────┴────────────────────────┘
Особенности:
✅ Модальное создание (без перехода на другую страницу)
✅ Inline-редактирование в модальном окне
✅ Массовое удаление
❌ Нет drag & drop для сортировки (хотя поле index есть)
┌─────────────────────────────────────────────────────────────────────────────┐
│ Создать тип │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Название* │
│ [ ] │
│ │
│ [Отмена] [Создать] │
└─────────────────────────────────────────────────────────────────────────────┘
Типы связаны с товарами через строковое поле product.type:
// Product/Listing.php
Tables\Columns\SelectColumn :: make ( 'type' )
-> options ( Type :: all () -> pluck ( 'name' , 'name' )) // name → name!
-> searchable (),
// Product/Control.php
Select :: make ( 'type' )
-> options ( Type :: all () -> pluck ( 'name' , 'name' )) // name → name!
⚠️ Внимание! Это не FK, а строковое значение! Если переименовать тип — связи с товарами разорвутся.
// В карточке товара
Select :: make ( 'type' )
-> required ()
-> options ( Type :: all () -> pluck ( 'name' , 'name' ))
// Дополнительный тип
Select :: make ( 'add_type' )
-> options ( Type :: all () -> pluck ( 'name' , 'name' )) // Опционально
Товар: "Топас 5"
├── type: "Септик"
└── add_type: null
Товар: "Погреб Волжанин"
├── type: "Погреб"
└── add_type: null
Типы используются для основной классификации товаров в каталоге:
Тип Описание Септик Очистные сооружения Погреб Погреба и хранилища Кессон Кессоны для скважин Другое Прочие товары
# Проблема Файл Влияние 1 Строковая связь вместо FK products.type При переименовании типа связи разрываются
# Проблема Описание Решение 2 Неиспользуемое поле index Есть поле для сортировки, но нет reorderable Добавить drag & drop 3 Неиспользуемая переменная $disableEdit не применяетсяУбрать или использовать 4 Нет защиты от удаления используемых Можно удалить тип, который привязан к товарам Добавить проверку
# Проблема Описание 5 Нет подсчёта товаров Не видно сколько товаров используют тип 6 Жёсткая проверка group_id В контроллере проверяется group_id, а не политики
Заменить строковую связь на FK:
// 1. Добавить миграцию
Schema :: table ( 'products' , function ( Blueprint $table) {
$table -> foreignId ( 'type_id' ) -> nullable () -> constrained ( 'types' );
});
// 2. Мигрировать данные
Product :: all () -> each ( function ($product) {
$type = Type :: where ( 'name' , $product -> type) -> first ();
if ($type) {
$product -> type_id = $type -> id;
$product -> save ();
}
});
// 3. Обновить формы
Select :: make ( 'type_id' )
-> options ( Type :: all () -> pluck ( 'name' , 'id' )) // name → id!
// Listing.php — в getActions()
Action :: make ( 'delete' )
-> hidden ( fn ( Type $record) => Product :: where ( 'type' , $record -> name) -> exists ())
-> requiresConfirmation ()
-> action ( fn ( Type $record) => $record -> delete ())
// TypeController.php — было:
if ( ! $user or $user -> group_id != 1 ) abort ( 403 );
// Стало:
if ( ! $user or ! $user -> check_access ( 'type_can_view' )) abort ( 403 );
// Listing.php
-> reorderable ( 'index' )
-> defaultSort ( 'index' , 'asc' )
Tables\Columns\TextColumn :: make ( 'products_count' )
-> label ( 'Товаров' )
-> getStateUsing ( fn ($record) => Product :: where ( 'type' , $record -> name) -> count ())
-> sortable (),
SELECT
t . id ,
t . name ,
COUNT ( p . id ) as products_count
FROM types t
LEFT JOIN products p ON t . name = p . type
GROUP BY t . id , t . name
ORDER BY products_count DESC ;
SELECT id, name
FROM products
WHERE type IS NULL OR type = '' ;
SELECT p . id , p . name , p . type
FROM products p
WHERE p . type NOT IN ( SELECT name FROM types)
AND p . type IS NOT NULL ;
SELECT t . id , t . name
FROM types t
WHERE t . name NOT IN ( SELECT DISTINCT type FROM products WHERE type IS NOT NULL );
Документ подготовлен на основе анализа исходного кода проекта