user() ->categories() ->forDisplay() ->get(); return Inertia::render('settings/categories', [ 'categories' => $categories, 'categoryTreeEnabled' => Feature::for(auth()->user())->active(CategoryTreeFeature::class), ]); } /** * Store a newly created category. */ public function store(StoreCategoryRequest $request): RedirectResponse { try { auth()->user()->categories()->create($request->validated()); } catch (UniqueConstraintViolationException $exception) { $this->throwDuplicateCategoryNameValidationException($exception); } return to_route('categories.index'); } /** * Update the specified category. */ public function update(UpdateCategoryRequest $request, Category $category): RedirectResponse { $this->authorize('update', $category); try { $category->update($request->validated()); } catch (UniqueConstraintViolationException $exception) { $this->throwDuplicateCategoryNameValidationException($exception); } $this->tree->syncDescendantTypes($category); return to_route('categories.index'); } /** * Soft delete the specified category, handling its children according to * the chosen strategy. */ public function destroy(DeleteCategoryRequest $request, Category $category): RedirectResponse { $this->authorize('delete', $category); match ($request->strategy()) { CategoryDeletionStrategy::Cascade => $this->tree->deleteSubtree($category), CategoryDeletionStrategy::Promote => $this->detachChildrenAndDelete($category, null), CategoryDeletionStrategy::Reparent => $this->detachChildrenAndDelete($category, $category->parent_id), }; return to_route('categories.index'); } /** * Move the category's direct children to a new parent, then soft delete it. */ private function detachChildrenAndDelete(Category $category, ?string $newParentId): void { try { $category->children()->update(['parent_id' => $newParentId]); } catch (UniqueConstraintViolationException) { throw ValidationException::withMessages([ 'strategy' => __('A category with the same name already exists at the destination level. Rename it first.'), ]); } $category->delete(); } private function throwDuplicateCategoryNameValidationException(UniqueConstraintViolationException $exception): never { if (! str_contains($exception->getMessage(), 'categories_user_id_name_unique') && ! str_contains($exception->getMessage(), 'categories_user_id_name_active_unique') && ! str_contains($exception->getMessage(), 'categories_user_id_parent_name_active_unique')) { throw $exception; } throw ValidationException::withMessages([ 'name' => __('validation.unique', ['attribute' => 'name']), ]); } }