From 934a3884adedc809fd91cd7e9b4850e226b2dd92 Mon Sep 17 00:00:00 2001 From: Amirmahdi Nourkazemi Date: Thu, 11 Jun 2026 09:48:59 +0330 Subject: [PATCH] feat: add type category --- app/Http/Controllers/CategoryController.php | 29 ++++++++++++- app/Models/Category.php | 12 +++++- ...11_100000_add_type_to_categories_table.php | 41 +++++++++++++++++++ 3 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 database/migrations/2026_06_11_100000_add_type_to_categories_table.php diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index 196254b..cc30392 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -6,6 +6,7 @@ use App\Traits\StoresUploads; use Illuminate\Http\Request; use Illuminate\Support\Facades\Storage; +use Illuminate\Validation\Rule; class CategoryController extends Controller { @@ -15,6 +16,10 @@ public function index(Request $request) { $query = Category::query()->withCount('subcategories'); + if ($request->filled('type')) { + $query->where('type', $request->input('type')); + } + if ($request->boolean('with_subcategories')) { $query->with('subcategories'); } @@ -27,13 +32,20 @@ public function index(Request $request) public function store(Request $request) { $data = $request->validate([ - 'name' => 'required|string|max:255|unique:categories,name', + 'name' => [ + 'required', + 'string', + 'max:255', + Rule::unique('categories', 'name')->where('type', $request->input('type', Category::TYPE_MEDIA)), + ], + 'type' => ['nullable', Rule::in(Category::TYPES)], 'description' => 'nullable|string', 'icon' => 'nullable|file|extensions:jpg,jpeg,png,gif,webp,bmp,svg|max:8192', ]); $category = Category::create([ 'name' => $data['name'], + 'type' => $data['type'] ?? Category::TYPE_MEDIA, 'description' => $data['description'] ?? null, 'icon' => $request->hasFile('icon') ? $this->storeUpload($request->file('icon'), 'categories/icons') @@ -57,12 +69,25 @@ public function update(Request $request, $id) { $category = Category::findOrFail($id); + $targetType = $request->input('type', $category->type); + $data = $request->validate([ - 'name' => 'sometimes|string|max:255|unique:categories,name,' . $category->id, + 'name' => [ + 'sometimes', + 'string', + 'max:255', + Rule::unique('categories', 'name') + ->where('type', $targetType) + ->ignore($category->id), + ], + 'type' => ['nullable', Rule::in(Category::TYPES)], 'description' => 'nullable|string', 'icon' => 'nullable|file|extensions:jpg,jpeg,png,gif,webp,bmp,svg|max:8192', ]); + if (array_key_exists('type', $data) && $data['type'] !== null) { + $category->type = $data['type']; + } if (array_key_exists('name', $data)) { $category->name = $data['name']; } diff --git a/app/Models/Category.php b/app/Models/Category.php index 7d2e08e..ed3b547 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -6,7 +6,17 @@ class Category extends Model { - protected $fillable = ['name', 'description', 'icon']; + public const TYPE_MEDIA = 'media'; + public const TYPE_PLAYLIST = 'playlist'; + public const TYPE_BREATHING_TEMPLATE = 'breathing_template'; + + public const TYPES = [ + self::TYPE_MEDIA, + self::TYPE_PLAYLIST, + self::TYPE_BREATHING_TEMPLATE, + ]; + + protected $fillable = ['name', 'type', 'description', 'icon']; protected $appends = ['icon_url']; diff --git a/database/migrations/2026_06_11_100000_add_type_to_categories_table.php b/database/migrations/2026_06_11_100000_add_type_to_categories_table.php new file mode 100644 index 0000000..d3d1283 --- /dev/null +++ b/database/migrations/2026_06_11_100000_add_type_to_categories_table.php @@ -0,0 +1,41 @@ +string('type')->default('media')->after('name')->index(); + }); + + // Existing categories were used for media content. + DB::table('categories')->update(['type' => 'media']); + + // Name is unique per type (same name may exist for media and playlist). + Schema::table('categories', function (Blueprint $table) { + $table->dropUnique('categories_name_unique'); + $table->unique(['name', 'type']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('categories', function (Blueprint $table) { + $table->dropUnique(['name', 'type']); + $table->unique('name'); + $table->dropColumn('type'); + }); + } +};