feat: implement product and attribute management services with corresponding controllers and feature tests

This commit is contained in:
2026-06-29 11:37:22 -03:00
parent b5b57a753c
commit 9b7d728117
7 changed files with 365 additions and 137 deletions

View File

@@ -2,23 +2,22 @@
namespace App\Domains\Catalog\Services;
use App\Domains\Catalog\Models\Attribute;
use App\Domains\Catalog\Models\Product;
use App\Domains\Catalog\Models\ProductVariant;
use App\Domains\Tenant\Models\Tenant;
use Illuminate\Support\Facades\DB;
class ProductService
{
/**
* Create a product along with its variants and variant definitions.
* Create a product.
*
* @param array<string, mixed> $data
*/
public function createProductWithVariants(Tenant $tenant, array $data): Product
public function create(Tenant $tenant, array $data): Product
{
return DB::transaction(function () use ($tenant, $data) {
$variantsData = $data['variants'] ?? [];
unset($data['variants']);
$attributeIds = $data['attribute_ids'] ?? [];
unset($data['attribute_ids']);
@@ -28,88 +27,124 @@ class ProductService
'tenant_codigo' => $tenant->codigo,
]);
foreach ($variantsData as $variantData) {
$definitions = $variantData['definitions'] ?? [];
unset($variantData['definitions']);
$product->attributes()->sync($attributeIds);
$variant = $product->variants()->create($variantData);
$variant->definitions()->createMany($definitions);
}
$attributeIdsFromVariants = collect($variantsData)->flatMap(function ($variant) {
return collect($variant['definitions'] ?? [])->pluck('attribute_id');
})->unique()->toArray();
$allAttributeIds = array_unique(array_merge($attributeIds, $attributeIdsFromVariants));
$product->attributes()->sync($allAttributeIds);
return $product->load(['variants.definitions.attribute', 'attributes']);
return $product->load('attributes');
});
}
/**
* Update a product along with its variants and variant definitions.
* Update a product.
*
* @param array<string, mixed> $data
*/
public function updateProductWithVariants(Product $product, array $data): Product
public function update(Product $product, array $data): Product
{
return DB::transaction(function () use ($product, $data) {
$hasVariants = array_key_exists('variants', $data);
$variantsData = $data['variants'] ?? [];
unset($data['variants']);
$hasAttributeIds = array_key_exists('attribute_ids', $data);
$attributeIds = $data['attribute_ids'] ?? [];
unset($data['attribute_ids']);
// Ensure tenant_codigo cannot be updated/changed
unset($data['tenant_codigo']);
$product->update($data);
if ($hasVariants) {
$existingVariantIds = $product->variants()->pluck('id')->toArray();
$incomingVariantIds = [];
foreach ($variantsData as $variantData) {
$definitions = $variantData['definitions'] ?? [];
unset($variantData['definitions']);
$variantId = $variantData['id'] ?? null;
if ($variantId && in_array($variantId, $existingVariantIds)) {
$variant = $product->variants()->findOrFail($variantId);
$variant->update($variantData);
$incomingVariantIds[] = (int) $variantId;
} else {
$variant = $product->variants()->create($variantData);
$incomingVariantIds[] = $variant->id;
}
$variant->definitions()->delete();
$variant->definitions()->createMany($definitions);
}
$variantsToDelete = array_diff($existingVariantIds, $incomingVariantIds);
if (!empty($variantsToDelete)) {
$product->variants()->whereIn('id', $variantsToDelete)->delete();
}
if ($hasAttributeIds) {
$product->attributes()->sync($attributeIds);
}
if ($hasAttributeIds || $hasVariants) {
$explicitIds = $hasAttributeIds ? $attributeIds : $product->attributes()->pluck('attribute_id')->toArray();
return $product->load('attributes');
});
}
$variantIds = $product->variants()->pluck('id')->toArray();
$attributeIdsFromVariants = DB::table('productos_variantes_values')
->whereIn('producto_variante_id', $variantIds)
->pluck('attribute_id')
->unique()
->toArray();
$allAttributeIds = array_unique(array_merge($explicitIds, $attributeIdsFromVariants));
$product->attributes()->sync($allAttributeIds);
/**
* Delete a product.
*/
public function delete(Product $product): void
{
DB::transaction(function () use ($product) {
foreach ($product->variants as $variant) {
$product->deleteVariant($variant);
}
$product->attributes()->detach();
$product->delete();
});
}
return $product->load(['variants.definitions.attribute', 'attributes']);
/**
* Create a product variant.
*
* @param array<string, mixed> $data
*/
public function createVariant(Product $product, array $data): ProductVariant
{
return DB::transaction(function () use ($product, $data) {
$variant = $product->createVariant($data);
return $variant->load(['product', 'definitions.attribute']);
});
}
/**
* Update a product variant.
*
* @param array<string, mixed> $data
*/
public function updateVariant(ProductVariant $variant, array $data): ProductVariant
{
return DB::transaction(function () use ($variant, $data) {
/** @var Product $product */
$product = $variant->product;
$updatedVariant = $product->updateVariant($variant, $data);
return $updatedVariant->load(['product', 'definitions.attribute']);
});
}
/**
* Delete a product variant.
*/
public function deleteVariant(ProductVariant $variant): void
{
DB::transaction(function () use ($variant) {
/** @var Product $product */
$product = $variant->product;
$product->deleteVariant($variant);
});
}
/**
* Create an attribute.
*
* @param array<string, mixed> $data
*/
public static function createAttribute(Tenant $tenant, array $data): Attribute
{
return DB::transaction(function () use ($tenant, $data) {
return Product::createAttribute($tenant, $data);
});
}
/**
* Update an attribute.
*
* @param array<string, mixed> $data
*/
public static function updateAttribute(Attribute $attribute, array $data): Attribute
{
return DB::transaction(function () use ($attribute, $data) {
return Product::updateAttribute($attribute, $data);
});
}
/**
* Delete an attribute.
*/
public static function deleteAttribute(Attribute $attribute): void
{
DB::transaction(function () use ($attribute) {
Product::deleteAttribute($attribute);
});
}
}