View source
<?php
namespace Drupal\Tests\commerce_product\Kernel;
use Drupal\commerce_product\Entity\Product;
use Drupal\commerce_product\Entity\ProductAttribute;
use Drupal\commerce_product\Entity\ProductAttributeValue;
use Drupal\commerce_product\Entity\ProductVariation;
use Drupal\commerce_product\Entity\ProductVariationType;
use Drupal\commerce_product\Entity\ProductVariationTypeInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\Tests\commerce\Kernel\CommerceKernelTestBase;
class ProductVariationAttributeMapperTest extends CommerceKernelTestBase {
public static $modules = [
'path',
'commerce_product',
];
protected $attributeFieldManager;
protected $mapper;
protected $colorAttributes;
protected $sizeAttributes;
protected $ramAttributes;
protected $disk1Attributes;
protected $disk2Attributes;
protected function setUp() : void {
parent::setUp();
$this
->installEntitySchema('commerce_product_variation');
$this
->installEntitySchema('commerce_product');
$this
->installEntitySchema('commerce_product_attribute');
$this
->installEntitySchema('commerce_product_attribute_value');
$this
->installConfig([
'commerce_product',
]);
$this->attributeFieldManager = $this->container
->get('commerce_product.attribute_field_manager');
$this->mapper = $this->container
->get('commerce_product.variation_attribute_mapper');
$variation_type = ProductVariationType::load('default');
$this->colorAttributes = $this
->createAttributeSet($variation_type, 'color', [
'black' => 'Black',
'blue' => 'Blue',
'green' => 'Green',
'red' => 'Red',
'white' => 'White',
'yellow' => 'Yellow',
]);
$this->sizeAttributes = $this
->createAttributeSet($variation_type, 'size', [
'small' => 'Small',
'medium' => 'Medium',
'large' => 'Large',
]);
$this->ramAttributes = $this
->createAttributeSet($variation_type, 'ram', [
'4gb' => '4GB',
'8gb' => '8GB',
'16gb' => '16GB',
'32gb' => '32GB',
]);
$this->disk1Attributes = $this
->createAttributeSet($variation_type, 'disk1', [
'1tb' => '1TB',
'2tb' => '2TB',
'3tb' => '3TB',
]);
$this->disk2Attributes = $this
->createAttributeSet($variation_type, 'disk2', [
'1tb' => '1TB',
'2tb' => '2TB',
'3tb' => '3TB',
], FALSE);
$user = $this
->createUser([], [
'administer commerce_product',
]);
$this->container
->get('current_user')
->setAccount($user);
}
public function testSelect() {
$product = $this
->generateThreeByTwoScenario();
$variations = $product
->getVariations();
$selected_variation = $this->mapper
->selectVariation($product
->getVariations());
$this
->assertNull($selected_variation);
$selected_variation = $this->mapper
->selectVariation($product
->getVariations(), [
'attribute_color' => '',
'attribute_size' => '',
]);
$this
->assertNull($selected_variation);
$selected_variation = $this->mapper
->selectVariation($variations, [
'attribute_color' => '',
'attribute_size' => $this->sizeAttributes['large']
->id(),
]);
$this
->assertNull($selected_variation);
$selected_variation = $this->mapper
->selectVariation($variations, [
'attribute_color' => $this->colorAttributes['blue']
->id(),
]);
$this
->assertEquals($variations[3]
->id(), $selected_variation
->id());
$selected_variation = $this->mapper
->selectVariation($variations, [
'attribute_size' => $this->sizeAttributes['large']
->id(),
]);
$this
->assertEquals($variations[2]
->id(), $selected_variation
->id());
$selected_variation = $this->mapper
->selectVariation($variations, [
'attribute_color' => $this->colorAttributes['red']
->id(),
'attribute_size' => $this->sizeAttributes['large']
->id(),
]);
$this
->assertEquals($variations[2]
->id(), $selected_variation
->id());
$selected_variation = $this->mapper
->selectVariation($variations, [
'attribute_color' => $this->colorAttributes['blue']
->id(),
'attribute_size' => $this->sizeAttributes['large']
->id(),
]);
$this
->assertEquals($variations[3]
->id(), $selected_variation
->id());
$this
->assertEquals('Blue', $selected_variation
->getAttributeValue('attribute_color')
->label());
$this
->assertEquals('Small', $selected_variation
->getAttributeValue('attribute_size')
->label());
}
public function testSelectWithOptionalAttributes() {
$product = $this
->generateThreeByTwoOptionalScenario();
$variations = $product
->getVariations();
$selected_variation = $this->mapper
->selectVariation($variations, [
'attribute_ram' => $this->ramAttributes['16gb']
->id(),
]);
$this
->assertEquals($variations[1]
->id(), $selected_variation
->id());
$selected_variation = $this->mapper
->selectVariation($variations, [
'attribute_ram' => $this->ramAttributes['16gb']
->id(),
'attribute_disk1' => $this->disk1Attributes['1tb']
->id(),
'attribute_disk2' => $this->disk2Attributes['1tb']
->id(),
]);
$this
->assertEquals($variations[2]
->id(), $selected_variation
->id());
$selected_variation = $this->mapper
->selectVariation($variations, [
'attribute_ram' => $this->ramAttributes['16gb']
->id(),
'attribute_disk1' => $this->disk1Attributes['1tb']
->id(),
'attribute_disk2' => $this->disk2Attributes['2tb']
->id(),
]);
$this
->assertEquals($variations[1]
->id(), $selected_variation
->id());
}
public function testPrepareAttributes() {
$product = $this
->generateThreeByTwoScenario();
$variations = $product
->getVariations();
$attributes = $this->mapper
->prepareAttributes(reset($variations), $variations);
$color_attribute = $attributes['attribute_color'];
$this
->assertEquals('color', $color_attribute
->getId());
$this
->assertEquals('Color', $color_attribute
->getLabel());
$this
->assertEquals('select', $color_attribute
->getElementType());
$this
->assertTrue($color_attribute
->isRequired());
$this
->assertEquals([
'2' => 'Blue',
'4' => 'Red',
], $color_attribute
->getValues());
$size_attribute = $attributes['attribute_size'];
$this
->assertEquals('size', $size_attribute
->getId());
$this
->assertEquals('Size', $size_attribute
->getLabel());
$this
->assertEquals('select', $size_attribute
->getElementType());
$this
->assertTrue($size_attribute
->isRequired());
$this
->assertEquals([
'7' => 'Small',
'8' => 'Medium',
'9' => 'Large',
], $size_attribute
->getValues());
$attributes = $this->mapper
->prepareAttributes($variations[4], $variations);
$color_attribute = $attributes['attribute_color'];
$this
->assertEquals('color', $color_attribute
->getId());
$this
->assertEquals('Color', $color_attribute
->getLabel());
$this
->assertEquals('select', $color_attribute
->getElementType());
$this
->assertTrue($color_attribute
->isRequired());
$this
->assertEquals([
'2' => 'Blue',
'4' => 'Red',
], $color_attribute
->getValues());
$size_attribute = $attributes['attribute_size'];
$this
->assertEquals('size', $size_attribute
->getId());
$this
->assertEquals('Size', $size_attribute
->getLabel());
$this
->assertEquals('select', $size_attribute
->getElementType());
$this
->assertTrue($size_attribute
->isRequired());
$this
->assertEquals([
'7' => 'Small',
'8' => 'Medium',
], $size_attribute
->getValues());
}
public function testPrepareAttributesOptional() {
$product = $this
->generateThreeByTwoOptionalScenario();
$variations = $product
->getVariations();
$attributes = $this->mapper
->prepareAttributes(reset($variations), $variations);
$ram_attribute = $attributes['attribute_ram'];
$this
->assertEquals('ram', $ram_attribute
->getId());
$this
->assertEquals('Ram', $ram_attribute
->getLabel());
$this
->assertEquals('select', $ram_attribute
->getElementType());
$this
->assertTrue($ram_attribute
->isRequired());
$this
->assertEquals([
'11' => '8GB',
'12' => '16GB',
], $ram_attribute
->getValues());
$disk1_attribute = $attributes['attribute_disk1'];
$this
->assertEquals('disk1', $disk1_attribute
->getId());
$this
->assertEquals('Disk1', $disk1_attribute
->getLabel());
$this
->assertEquals('select', $disk1_attribute
->getElementType());
$this
->assertTrue($disk1_attribute
->isRequired());
$this
->assertEquals([
'14' => '1TB',
], $disk1_attribute
->getValues());
$disk2_attribute = $attributes['attribute_disk2'];
$this
->assertEquals('disk2', $disk2_attribute
->getId());
$this
->assertEquals('Disk2', $disk2_attribute
->getLabel());
$this
->assertEquals('select', $disk2_attribute
->getElementType());
$this
->assertFalse($disk2_attribute
->isRequired());
$this
->assertEquals([
'_none' => '',
], $disk2_attribute
->getValues());
$attributes = $this->mapper
->prepareAttributes($variations[1], $variations);
$ram_attribute = $attributes['attribute_ram'];
$this
->assertEquals('ram', $ram_attribute
->getId());
$this
->assertEquals('Ram', $ram_attribute
->getLabel());
$this
->assertEquals('select', $ram_attribute
->getElementType());
$this
->assertTrue($ram_attribute
->isRequired());
$this
->assertEquals([
'11' => '8GB',
'12' => '16GB',
], $ram_attribute
->getValues());
$disk1_attribute = $attributes['attribute_disk1'];
$this
->assertEquals('disk1', $disk1_attribute
->getId());
$this
->assertEquals('Disk1', $disk1_attribute
->getLabel());
$this
->assertEquals('select', $disk1_attribute
->getElementType());
$this
->assertTrue($disk1_attribute
->isRequired());
$this
->assertEquals([
'14' => '1TB',
], $disk1_attribute
->getValues());
$disk2_attribute = $attributes['attribute_disk2'];
$this
->assertEquals('disk2', $disk2_attribute
->getId());
$this
->assertEquals('Disk2', $disk2_attribute
->getLabel());
$this
->assertEquals('select', $disk2_attribute
->getElementType());
$this
->assertFalse($disk2_attribute
->isRequired());
$this
->assertEquals([
'_none' => '',
'17' => '1TB',
], $disk2_attribute
->getValues());
}
public function testMutuallyExclusiveAttributeMatrixTwoByTwoByTwo() {
$product = Product::create([
'type' => 'default',
'title' => $this
->randomMachineName(),
'stores' => [
$this->store,
],
'variations' => [],
]);
$attribute_values_matrix = [
[
'4gb',
'2tb',
'2tb',
],
[
'8gb',
'1tb',
'2tb',
],
[
'8gb',
'2tb',
'1tb',
],
];
$variations = [];
foreach ($attribute_values_matrix as $key => $value) {
$variation = ProductVariation::create([
'type' => 'default',
'sku' => $this
->randomMachineName(),
'price' => [
'number' => 999,
'currency_code' => 'USD',
],
'attribute_ram' => $this->ramAttributes[$value[0]],
'attribute_disk1' => $this->disk1Attributes[$value[1]],
'attribute_disk2' => isset($this->disk2Attributes[$value[2]]) ? $this->disk2Attributes[$value[2]] : NULL,
]);
$variation
->save();
$variations[] = $variation;
$product
->addVariation($variation);
}
$product
->save();
$attributes = $this->mapper
->prepareAttributes(reset($variations), $variations);
$ram_attribute = $attributes['attribute_ram'];
$this
->assertEquals('ram', $ram_attribute
->getId());
$this
->assertEquals('Ram', $ram_attribute
->getLabel());
$this
->assertEquals('select', $ram_attribute
->getElementType());
$this
->assertTrue($ram_attribute
->isRequired());
$this
->assertEquals([
'11' => '8GB',
'10' => '4GB',
], $ram_attribute
->getValues());
$disk1_attribute = $attributes['attribute_disk1'];
$this
->assertEquals('disk1', $disk1_attribute
->getId());
$this
->assertEquals('Disk1', $disk1_attribute
->getLabel());
$this
->assertEquals('select', $disk1_attribute
->getElementType());
$this
->assertTrue($disk1_attribute
->isRequired());
$this
->assertNotCount(3, $disk1_attribute
->getValues(), 'Out of the three available attribute values, only the one used is returned.');
$this
->assertCount(1, $disk1_attribute
->getValues());
$this
->assertEquals([
'15' => '2TB',
], $disk1_attribute
->getValues());
$disk2_attribute = $attributes['attribute_disk2'];
$this
->assertEquals('disk2', $disk2_attribute
->getId());
$this
->assertEquals('Disk2', $disk2_attribute
->getLabel());
$this
->assertEquals('select', $disk2_attribute
->getElementType());
$this
->assertFalse($disk2_attribute
->isRequired());
$this
->assertEquals([
'18' => '2TB',
], $disk2_attribute
->getValues());
$attributes = $this->mapper
->prepareAttributes($variations[1], $variations);
$ram_attribute = $attributes['attribute_ram'];
$this
->assertEquals('ram', $ram_attribute
->getId());
$this
->assertEquals('Ram', $ram_attribute
->getLabel());
$this
->assertEquals('select', $ram_attribute
->getElementType());
$this
->assertTrue($ram_attribute
->isRequired());
$this
->assertNotCount(4, $ram_attribute
->getValues(), 'Out of the four available attribute values, only the two used are returned.');
$this
->assertCount(2, $ram_attribute
->getValues());
$this
->assertEquals([
'11' => '8GB',
'10' => '4GB',
], $ram_attribute
->getValues());
$disk1_attribute = $attributes['attribute_disk1'];
$this
->assertEquals('disk1', $disk1_attribute
->getId());
$this
->assertEquals('Disk1', $disk1_attribute
->getLabel());
$this
->assertEquals('select', $disk1_attribute
->getElementType());
$this
->assertTrue($disk1_attribute
->isRequired());
$this
->assertEquals([
'15' => '2TB',
'14' => '1TB',
], $disk1_attribute
->getValues());
$disk2_attribute = $attributes['attribute_disk2'];
$this
->assertEquals('disk2', $disk2_attribute
->getId());
$this
->assertEquals('Disk2', $disk2_attribute
->getLabel());
$this
->assertEquals('select', $disk2_attribute
->getElementType());
$this
->assertFalse($disk2_attribute
->isRequired());
$this
->assertEquals([
'18' => '2TB',
], $disk2_attribute
->getValues());
$attributes = $this->mapper
->prepareAttributes($variations[2], $variations);
$ram_attribute = $attributes['attribute_ram'];
$this
->assertEquals('ram', $ram_attribute
->getId());
$this
->assertEquals('Ram', $ram_attribute
->getLabel());
$this
->assertEquals('select', $ram_attribute
->getElementType());
$this
->assertTrue($ram_attribute
->isRequired());
$this
->assertEquals([
'11' => '8GB',
'10' => '4GB',
], $ram_attribute
->getValues());
$disk1_attribute = $attributes['attribute_disk1'];
$this
->assertEquals('disk1', $disk1_attribute
->getId());
$this
->assertEquals('Disk1', $disk1_attribute
->getLabel());
$this
->assertEquals('select', $disk1_attribute
->getElementType());
$this
->assertTrue($disk1_attribute
->isRequired());
$this
->assertEquals([
'15' => '2TB',
'14' => '1TB',
], $disk1_attribute
->getValues());
$disk2_attribute = $attributes['attribute_disk2'];
$this
->assertEquals('disk2', $disk2_attribute
->getId());
$this
->assertEquals('Disk2', $disk2_attribute
->getLabel());
$this
->assertEquals('select', $disk2_attribute
->getElementType());
$this
->assertFalse($disk2_attribute
->isRequired());
$this
->assertEquals([
'17' => '1TB',
], $disk2_attribute
->getValues());
}
public function testThreeAttributesSixVariations() {
$variation_type = ProductVariationType::load('default');
$pack = $this
->createAttributeSet($variation_type, 'pack', [
'one' => '1',
'twenty' => '20',
'hundred' => '100',
'twohundred' => '200',
]);
$product = Product::create([
'type' => 'default',
'title' => $this
->randomMachineName(),
'stores' => [
$this->store,
],
'variations' => [],
]);
$product
->save();
$form_display = commerce_get_entity_display('commerce_product_variation', $variation_type
->id(), 'form');
$form_display
->setComponent('attribute_size', [
'weight' => 0,
] + $form_display
->getComponent('attribute_size'));
$form_display
->setComponent('attribute_color', [
'weight' => 1,
] + $form_display
->getComponent('attribute_color'));
$form_display
->setComponent('attribute_pack', [
'weight' => 2,
] + $form_display
->getComponent('attribute_pack'));
$form_display
->save();
$attribute_values_matrix = [
[
'small',
'black',
'one',
],
[
'small',
'blue',
'twenty',
],
[
'medium',
'green',
'hundred',
],
[
'medium',
'red',
'twohundred',
],
[
'large',
'white',
'hundred',
],
[
'large',
'yellow',
'twenty',
],
];
$variations = [];
foreach ($attribute_values_matrix as $key => $value) {
$variation = ProductVariation::create([
'type' => 'default',
'sku' => $this
->randomMachineName(),
'price' => [
'number' => 999,
'currency_code' => 'USD',
],
'attribute_size' => $this->sizeAttributes[$value[0]],
'attribute_color' => $this->colorAttributes[$value[1]],
'attribute_pack' => $pack[$value[2]],
]);
$variation
->save();
$variations[] = $variation;
$product
->addVariation($variation);
}
$product
->save();
$selected_variation = $product
->getDefaultVariation();
$attributes = $this->mapper
->prepareAttributes($selected_variation, $product
->getVariations());
$size_attribute = $attributes['attribute_size'];
$this
->assertEquals([
'7' => 'Small',
'8' => 'Medium',
'9' => 'Large',
], $size_attribute
->getValues());
$color_attribute = $attributes['attribute_color'];
$this
->assertEquals([
'2' => 'Blue',
'1' => 'Black',
], $color_attribute
->getValues());
$pack_attribute = $attributes['attribute_pack'];
$this
->assertEquals([
'20' => '1',
], $pack_attribute
->getValues());
$selected_variation = $this->mapper
->selectVariation($variations, [
'attribute_size' => $this->sizeAttributes['small']
->id(),
'attribute_color' => $this->colorAttributes['blue']
->id(),
]);
$this
->assertEquals($variations[1]
->id(), $selected_variation
->id());
$selected_variation = $this->mapper
->selectVariation($variations, [
'attribute_size' => $this->sizeAttributes['medium']
->id(),
'attribute_color' => $this->colorAttributes['blue']
->id(),
]);
$this
->assertEquals($variations[2]
->id(), $selected_variation
->id());
$this
->assertEquals('Medium', $selected_variation
->getAttributeValue('attribute_size')
->label());
$this
->assertEquals('Green', $selected_variation
->getAttributeValue('attribute_color')
->label());
$this
->assertEquals('100', $selected_variation
->getAttributeValue('attribute_pack')
->label());
$attributes = $this->mapper
->prepareAttributes($selected_variation, $product
->getVariations());
$size_attribute = $attributes['attribute_size'];
$this
->assertEquals([
'7' => 'Small',
'8' => 'Medium',
'9' => 'Large',
], $size_attribute
->getValues());
$color_attribute = $attributes['attribute_color'];
$this
->assertEquals([
'3' => 'Green',
'4' => 'Red',
], $color_attribute
->getValues());
$pack_attribute = $attributes['attribute_pack'];
$this
->assertEquals([
'22' => '100',
], $pack_attribute
->getValues());
}
protected function generateThreeByTwoScenario() {
$product = Product::create([
'type' => 'default',
'title' => $this
->randomMachineName(),
'stores' => [
$this->store,
],
'variations' => [],
]);
$attribute_values_matrix = [
[
'red',
'small',
],
[
'red',
'medium',
],
[
'red',
'large',
],
[
'blue',
'small',
],
[
'blue',
'medium',
],
];
$variations = [];
foreach ($attribute_values_matrix as $key => $value) {
$variation = ProductVariation::create([
'type' => 'default',
'sku' => $this
->randomMachineName(),
'price' => [
'number' => 999,
'currency_code' => 'USD',
],
'attribute_color' => $this->colorAttributes[$value[0]],
'attribute_size' => $this->sizeAttributes[$value[1]],
]);
$variation
->save();
$variations[] = $variation;
$product
->addVariation($variation);
}
$product
->save();
return $product;
}
protected function generateThreeByTwoOptionalScenario() {
$product = Product::create([
'type' => 'default',
'title' => $this
->randomMachineName(),
'stores' => [
$this->store,
],
'variations' => [],
]);
$attribute_values_matrix = [
[
'8gb',
'1tb',
'',
],
[
'16gb',
'1tb',
'',
],
[
'16gb',
'1tb',
'1tb',
],
];
$variations = [];
foreach ($attribute_values_matrix as $key => $value) {
$variation = ProductVariation::create([
'type' => 'default',
'sku' => $this
->randomMachineName(),
'price' => [
'number' => 999,
'currency_code' => 'USD',
],
'attribute_ram' => $this->ramAttributes[$value[0]],
'attribute_disk1' => $this->disk1Attributes[$value[1]],
'attribute_disk2' => isset($this->disk2Attributes[$value[2]]) ? $this->disk2Attributes[$value[2]] : NULL,
]);
$variation
->save();
$variations[] = $variation;
$product
->addVariation($variation);
}
$product
->save();
return $product;
}
protected function createAttributeSet(ProductVariationTypeInterface $variation_type, $name, array $options, $required = TRUE) {
$attribute = ProductAttribute::create([
'id' => $name,
'label' => ucfirst($name),
]);
$attribute
->save();
$this->attributeFieldManager
->createField($attribute, $variation_type
->id());
if (!$required) {
$field = FieldConfig::loadByName('commerce_product_variation', $variation_type
->id(), 'attribute_' . $name);
$field
->setRequired(FALSE);
$field
->save();
}
$attribute_set = [];
foreach ($options as $key => $value) {
$attribute_set[$key] = $this
->createAttributeValue($name, $value);
}
return $attribute_set;
}
protected function createAttributeValue($attribute, $name) {
$attribute_value = ProductAttributeValue::create([
'attribute' => $attribute,
'name' => $name,
]);
$attribute_value
->save();
return $attribute_value;
}
}