refactor(catalog): Complete catalog refactor to simplify its data model and its querying.
source commits: refactor/catalog
This commit is contained in:
@@ -3,15 +3,16 @@
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Domains\Catalog\Enums\InventoryPolicy;
|
||||
use App\Domains\Catalog\Enums\ProductLayout;
|
||||
use App\Domains\Catalog\Models\Attribute;
|
||||
use App\Domains\Catalog\Models\Brand;
|
||||
use App\Domains\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Catalog\Models\Category;
|
||||
use App\Domains\Catalog\Models\Product;
|
||||
use App\Domains\Catalog\Services\ProductService;
|
||||
use App\Domains\Catalog\Models\FeaturedGroup;
|
||||
use App\Domains\Catalog\Services\CatalogService;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Str;
|
||||
use RuntimeException;
|
||||
@@ -65,7 +66,7 @@ class ProductCatalogFromImagesSeeder extends Seeder
|
||||
'istockphoto-1675347112-2048x2048.jpg',
|
||||
];
|
||||
|
||||
public function __construct(private readonly ProductService $productService) {}
|
||||
public function __construct(private readonly CatalogService $catalogService) {}
|
||||
|
||||
/**
|
||||
* Run the database seeds.
|
||||
@@ -140,14 +141,14 @@ class ProductCatalogFromImagesSeeder extends Seeder
|
||||
->unique()
|
||||
->values();
|
||||
|
||||
$existingProducts = Product::query()
|
||||
->where('tenant_codigo', $tenant->codigo)
|
||||
$existingProducts = CatalogItem::query()
|
||||
->where('tenant_code', $tenant->codigo)
|
||||
->whereIn('slug', $productSlugsToDelete)
|
||||
->with(['attachments', 'variants.attachments'])
|
||||
->with('variants:id,catalog_item_id,inventory_id')
|
||||
->get();
|
||||
|
||||
foreach ($existingProducts as $existingProduct) {
|
||||
$this->productService->delete($existingProduct);
|
||||
$this->catalogService->delete($existingProduct);
|
||||
}
|
||||
|
||||
foreach ($productsToSeed as $catalogProduct) {
|
||||
@@ -158,6 +159,34 @@ class ProductCatalogFromImagesSeeder extends Seeder
|
||||
$catalogProduct['variant_groups'],
|
||||
);
|
||||
}
|
||||
|
||||
$this->seedFeaturedProducts($tenant);
|
||||
}
|
||||
|
||||
private function seedFeaturedProducts(Tenant $tenant): void
|
||||
{
|
||||
FeaturedGroup::query()->where('tenant_code', $tenant->codigo)->delete();
|
||||
|
||||
$group = FeaturedGroup::query()->create([
|
||||
'tenant_code' => $tenant->codigo,
|
||||
'product_layout' => ProductLayout::ColumnWithImage,
|
||||
'group_name' => 'Productos',
|
||||
'group_order' => 0,
|
||||
]);
|
||||
|
||||
$items = CatalogItem::query()
|
||||
->where('tenant_code', $tenant->codigo)
|
||||
->orderBy('id')
|
||||
->get('id');
|
||||
|
||||
$group->featuredItems()->createMany(
|
||||
$items->values()->map(
|
||||
fn (CatalogItem $item, int $order): array => [
|
||||
'catalog_item_id' => $item->id,
|
||||
'order' => $order,
|
||||
]
|
||||
)->all()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,33 +218,21 @@ class ProductCatalogFromImagesSeeder extends Seeder
|
||||
throw new RuntimeException("Category '{$metadata['category_name']}' not found.");
|
||||
}
|
||||
|
||||
$attributeIds = Attribute::query()
|
||||
$attributeCodes = Attribute::query()
|
||||
->where('tenant_codigo', $tenant->codigo)
|
||||
->whereIn('codigo', $metadata['attribute_codes'])
|
||||
->pluck('id', 'codigo');
|
||||
->pluck('codigo');
|
||||
|
||||
if ($attributeIds->count() !== count($metadata['attribute_codes'])) {
|
||||
if ($attributeCodes->count() !== count($metadata['attribute_codes'])) {
|
||||
throw new RuntimeException("Missing attributes for product '{$metadata['product_slug']}'.");
|
||||
}
|
||||
|
||||
$product = $this->productService->create($tenant, [
|
||||
'categoria_id' => $category->id,
|
||||
'brand_id' => $brand?->id,
|
||||
'slug' => $metadata['product_slug'],
|
||||
'nombre' => $metadata['product_name'],
|
||||
'descripcion' => $metadata['description'],
|
||||
'precio' => $pricing['price'],
|
||||
'inventory_policy' => InventoryPolicy::Tracked->value,
|
||||
'attribute_ids' => array_values($attributeIds->all()),
|
||||
]);
|
||||
|
||||
$productAttributeIds = DB::table('products_attributes')
|
||||
->where('product_id', $product->id)
|
||||
->pluck('id', 'attribute_id');
|
||||
|
||||
$sizes = $metadata['type'] === 'zapatillas'
|
||||
? self::SHOE_SIZES
|
||||
: self::CLOTHING_SIZES;
|
||||
$variants = [];
|
||||
$itemImages = [];
|
||||
$directStock = 0;
|
||||
|
||||
foreach ($variantGroups as $variantGroup) {
|
||||
$images = array_map(
|
||||
@@ -223,47 +240,29 @@ class ProductCatalogFromImagesSeeder extends Seeder
|
||||
$variantGroup['files']
|
||||
);
|
||||
|
||||
if ($attributeIds->isEmpty()) {
|
||||
$this->productService->createVariant($product, [
|
||||
'stock' => $variantGroup['stock'],
|
||||
'inventory_policy' => InventoryPolicy::Tracked->value,
|
||||
'definitions' => [],
|
||||
'images' => $images,
|
||||
]);
|
||||
if ($attributeCodes->isEmpty()) {
|
||||
$directStock = $variantGroup['stock'];
|
||||
$itemImages = [...$itemImages, ...$images];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($sizes as $size) {
|
||||
$definitions = [];
|
||||
$values = [];
|
||||
|
||||
if (isset($attributeIds['color']) && $variantGroup['metadata']['color'] !== null) {
|
||||
$colorProductAttributeId = $productAttributeIds[$attributeIds['color']] ?? null;
|
||||
|
||||
if ($colorProductAttributeId === null) {
|
||||
throw new RuntimeException("Missing product attribute for color on product '{$metadata['product_slug']}'.");
|
||||
}
|
||||
|
||||
$definitions[] = [
|
||||
'products_attribute_id' => $colorProductAttributeId,
|
||||
'value' => $variantGroup['metadata']['color'],
|
||||
];
|
||||
if ($attributeCodes->contains('color') && $variantGroup['metadata']['color'] !== null) {
|
||||
$values['color'] = $variantGroup['metadata']['color'];
|
||||
}
|
||||
|
||||
$sizeAttributeCode = $metadata['type'] === 'zapatillas'
|
||||
? 'talle_numerico'
|
||||
: 'talle';
|
||||
|
||||
$sizeProductAttributeId = $productAttributeIds[$attributeIds[$sizeAttributeCode]] ?? null;
|
||||
|
||||
if ($sizeProductAttributeId === null) {
|
||||
if (! $attributeCodes->contains($sizeAttributeCode)) {
|
||||
throw new RuntimeException("Missing product attribute for size on product '{$metadata['product_slug']}'.");
|
||||
}
|
||||
|
||||
$definitions[] = [
|
||||
'products_attribute_id' => $sizeProductAttributeId,
|
||||
'value' => $size,
|
||||
];
|
||||
$values[$sizeAttributeCode] = $size;
|
||||
|
||||
// Override stock for specific variants as requested by the user
|
||||
$stock = $variantGroup['stock'];
|
||||
@@ -280,14 +279,30 @@ class ProductCatalogFromImagesSeeder extends Seeder
|
||||
$stock = 0;
|
||||
}
|
||||
|
||||
$this->productService->createVariant($product, [
|
||||
'stock' => $stock,
|
||||
'inventory_policy' => InventoryPolicy::Tracked->value,
|
||||
'definitions' => $definitions,
|
||||
$variants[] = [
|
||||
'real_stock' => $stock,
|
||||
'values' => $values,
|
||||
'images' => $images,
|
||||
]);
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$this->catalogService->create([
|
||||
'tenant_code' => $tenant->codigo,
|
||||
'category_id' => $category->id,
|
||||
'brand_id' => $brand?->id,
|
||||
'slug' => $metadata['product_slug'],
|
||||
'nombre' => $metadata['product_name'],
|
||||
'descripcion' => $metadata['description'],
|
||||
'precio' => $pricing['price'],
|
||||
'inventory_policy' => InventoryPolicy::Tracked->value,
|
||||
...($attributeCodes->isEmpty()
|
||||
? ['real_stock' => $directStock, 'images' => $itemImages]
|
||||
: [
|
||||
'attribute_codes' => array_values($metadata['attribute_codes']),
|
||||
'variants' => $variants,
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user