feat: Implement API endpoints for retrieving product categories and their associated products.

This commit is contained in:
pbao9
2026-02-15 18:42:41 +07:00
parent 73ed05b20f
commit 4510cdc8eb
3 changed files with 344 additions and 1 deletions
@@ -0,0 +1,333 @@
<?php
namespace App\Http\Controllers\Api\Service;
use App\Http\Controllers\Controller;
use App\Http\Resources\Api\Service\ServiceResource;
use App\Models\Product;
use App\Models\ProductCategories;
use Illuminate\Http\Request;
/**
* @OA\Tag(
* name="Product Categories",
* description="API Endpoints for Product Categories"
* )
*/
class ProductCategoryController extends Controller
{
/**
* @OA\Get(
* path="/product-categories/get-list",
* operationId="getProductCategoryList",
* tags={"Product Categories"},
* summary="Get list of product categories",
* description="Returns a list of active product categories with their children",
* @OA\Response(
* response=200,
* description="Successful operation",
* @OA\JsonContent(
* @OA\Property(property="error", type="boolean", example=false),
* @OA\Property(property="message", type="string", example="Lấy dữ liệu thành công"),
* @OA\Property(property="data", type="array", @OA\Items(
* @OA\Property(property="id", type="integer", example=1),
* @OA\Property(property="name", type="string", example="Cloud Services"),
* @OA\Property(property="slug", type="string", example="cloud-services"),
* @OA\Property(property="parent_id", type="integer", nullable=true, example=null),
* @OA\Property(property="is_active", type="boolean", example=true),
* @OA\Property(property="sort_order", type="integer", example=1),
* @OA\Property(property="children", type="array", @OA\Items(
* @OA\Property(property="id", type="integer", example=2),
* @OA\Property(property="name", type="string", example="VPS"),
* @OA\Property(property="slug", type="string", example="vps"),
* @OA\Property(property="parent_id", type="integer", example=1),
* @OA\Property(property="is_active", type="boolean", example=true),
* @OA\Property(property="sort_order", type="integer", example=1)
* ))
* ))
* )
* )
* )
*/
public function getCategories(Request $request)
{
$categories = ProductCategories::where('is_active', true)
->whereNull('parent_id')
->with(['children' => function($query) {
$query->where('is_active', true)
->orderBy('sort_order', 'asc');
}])
->orderBy('sort_order', 'asc')
->get();
return response()->json([
'error' => false,
'message' => 'Lấy dữ liệu thành công',
'data' => $categories,
]);
}
/**
* @OA\Get(
* path="/product-categories/{slug}",
* operationId="getProductCategoryDetail",
* tags={"Product Categories"},
* summary="Get product category details",
* description="Returns detailed information about a specific product category with its products",
* @OA\Parameter(
* name="slug",
* in="path",
* description="Category slug",
* required=true,
* @OA\Schema(type="string")
* ),
* @OA\Response(
* response=200,
* description="Successful operation",
* @OA\JsonContent(
* @OA\Property(property="error", type="boolean", example=false),
* @OA\Property(property="message", type="string", example="Lấy dữ liệu thành công"),
* @OA\Property(property="data", type="object",
* @OA\Property(property="id", type="integer", example=1),
* @OA\Property(property="name", type="string", example="Cloud Services"),
* @OA\Property(property="slug", type="string", example="cloud-services"),
* @OA\Property(property="parent_id", type="integer", nullable=true, example=null),
* @OA\Property(property="is_active", type="boolean", example=true),
* @OA\Property(property="sort_order", type="integer", example=1),
* @OA\Property(property="children", type="array", @OA\Items()),
* @OA\Property(property="products", type="array", @OA\Items())
* )
* ),
* @OA\Response(
* response=404,
* description="Category not found"
* )
* )
* )
*/
public function getDetailCategory($slug)
{
$category = ProductCategories::where('slug', $slug)
->where('is_active', true)
->with([
'children' => function($query) {
$query->where('is_active', true)
->orderBy('sort_order', 'asc');
}
])
->first();
if (!$category) {
return response()->json([
'error' => true,
'message' => 'Không tìm thấy danh mục',
], 404);
}
// Get products with full details
$products = Product::where('product_categories_id', $category->id)
->where('is_active', true)
->with([
'osVersions.osGroup',
'category',
'pricings' => fn($q) => $q->where('is_active', 1)
])
->get();
$productResources = $products->map(function ($product) {
return [
'uuid' => $product->uuid,
'name' => $product->name,
'slug' => $product->slug,
'short_description' => $product->short_description,
'description' => $product->description,
'thumbnail' => $product->thumbnail ? asset($product->thumbnail) : null,
'product_type' => $product->product_type,
'specs' => [
'cpu' => $product->cpu,
'ram' => $product->ram,
'disk' => $product->disk,
'bandwidth' => $product->bandwidth,
'gpu' => $product->gpu
],
'stock' => $product->stock,
'price' => $product->price,
'price_sale' => $product->price_sale,
'group' => $product->category->name ?? null,
'os' => $product->osVersions->groupBy('os_group_id')->map(function ($versions) {
$group = $versions->first()->osGroup;
return [
'name' => $group?->name,
'thumbnail' => $group?->thumbnail,
'versions' => $versions->map(fn($os) => [
'version' => $os->version,
'name' => $os->name,
])->values(),
];
})->values(),
'pricing' => $product->pricings->map(fn($p) => [
'id' => $p->id,
'name' => $p->name,
'price' => $p->price,
'price_sale' => $p->price_sale,
'period' => $p->period,
'period_unit' => $p->period_unit,
]),
];
});
$categoryArray = $category->toArray();
$categoryArray['products'] = $productResources;
return response()->json([
'error' => false,
'message' => 'Lấy dữ liệu thành công',
'data' => $categoryArray,
]);
}
/**
* @OA\Get(
* path="/product-categories/{parentSlug}/{childSlug}",
* operationId="getChildCategoryWithParent",
* tags={"Product Categories"},
* summary="Get child category with parent",
* description="Returns child category detail with parent information",
* @OA\Parameter(
* name="parentSlug",
* in="path",
* description="Parent category slug",
* required=true,
* @OA\Schema(type="string")
* ),
* @OA\Parameter(
* name="childSlug",
* in="path",
* description="Child category slug",
* required=true,
* @OA\Schema(type="string")
* ),
* @OA\Response(
* response=200,
* description="Successful operation",
* @OA\JsonContent(
* @OA\Property(property="error", type="boolean", example=false),
* @OA\Property(property="message", type="string", example="Lấy dữ liệu thành công"),
* @OA\Property(property="data", type="object",
* @OA\Property(property="id", type="integer", example=3),
* @OA\Property(property="name", type="string", example="VPS Windows VN"),
* @OA\Property(property="slug", type="string", example="vps-windows-vn"),
* @OA\Property(property="parent_id", type="integer", example=1),
* @OA\Property(property="is_active", type="boolean", example=true),
* @OA\Property(property="sort_order", type="integer", example=0),
* @OA\Property(property="parent", type="object",
* @OA\Property(property="id", type="integer", example=1),
* @OA\Property(property="name", type="string", example="VPS Windows"),
* @OA\Property(property="slug", type="string", example="vps-windows")
* ),
* @OA\Property(property="products", type="array", @OA\Items())
* )
* ),
* @OA\Response(
* response=404,
* description="Category not found"
* )
* )
* )
*/
public function getChildCategoryWithParent($parentSlug, $childSlug)
{
// Find parent category
$parent = ProductCategories::where('slug', $parentSlug)
->where('is_active', true)
->whereNull('parent_id')
->first();
if (!$parent) {
return response()->json([
'error' => true,
'message' => 'Không tìm thấy danh mục cha',
], 404);
}
// Find child category
$child = ProductCategories::where('slug', $childSlug)
->where('is_active', true)
->where('parent_id', $parent->id)
->with([
'parent' => function($query) {
$query->select('id', 'name', 'slug', 'parent_id', 'is_active', 'sort_order');
}
])
->first();
if (!$child) {
return response()->json([
'error' => true,
'message' => 'Không tìm thấy danh mục con',
], 404);
}
// Get products with full details (like ServiceController::getDetailService)
$products = Product::where('product_categories_id', $child->id)
->where('is_active', true)
->with([
'osVersions.osGroup',
'category',
'pricings' => fn($q) => $q->where('is_active', 1)
])
->get();
$productResources = $products->map(function ($product) {
return [
'uuid' => $product->uuid,
'name' => $product->name,
'slug' => $product->slug,
'short_description' => $product->short_description,
'description' => $product->description,
'thumbnail' => $product->thumbnail ? asset($product->thumbnail) : null,
'product_type' => $product->product_type,
'specs' => [
'cpu' => $product->cpu,
'ram' => $product->ram,
'disk' => $product->disk,
'bandwidth' => $product->bandwidth,
'gpu' => $product->gpu
],
'stock' => $product->stock,
'price' => $product->price,
'price_sale' => $product->price_sale,
'group' => $product->category->name ?? null,
'os' => $product->osVersions->groupBy('os_group_id')->map(function ($versions) {
$group = $versions->first()->osGroup;
return [
'name' => $group?->name,
'thumbnail' => $group?->thumbnail,
'versions' => $versions->map(fn($os) => [
'version' => $os->version,
'name' => $os->name,
])->values(),
];
})->values(),
'pricing' => $product->pricings->map(fn($p) => [
'id' => $p->id,
'name' => $p->name,
'price' => $p->price,
'price_sale' => $p->price_sale,
'period' => $p->period,
'period_unit' => $p->period_unit,
]),
];
});
$childArray = $child->toArray();
$childArray['parent'] = $child->parent ? $child->parent->toArray() : null;
$childArray['products'] = $productResources;
return response()->json([
'error' => false,
'message' => 'Lấy dữ liệu thành công',
'data' => $childArray,
]);
}
}