feat: refactor product attributes handling to use product_attribute table and update related models and requests
This commit is contained in:
@@ -925,7 +925,7 @@
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"producto_id\": {{producto_id}},\n \"slug\": \"remera-basica-blanca-m\",\n \"nombre\": \"Remera Basica Blanca - Talle M\",\n \"stock\": 8,\n \"descripcion\": \"Variante de prueba\",\n \"precio\": 26999,\n \"definitions\": [\n {\n \"attribute_id\": {{color_attribute_id}},\n \"value\": \"Blanco\"\n },\n {\n \"attribute_id\": {{talle_attribute_id}},\n \"value\": \"M\"\n }\n ],\n \"images\": []\n}"
|
||||
"raw": "{\n \"producto_id\": {{producto_id}},\n \"slug\": \"remera-basica-blanca-m\",\n \"nombre\": \"Remera Basica Blanca - Talle M\",\n \"stock\": 8,\n \"descripcion\": \"Variante de prueba\",\n \"precio\": 26999,\n \"definitions\": [\n {\n \"products_attribute_id\": {{color_product_attribute_id}},\n \"value\": \"Blanco\"\n },\n {\n \"products_attribute_id\": {{talle_product_attribute_id}},\n \"value\": \"M\"\n }\n ],\n \"images\": []\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{base_url}}/api/tenants/{{tenant_codigo}}/product-variants",
|
||||
@@ -987,7 +987,7 @@
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"slug\": \"remera-basica-blanca-m\",\n \"nombre\": \"Remera Basica Blanca - Talle M Updated\",\n \"stock\": 12,\n \"descripcion\": \"Variante actualizada\",\n \"precio\": 27999,\n \"definitions\": [\n {\n \"attribute_id\": {{color_attribute_id}},\n \"value\": \"Blanco\"\n },\n {\n \"attribute_id\": {{talle_attribute_id}},\n \"value\": \"M\"\n }\n ],\n \"images\": []\n}"
|
||||
"raw": "{\n \"slug\": \"remera-basica-blanca-m\",\n \"nombre\": \"Remera Basica Blanca - Talle M Updated\",\n \"stock\": 12,\n \"descripcion\": \"Variante actualizada\",\n \"precio\": 27999,\n \"definitions\": [\n {\n \"products_attribute_id\": {{color_product_attribute_id}},\n \"value\": \"Blanco\"\n },\n {\n \"products_attribute_id\": {{talle_product_attribute_id}},\n \"value\": \"M\"\n }\n ],\n \"images\": []\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{base_url}}/api/tenants/{{tenant_codigo}}/product-variants/{{product_variant_id}}",
|
||||
@@ -1499,6 +1499,16 @@
|
||||
"value": "2",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "color_product_attribute_id",
|
||||
"value": "1",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "talle_product_attribute_id",
|
||||
"value": "2",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "producto_id",
|
||||
"value": "1",
|
||||
|
||||
@@ -94,7 +94,7 @@ class CartService
|
||||
{
|
||||
return $cart->fresh()->load([
|
||||
'items.variant.product',
|
||||
'items.variant.definitions.attribute',
|
||||
'items.variant.definitions.productAttribute.attribute',
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ class ProductVariantController extends Controller
|
||||
|
||||
$query = ProductVariant::query()
|
||||
->where('producto_id', $producto->id)
|
||||
->with(['product', 'definitions.attribute.options', 'attachments'])
|
||||
->with(['product', 'definitions.productAttribute.attribute.options', 'attachments'])
|
||||
->latest();
|
||||
|
||||
return ProductVariantResource::collection($query->paginateFromRequest())->response();
|
||||
@@ -41,7 +41,7 @@ class ProductVariantController extends Controller
|
||||
$producto = $this->resolveScopedProduct($tenant, $producto);
|
||||
$productVariant = $this->resolveScopedVariant($producto, $productVariant);
|
||||
|
||||
return ProductVariantResource::make($productVariant->load(['product', 'definitions.attribute.options', 'attachments']));
|
||||
return ProductVariantResource::make($productVariant->load(['product', 'definitions.productAttribute.attribute.options', 'attachments']));
|
||||
}
|
||||
|
||||
public function update(UpdateProductVariantRequest $request, Tenant $tenant, Product $producto, ProductVariant $productVariant, ProductService $productService): ProductVariantResource
|
||||
|
||||
@@ -9,6 +9,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
||||
|
||||
#[Fillable([
|
||||
'tenant_codigo',
|
||||
@@ -53,10 +54,25 @@ class Attribute extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<ProductVariantDefinition, $this>
|
||||
* @return HasMany<ProductAttribute, $this>
|
||||
*/
|
||||
public function variantDefinitions(): HasMany
|
||||
public function productAttributes(): HasMany
|
||||
{
|
||||
return $this->hasMany(ProductVariantDefinition::class, 'attribute_id');
|
||||
return $this->hasMany(ProductAttribute::class, 'attribute_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasManyThrough<ProductVariantDefinition, ProductAttribute, $this>
|
||||
*/
|
||||
public function variantDefinitions(): HasManyThrough
|
||||
{
|
||||
return $this->hasManyThrough(
|
||||
ProductVariantDefinition::class,
|
||||
ProductAttribute::class,
|
||||
'attribute_id',
|
||||
'products_attribute_id',
|
||||
'id',
|
||||
'id'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Domains\Catalog\Models;
|
||||
use App\Domains\Attachable\Models\Attachment;
|
||||
use App\Domains\Catalog\Models\Brand;
|
||||
use App\Domains\Catalog\Models\Category;
|
||||
use App\Domains\Catalog\Models\ProductAttribute;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -83,6 +84,14 @@ class Product extends Model
|
||||
)->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<ProductAttribute, $this>
|
||||
*/
|
||||
public function productAttributes(): HasMany
|
||||
{
|
||||
return $this->hasMany(ProductAttribute::class, 'product_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsToMany<Attachment, $this>
|
||||
*/
|
||||
@@ -170,20 +179,25 @@ class Product extends Model
|
||||
protected function validateVariantDefinitions(array $definitions): void
|
||||
{
|
||||
foreach ($definitions as $definition) {
|
||||
$attributeId = $definition['attribute_id'] ?? null;
|
||||
if (!$attributeId) {
|
||||
$productAttributeId = $definition['products_attribute_id'] ?? null;
|
||||
if (! $productAttributeId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$attribute = Attribute::find($attributeId);
|
||||
if (!$attribute) {
|
||||
throw new \InvalidArgumentException("Attribute with ID {$attributeId} not found.");
|
||||
$productAttribute = $this->productAttributes()
|
||||
->with('attribute.options')
|
||||
->find($productAttributeId);
|
||||
|
||||
$attribute = $productAttribute?->attribute;
|
||||
|
||||
if (! $productAttribute || ! $attribute) {
|
||||
throw new \InvalidArgumentException("Product attribute with ID {$productAttributeId} not found for product {$this->id}.");
|
||||
}
|
||||
|
||||
if ($attribute->type === \App\Domains\Shared\Enums\FieldType::Select) {
|
||||
$allowedValues = $attribute->options()->pluck('value')->toArray();
|
||||
$val = $definition['value'] ?? null;
|
||||
if ($val !== null && !in_array($val, $allowedValues, true)) {
|
||||
if ($val !== null && ! in_array($val, $allowedValues, true)) {
|
||||
throw new \InvalidArgumentException("The value '{$val}' is not a valid option for the select attribute '{$attribute->nombre}'.");
|
||||
}
|
||||
} elseif ($attribute->type === \App\Domains\Shared\Enums\FieldType::Multiselect) {
|
||||
@@ -202,7 +216,7 @@ class Product extends Model
|
||||
}
|
||||
}
|
||||
foreach ($values as $v) {
|
||||
if (!in_array($v, $allowedValues, true)) {
|
||||
if (! in_array($v, $allowedValues, true)) {
|
||||
throw new \InvalidArgumentException("The value '{$v}' is not a valid option for the multiselect attribute '{$attribute->nombre}'.");
|
||||
}
|
||||
}
|
||||
|
||||
44
app/Domains/Catalog/Models/ProductAttribute.php
Normal file
44
app/Domains/Catalog/Models/ProductAttribute.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Catalog\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
#[Fillable([
|
||||
'product_id',
|
||||
'attribute_id',
|
||||
])]
|
||||
class ProductAttribute extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'products_attributes';
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Product, $this>
|
||||
*/
|
||||
public function product(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Product::class, 'product_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Attribute, $this>
|
||||
*/
|
||||
public function attribute(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Attribute::class, 'attribute_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<ProductVariantDefinition, $this>
|
||||
*/
|
||||
public function variantDefinitions(): HasMany
|
||||
{
|
||||
return $this->hasMany(ProductVariantDefinition::class, 'products_attribute_id');
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
#[Fillable([
|
||||
'producto_variante_id',
|
||||
'attribute_id',
|
||||
'products_attribute_id',
|
||||
'value',
|
||||
])]
|
||||
class ProductVariantDefinition extends Model
|
||||
@@ -27,10 +27,10 @@ class ProductVariantDefinition extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Attribute, $this>
|
||||
* @return BelongsTo<ProductAttribute, $this>
|
||||
*/
|
||||
public function attribute(): BelongsTo
|
||||
public function productAttribute(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Attribute::class, 'attribute_id');
|
||||
return $this->belongsTo(ProductAttribute::class, 'products_attribute_id');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,25 +21,13 @@ class StoreProductVariantRequest extends FormRequest
|
||||
return [
|
||||
'stock' => ['sometimes', 'integer', 'min:0'],
|
||||
'definitions' => ['sometimes', 'array'],
|
||||
'definitions.*.attribute_id' => [
|
||||
'definitions.*.products_attribute_id' => [
|
||||
'required',
|
||||
'integer',
|
||||
'distinct',
|
||||
Rule::exists('attribute', 'id')->where(
|
||||
fn ($query) => $query->where('tenant_codigo', $this->route('tenant')?->codigo)
|
||||
Rule::exists('products_attributes', 'id')->where(
|
||||
fn ($query) => $query->where('product_id', $this->route('producto')?->id)
|
||||
),
|
||||
function ($attribute, $value, $fail) {
|
||||
$productId = $this->route('producto')?->id;
|
||||
if ($productId) {
|
||||
$exists = \Illuminate\Support\Facades\DB::table('products_attributes')
|
||||
->where('product_id', $productId)
|
||||
->where('attribute_id', $value)
|
||||
->exists();
|
||||
if (!$exists) {
|
||||
$fail('The selected attribute is not associated with the product.');
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
'definitions.*.value' => ['nullable', 'string'],
|
||||
'images' => ['sometimes', 'nullable', 'array'],
|
||||
|
||||
@@ -21,25 +21,13 @@ class UpdateProductVariantRequest extends FormRequest
|
||||
return [
|
||||
'stock' => ['sometimes', 'integer', 'min:0'],
|
||||
'definitions' => ['sometimes', 'array'],
|
||||
'definitions.*.attribute_id' => [
|
||||
'definitions.*.products_attribute_id' => [
|
||||
'required',
|
||||
'integer',
|
||||
'distinct',
|
||||
Rule::exists('attribute', 'id')->where(
|
||||
fn ($query) => $query->where('tenant_codigo', $this->route('tenant')?->codigo)
|
||||
Rule::exists('products_attributes', 'id')->where(
|
||||
fn ($query) => $query->where('product_id', $this->route('producto')?->id)
|
||||
),
|
||||
function ($attribute, $value, $fail) {
|
||||
$productId = $this->route('producto')?->id;
|
||||
if ($productId) {
|
||||
$exists = \Illuminate\Support\Facades\DB::table('products_attributes')
|
||||
->where('product_id', $productId)
|
||||
->where('attribute_id', $value)
|
||||
->exists();
|
||||
if (!$exists) {
|
||||
$fail('The selected attribute is not associated with the product.');
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
'definitions.*.value' => ['nullable', 'string'],
|
||||
'images' => ['sometimes', 'nullable', 'array'],
|
||||
|
||||
@@ -43,7 +43,7 @@ class ProductResource extends JsonResource
|
||||
? $selectedVariant->attachments->map(fn ($attachment) => $attachment->getTemporaryUrl(1440))->values()
|
||||
: $this->attachments->map(fn ($attachment) => $attachment->getTemporaryUrl(1440))->values(),
|
||||
'attributes' => $selectedVariant->definitions->mapWithKeys(function ($definition) {
|
||||
return [$definition->attribute?->codigo => $definition->value];
|
||||
return [$definition->productAttribute?->attribute?->codigo => $definition->value];
|
||||
})->toArray(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -18,9 +18,10 @@ class ProductVariantDefinitionResource extends JsonResource
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'producto_variante_id' => $this->producto_variante_id,
|
||||
'attribute_id' => $this->attribute_id,
|
||||
'products_attribute_id' => $this->products_attribute_id,
|
||||
'attribute_id' => $this->whenLoaded('productAttribute', fn () => $this->productAttribute?->attribute_id),
|
||||
'value' => $this->value,
|
||||
'attribute' => $this->whenLoaded('attribute', fn () => $this->attribute?->nombre),
|
||||
'attribute' => $this->whenLoaded('productAttribute', fn () => $this->productAttribute?->attribute?->nombre),
|
||||
'metadata' => $this->resolveMetadata(),
|
||||
];
|
||||
}
|
||||
@@ -30,11 +31,17 @@ class ProductVariantDefinitionResource extends JsonResource
|
||||
*/
|
||||
protected function resolveMetadata(): ?array
|
||||
{
|
||||
if (! $this->relationLoaded('attribute') || ! $this->attribute?->relationLoaded('options')) {
|
||||
if (! $this->relationLoaded('productAttribute')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$option = $this->attribute->options->firstWhere('value', $this->value);
|
||||
$attribute = $this->productAttribute?->attribute;
|
||||
|
||||
if (! $attribute?->relationLoaded('options')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$option = $attribute->options->firstWhere('value', $this->value);
|
||||
|
||||
if ($option === null || $option->metadata === null) {
|
||||
return null;
|
||||
|
||||
@@ -112,7 +112,7 @@ class ProductService
|
||||
$this->syncVariantImages($variant, $images);
|
||||
}
|
||||
|
||||
return $variant->load(['product', 'definitions.attribute.options', 'attachments']);
|
||||
return $variant->load(['product', 'definitions.productAttribute.attribute.options', 'attachments']);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ class ProductService
|
||||
$this->syncVariantImages($updatedVariant, $images);
|
||||
}
|
||||
|
||||
return $updatedVariant->load(['product', 'definitions.attribute.options', 'attachments']);
|
||||
return $updatedVariant->load(['product', 'definitions.productAttribute.attribute.options', 'attachments']);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -291,7 +291,7 @@ class ProductService
|
||||
$selectedVariantQuery = $product->variants()
|
||||
->with([
|
||||
'attachments',
|
||||
'definitions.attribute.options',
|
||||
'definitions.productAttribute.attribute.options',
|
||||
]);
|
||||
|
||||
$selectedVariant = $variantId !== null
|
||||
@@ -302,7 +302,7 @@ class ProductService
|
||||
$selectedVariant = $product->variants()
|
||||
->with([
|
||||
'attachments',
|
||||
'definitions.attribute.options',
|
||||
'definitions.productAttribute.attribute.options',
|
||||
])
|
||||
->first();
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ class PurchaseController extends Controller
|
||||
{
|
||||
return PurchaseResource::collection(
|
||||
Purchase::query()
|
||||
->with(['items.variant.product', 'items.variant.definitions'])
|
||||
->with(['items.variant.product', 'items.variant.definitions.productAttribute.attribute'])
|
||||
->where('tenant_codigo', $tenant->codigo)
|
||||
->where('user_id', $request->user()->id)
|
||||
->latest()
|
||||
@@ -48,7 +48,7 @@ class PurchaseController extends Controller
|
||||
|
||||
$purchase->items()->createMany($purchaseItems);
|
||||
|
||||
return $purchase->load(['items.variant.product', 'items.variant.definitions']);
|
||||
return $purchase->load(['items.variant.product', 'items.variant.definitions.productAttribute.attribute']);
|
||||
});
|
||||
|
||||
return PurchaseResource::make($purchase)->response()->setStatusCode(201);
|
||||
@@ -59,7 +59,7 @@ class PurchaseController extends Controller
|
||||
$compra = $this->resolveScopedPurchase($tenant, $request->user()->id, $compra);
|
||||
|
||||
return PurchaseResource::make(
|
||||
$compra->loadMissing(['items.variant.product', 'items.variant.definitions'])
|
||||
$compra->loadMissing(['items.variant.product', 'items.variant.definitions.productAttribute.attribute'])
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,29 @@ return new class extends Migration
|
||||
|
||||
$table->unique(['product_id', 'attribute_id']);
|
||||
});
|
||||
|
||||
// 4. Point variant values to the product-attribute pivot instead of the base attribute
|
||||
Schema::table('productos_variantes_values', function (Blueprint $table) {
|
||||
$table->dropForeign('productos_variantes_definiciones_attribute_id_foreign');
|
||||
$table->dropUnique('prod_var_def_variant_attr_unique');
|
||||
});
|
||||
|
||||
Schema::table('productos_variantes_values', function (Blueprint $table) {
|
||||
$table->renameColumn('attribute_id', 'products_attribute_id');
|
||||
});
|
||||
|
||||
Schema::table('productos_variantes_values', function (Blueprint $table) {
|
||||
$table->foreign('products_attribute_id')
|
||||
->references('id')
|
||||
->on('products_attributes')
|
||||
->cascadeOnUpdate()
|
||||
->cascadeOnDelete();
|
||||
|
||||
$table->unique(
|
||||
['producto_variante_id', 'products_attribute_id'],
|
||||
'prod_var_values_variant_product_attr_unique'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,6 +66,25 @@ return new class extends Migration
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('productos_variantes_values', function (Blueprint $table) {
|
||||
$table->dropForeign(['products_attribute_id']);
|
||||
$table->dropUnique('prod_var_values_variant_product_attr_unique');
|
||||
});
|
||||
|
||||
Schema::table('productos_variantes_values', function (Blueprint $table) {
|
||||
$table->renameColumn('products_attribute_id', 'attribute_id');
|
||||
});
|
||||
|
||||
Schema::table('productos_variantes_values', function (Blueprint $table) {
|
||||
$table->foreign('attribute_id')
|
||||
->references('id')
|
||||
->on('attribute')
|
||||
->cascadeOnUpdate()
|
||||
->cascadeOnDelete();
|
||||
|
||||
$table->unique(['producto_variante_id', 'attribute_id'], 'prod_var_def_variant_attr_unique');
|
||||
});
|
||||
|
||||
Schema::dropIfExists('products_attributes');
|
||||
Schema::rename('productos_variantes_values', 'productos_variantes_definiciones');
|
||||
Schema::rename('attribute', 'productos_attributes');
|
||||
|
||||
@@ -10,6 +10,7 @@ use App\Domains\Catalog\Services\ProductService;
|
||||
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;
|
||||
@@ -205,6 +206,10 @@ class ProductCatalogFromImagesSeeder extends Seeder
|
||||
'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;
|
||||
@@ -219,8 +224,14 @@ class ProductCatalogFromImagesSeeder extends Seeder
|
||||
$definitions = [];
|
||||
|
||||
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[] = [
|
||||
'attribute_id' => $attributeIds['color'],
|
||||
'products_attribute_id' => $colorProductAttributeId,
|
||||
'value' => $variantGroup['metadata']['color'],
|
||||
];
|
||||
}
|
||||
@@ -229,8 +240,14 @@ class ProductCatalogFromImagesSeeder extends Seeder
|
||||
? 'talle_numerico'
|
||||
: 'talle';
|
||||
|
||||
$sizeProductAttributeId = $productAttributeIds[$attributeIds[$sizeAttributeCode]] ?? null;
|
||||
|
||||
if ($sizeProductAttributeId === null) {
|
||||
throw new RuntimeException("Missing product attribute for size on product '{$metadata['product_slug']}'.");
|
||||
}
|
||||
|
||||
$definitions[] = [
|
||||
'attribute_id' => $attributeIds[$sizeAttributeCode],
|
||||
'products_attribute_id' => $sizeProductAttributeId,
|
||||
'value' => $size,
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user