get(); return view('admin.manuals.index', compact('sections')); } // Форма создания нового раздела справочника // Вьюха: resources/views/admin/manuals/section_form.blade.php public function sectionCreate() { return view('admin.manuals.section_form'); } // Сохранение нового раздела; is_system всегда false — системные разделы создаются только через сидер public function sectionStore(Request $request) { $request->validate([ 'code' => 'required|string|max:64|unique:dict_sections,code', 'label' => 'required|string|max:128', 'is_hierarchical' => 'nullable|boolean', 'sort_order' => 'nullable|integer', ]); DictSection::create([ 'code' => $request->code, 'label' => $request->label, 'is_system' => false, 'is_hierarchical' => (bool) $request->input('is_hierarchical', false), 'sort_order' => (int) $request->input('sort_order', 0), ]); return redirect()->route('admin.manuals.index')->with('success', 'Раздел добавлен.'); } // Форма редактирования раздела справочника (та же вьюха, что и sectionCreate) public function sectionEdit(DictSection $section) { return view('admin.manuals.section_form', compact('section')); } // Обновление раздела; unique-правило исключает текущую запись: unique:dict_sections,code,{id} public function sectionUpdate(Request $request, DictSection $section) { $request->validate([ 'code' => 'required|string|max:64|unique:dict_sections,code,'.$section->id, 'label' => 'required|string|max:128', 'is_hierarchical' => 'nullable|boolean', 'sort_order' => 'nullable|integer', ]); $section->update([ 'code' => $request->code, 'label' => $request->label, 'is_hierarchical' => (bool) $request->input('is_hierarchical', false), 'sort_order' => (int) $request->input('sort_order', 0), ]); return redirect()->route('admin.manuals.index')->with('success', 'Раздел обновлён.'); } // Удаление раздела; системные (is_system=true) удалять нельзя — защита от разрушения структуры // При удалении раздела cascade удаляет все его значения (настроено в миграции FK) public function sectionDestroy(DictSection $section) { if ($section->is_system) { return back()->with('error', 'Системный раздел нельзя удалить.'); } $section->delete(); return redirect()->route('admin.manuals.index')->with('success', 'Раздел удалён.'); } // ── Значения справочника ──────────────────────────────────── // Список значений раздела; иерархические — с children (eager load); плоские — простой список // Вьюха: resources/views/admin/manuals/values.blade.php (показывает оба варианта через @if) public function values(DictSection $section) { if ($section->is_hierarchical) { $items = DictValue::where('section_id', $section->id) ->whereNull('parent_id') ->with('children') ->orderBy('sort_order') ->get(); } else { $items = DictValue::where('section_id', $section->id) ->orderBy('sort_order') ->get(); } return view('admin.manuals.values', compact('section', 'items')); } // Форма добавления значения; если передан parent_id в URL → предзаполняет родителя // Вьюха: resources/views/admin/manuals/value_form.blade.php public function valueCreate(DictSection $section, Request $request) { $parent = null; if ($request->filled('parent_id')) { $parent = DictValue::findOrFail($request->parent_id); } return view('admin.manuals.value_form', compact('section', 'parent')); } // Сохранение нового значения; parent_id = null для верхнего уровня иерархии public function valueStore(DictSection $section, Request $request) { $request->validate([ 'value' => 'required|string|max:255', 'flag' => 'nullable|string|max:10', 'parent_id' => 'nullable|integer|exists:dict_values,id', 'sort_order' => 'nullable|integer', 'logo' => 'nullable|image|max:2048', ]); $logoPath = null; if ($request->hasFile('logo') && $request->file('logo')->isValid()) { $logoPath = $request->file('logo')->store('makes/logos', 'public'); } DictValue::create([ 'section_id' => $section->id, 'parent_id' => $request->input('parent_id') ?: null, 'value' => $request->value, 'flag' => $request->input('flag', ''), 'logo' => $logoPath, 'sort_order' => (int) $request->input('sort_order', 0), ]); return redirect()->route('admin.manuals.values', $section)->with('success', 'Значение добавлено.'); } // Форма редактирования значения; загружает текущего родителя для отображения в форме public function valueEdit(DictSection $section, DictValue $value) { $parent = $value->parent_id ? DictValue::find($value->parent_id) : null; return view('admin.manuals.value_form', compact('section', 'value', 'parent')); } // Обновление значения; parent_id обнуляется если пустой (ternary: '' → null) public function valueUpdate(Request $request, DictSection $section, DictValue $value) { $request->validate([ 'value' => 'required|string|max:255', 'flag' => 'nullable|string|max:10', 'parent_id' => 'nullable|integer|exists:dict_values,id', 'sort_order' => 'nullable|integer', 'logo' => 'nullable|image|max:2048', 'remove_logo' => 'nullable|boolean', ]); $data = [ 'parent_id' => $request->input('parent_id') ?: null, 'value' => $request->value, 'flag' => $request->input('flag', ''), 'sort_order' => (int) $request->input('sort_order', 0), ]; if ($request->hasFile('logo') && $request->file('logo')->isValid()) { // Удаляем старый логотип перед заменой if ($value->logo) { Storage::disk('public')->delete($value->logo); } $data['logo'] = $request->file('logo')->store('makes/logos', 'public'); } elseif ($request->boolean('remove_logo') && $value->logo) { Storage::disk('public')->delete($value->logo); $data['logo'] = null; } $value->update($data); return redirect()->route('admin.manuals.values', $section)->with('success', 'Значение обновлено.'); } // Удаление значения; cascade из FK удалит и все дочерние значения (если иерархия) public function valueDestroy(DictSection $section, DictValue $value) { $value->delete(); return redirect()->route('admin.manuals.values', $section)->with('success', 'Значение удалено.'); } }