feat: Implement API endpoints for retrieving product categories and their associated products.
This commit is contained in:
@@ -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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -19,7 +19,7 @@ return [
|
||||
|
||||
'allowed_methods' => ['*'],
|
||||
|
||||
'allowed_origins' => ['*'],
|
||||
'allowed_origins' => ['*', 'http://localhost:5173'],
|
||||
|
||||
'allowed_origins_patterns' => [],
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use App\Http\Controllers\Api\Setting\SettingController;
|
||||
use App\Http\Controllers\Api\Client\ClientController;
|
||||
use App\Http\Controllers\Api\Service\OrderController;
|
||||
use App\Http\Controllers\Api\Service\ProductCategoryController;
|
||||
use App\Http\Controllers\Api\Service\ServiceController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
@@ -42,6 +43,15 @@ Route::prefix('v1')->group(function () {
|
||||
Route::get('/', 'getSetting')->name('get');
|
||||
});
|
||||
});
|
||||
|
||||
// Product Categories - Protected Routes
|
||||
Route::middleware(['api.auth'])->prefix('group')->as('product-categories.')->group(function () {
|
||||
Route::controller(ProductCategoryController::class)->group(function () {
|
||||
Route::get('get-list', 'getCategories')->name('getList');
|
||||
Route::get('{product_categories:slug}', 'getDetailCategory')->name('detail');
|
||||
Route::get('{parentSlug}/{childSlug}', 'getChildCategoryWithParent')->name('childWithParent');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user