Skip to content
This repository has been archived by the owner on Aug 3, 2018. It is now read-only.

Commit

Permalink
Merge pull request #22 from Sylius/white-list-attributes
Browse files Browse the repository at this point in the history
[ElasticSearch] Add attribute white list feature
  • Loading branch information
lchrusciel authored Jun 26, 2017
2 parents 16516f3 + f2ad53a commit 5a0797e
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 65 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ Elastic search for Sylius.
document_field: name,description,attribute_values.value
options:
operator: and
sylius_elastic_search:
attribute_whitelist: ['MUG_COLLECTION_CODE', 'MUG_MATERIAL_CODE'] #Only attibutes with these codes will be indexed
```
8. Import routing file:
Expand Down
51 changes: 7 additions & 44 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public function getConfigTreeBuilder()
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('sylius_elastic_search');

$this->buildFilterSetNode($rootNode);
$this->buildAttributeWhitelistNode($rootNode);
$this->buildDocumentClassesNode($rootNode);
$this->buildViewClassesNode($rootNode);

Expand Down Expand Up @@ -98,52 +98,15 @@ private function buildViewClassesNode(ArrayNodeDefinition $rootNode)
/**
* @param ArrayNodeDefinition $rootNode
*/
private function buildFilterSetNode(ArrayNodeDefinition $rootNode)
private function buildAttributeWhitelistNode(ArrayNodeDefinition $rootNode)
{
$filterSetNode = $rootNode
->children()
->arrayNode('filter_sets')
->useAttributeAsKey('name')
->prototype('array')
->validate()
->ifEmpty()
->thenInvalid('"%s" cannot be empty')
->end()
;

$this->buildFiltersNode($filterSetNode);
}

/**
* @param ArrayNodeDefinition $filterSetNode
*/
private function buildFiltersNode(ArrayNodeDefinition $filterSetNode)
{
$filtersNode = $filterSetNode
$rootNode
->addDefaultsIfNotSet()
->children()
->arrayNode('filters')
->useAttributeAsKey('name')
->prototype('array')
->validate()
->ifEmpty()
->thenInvalid('"%s"" cannot be empty')
->arrayNode('attribute_whitelist')
->prototype('scalar')->end()
->end()
->end()
;

$this->buildFilterNode($filtersNode);
}

/**
* @param ArrayNodeDefinition $filtersNode
*/
private function buildFilterNode(ArrayNodeDefinition $filtersNode)
{
$filtersNode
->children()
->scalarNode('type')->cannotBeEmpty()->end()
->arrayNode('options')
->useAttributeAsKey('name')
->prototype('scalar')
;
}
}
2 changes: 1 addition & 1 deletion src/DependencyInjection/SyliusElasticSearchExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public function load(array $configs, ContainerBuilder $container)
$config = $this->processConfiguration($this->getConfiguration([], $container), $configs);
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));

$container->setParameter('sylius_elastic_search.filter_sets', $config['filter_sets']);
$container->setParameter('sylius_elastic_search.attribute_whitelist', $config['attribute_whitelist']);

foreach ($config['document_classes'] as $document => $class) {
$container->setParameter(sprintf('sylius_elastic_search.document.%s.class', $document), $class);
Expand Down
34 changes: 21 additions & 13 deletions src/Factory/ProductDocumentFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,26 @@ final class ProductDocumentFactory implements ProductDocumentFactoryInterface
/** @var string */
private $taxonDocumentClass;

/** @var array */
private $attributeWhitelist = [];

/**
* @param string $productDocumentClass
* @param string $attributeDocumentClass
* @param string $attributeValueDocumentClass
* @param string $imageDocumentClass
* @param string $priceDocumentClass
* @param string $taxonDocumentClass
* @param array $attributeWhitelist
*/
public function __construct(
$productDocumentClass,
$attributeDocumentClass,
$attributeValueDocumentClass,
$imageDocumentClass,
$priceDocumentClass,
$taxonDocumentClass
$taxonDocumentClass,
array $attributeWhitelist
) {
$this->assertClassExtends($productDocumentClass, ProductDocument::class);
$this->productDocumentClass = $productDocumentClass;
Expand All @@ -71,6 +76,8 @@ public function __construct(

$this->assertClassExtends($taxonDocumentClass, TaxonDocument::class);
$this->taxonDocumentClass = $taxonDocumentClass;

$this->attributeWhitelist = $attributeWhitelist;
}

/**
Expand Down Expand Up @@ -163,20 +170,21 @@ public function createFromSyliusSimpleProductModel(ProductInterface $syliusProdu
$productTaxons[] = $productTaxon;
}
$product->setTaxons(new Collection($productTaxons));

$productAttributeValues = [];
foreach ($syliusProductAttributes as $syliusProductAttributeValue) {
/** @var AttributeValueDocument $productAttributeValue */
$productAttributeValue = new $this->attributeValueDocumentClass();
$productAttributeValue->setValue($syliusProductAttributeValue->getValue());

/** @var AttributeDocument $attribute */
$attribute = new $this->attributeDocumentClass();
$attribute->setCode($syliusProductAttributeValue->getAttribute()->getCode());
$attribute->setName($syliusProductAttributeValue->getAttribute()->getName());
$productAttributeValue->setAttribute($attribute);

$productAttributeValues[] = $productAttributeValue;
if (in_array($syliusProductAttributeValue->getAttribute()->getCode(), $this->attributeWhitelist)) {
/** @var AttributeValueDocument $productAttributeValue */
$productAttributeValue = new $this->attributeValueDocumentClass();
$productAttributeValue->setValue($syliusProductAttributeValue->getValue());

/** @var AttributeDocument $attribute */
$attribute = new $this->attributeDocumentClass();
$attribute->setCode($syliusProductAttributeValue->getAttribute()->getCode());
$attribute->setName($syliusProductAttributeValue->getAttribute()->getName());
$productAttributeValue->setAttribute($attribute);

$productAttributeValues[] = $productAttributeValue;
}
}
$productAttributeValues = new Collection($productAttributeValues);
$product->setAttributeValues($productAttributeValues);
Expand Down
1 change: 1 addition & 0 deletions src/Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<argument>%sylius_elastic_search.document.image.class%</argument>
<argument>%sylius_elastic_search.document.price.class%</argument>
<argument>%sylius_elastic_search.document.taxon.class%</argument>
<argument>%sylius_elastic_search.attribute_whitelist%</argument>
</service>
<service id="sylius_elastic_search.projector.product" class="Sylius\ElasticSearchPlugin\Projection\ProductProjector">
<argument type="service" id="es.manager.default" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<call method="addFilterSetProvider">
<argument type="service">
<service class="Sylius\ElasticSearchPlugin\Form\Configuration\Provider\FromArrayFilterSetProvider">
<argument>%sylius_elastic_search.filter_sets%</argument>
<argument>[]</argument>
</service>
</argument>
<argument>1</argument>
Expand Down
3 changes: 3 additions & 0 deletions tests/Application/app/config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,6 @@ ongr_elasticsearch:
index_name: sylius
mappings:
SyliusElasticSearchPlugin: {}

sylius_elastic_search:
attribute_whitelist: ['MUG_COLLECTION_CODE', 'MUG_MATERIAL_CODE']
12 changes: 12 additions & 0 deletions tests/DependencyInjection/ConfigurationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,18 @@ public function it_has_view_classes()
]], 'view_classes');
}

/**
* @test
*/
public function it_has_attribute_white_list()
{
$this->assertProcessedConfigurationEquals(
['sylius_elastic_search' => ['attribute_whitelist' => ['color']]],
['attribute_whitelist' => ['color']],
'attribute_whitelist'
);
}

/**
* {@inheritdoc}
*/
Expand Down
10 changes: 10 additions & 0 deletions tests/DependencyInjection/SyliusElasticSearchExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,14 @@ public function it_defines_view_classes_parameters()
$this->assertContainerBuilderHasParameter('sylius_elastic_search.view.price.class', PriceView::class);
$this->assertContainerBuilderHasParameter('sylius_elastic_search.view.taxon.class', TaxonView::class);
}

/**
* @test
*/
public function it_defines_attribute_whitelist_parameter()
{
$this->load([]);

$this->assertContainerBuilderHasParameter('sylius_elastic_search.attribute_whitelist', []);
}
}
150 changes: 144 additions & 6 deletions tests/Factory/ProductDocumentFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ public function it_creates_product_document_from_sylius_product_model()
$syliusProductAttributeValue = new ProductAttributeValue();
$syliusProductAttribute = new ProductAttribute();
$syliusProductAttribute->setCurrentLocale('en_US');
$syliusProductAttribute->setCode('red');
$syliusProductAttribute->setName('Color red');
$syliusProductAttribute->setCode('color');
$syliusProductAttribute->setName('Color');
$syliusProductAttributeValue->setLocaleCode('en_US');
$syliusProductAttribute->setType(TextAttributeType::TYPE);
$syliusProductAttribute->setStorageType(TextAttributeType::TYPE);
Expand Down Expand Up @@ -89,7 +89,8 @@ public function it_creates_product_document_from_sylius_product_model()
AttributeValueDocument::class,
ImageDocument::class,
PriceDocument::class,
TaxonDocument::class
TaxonDocument::class,
['color']
);
/** @var ProductDocument $product */
$product = $factory->createFromSyliusSimpleProductModel(
Expand All @@ -111,8 +112,8 @@ public function it_creates_product_document_from_sylius_product_model()
$productTaxon->setDescription('Lorem ipsum');

$productAttribute = new AttributeDocument();
$productAttribute->setCode('red');
$productAttribute->setName('Color red');
$productAttribute->setCode('color');
$productAttribute->setName('Color');

$productAttributeValue = new AttributeValueDocument();
$productAttributeValue->setValue('red');
Expand Down Expand Up @@ -151,7 +152,8 @@ public function it_cannot_create_product_document_from_configurable_product()
AttributeValueDocument::class,
ImageDocument::class,
PriceDocument::class,
TaxonDocument::class
TaxonDocument::class,
[]
);

$syliusProduct = new SyliusProduct();
Expand All @@ -162,4 +164,140 @@ public function it_cannot_create_product_document_from_configurable_product()

$factory->createFromSyliusSimpleProductModel($syliusProduct, $syliusLocale, $syliusChannel);
}

/**
* @test
*/
public function it_creates_product_document_only_with_whitelisted_attributes()
{
$createdAt = \DateTime::createFromFormat(\DateTime::W3C, '2017-04-18T16:12:55+02:00');
$firstSyliusProductAttributeValue = new ProductAttributeValue();
$firstSyliusProductAttribute = new ProductAttribute();
$firstSyliusProductAttribute->setCurrentLocale('en_US');
$firstSyliusProductAttribute->setCode('material');
$firstSyliusProductAttribute->setName('Material');
$firstSyliusProductAttributeValue->setLocaleCode('en_US');
$firstSyliusProductAttribute->setType(TextAttributeType::TYPE);
$firstSyliusProductAttribute->setStorageType(TextAttributeType::TYPE);
$firstSyliusProductAttributeValue->setAttribute($firstSyliusProductAttribute);
$firstSyliusProductAttributeValue->setValue('wood');

$secondSyliusProductAttributeValue = new ProductAttributeValue();
$secondSyliusProductAttribute = new ProductAttribute();
$secondSyliusProductAttribute->setCurrentLocale('en_US');
$secondSyliusProductAttribute->setCode('size');
$secondSyliusProductAttribute->setName('Size');
$secondSyliusProductAttributeValue->setLocaleCode('en_US');
$secondSyliusProductAttribute->setType(TextAttributeType::TYPE);
$secondSyliusProductAttribute->setStorageType(TextAttributeType::TYPE);
$secondSyliusProductAttributeValue->setAttribute($secondSyliusProductAttribute);
$secondSyliusProductAttributeValue->setValue('M');

$syliusTaxon = new SyliusTaxon();
$syliusTaxon->setCurrentLocale('en_US');
$syliusTaxon->setCode('tree');
$syliusTaxon->setSlug('/tree');
$syliusTaxon->setDescription('Lorem ipsum');
$syliusProductTaxon = new ProductTaxon();

$syliusLocale = new Locale();
$syliusLocale->setCode('en_US');

$syliusProduct = new SyliusProduct();
$syliusProductVariant = new ProductVariant();
$channelPrice = new ChannelPricing();
$syliusChannel = new Channel();
$currency = new Currency();
$currency->setCode('USD');

$syliusProductTaxon->setProduct($syliusProduct);
$syliusProductTaxon->setTaxon($syliusTaxon);
$channelPrice->setPrice(1000);
$channelPrice->setChannelCode('mobile');

$syliusChannel->setCode('mobile');
$syliusChannel->setDefaultLocale($syliusLocale);
$syliusChannel->addLocale($syliusLocale);
$syliusChannel->addCurrency($currency);
$syliusChannel->setBaseCurrency($currency);

$syliusProductVariant->addChannelPricing($channelPrice);
$syliusProduct->addVariant($syliusProductVariant);
$syliusProduct->addChannel($syliusChannel);
$syliusProduct->setMainTaxon($syliusTaxon);
$syliusProduct->addProductTaxon($syliusProductTaxon);
$syliusProduct->setCreatedAt($createdAt);
$syliusProduct->setCurrentLocale('en_US');
$syliusProduct->setName('Banana');
$syliusProduct->setSlug('/banana');
$syliusProduct->setDescription('Lorem ipsum');
$syliusProduct->setCode('banana');
$syliusProduct->addAttribute($firstSyliusProductAttributeValue);
$syliusProduct->addAttribute($secondSyliusProductAttributeValue);

$factory = new ProductDocumentFactory(
ProductDocument::class,
AttributeDocument::class,
AttributeValueDocument::class,
ImageDocument::class,
PriceDocument::class,
TaxonDocument::class,
['material']
);
/** @var ProductDocument $product */
$product = $factory->createFromSyliusSimpleProductModel(
$syliusProduct,
$syliusLocale,
$syliusChannel
);

$taxon = new TaxonDocument();
$taxon->setCode('tree');
$taxon->setPosition(0);
$taxon->setSlug('/tree');
$taxon->setDescription('Lorem ipsum');

$productTaxon = new TaxonDocument();
$productTaxon->setCode('tree');
$productTaxon->setSlug('/tree');
$productTaxon->setPosition(0);
$productTaxon->setDescription('Lorem ipsum');

$firstProductAttribute = new AttributeDocument();
$firstProductAttribute->setCode('material');
$firstProductAttribute->setName('Material');

$firstProductAttributeValue = new AttributeValueDocument();
$firstProductAttributeValue->setValue('wood');
$firstProductAttributeValue->setAttribute($firstProductAttribute);

$secondProductAttribute = new AttributeDocument();
$secondProductAttribute->setCode('size');
$secondProductAttribute->setName('Size');

$secondProductAttributeValue = new AttributeValueDocument();
$secondProductAttributeValue->setValue('M');
$secondProductAttributeValue->setAttribute($secondProductAttribute);

$this->assertEquals('banana', $product->getCode());
$this->assertEquals('Banana', $product->getName());
$this->assertEquals('en_US', $product->getLocaleCode());
$this->assertEquals(
new Collection([
$firstProductAttributeValue,
]),
$product->getAttributeValues()
);

$this->assertEquals(1000, $product->getPrice()->getAmount());
$this->assertEquals('USD', $product->getPrice()->getCurrency());
$this->assertEquals('en_US', $product->getLocaleCode());
$this->assertEquals('mobile', $product->getChannelCode());
$this->assertEquals('/banana', $product->getSlug());
$this->assertEquals('Banana', $product->getName());
$this->assertEquals($createdAt, $product->getCreatedAt());
$this->assertEquals('Lorem ipsum', $product->getDescription());
$this->assertEquals($taxon, $product->getMainTaxon());
$this->assertEquals(new Collection([$productTaxon]), $product->getTaxons());
}
}

0 comments on commit 5a0797e

Please sign in to comment.