<?php namespace App\Domain\Services; use App\Domain\Models\RetailChain; use App\Domain\Models\TrainingCategory; use App\Domain\Models\User; use App\Domain\Repositories\TrainingCategoryRepository; use App\Domain\Support\RetailChainScope; use Illuminate\Database\UniqueConstraintViolationException; use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Support\Fluent; use Illuminate\Support\Str; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class TrainingCategoryService { public function __construct(private readonly TrainingCategoryRepository $repository) { } /** * @param array<string, mixed> $filters * @return Collection<int, TrainingCategory> */ public function listForUser(User $user, array $filters = []): Collection { $query = TrainingCategory::query() ->with('retailChain:id,uuid,name,searchable') ->withCount('trainings') ->orderBy('sort_order') ->orderBy('name'); $this->applyRetailChainScope($query, $user, $filters); return $query->get(); } public function findOneForUser(User $user, string $uuid): TrainingCategory { $category = TrainingCategory::query()->where('uuid', $uuid)->first(); if (!$category instanceof TrainingCategory) { throw new NotFoundHttpException(__('exception.training.category_not_found')); } $scope = RetailChainScope::fromUser($user, []); if ($scope->shouldFilter() && !in_array($category->retail_chain_id, $scope->retailChainIds(), true)) { throw new NotFoundHttpException(__('exception.training.category_not_found')); } return $category; } public function findForRetailChain(string $uuid, int $retailChainId): TrainingCategory { $category = TrainingCategory::query() ->where('uuid', $uuid) ->where('retail_chain_id', $retailChainId) ->first(); if (!$category instanceof TrainingCategory) { throw new NotFoundHttpException(__('exception.training.category_not_found')); } return $category; } /** * @param array<string, mixed> $attributes */ public function create(RetailChain $retailChain, array $attributes): TrainingCategory { $name = trim((string) $attributes['name']); $slug = Str::slug($name); if ($slug === '') { throw new BadRequestHttpException(__('exception.training.category_invalid_name')); } $existing = TrainingCategory::withTrashed() ->where('retail_chain_id', $retailChain->id) ->where('slug', $slug) ->first(); if ($existing instanceof TrainingCategory && !$existing->trashed()) { throw new BadRequestHttpException(__('exception.training.category_slug_exists')); } if ($existing instanceof TrainingCategory && $existing->trashed()) { $existing->restore(); $existing->fill(['name' => $name])->save(); return $existing->refresh(); } $maxOrder = (int) TrainingCategory::query() ->where('retail_chain_id', $retailChain->id) ->max('sort_order'); try { return $this->repository->create([ 'retail_chain_id' => $retailChain->id, 'name' => $name, 'slug' => $slug, 'sort_order' => $maxOrder + 1, ]); } catch (UniqueConstraintViolationException) { throw new BadRequestHttpException(__('exception.training.category_slug_exists')); } } /** * @param array<string, mixed> $attributes */ public function update(TrainingCategory $category, array $attributes): TrainingCategory { $name = trim((string) $attributes['name']); $slug = Str::slug($name); if ($slug === '') { throw new BadRequestHttpException(__('exception.training.category_invalid_name')); } if ($this->slugExists($category->retail_chain_id, $slug, $category->id)) { throw new BadRequestHttpException(__('exception.training.category_slug_exists')); } return $this->repository->update([ 'name' => $name, 'slug' => $slug, ], $category->id); } public function delete(TrainingCategory $category): bool { if ($category->trainings()->exists()) { throw new BadRequestHttpException(__('exception.training.category_has_trainings')); } return (bool) $this->repository->delete($category->id); } /** * @param \Illuminate\Database\Eloquent\Builder $query * @param array<string, mixed> $filters */ private function applyRetailChainScope($query, User $user, array $filters): void { $scope = RetailChainScope::fromUser($user, $this->retailChainFilters($filters)); if (!$scope->shouldFilter()) { return; } $retailChainIds = $scope->retailChainIds(); if ($retailChainIds === []) { $query->whereRaw('0 = 1'); return; } $query->whereIn('retail_chain_id', $retailChainIds); } /** * @param array<string, mixed> $filters * @return array<int, string> */ private function retailChainFilters(array $filters): array { $filter = new Fluent($filters); return array_values(array_filter(array_merge( Arr::wrap($filter->get('retail_chains')), Arr::wrap($filter->get('retail_chain_uuids')), Arr::wrap($filter->get('retail_chain_uuid')) ))); } private function slugExists(int $retailChainId, string $slug, ?int $ignoreId = null): bool { return TrainingCategory::withTrashed() ->where('retail_chain_id', $retailChainId) ->where('slug', $slug) ->when($ignoreId !== null, fn ($query) => $query->where('id', '!=', $ignoreId)) ->exists(); } } Server Error
500
Server Error