feat: implement product creation workflow with variants, attribute definitions, and validation logic

This commit is contained in:
2026-06-29 11:02:04 -03:00
parent 9d3ed17c00
commit 18504594eb
23 changed files with 774 additions and 146 deletions

View File

@@ -0,0 +1,103 @@
<?php
namespace App\Domains\Catalog\Controllers;
use App\Domains\Catalog\Models\Attribute;
use App\Domains\Catalog\Requests\StoreAttributeRequest;
use App\Domains\Catalog\Requests\UpdateAttributeRequest;
use App\Domains\Catalog\Resources\AttributeResource;
use App\Domains\Shared\Enums\FieldType;
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 AttributeController extends Controller
{
public function index(Tenant $tenant): JsonResponse
{
$query = Attribute::query()
->where('tenant_codigo', $tenant->codigo)
->with('options')
->latest();
return AttributeResource::collection($query->paginateFromRequest())->response();
}
public function store(StoreAttributeRequest $request, Tenant $tenant): JsonResponse
{
$attribute = DB::transaction(function () use ($request, $tenant): Attribute {
$validated = $request->validated();
$options = $validated['options'] ?? [];
unset($validated['options']);
$type = FieldType::from((string) $validated['type']);
if (! $type->supportsOptions()) {
$validated['metadata_schema'] = null;
$options = [];
}
/** @var Attribute $attribute */
$attribute = Attribute::query()->create([
...$validated,
'tenant_codigo' => $tenant->codigo,
]);
$attribute->options()->createMany($options);
return $attribute->load('options');
});
return AttributeResource::make($attribute)->response()->setStatusCode(201);
}
public function show(Tenant $tenant, Attribute $attribute): AttributeResource
{
$attribute = $this->resolveScopedAttribute($tenant, $attribute);
return AttributeResource::make($attribute->load('options'));
}
public function update(UpdateAttributeRequest $request, Tenant $tenant, Attribute $attribute): AttributeResource
{
$attribute = $this->resolveScopedAttribute($tenant, $attribute);
$attribute = DB::transaction(function () use ($request, $attribute): Attribute {
$validated = $request->validated();
$options = $validated['options'] ?? [];
unset($validated['options']);
$type = FieldType::from((string) $validated['type']);
if (! $type->supportsOptions()) {
$validated['metadata_schema'] = null;
$options = [];
}
$attribute->update($validated);
$attribute->options()->delete();
$attribute->options()->createMany($options);
return $attribute->load('options');
});
return AttributeResource::make($attribute);
}
public function destroy(Tenant $tenant, Attribute $attribute): Response
{
$attribute = $this->resolveScopedAttribute($tenant, $attribute);
$attribute->delete();
return response()->noContent();
}
protected function resolveScopedAttribute(Tenant $tenant, Attribute $attribute): Attribute
{
if ($attribute->tenant_codigo !== $tenant->codigo) {
throw new NotFoundHttpException('Attribute not found for tenant.');
}
return $attribute;
}
}

View File

@@ -1,103 +0,0 @@
<?php
namespace App\Domains\Catalog\Controllers;
use App\Domains\Catalog\Models\ProductAttribute;
use App\Domains\Catalog\Requests\StoreProductAttributeRequest;
use App\Domains\Catalog\Requests\UpdateProductAttributeRequest;
use App\Domains\Catalog\Resources\ProductAttributeResource;
use App\Domains\Shared\Enums\FieldType;
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 ProductAttributeController extends Controller
{
public function index(Tenant $tenant): JsonResponse
{
$query = ProductAttribute::query()
->where('tenant_codigo', $tenant->codigo)
->with('options')
->latest();
return ProductAttributeResource::collection($query->paginateFromRequest())->response();
}
public function store(StoreProductAttributeRequest $request, Tenant $tenant): JsonResponse
{
$attribute = DB::transaction(function () use ($request, $tenant): ProductAttribute {
$validated = $request->validated();
$options = $validated['options'] ?? [];
unset($validated['options']);
$type = FieldType::from((string) $validated['type']);
if (! $type->supportsOptions()) {
$validated['metadata_schema'] = null;
$options = [];
}
/** @var ProductAttribute $attribute */
$attribute = ProductAttribute::query()->create([
...$validated,
'tenant_codigo' => $tenant->codigo,
]);
$attribute->options()->createMany($options);
return $attribute->load('options');
});
return ProductAttributeResource::make($attribute)->response()->setStatusCode(201);
}
public function show(Tenant $tenant, ProductAttribute $productAttribute): ProductAttributeResource
{
$productAttribute = $this->resolveScopedAttribute($tenant, $productAttribute);
return ProductAttributeResource::make($productAttribute->load('options'));
}
public function update(UpdateProductAttributeRequest $request, Tenant $tenant, ProductAttribute $productAttribute): ProductAttributeResource
{
$productAttribute = $this->resolveScopedAttribute($tenant, $productAttribute);
$productAttribute = DB::transaction(function () use ($request, $productAttribute): ProductAttribute {
$validated = $request->validated();
$options = $validated['options'] ?? [];
unset($validated['options']);
$type = FieldType::from((string) $validated['type']);
if (! $type->supportsOptions()) {
$validated['metadata_schema'] = null;
$options = [];
}
$productAttribute->update($validated);
$productAttribute->options()->delete();
$productAttribute->options()->createMany($options);
return $productAttribute->load('options');
});
return ProductAttributeResource::make($productAttribute);
}
public function destroy(Tenant $tenant, ProductAttribute $productAttribute): Response
{
$productAttribute = $this->resolveScopedAttribute($tenant, $productAttribute);
$productAttribute->delete();
return response()->noContent();
}
protected function resolveScopedAttribute(Tenant $tenant, ProductAttribute $attribute): ProductAttribute
{
if ($attribute->tenant_codigo !== $tenant->codigo) {
throw new NotFoundHttpException('Product attribute not found for tenant.');
}
return $attribute;
}
}

View File

@@ -6,6 +6,7 @@ use App\Domains\Catalog\Models\Product;
use App\Domains\Catalog\Requests\StoreProductRequest;
use App\Domains\Catalog\Requests\UpdateProductRequest;
use App\Domains\Catalog\Resources\ProductResource;
use App\Domains\Catalog\Services\ProductService;
use App\Domains\Tenant\Models\Tenant;
use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
@@ -21,12 +22,9 @@ class ProductController extends Controller
)->response();
}
public function store(StoreProductRequest $request, Tenant $tenant): JsonResponse
public function store(StoreProductRequest $request, Tenant $tenant, ProductService $productService): JsonResponse
{
$product = Product::query()->create([
...$request->validated(),
'tenant_codigo' => $tenant->codigo,
]);
$product = $productService->createProductWithVariants($tenant, $request->validated());
return ProductResource::make($product)->response()->setStatusCode(201);
}
@@ -35,13 +33,13 @@ class ProductController extends Controller
{
$producto = $this->resolveScopedProduct($tenant, $producto);
return ProductResource::make($producto);
return ProductResource::make($producto->load('variants.definitions.attribute'));
}
public function update(UpdateProductRequest $request, Tenant $tenant, Product $producto): ProductResource
public function update(UpdateProductRequest $request, Tenant $tenant, Product $producto, ProductService $productService): ProductResource
{
$producto = $this->resolveScopedProduct($tenant, $producto);
$producto->update($request->validated());
$producto = $productService->updateProductWithVariants($producto, $request->validated());
return ProductResource::make($producto);
}