feature: add index endpoint for products

- implement DTO for product and modify productImageDTO.
- add products resources and collection.
This commit is contained in:
kusowl 2026-02-27 18:16:02 +05:30
parent aef951f71d
commit 920666c201
7 changed files with 109 additions and 4 deletions

View File

@ -0,0 +1,53 @@
<?php
namespace App\Data;
use App\Contracts\OutputDataTransferObject;
use App\Models\Product;
use App\Models\ProductImage;
final readonly class ProductDTO implements OutputDataTransferObject
{
/**
* @param ProductImageDTO[] $productImages
*/
public function __construct(
public int $id,
public string $title,
public string $description,
public int $actualPrice,
public int $listPrice,
public ProductCategoryDTO $category,
public array $productImages,
) {}
/**
* @return array<string, mixed>
*/
public function toArray(): array
{
return [
'id' => $this->id,
'title' => $this->title,
'description' => $this->description,
'actualPrice' => $this->actualPrice,
'listPrice' => $this->listPrice,
'category' => $this->category->toArray(),
'productImage' => array_map(fn (ProductImageDTO $productImage) => $productImage->toArray(),
$this->productImages),
];
}
public static function fromModel(Product $product): self
{
return new self(
id: $product->id,
title: $product->title,
description: $product->description,
actualPrice: $product->actual_price,
listPrice: $product->list_price,
category: ProductCategoryDTO::fromModel($product->category),
productImages: $product->images->map(fn (ProductImage $productImage) => ProductImageDTO::fromModel($productImage))->all(),
);
}
}

View File

@ -3,6 +3,7 @@
namespace App\Data;
use App\Contracts\OutputDataTransferObject;
use App\Models\ProductImage;
final readonly class ProductImageDTO implements OutputDataTransferObject
{
@ -23,4 +24,13 @@ public function toArray(): array
'productId' => $this->productId,
];
}
public static function fromModel(ProductImage $productImage): self
{
return new self(
$productImage->id,
$productImage->path,
$productImage->product_id
);
}
}

View File

@ -2,13 +2,21 @@
namespace App\Http\Controllers;
use App\Data\ProductDTO;
use App\Http\Requests\CreateProductRequest;
use App\Http\Resources\ProductResource;
use App\Models\Product;
use Illuminate\Http\Request;
class ProductController extends Controller
{
public function index() {}
public function index()
{
$paginator = Product::query()->with(['category:id,name,slug', 'images:id,path,product_id'])->paginate();
$paginatedDtos = $paginator->through(fn ($product) => ProductDTO::fromModel($product));
return ProductResource::collection($paginatedDtos);
}
public function store(CreateProductRequest $request)
{

View File

@ -0,0 +1,16 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection;
class ProductCollection extends ResourceCollection
{
public function toArray(Request $request): array
{
return [
'data' => $this->collection,
];
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace App\Http\Resources;
use App\Data\ProductDTO;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
/**
* @property ProductDTO $resource
*/
class ProductResource extends JsonResource
{
public function toArray(Request $request): array
{
return $this->resource->toArray();
}
}

View File

@ -16,9 +16,9 @@ class Product extends Model
'product_category_id',
];
public function productCategory(): BelongsTo
public function category(): BelongsTo
{
return $this->belongsTo(ProductCategory::class);
return $this->belongsTo(ProductCategory::class, 'product_category_id');
}
public function images(): HasMany

View File

@ -15,6 +15,6 @@
Route::get('/user', [AuthenticatedUserController::class, 'show']);
Route::post('/logout', [AuthenticatedUserController::class, 'destroy']);
Route::post('/upload/images', action: [ProductImagesController::class, 'store']);
Route::apiResource('products', ProductController::class);
});
Route::get('/categories', [ProductCategoryController::class, 'index']);
Route::apiResource('products', ProductController::class);