diff --git a/CHANGELOG.md b/CHANGELOG.md index e4b85ab..e3021e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +1.0.6.0 +============= +* now it is possible to configure attribute data from a Pimcore. Additional informations kept in 'attr_conf' key in attribute will be merged and override default configuration of an attribute. `\Divante\PimcoreIntegration\Model\Catalog\Product\Attribute\Creator\Strategy\AbstractStrategy::getMergedConfig` + 1.0.5.5 ============= * add .gitignore diff --git a/Model/Catalog/Product/Attribute/Creator/Strategy/AbstractStrategy.php b/Model/Catalog/Product/Attribute/Creator/Strategy/AbstractStrategy.php index 5d130f3..af74b95 100644 --- a/Model/Catalog/Product/Attribute/Creator/Strategy/AbstractStrategy.php +++ b/Model/Catalog/Product/Attribute/Creator/Strategy/AbstractStrategy.php @@ -77,4 +77,19 @@ public function __construct( $this->attrData = $attrData; $this->code = $code; } + + /** + * @param array $base + * + * @return array + */ + public function getMergedConfig(array $base = []): array + { + return array_merge(self::$defaultAttrConfig, $base, $this->attrData['attr_conf'] ?? []); + } + + /** + * @return array + */ + abstract public function getBaseAttrConfig(): array; } diff --git a/Model/Catalog/Product/Attribute/Creator/Strategy/DatetimeStrategy.php b/Model/Catalog/Product/Attribute/Creator/Strategy/DatetimeStrategy.php index f4bd98c..827d3df 100644 --- a/Model/Catalog/Product/Attribute/Creator/Strategy/DatetimeStrategy.php +++ b/Model/Catalog/Product/Attribute/Creator/Strategy/DatetimeStrategy.php @@ -22,17 +22,26 @@ class DatetimeStrategy extends AbstractStrategy public function execute(): int { $eavSetup = $this->eavSetupFactory->create(); + $eavSetup->addAttribute( Product::ENTITY, $this->code, - array_merge(self::$defaultAttrConfig, [ - 'type' => 'datetime', - 'label' => $this->attrData['label'], - 'input' => 'date', - 'backend' => Datetime::class, - ]) + $this->getMergedConfig($this->getBaseAttrConfig()) ); return $eavSetup->getAttributeId(Product::ENTITY, $this->code); } + + /** + * @return array + */ + public function getBaseAttrConfig(): array + { + return [ + 'type' => 'datetime', + 'label' => $this->attrData['label'], + 'input' => 'date', + 'backend' => Datetime::class, + ]; + } } diff --git a/Model/Catalog/Product/Attribute/Creator/Strategy/MultiselectStrategy.php b/Model/Catalog/Product/Attribute/Creator/Strategy/MultiselectStrategy.php index fc17cda..7a7c109 100644 --- a/Model/Catalog/Product/Attribute/Creator/Strategy/MultiselectStrategy.php +++ b/Model/Catalog/Product/Attribute/Creator/Strategy/MultiselectStrategy.php @@ -84,18 +84,24 @@ protected function createNewAttribute(array $options) { $eavSetup = $this->eavSetupFactory->create(); - $data = [ + $eavSetup->addAttribute( + Product::ENTITY, + $this->code, + $this->getMergedConfig($this->getBaseAttrConfig()) + ); + } + + /** + * @return array + */ + public function getBaseAttrConfig(): array + { + return [ 'type' => 'varchar', 'label' => $this->attrData['label'], 'input' => 'multiselect', 'user_defined' => true, 'backend' => ArrayBackend::class, ]; - - $eavSetup->addAttribute( - Product::ENTITY, - $this->code, - array_merge(self::$defaultAttrConfig, $data) - ); } } diff --git a/Model/Catalog/Product/Attribute/Creator/Strategy/SelectStrategy.php b/Model/Catalog/Product/Attribute/Creator/Strategy/SelectStrategy.php index 9251724..3fef171 100644 --- a/Model/Catalog/Product/Attribute/Creator/Strategy/SelectStrategy.php +++ b/Model/Catalog/Product/Attribute/Creator/Strategy/SelectStrategy.php @@ -80,6 +80,26 @@ protected function createNewAttribute(array $options) { $eavSetup = $this->eavSetupFactory->create(); + $eavSetup->addAttribute( + Product::ENTITY, + $this->code, + $this->getMergedConfig($this->getBaseAttrConfig()) + ); + } + + /** + * @return bool + */ + private function isConfigurable(): bool + { + return (!empty($this->attrData['is_configurable']) && true === $this->attrData['is_configurable']); + } + + /** + * @return array + */ + public function getBaseAttrConfig(): array + { $data = [ 'type' => 'int', 'label' => $this->attrData['label'], @@ -93,18 +113,6 @@ protected function createNewAttribute(array $options) ]); } - $eavSetup->addAttribute( - Product::ENTITY, - $this->code, - array_merge(self::$defaultAttrConfig, $data) - ); - } - - /** - * @return bool - */ - private function isConfigurable(): bool - { - return (!empty($this->attrData['is_configurable']) && true === $this->attrData['is_configurable']); + return $data; } } diff --git a/Model/Catalog/Product/Attribute/Creator/Strategy/TextStrategy.php b/Model/Catalog/Product/Attribute/Creator/Strategy/TextStrategy.php index e131d75..89cd7ce 100644 --- a/Model/Catalog/Product/Attribute/Creator/Strategy/TextStrategy.php +++ b/Model/Catalog/Product/Attribute/Creator/Strategy/TextStrategy.php @@ -26,13 +26,21 @@ public function execute(): int $eavSetup->addAttribute( Product::ENTITY, $this->code, - array_merge(self::$defaultAttrConfig, [ - 'type' => 'varchar', - 'label' => $this->attrData['label'], - 'input' => 'text', - ]) + $this->getMergedConfig($this->getBaseAttrConfig()) ); return $eavSetup->getAttributeId(Product::ENTITY, $this->code); } + + /** + * @return array + */ + public function getBaseAttrConfig(): array + { + return [ + 'type' => 'varchar', + 'label' => $this->attrData['label'], + 'input' => 'text', + ]; + } } diff --git a/Model/Catalog/Product/Attribute/Creator/Strategy/TextareaStrategy.php b/Model/Catalog/Product/Attribute/Creator/Strategy/TextareaStrategy.php index a4ff65a..63a7f93 100644 --- a/Model/Catalog/Product/Attribute/Creator/Strategy/TextareaStrategy.php +++ b/Model/Catalog/Product/Attribute/Creator/Strategy/TextareaStrategy.php @@ -24,15 +24,23 @@ public function execute(): int $eavSetup->addAttribute( Product::ENTITY, $this->code, - array_merge(self::$defaultAttrConfig, [ - 'type' => 'text', - 'label' => $this->attrData['label'], - 'input' => 'textarea', - 'wysiwyg_enabled' => false, - 'is_html_allowed_on_front' => false, - ]) + $this->getMergedConfig($this->getBaseAttrConfig()) ); return $eavSetup->getAttributeId(Product::ENTITY, $this->code); } + + /** + * @return array + */ + public function getBaseAttrConfig(): array + { + return [ + 'type' => 'text', + 'label' => $this->attrData['label'], + 'input' => 'textarea', + 'wysiwyg_enabled' => false, + 'is_html_allowed_on_front' => false, + ]; + } } diff --git a/Model/Catalog/Product/Attribute/Creator/Strategy/WysiwygStrategy.php b/Model/Catalog/Product/Attribute/Creator/Strategy/WysiwygStrategy.php index a42e00b..b435e28 100644 --- a/Model/Catalog/Product/Attribute/Creator/Strategy/WysiwygStrategy.php +++ b/Model/Catalog/Product/Attribute/Creator/Strategy/WysiwygStrategy.php @@ -24,15 +24,23 @@ public function execute(): int $eavSetup->addAttribute( Product::ENTITY, $this->code, - array_merge(self::$defaultAttrConfig, [ - 'type' => 'text', - 'label' => $this->attrData['label'], - 'input' => 'textarea', - 'wysiwyg_enabled' => true, - 'is_html_allowed_on_front' => true, - ]) + $this->getMergedConfig($this->getBaseAttrConfig()) ); return $eavSetup->getAttributeId(Product::ENTITY, $this->code); } + + /** + * @return array + */ + public function getBaseAttrConfig(): array + { + return [ + 'type' => 'text', + 'label' => $this->attrData['label'], + 'input' => 'textarea', + 'wysiwyg_enabled' => true, + 'is_html_allowed_on_front' => true, + ]; + } } diff --git a/Model/Catalog/Product/Attribute/Creator/Strategy/YesnoStrategy.php b/Model/Catalog/Product/Attribute/Creator/Strategy/YesnoStrategy.php index 4866ca8..0c71894 100644 --- a/Model/Catalog/Product/Attribute/Creator/Strategy/YesnoStrategy.php +++ b/Model/Catalog/Product/Attribute/Creator/Strategy/YesnoStrategy.php @@ -24,14 +24,22 @@ public function execute(): int $eavSetup->addAttribute( Product::ENTITY, $this->code, - array_merge(self::$defaultAttrConfig, [ - 'type' => 'int', - 'label' => $this->attrData['label'], - 'input' => 'boolean', - 'source' => \Magento\Eav\Model\Entity\Attribute\Source\Boolean::class, - ]) + $this->getMergedConfig($this->getBaseAttrConfig()) ); return $eavSetup->getAttributeId(Product::ENTITY, $this->code); } + + /** + * @return array + */ + public function getBaseAttrConfig(): array + { + return [ + 'type' => 'int', + 'label' => $this->attrData['label'], + 'input' => 'boolean', + 'source' => \Magento\Eav\Model\Entity\Attribute\Source\Boolean::class, + ]; + } } diff --git a/Test/Unit/Model/Catalog/Product/Attribute/Creator/Strategy/AbstractStrategyTest.php b/Test/Unit/Model/Catalog/Product/Attribute/Creator/Strategy/AbstractStrategyTest.php new file mode 100644 index 0000000..3da09a7 --- /dev/null +++ b/Test/Unit/Model/Catalog/Product/Attribute/Creator/Strategy/AbstractStrategyTest.php @@ -0,0 +1,139 @@ + + * @copyright 2020 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\PimcoreIntegration\Test\Unit\Model\Catalog\Product\Attribute\Creator\Strategy; + +use Divante\PimcoreIntegration\Model\Catalog\Product\Attribute\Creator\Strategy\AbstractStrategy; +use Magento\Catalog\Model\Category\AttributeRepository; +use Magento\Eav\Api\AttributeRepositoryInterface; +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; +use Magento\Eav\Setup\EavSetup; +use Magento\Eav\Setup\EavSetupFactory; +use PHPUnit\Framework\MockObject\MockObject; + +/** + * Class TextStrategyTest + */ +class AbstractStrategyTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var MockObject|EavSetupFactory\ + */ + private $eavSetupFactoryMock; + + /** + * @var MockObject|EavSetup + */ + private $eavSetupMock; + + /** + * @var MockObject|AttributeRepository + */ + private $attributeRepositoryMock; + + /** + * @var array + */ + private $defaultAttrConfig = [ + 'backend' => '', + 'frontend' => '', + 'input' => 'text', + 'class' => '', + 'source' => '', + 'global' => ScopedAttributeInterface::SCOPE_STORE, + 'visible' => true, + 'required' => false, + 'user_defined' => true, + 'searchable' => true, + 'filterable' => true, + 'comparable' => true, + 'visible_on_front' => true, + 'used_in_product_listing' => true, + 'unique' => false, + ]; + + public function setUp() + { + $this->eavSetupFactoryMock = $this->getMockBuilder(EavSetupFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->eavSetupMock = $this->getMockBuilder(EavSetup::class) + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + + $this->eavSetupMock = $this->getMockBuilder(EavSetup::class) + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + + $this->attributeRepositoryMock = $this->getMockBuilder(AttributeRepositoryInterface::class) + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + } + + /** + * @return array[] + */ + public function attrDataProvider() + { + return [ + [['attr_conf' => ['test' => 1]], []], + [['attr_conf' => ['test' => 1]], ['input' => 'select']], + [['attr_conf' => ['test' => 1]], ['input' => 'select']], + [['attr_conf' => ['test' => 1]], ['input' => 'select']], + [['something_else' => [], 'attr_conf' => ['test' => 1]], ['input' => 'select']], + [['missed_key' => ['test' => 1, 'required' => true]], []], + ]; + } + + public function testGetDefaultAttrConfig() + { + $strategy = $this->getAbstractStrategyMockImplementation('test'); + $this->assertEquals($this->defaultAttrConfig, $strategy->getDefaultAttrConfig()); + } + + /** + * @dataProvider attrDataProvider + */ + public function testGetMergedConfig(array $attrData, $base) + { + $strategy = $this->getAbstractStrategyMockImplementation('test', $attrData); + $result = array_merge($this->defaultAttrConfig, $base, $attrData['attr_conf'] ?? []); + $this->assertEquals($result, $strategy->getMergedConfig($base)); + } + + /** + * @param string $code + * @param array $attrData + * + * @return AbstractStrategy + */ + private function getAbstractStrategyMockImplementation(string $code, array $attrData = []) + { + return new class( + $this->eavSetupFactoryMock, + $this->attributeRepositoryMock, + $attrData, + $code + ) extends AbstractStrategy { + public function getBaseAttrConfig(): array + { + return []; + } + + public function execute(): int + { + return 1; + } + }; + } +} diff --git a/Test/Unit/Model/Catalog/Product/Attribute/Creator/Strategy/TextStrategyTest.php b/Test/Unit/Model/Catalog/Product/Attribute/Creator/Strategy/TextStrategyTest.php new file mode 100644 index 0000000..b56ba06 --- /dev/null +++ b/Test/Unit/Model/Catalog/Product/Attribute/Creator/Strategy/TextStrategyTest.php @@ -0,0 +1,135 @@ + + * @copyright 2020 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\PimcoreIntegration\Test\Unit\Model\Catalog\Product\Attribute\Creator\Strategy; + +use Divante\PimcoreIntegration\Model\Catalog\Product\Attribute\Creator\Strategy\TextStrategy; +use Magento\Catalog\Model\Category\AttributeRepository; +use Magento\Catalog\Model\Product; +use Magento\Eav\Api\AttributeRepositoryInterface; +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; +use Magento\Eav\Setup\EavSetup; +use Magento\Eav\Setup\EavSetupFactory; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; + +/** + * Class TextStrategyTest + */ +class TextStrategyTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManager + */ + private $om; + + /** + * @var MockObject|EavSetupFactory\ + */ + private $eavSetupFactoryMock; + + /** + * @var MockObject|EavSetup + */ + private $eavSetupMock; + + /** + * @var MockObject|AttributeRepository + */ + private $attributeRepositoryMock; + + /** + * @var string[] + */ + private $baseAttrConf = [ + 'type' => 'varchar', + 'label' => 'test_label', + 'input' => 'text', + ]; + + public function setUp() + { + $this->om = new ObjectManager($this); + + $this->eavSetupFactoryMock = $this->getMockBuilder(EavSetupFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->eavSetupMock = $this->getMockBuilder(EavSetup::class) + ->disableOriginalConstructor() + ->setMethods(['addAttribute', 'getAttributeId']) + ->getMock(); + + $this->eavSetupMock = $this->getMockBuilder(EavSetup::class) + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + + $this->attributeRepositoryMock = $this->getMockBuilder(AttributeRepositoryInterface::class) + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + } + + /** + * @return array[] + */ + public function attrDataProvider() + { + return [ + [['label' => 'test_label'], 'test'], + ]; + } + + /** + * @dataProvider attrDataProvider + */ + public function testExecution(array $attrData, $code) + { + $strategy = $this->createStrategyObject($code, $attrData); + + $this->eavSetupFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->eavSetupMock); + + $this->eavSetupMock->expects($this->once()) + ->method('addAttribute'); + + $this->eavSetupMock->expects($this->once()) + ->method('getAttributeId') + ->willReturn(1); + + $strategy->execute(); + } + + /** + * @dataProvider attrDataProvider + */ + public function testGetBaseAttrConf(array $attrData, $code) + { + $strategy = $this->createStrategyObject($code, $attrData); + $this->assertEquals($this->baseAttrConf, $strategy->getBaseAttrConfig()); + } + + /** + * @param string $code + * @param array $attrData + * + * @return TextStrategy|object + */ + private function createStrategyObject(string $code, array $attrData = []) + { + return $this->om->getObject(TextStrategy::class, [ + 'eavSetupFactory' => $this->eavSetupFactoryMock, + 'attributeRepository' => $this->attributeRepositoryMock, + 'attrData' => $attrData, + 'code' => $code + ]); + } +}