Skip to content

Commit cb60d11

Browse files
committed
feat: Implement Catalog API with CRUD operations for categories, products, and product variants
- Added public routes for catalog management including categories and products. - Implemented CategoryController with methods for listing, creating, updating, showing, and deleting categories. - Implemented ProductController with methods for listing, creating, updating, showing, and deleting products. - Implemented ProductVariantController with methods for managing product variants. - Added request validation for storing categories and products. - Created comprehensive tests for the Catalog API, covering all endpoints and validation rules.
1 parent 8831e7f commit cb60d11

File tree

7 files changed

+1337
-1
lines changed

7 files changed

+1337
-1
lines changed
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
<?php
2+
3+
namespace App\Http\Controllers\Api;
4+
5+
use App\Http\Controllers\Controller;
6+
use App\Models\Category;
7+
use Illuminate\Http\Request;
8+
use Illuminate\Http\JsonResponse;
9+
use Illuminate\Validation\Rule;
10+
11+
class CategoryController extends Controller
12+
{
13+
/**
14+
* Display a listing of categories.
15+
*/
16+
public function index(Request $request): JsonResponse
17+
{
18+
try {
19+
$query = Category::query()->with('children');
20+
21+
// Filter by parent category
22+
if ($request->has('parent_id')) {
23+
$query->where('parent_id', $request->parent_id);
24+
}
25+
26+
// Filter by active status
27+
if ($request->has('active')) {
28+
$query->where('is_active', $request->boolean('active'));
29+
}
30+
31+
// Only top-level categories if no parent specified
32+
if (!$request->has('parent_id')) {
33+
$query->whereNull('parent_id');
34+
}
35+
36+
$categories = $query->orderBy('sort_order')
37+
->orderBy('name')
38+
->get();
39+
40+
return response()->json([
41+
'success' => true,
42+
'data' => $categories,
43+
'message' => 'Categories retrieved successfully'
44+
]);
45+
46+
} catch (\Exception $e) {
47+
return response()->json([
48+
'success' => false,
49+
'message' => 'Failed to retrieve categories',
50+
'error' => $e->getMessage()
51+
], 500);
52+
}
53+
}
54+
55+
/**
56+
* Store a newly created category.
57+
*/
58+
public function store(Request $request): JsonResponse
59+
{
60+
try {
61+
$validated = $request->validate([
62+
'name' => 'required|string|max:255',
63+
'slug' => 'required|string|max:255|unique:categories',
64+
'description' => 'nullable|string',
65+
'parent_id' => 'nullable|exists:categories,id',
66+
'sort_order' => 'integer|min:0',
67+
'is_active' => 'boolean',
68+
'seo_title' => 'nullable|string|max:255',
69+
'seo_description' => 'nullable|string',
70+
]);
71+
72+
$category = Category::create($validated);
73+
74+
return response()->json([
75+
'success' => true,
76+
'data' => $category->load('parent'),
77+
'message' => 'Category created successfully'
78+
], 201);
79+
80+
} catch (\Illuminate\Validation\ValidationException $e) {
81+
return response()->json([
82+
'success' => false,
83+
'message' => 'Validation failed',
84+
'errors' => $e->errors()
85+
], 422);
86+
87+
} catch (\Exception $e) {
88+
return response()->json([
89+
'success' => false,
90+
'message' => 'Failed to create category',
91+
'error' => $e->getMessage()
92+
], 500);
93+
}
94+
}
95+
96+
/**
97+
* Display the specified category.
98+
*/
99+
public function show(Category $category): JsonResponse
100+
{
101+
try {
102+
$category->load(['parent', 'children', 'products' => function($query) {
103+
$query->where('is_active', true)->with('primaryVariant');
104+
}]);
105+
106+
return response()->json([
107+
'success' => true,
108+
'data' => $category,
109+
'message' => 'Category retrieved successfully'
110+
]);
111+
112+
} catch (\Exception $e) {
113+
return response()->json([
114+
'success' => false,
115+
'message' => 'Failed to retrieve category',
116+
'error' => $e->getMessage()
117+
], 500);
118+
}
119+
}
120+
121+
/**
122+
* Update the specified category.
123+
*/
124+
public function update(Request $request, Category $category): JsonResponse
125+
{
126+
try {
127+
$validated = $request->validate([
128+
'name' => 'required|string|max:255',
129+
'slug' => ['required', 'string', 'max:255', Rule::unique('categories')->ignore($category->id)],
130+
'description' => 'nullable|string',
131+
'parent_id' => 'nullable|exists:categories,id',
132+
'sort_order' => 'integer|min:0',
133+
'is_active' => 'boolean',
134+
'seo_title' => 'nullable|string|max:255',
135+
'seo_description' => 'nullable|string',
136+
]);
137+
138+
$category->update($validated);
139+
140+
return response()->json([
141+
'success' => true,
142+
'data' => $category->fresh()->load('parent'),
143+
'message' => 'Category updated successfully'
144+
]);
145+
146+
} catch (\Illuminate\Validation\ValidationException $e) {
147+
return response()->json([
148+
'success' => false,
149+
'message' => 'Validation failed',
150+
'errors' => $e->errors()
151+
], 422);
152+
153+
} catch (\Exception $e) {
154+
return response()->json([
155+
'success' => false,
156+
'message' => 'Failed to update category',
157+
'error' => $e->getMessage()
158+
], 500);
159+
}
160+
}
161+
162+
/**
163+
* Remove the specified category.
164+
*/
165+
public function destroy(Category $category): JsonResponse
166+
{
167+
try {
168+
// Check if category has children
169+
if ($category->children()->count() > 0) {
170+
return response()->json([
171+
'success' => false,
172+
'message' => 'Cannot delete category with child categories'
173+
], 422);
174+
}
175+
176+
// Check if category has products
177+
if ($category->products()->count() > 0) {
178+
return response()->json([
179+
'success' => false,
180+
'message' => 'Cannot delete category with associated products'
181+
], 422);
182+
}
183+
184+
$category->delete();
185+
186+
return response()->json([
187+
'success' => true,
188+
'message' => 'Category deleted successfully'
189+
]);
190+
191+
} catch (\Exception $e) {
192+
return response()->json([
193+
'success' => false,
194+
'message' => 'Failed to delete category',
195+
'error' => $e->getMessage()
196+
], 500);
197+
}
198+
}
199+
200+
/**
201+
* Get category tree structure.
202+
*/
203+
public function tree(): JsonResponse
204+
{
205+
try {
206+
$categories = Category::whereNull('parent_id')
207+
->where('is_active', true)
208+
->with(['children' => function($query) {
209+
$query->where('is_active', true)
210+
->orderBy('sort_order')
211+
->orderBy('name');
212+
}])
213+
->orderBy('sort_order')
214+
->orderBy('name')
215+
->get();
216+
217+
return response()->json([
218+
'success' => true,
219+
'data' => $categories,
220+
'message' => 'Category tree retrieved successfully'
221+
]);
222+
223+
} catch (\Exception $e) {
224+
return response()->json([
225+
'success' => false,
226+
'message' => 'Failed to retrieve category tree',
227+
'error' => $e->getMessage()
228+
], 500);
229+
}
230+
}
231+
}

0 commit comments

Comments
 (0)