From edcb73f748810e6c7e4e510d1fb2a12c8b2ed589 Mon Sep 17 00:00:00 2001 From: ncoronel Date: Mon, 29 Jun 2026 14:51:36 -0300 Subject: [PATCH] feat: refactor ProductVariantController and requests to use scoped product handling and update route parameters --- .../Controllers/ProductVariantController.php | 62 ++++++++----------- .../Requests/StoreProductVariantRequest.php | 14 ++--- .../Requests/UpdateProductVariantRequest.php | 13 ++-- app/Domains/Catalog/routes/api.php | 7 ++- 4 files changed, 41 insertions(+), 55 deletions(-) diff --git a/app/Domains/Catalog/Controllers/ProductVariantController.php b/app/Domains/Catalog/Controllers/ProductVariantController.php index 095aea8..a27dbb2 100644 --- a/app/Domains/Catalog/Controllers/ProductVariantController.php +++ b/app/Domains/Catalog/Controllers/ProductVariantController.php @@ -12,80 +12,72 @@ use App\Domains\Tenant\Models\Tenant; use App\Http\Controllers\Controller; use Illuminate\Http\JsonResponse; use Illuminate\Http\Response; -use Illuminate\Support\Facades\DB; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class ProductVariantController extends Controller { - public function index(Tenant $tenant): JsonResponse + public function index(Tenant $tenant, Product $producto): JsonResponse { + $query = ProductVariant::query() - ->whereHas('product', fn ($query) => $query->where('tenant_codigo', $tenant->codigo)) + ->where('producto_id', $producto->id) ->with(['product', 'definitions.attribute.options', 'attachments']) ->latest(); return ProductVariantResource::collection($query->paginateFromRequest())->response(); } - public function store(StoreProductVariantRequest $request, Tenant $tenant, ProductService $productService): JsonResponse + public function store(StoreProductVariantRequest $request, Tenant $tenant, Product $producto, ProductService $productService): JsonResponse { - $validated = $request->validated(); - $product = $this->resolveTenantProduct($tenant, (int) $validated['producto_id']); + $producto = $this->resolveScopedProduct($tenant, $producto); - $variant = $productService->createVariant($product, $validated); + $variant = $productService->createVariant($producto, $request->validated()); return ProductVariantResource::make($variant)->response()->setStatusCode(201); } - public function show(Tenant $tenant, ProductVariant $productVariant): ProductVariantResource + public function show(Tenant $tenant, Product $producto, ProductVariant $productVariant): ProductVariantResource { - $productVariant = $this->resolveScopedVariant($tenant, $productVariant); + $producto = $this->resolveScopedProduct($tenant, $producto); + $productVariant = $this->resolveScopedVariant($producto, $productVariant); return ProductVariantResource::make($productVariant->load(['product', 'definitions.attribute.options', 'attachments'])); } - public function update(UpdateProductVariantRequest $request, Tenant $tenant, ProductVariant $productVariant, ProductService $productService): ProductVariantResource + public function update(UpdateProductVariantRequest $request, Tenant $tenant, Product $producto, ProductVariant $productVariant, ProductService $productService): ProductVariantResource { - $productVariant = $this->resolveScopedVariant($tenant, $productVariant); - $validated = $request->validated(); + $producto = $this->resolveScopedProduct($tenant, $producto); + $productVariant = $this->resolveScopedVariant($producto, $productVariant); - $this->resolveTenantProduct($tenant, (int) $validated['producto_id']); - - $productVariant = $productService->updateVariant($productVariant, $validated); + $productVariant = $productService->updateVariant($productVariant, $request->validated()); return ProductVariantResource::make($productVariant); } - public function destroy(Tenant $tenant, ProductVariant $productVariant, ProductService $productService): Response + public function destroy(Tenant $tenant, Product $producto, ProductVariant $productVariant, ProductService $productService): Response { - $productVariant = $this->resolveScopedVariant($tenant, $productVariant); + $producto = $this->resolveScopedProduct($tenant, $producto); + $productVariant = $this->resolveScopedVariant($producto, $productVariant); $productService->deleteVariant($productVariant); return response()->noContent(); } - protected function resolveScopedVariant(Tenant $tenant, ProductVariant $variant): ProductVariant + protected function resolveScopedProduct(Tenant $tenant, Product $product): Product { - $variant->loadMissing('product'); - - if ($variant->product === null || $variant->product->tenant_codigo !== $tenant->codigo) { - throw new NotFoundHttpException('Product variant not found for tenant.'); - } - - return $variant; - } - - protected function resolveTenantProduct(Tenant $tenant, int $productId): Product - { - $product = Product::query() - ->whereKey($productId) - ->where('tenant_codigo', $tenant->codigo) - ->first(); - - if ($product === null) { + if ($product->tenant_codigo !== $tenant->codigo) { throw new NotFoundHttpException('Product not found for tenant.'); } return $product; } + + protected function resolveScopedVariant(Product $product, ProductVariant $variant): ProductVariant + { + if ($variant->producto_id !== $product->id) { + throw new NotFoundHttpException('Product variant not found for product.'); + } + + return $variant; + } } diff --git a/app/Domains/Catalog/Requests/StoreProductVariantRequest.php b/app/Domains/Catalog/Requests/StoreProductVariantRequest.php index a8d9512..b2a9ddb 100644 --- a/app/Domains/Catalog/Requests/StoreProductVariantRequest.php +++ b/app/Domains/Catalog/Requests/StoreProductVariantRequest.php @@ -18,20 +18,16 @@ class StoreProductVariantRequest extends FormRequest */ public function rules(): array { + /** @var \App\Domains\Catalog\Models\Product|null $product */ + $product = $this->route('producto'); + return [ - 'producto_id' => [ - 'required', - 'integer', - Rule::exists('productos', 'id')->where( - fn ($query) => $query->where('tenant_codigo', $this->route('tenant')?->codigo) - ), - ], 'slug' => [ 'nullable', 'string', 'max:255', Rule::unique('productos_variantes', 'slug')->where( - fn ($query) => $query->where('producto_id', $this->input('producto_id')) + fn ($query) => $query->where('producto_id', $product?->id) ), ], 'nombre' => ['nullable', 'string', 'max:255'], @@ -47,7 +43,7 @@ class StoreProductVariantRequest extends FormRequest fn ($query) => $query->where('tenant_codigo', $this->route('tenant')?->codigo) ), function ($attribute, $value, $fail) { - $productId = $this->input('producto_id'); + $productId = $this->route('producto')?->id; if ($productId) { $exists = \Illuminate\Support\Facades\DB::table('products_attributes') ->where('product_id', $productId) diff --git a/app/Domains/Catalog/Requests/UpdateProductVariantRequest.php b/app/Domains/Catalog/Requests/UpdateProductVariantRequest.php index 90d3c31..496ae65 100644 --- a/app/Domains/Catalog/Requests/UpdateProductVariantRequest.php +++ b/app/Domains/Catalog/Requests/UpdateProductVariantRequest.php @@ -21,22 +21,17 @@ class UpdateProductVariantRequest extends FormRequest { /** @var ProductVariant|null $productVariant */ $productVariant = $this->route('productVariant'); + /** @var \App\Domains\Catalog\Models\Product|null $product */ + $product = $this->route('producto'); return [ - 'producto_id' => [ - 'required', - 'integer', - Rule::exists('productos', 'id')->where( - fn ($query) => $query->where('tenant_codigo', $this->route('tenant')?->codigo) - ), - ], 'slug' => [ 'nullable', 'string', 'max:255', Rule::unique('productos_variantes', 'slug') ->ignore($productVariant?->id) - ->where(fn ($query) => $query->where('producto_id', $this->input('producto_id'))), + ->where(fn ($query) => $query->where('producto_id', $product?->id)), ], 'nombre' => ['nullable', 'string', 'max:255'], 'stock' => ['sometimes', 'integer', 'min:0'], @@ -51,7 +46,7 @@ class UpdateProductVariantRequest extends FormRequest fn ($query) => $query->where('tenant_codigo', $this->route('tenant')?->codigo) ), function ($attribute, $value, $fail) { - $productId = $this->input('producto_id'); + $productId = $this->route('producto')?->id; if ($productId) { $exists = \Illuminate\Support\Facades\DB::table('products_attributes') ->where('product_id', $productId) diff --git a/app/Domains/Catalog/routes/api.php b/app/Domains/Catalog/routes/api.php index d5c6e65..666d423 100644 --- a/app/Domains/Catalog/routes/api.php +++ b/app/Domains/Catalog/routes/api.php @@ -12,6 +12,9 @@ Route::prefix('tenants/{tenant:codigo}')->group(function (): void { Route::apiResource('categorias', CategoryController::class)->parameters(['categorias' => 'categoria']); Route::apiResource('productos', ProductController::class); Route::apiResource('attributes', AttributeController::class); - Route::apiResource('product-variants', ProductVariantController::class) - ->parameters(['product-variants' => 'productVariant']); + Route::apiResource('productos.variants', ProductVariantController::class) + ->parameters([ + 'productos' => 'producto', + 'variants' => 'productVariant', + ]); });