From 1d0f9e323d68bbcefff206ab7b11d7e1a350fe81 Mon Sep 17 00:00:00 2001 From: ncoronel Date: Tue, 30 Jun 2026 16:11:18 -0300 Subject: [PATCH] feat: update product resource and seeder to support variant groups and remove unused fields --- .../Catalog/Resources/ProductResource.php | 1 - .../Catalog/Services/ProductService.php | 1 + .../ProductCatalogFromImagesSeeder.php | 149 ++++++++++++------ 3 files changed, 105 insertions(+), 46 deletions(-) diff --git a/app/Domains/Catalog/Resources/ProductResource.php b/app/Domains/Catalog/Resources/ProductResource.php index c9b8a46..9098e00 100644 --- a/app/Domains/Catalog/Resources/ProductResource.php +++ b/app/Domains/Catalog/Resources/ProductResource.php @@ -17,7 +17,6 @@ class ProductResource extends JsonResource { return [ 'id' => $this->id, - 'tenant_codigo' => $this->tenant_codigo, 'category_id' => $this->categoria_id, 'brand_id' => $this->brand_id, 'slug' => $this->slug, diff --git a/app/Domains/Catalog/Services/ProductService.php b/app/Domains/Catalog/Services/ProductService.php index d35b07a..f367af0 100644 --- a/app/Domains/Catalog/Services/ProductService.php +++ b/app/Domains/Catalog/Services/ProductService.php @@ -273,6 +273,7 @@ class ProductService } $product->setRelation('attachments', $resolvedAttachment ? collect([$resolvedAttachment]) : collect()); + $product->unsetRelation('variants'); } return $products; diff --git a/database/seeders/ProductCatalogFromImagesSeeder.php b/database/seeders/ProductCatalogFromImagesSeeder.php index 2c96e47..ae4a1f2 100644 --- a/database/seeders/ProductCatalogFromImagesSeeder.php +++ b/database/seeders/ProductCatalogFromImagesSeeder.php @@ -17,7 +17,7 @@ use RuntimeException; class ProductCatalogFromImagesSeeder extends Seeder { /** - * @var array + * @var array}> */ private const PRODUCT_CATALOG = [ 'buzo_blanco_adidas' => ['price' => 89999, 'stock' => 12], @@ -25,10 +25,20 @@ class ProductCatalogFromImagesSeeder extends Seeder 'buzo_gris_under_armour' => ['price' => 96999, 'stock' => 8], 'buzo_negro_adidas' => ['price' => 91999, 'stock' => 9], 'camiseta_rosario_central' => ['price' => 69999, 'stock' => 20], - 'pantalon_blanco' => ['price' => 55999, 'stock' => 14], - 'pantalon_negro' => ['price' => 57999, 'stock' => 14], - 'remera_blanca' => ['price' => 35999, 'stock' => 22], - 'remera_negra' => ['price' => 36999, 'stock' => 22], + 'pantalon' => [ + 'price' => 55999, + 'variant_groups' => [ + 'pantalon_blanco' => ['stock' => 14], + 'pantalon_negro' => ['stock' => 14], + ], + ], + 'remera' => [ + 'price' => 35999, + 'variant_groups' => [ + 'remera_blanca' => ['stock' => 22], + 'remera_negra' => ['stock' => 22], + ], + ], 'zapatillas_lecoq' => ['price' => 109999, 'stock' => 6], 'zapatillas_topper' => ['price' => 104999, 'stock' => 7], ]; @@ -84,28 +94,52 @@ class ProductCatalogFromImagesSeeder extends Seeder }); $productsToSeed = collect(self::PRODUCT_CATALOG) - ->map(function (array $pricing, string $groupKey) use ($groupedFiles, $tenant): array { - $filesForGroup = $groupedFiles->get($groupKey); - - if ($filesForGroup === null || $filesForGroup->isEmpty()) { - throw new RuntimeException("No images found for catalog group '{$groupKey}'."); + ->map(function (array $catalogConfig, string $productKey) use ($groupedFiles): array { + if (! isset($catalogConfig['variant_groups']) && ! array_key_exists('stock', $catalogConfig)) { + throw new RuntimeException("Missing stock configuration for catalog product '{$productKey}'."); } - $metadata = $this->parseGroupKey($groupKey); + $variantGroups = collect($catalogConfig['variant_groups'] ?? [ + $productKey => ['stock' => $catalogConfig['stock']], + ]) + ->map(function (array $variantConfig, string $groupKey) use ($groupedFiles): array { + $filesForGroup = $groupedFiles->get($groupKey); + + if ($filesForGroup === null || $filesForGroup->isEmpty()) { + throw new RuntimeException("No images found for catalog group '{$groupKey}'."); + } + + return [ + 'group_key' => $groupKey, + 'stock' => $variantConfig['stock'], + 'files' => $filesForGroup->all(), + 'metadata' => $this->parseGroupKey($groupKey), + ]; + }) + ->values() + ->all(); return [ - 'group_key' => $groupKey, - 'pricing' => $pricing, - 'tenant' => $tenant, - 'files' => $filesForGroup->all(), - 'metadata' => $metadata, + 'pricing' => ['price' => $catalogConfig['price']], + 'metadata' => $this->buildProductMetadata($productKey, $variantGroups), + 'variant_groups' => $variantGroups, ]; }) ->values(); + $productSlugsToDelete = $productsToSeed + ->flatMap(fn (array $catalogProduct): array => [ + $catalogProduct['metadata']['product_slug'], + ...collect($catalogProduct['variant_groups']) + ->pluck('group_key') + ->all(), + ]) + ->unique() + ->values(); + $existingProducts = Product::query() ->where('tenant_codigo', $tenant->codigo) - ->whereIn('slug', $productsToSeed->pluck('metadata.product_slug')) + ->whereIn('slug', $productSlugsToDelete) ->with(['attachments', 'variants.attachments']) ->get(); @@ -118,17 +152,17 @@ class ProductCatalogFromImagesSeeder extends Seeder $tenant, $catalogProduct['metadata'], $catalogProduct['pricing'], - $catalogProduct['files'], + $catalogProduct['variant_groups'], ); } } /** * @param array{attribute_codes: array, brand_name: string|null, category_name: string, color: string|null, description: string, product_name: string, product_slug: string, type: string} $metadata - * @param array{price: int, stock: int} $pricing - * @param array $files + * @param array{price: int} $pricing + * @param array, group_key: string, metadata: array{attribute_codes: array, brand_name: string|null, category_name: string, color: string|null, description: string, product_name: string, product_slug: string, type: string}, stock: int}> $variantGroups */ - private function seedProduct(Tenant $tenant, array $metadata, array $pricing, array $files): void + private function seedProduct(Tenant $tenant, array $metadata, array $pricing, array $variantGroups): void { $brand = null; @@ -175,38 +209,63 @@ class ProductCatalogFromImagesSeeder extends Seeder ? self::SHOE_SIZES : self::CLOTHING_SIZES; - $images = array_map( - fn (\SplFileInfo $file): UploadedFile => $this->createUploadedFile($file->getPathname()), - $files - ); + foreach ($variantGroups as $variantGroup) { + $images = array_map( + fn (\SplFileInfo $file): UploadedFile => $this->createUploadedFile($file->getPathname()), + $variantGroup['files'] + ); - foreach ($sizes as $size) { - $definitions = []; + foreach ($sizes as $size) { + $definitions = []; + + if (isset($attributeIds['color']) && $variantGroup['metadata']['color'] !== null) { + $definitions[] = [ + 'attribute_id' => $attributeIds['color'], + 'value' => $variantGroup['metadata']['color'], + ]; + } + + $sizeAttributeCode = $metadata['type'] === 'zapatillas' + ? 'talle_numerico' + : 'talle'; - if (isset($attributeIds['color']) && $metadata['color'] !== null) { $definitions[] = [ - 'attribute_id' => $attributeIds['color'], - 'value' => $metadata['color'], + 'attribute_id' => $attributeIds[$sizeAttributeCode], + 'value' => $size, ]; + + $this->productService->createVariant($product, [ + 'stock' => $variantGroup['stock'], + 'definitions' => $definitions, + 'images' => $images, + ]); } - - $sizeAttributeCode = $metadata['type'] === 'zapatillas' - ? 'talle_numerico' - : 'talle'; - - $definitions[] = [ - 'attribute_id' => $attributeIds[$sizeAttributeCode], - 'value' => $size, - ]; - - $this->productService->createVariant($product, [ - 'stock' => $pricing['stock'], - 'definitions' => $definitions, - 'images' => $images, - ]); } } + /** + * @param array, group_key: string, metadata: array{attribute_codes: array, brand_name: string|null, category_name: string, color: string|null, description: string, product_name: string, product_slug: string, type: string}, stock: int}> $variantGroups + * @return array{attribute_codes: array, brand_name: string|null, category_name: string, color: string|null, description: string, product_name: string, product_slug: string, type: string} + */ + private function buildProductMetadata(string $productKey, array $variantGroups): array + { + $metadata = $this->parseGroupKey($productKey); + $hasColorVariants = collect($variantGroups) + ->contains(fn (array $variantGroup): bool => $variantGroup['metadata']['color'] !== null); + + if ($hasColorVariants && ! in_array('color', $metadata['attribute_codes'], true)) { + array_unshift($metadata['attribute_codes'], 'color'); + } + + if ($hasColorVariants && $metadata['color'] === null) { + $category = Str::lower($metadata['category_name']); + $brandText = $metadata['brand_name'] !== null ? " {$metadata['brand_name']}" : ''; + $metadata['description'] = "Producto seed de {$category}{$brandText} con variantes por color y talle."; + } + + return $metadata; + } + /** * @return array{attribute_codes: array, brand_name: string|null, category_name: string, color: string|null, description: string, product_name: string, product_slug: string, type: string} */