diff --git a/.travis.yml b/.travis.yml index 1ffb05b..0e50c11 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,15 +2,17 @@ language: php php: - 7.1 - - 7.0 - - 5.6 + +jdk: + - oraclejdk8 addons: apt: sources: - - elasticsearch-2.x + - elasticsearch-5.x packages: - elasticsearch + - oracle-java8-set-default services: - elasticsearch @@ -29,7 +31,7 @@ before_install: install: - composer update --prefer-dist - tests/Application/bin/console doctrine:schema:create --env=test -vvv --no-interaction - - tests/Application/bin/console fos:elastica:reset --env=test -vvv --no-interaction + - tests/Application/bin/console ongr:es:index:create -vvv --no-interaction script: - bin/phpspec run --no-interaction diff --git a/README.md b/README.md index 3ba7e30..1a6849e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Sylius ElasticSearchBundle +Sylius ElasticSearchPlugin ========================== Elastic search for Sylius. [![Build status on Linux](https://img.shields.io/travis/Lakion/SyliusElasticSearchBundle/master.svg)](http://travis-ci.org/Lakion/SyliusELasticSearchBundle) @@ -8,12 +8,12 @@ Elastic search for Sylius. 1. Install it: ```bash - $ composer require lakion/sylius-elastic-search-bundle + $ composer require sylius/elastic-search-plugin ``` 2. Install elastic search server: ```bash - $ brew install elasticsearch@2.4 + $ brew install elasticsearch@5.0 ``` 3. Run elastic search server: @@ -25,8 +25,8 @@ Elastic search for Sylius. 4. Add this bundle to `AppKernel.php`: ```php - new \FOS\ElasticaBundle\FOSElasticaBundle(), - new \Lakion\SyliusElasticSearchBundle\LakionSyliusElasticSearchBundle(), + new \ONGR\ElasticsearchBundle\ONGRElasticsearchBundle(), + new \Lakion\SyliusElasticSearchBundle\SyliusElasticSearchPlugin(), ``` 5. Create/Setup database: @@ -37,37 +37,16 @@ Elastic search for Sylius. $ app/console syl:fix:lo ``` -6. Populate your elastic search server with command or your custom code: - - ```bash - $ app/console fos:elastic:pop - ``` - 7. Import config file in `app/config/config.yml` for default filter set configuration: ```yaml imports: - - { resource: "@LakionSyliusElasticSearchBundle/Resources/config/app/config.yml" } + - { resource: "@SyliusElasticSearchPlugin/Resources/config/app/config.yml" } ``` 8. Import routing files in `app/config/routing.yml`: ```yaml sylius_search: - resource: "@LakionSyliusElasticSearchBundle/Resources/config/routing.yml" - ``` - -8. Configuration reference: - - ```yaml - lakion_sylius_elastic_search: - filter_sets: - mugs: - filters: - product_options: - type: option - options: - code: mug_type - product_price: - type: price + resource: "@SyliusElasticSearchPlugin/Resources/config/routing.yml" ``` diff --git a/composer.json b/composer.json index 9307021..f374e9d 100644 --- a/composer.json +++ b/composer.json @@ -1,29 +1,29 @@ { - "name": "lakion/sylius-elastic-search-bundle", + "name": "sylius/elastic-search-plugin", "type": "sylius-bundle", "description": "Elastic search integration for Sylius.", "license": "MIT", "authors": [ { "name": "Arkadiusz Krakowiak", - "email": "arkadiusz.krakowiak@lakion.com", - "homepage": "http://lakion.com" + "email": "arkadiusz.k.e@gmail.com", + "homepage": "http://github.com/arminek" } ], "require": { "php": "^5.6|^7.0", "sylius/sylius": "dev-master", - - "friendsofsymfony/elastica-bundle": "^4.0", - "ongr/elasticsearch-dsl": "^2.0" + "ongr/elasticsearch-dsl": "^5.0", + "ongr/elasticsearch-bundle": "^5.0", + "simple-bus/symfony-bridge": "^4.1", + "beberlei/porpaginas": "^1.0" }, "require-dev": { "behat/behat": "^3.2", "behat/mink": "^1.7", "behat/mink-browserkit-driver": "^1.3", "behat/mink-extension": "^2.2", - "behat/mink-selenium2-driver": "^1.3", "friends-of-behat/context-service-extension": "^0.3", "friends-of-behat/cross-container-extension": "^0.2", "friends-of-behat/performance-extension": "^1.0", @@ -35,7 +35,8 @@ "matthiasnoback/symfony-dependency-injection-test": "^1.1", "php-http/guzzle6-adapter": "^1.1", "phpspec/phpspec": "^3.2", - "phpunit/phpunit": "^5.6" + "phpunit/phpunit": "^5.6", + "behat/mink-selenium2-driver": "^1.3" }, "config": { "bin-dir": "bin" @@ -44,8 +45,8 @@ "prefer-stable": true, "autoload": { "psr-4": { - "Lakion\\SyliusElasticSearchBundle\\": "src/", - "Tests\\Lakion\\SyliusElasticSearchBundle\\": "tests/" + "Sylius\\ElasticSearchPlugin\\": "src/", + "Tests\\Sylius\\ElasticSearchPlugin\\": "tests/" } } } diff --git a/features/shop/product/searching/filtering_list_of_products_by_channel.feature b/features/shop/product/searching/filtering_list_of_products_by_channel.feature index 517e142..48b20de 100644 --- a/features/shop/product/searching/filtering_list_of_products_by_channel.feature +++ b/features/shop/product/searching/filtering_list_of_products_by_channel.feature @@ -5,15 +5,12 @@ Feature: Filtering list of products by channels I want to be able to filter the products Background: - Given the store operates on a channel named "Europe" - And the store also operates on another channel named "Mobile" - And the store also operates on another channel named "Tablets" - And the store has a product "Banana T-Shirt" priced at "$100" in "Tablets" channel - And the store also has a product "Star Wars T-Shirt" priced at "$150" in "Mobile" channel - And the store also has a product "LOTR T-Shirt" priced at "$300" in "Mobile" channel - And the store also has a product "Breaking Bad T-Shirt" priced at "$50" in "Mobile" channel - And the store also has a product "Westworld T-Shirt" priced at "$1000" in "Europe" channel - And the store also has a product "Orange T-Shirt" priced at "$1000" in "Europe" channel + Given the store has a product "Banana T-Shirt" priced at "$100" in "tablets" channel + And the store also has a product "Star Wars T-Shirt" priced at "$150" in "mobile" channel + And the store also has a product "LOTR T-Shirt" priced at "$300" in "mobile" channel + And the store also has a product "Breaking Bad T-Shirt" priced at "$50" in "mobile" channel + And the store also has a product "Westworld T-Shirt" priced at "$1000" in "europe" channel + And the store also has a product "Orange T-Shirt" priced at "$1000" in "europe" channel @domain Scenario: Filtering products by mobile channel diff --git a/features/shop/product/searching/filtering_list_of_products_by_options.feature b/features/shop/product/searching/filtering_list_of_products_by_options.feature deleted file mode 100644 index 2ced0a9..0000000 --- a/features/shop/product/searching/filtering_list_of_products_by_options.feature +++ /dev/null @@ -1,29 +0,0 @@ -@searching_products -Feature: Filtering list of products by options - In order to see more specific list of the products - As an Visitor - I want to be able to filter the products - - Background: - Given the store operates on a single channel in "United States" - And the store has 10 Mugs, 20 Stickers and 25 Books - - @domain @ui - Scenario: Filtering products by their type - When I filter them by double mug type - Then I should see 10 products on the list - - @domain @ui - Scenario: Filtering product by their type or size - When I filter them by double mug type or sticker size 7 - Then I should see 30 products on the list - - @domain @ui - Scenario: Filtering product by their size - When I filter them by stickier size 7 - Then I should see 20 products on the list - - @domain @ui - Scenario: List of all products without filtering - When I view the list of the products without filtering - Then I should see 55 products on the list diff --git a/features/shop/product/searching/filtering_list_of_products_by_price_range.feature b/features/shop/product/searching/filtering_list_of_products_by_price_range.feature index 41353a5..2fdf913 100644 --- a/features/shop/product/searching/filtering_list_of_products_by_price_range.feature +++ b/features/shop/product/searching/filtering_list_of_products_by_price_range.feature @@ -5,13 +5,12 @@ Feature: Filtering list of products by price range I want to be able to filter the products Background: - Given the store operates on a single channel in "United States" - And the store has a product "Banana T-Shirt" priced at "$100" + Given the store has a product "Banana T-Shirt" priced at "$100" And the store also has a product "Star Wars T-Shirt" priced at "$150" And the store also has a product "LOTR T-Shirt" priced at "$300" And the store also has a product "Breaking Bad T-Shirt" priced at "$50" - @domain @ui + @domain Scenario: Filtering products by price range When I filter them by price between "$100" and "$200" Then I should see 2 products on the list diff --git a/features/shop/product/searching/filtering_list_of_products_by_price_range_and_channel.feature b/features/shop/product/searching/filtering_list_of_products_by_price_range_and_channel.feature index f3c89e0..acc18e1 100644 --- a/features/shop/product/searching/filtering_list_of_products_by_price_range_and_channel.feature +++ b/features/shop/product/searching/filtering_list_of_products_by_price_range_and_channel.feature @@ -5,15 +5,12 @@ Feature: Filtering list of products by channels and price I want to be able to filter the products Background: - Given the store operates on a channel named "Europe" - And the store also operates on another channel named "Mobile" - And the store also operates on another channel named "Tablets" - And the store has a product "Banana T-Shirt" priced at "$100" in "Tablets" channel - And the store also has a product "Star Wars T-Shirt" priced at "$150" in "Mobile" channel - And the store also has a product "LOTR T-Shirt" priced at "$300" in "Mobile" channel - And the store also has a product "Breaking Bad T-Shirt" priced at "$50" in "Mobile" channel - And the store also has a product "Westworld T-Shirt" priced at "$1000" in "Europe" channel - And the store also has a product "Orange T-Shirt" priced at "$1000" in "Europe" channel + Given the store has a product "Banana T-Shirt" priced at "$100" in "tablets" channel + And the store also has a product "Star Wars T-Shirt" priced at "$150" in "mobile" channel + And the store also has a product "LOTR T-Shirt" priced at "$300" in "mobile" channel + And the store also has a product "Breaking Bad T-Shirt" priced at "$50" in "mobile" channel + And the store also has a product "Westworld T-Shirt" priced at "$1000" in "europe" channel + And the store also has a product "Orange T-Shirt" priced at "$1000" in "europe" channel @domain Scenario: Filtering products by mobile channel and price range diff --git a/features/shop/product/searching/filtering_list_of_products_by_taxon.feature b/features/shop/product/searching/filtering_list_of_products_by_taxon.feature index 887d36d..d82a25e 100644 --- a/features/shop/product/searching/filtering_list_of_products_by_taxon.feature +++ b/features/shop/product/searching/filtering_list_of_products_by_taxon.feature @@ -5,25 +5,24 @@ Feature: Filtering list of products by taxon I want to be able to filter the products Background: - Given the store classifies its products as Mugs, Stickers and Books - And the store has 40 Mugs, 15 Stickers and 50 Books + Given the store has 3 Mugs, 2 Stickers and 5 Books @domain Scenario: Filtering products by book - When I filter them by "Books" taxon - Then I should see 50 products on the list + When I filter them by "books" taxon + Then I should see 5 products on the list @domain Scenario: Filtering product by stickers - When I filter them by "Stickers" taxon - Then I should see 15 products on the list + When I filter them by "stickers" taxon + Then I should see 2 products on the list @domain Scenario: Filtering product by mugs - When I filter them by "Mugs" taxon - Then I should see 40 products on the list + When I filter them by "mugs" taxon + Then I should see 3 products on the list @domain Scenario: List of all products without filtering When I view the list of the products without filtering - Then I should see 105 products on the list + Then I should see 10 products on the list diff --git a/features/shop/product/searching/searching_products_by_name.feature b/features/shop/product/searching/searching_products_by_name.feature index 65c1acc..92da2b7 100644 --- a/features/shop/product/searching/searching_products_by_name.feature +++ b/features/shop/product/searching/searching_products_by_name.feature @@ -5,8 +5,7 @@ Feature: Searching products by name I want to be able to filter the products Background: - Given the store operates on a channel named "Europe" - And the store has a product "Banana T-Shirt" + Given the store has a product "Banana T-Shirt" And the store has a product "Star Wars T-Shirt" And the store has a product "Star Wars Rogue T-Shirt" And the store has a product "Breaking Bad Saul T-Shirt" @@ -17,37 +16,37 @@ Feature: Searching products by name And the store has a product "Westworld Hosts T-Shirt" And the store has a product "Westworld Hopkins T-Shirt" - @domain @ui + @domain Scenario: Searching products by name When I search for products with name "Banana" Then I should see 1 products on the list And It should be "Banana T-Shirt" - @domain @ui + @domain Scenario: Searching products by name When I search for products with name "Star" Then I should see 2 products on the list And It should be "Star Wars T-Shirt", "Star Wars Rogue T-Shirt" - @domain @ui + @domain Scenario: Searching products by name When I search for products with name "Bad" Then I should see 4 products on the list And It should be "Breaking Bad Saul T-Shirt", "Breaking Bad Mike T-Shirt", "Breaking Bad Jesse T-Shirt", "Breaking Bad Heisenberg T-Shirt" - @domain @ui + @domain Scenario: Searching products by name When I search for products with name "Westworld" Then I should see 3 products on the list And It should be "Westworld T-Shirt", "Westworld Hosts T-Shirt", "Westworld Hopkins T-Shirt" - @domain @ui + @domain Scenario: Searching products by name When I search for products with name "Hopkins" Then I should see 1 products on the list And It should be "Westworld Hopkins T-Shirt" - @domain @ui + @domain Scenario: Searching products by name When I search for products with name "T-Shirt" Then I should see 10 products on the list diff --git a/features/shop/product/sorting/sorting_list_of_products_by_name.feature b/features/shop/product/sorting/sorting_list_of_products_by_name.feature index c1bf0a8..124bb81 100644 --- a/features/shop/product/sorting/sorting_list_of_products_by_name.feature +++ b/features/shop/product/sorting/sorting_list_of_products_by_name.feature @@ -5,15 +5,12 @@ Feature: Sorting list of products by name I want to be able to sort the products Background: - Given the store operates on a channel named "Europe" - And the store also operates on another channel named "Mobile" - And the store also operates on another channel named "Tablets" - And the store has a product "Banana T-Shirt" priced at "$100" in "Tablets" channel - And the store also has a product "Star Wars T-Shirt" priced at "$150" in "Mobile" channel - And the store also has a product "LOTR T-Shirt" priced at "$300" in "Mobile" channel - And the store also has a product "Breaking Bad T-Shirt" priced at "$50" in "Mobile" channel - And the store also has a product "Westworld T-Shirt" priced at "$1000" in "Europe" channel - And the store also has a product "Orange T-Shirt" priced at "$1000" in "Europe" channel + Given the store has a product "Banana T-Shirt" priced at "$100" in "tablets" channel + And the store also has a product "Star Wars T-Shirt" priced at "$150" in "mobile" channel + And the store also has a product "LOTR T-Shirt" priced at "$300" in "mobile" channel + And the store also has a product "Breaking Bad T-Shirt" priced at "$50" in "mobile" channel + And the store also has a product "Westworld T-Shirt" priced at "$1000" in "europe" channel + And the store also has a product "Orange T-Shirt" priced at "$1000" in "europe" channel @domain Scenario: Sorting products by name in ascending order diff --git a/phpspec.yml.dist b/phpspec.yml.dist index ebc2561..681f57f 100644 --- a/phpspec.yml.dist +++ b/phpspec.yml.dist @@ -1,4 +1,4 @@ suites: default: - namespace: Lakion\SyliusElasticSearchBundle - psr4_prefix: Lakion\SyliusElasticSearchBundle + namespace: Sylius\ElasticSearchPlugin + psr4_prefix: Sylius\ElasticSearchPlugin diff --git a/spec/Document/AttributeSpec.php b/spec/Document/AttributeSpec.php new file mode 100644 index 0000000..d98c7b2 --- /dev/null +++ b/spec/Document/AttributeSpec.php @@ -0,0 +1,29 @@ +shouldHaveType(Attribute::class); + } + + function it_has_code() + { + $this->setCode('color'); + + $this->getCode()->shouldReturn('color'); + } + + function it_has_name() + { + $this->setName('color'); + + $this->getName()->shouldReturn('color'); + } +} diff --git a/spec/Document/AttributeValueSpec.php b/spec/Document/AttributeValueSpec.php new file mode 100644 index 0000000..8ae2ca1 --- /dev/null +++ b/spec/Document/AttributeValueSpec.php @@ -0,0 +1,38 @@ +shouldHaveType(AttributeValue::class); + } + + function it_has_value() + { + $this->setValue('Red'); + + $this->getValue()->shouldReturn('Red'); + } + + function it_has_attribute() + { + $attribute = new Attribute(); + $this->setAttribute($attribute); + + $this->getAttribute()->shouldReturn($attribute); + } + + function it_has_code() + { + $this->setCode('red'); + + $this->getCode()->shouldReturn('red'); + } +} diff --git a/spec/Document/PriceSpec.php b/spec/Document/PriceSpec.php new file mode 100644 index 0000000..42d64f5 --- /dev/null +++ b/spec/Document/PriceSpec.php @@ -0,0 +1,28 @@ +shouldHaveType(Price::class); + } + + function it_has_amount() + { + $this->setAmount(1000); + + $this->getAmount()->shouldReturn(1000); + } + + function it_has_currency() + { + $this->setCurrency('EUR'); + + $this->getCurrency()->shouldReturn('EUR'); + } +} diff --git a/spec/Document/ProductSpec.php b/spec/Document/ProductSpec.php new file mode 100644 index 0000000..5124b3e --- /dev/null +++ b/spec/Document/ProductSpec.php @@ -0,0 +1,84 @@ +shouldHaveType(Product::class); + } + + function it_has_code() + { + $this->setCode('Mug'); + + $this->getCode()->shouldReturn('Mug'); + } + + function it_has_name() + { + $this->setName('Big Mug'); + + $this->getName()->shouldReturn('Big Mug'); + } + + function it_has_channel_code() + { + $this->setChannelCode('WEB'); + + $this->getChannelCode()->shouldReturn('WEB'); + } + + function it_has_locale_code() + { + $this->setLocaleCode('en'); + + $this->getLocaleCode()->shouldReturn('en'); + } + + function it_has_description() + { + $this->setDescription('Lorem ipsum'); + + $this->getDescription()->shouldReturn('Lorem ipsum'); + } + + function it_has_price() + { + $price = new Price(); + $this->setPrice($price); + + $this->getPrice()->shouldReturn($price); + } + + function it_has_main_taxon_code() + { + $taxonCode = new TaxonCode(); + $this->setMainTaxonCode($taxonCode); + + $this->getMainTaxonCode()->shouldReturn($taxonCode); + } + + function it_has_taxon_codes() + { + $taxonCodes = new Collection(); + $this->setTaxonCodes($taxonCodes); + + $this->getTaxonCodes()->shouldReturn($taxonCodes); + } + + function it_has_attributes() + { + $attributeValues = new Collection(); + $this->setAttributeValues($attributeValues); + + $this->getAttributeValues()->shouldReturn($attributeValues); + } +} diff --git a/spec/Document/TaxonCodeSpec.php b/spec/Document/TaxonCodeSpec.php new file mode 100644 index 0000000..83d0f65 --- /dev/null +++ b/spec/Document/TaxonCodeSpec.php @@ -0,0 +1,22 @@ +shouldHaveType(TaxonCode::class); + } + + function it_has_value() + { + $this->setValue('mug'); + + $this->getValue()->shouldReturn('mug'); + } +} diff --git a/spec/EventListener/ProductPublisherSpec.php b/spec/EventListener/ProductPublisherSpec.php new file mode 100644 index 0000000..366ec20 --- /dev/null +++ b/spec/EventListener/ProductPublisherSpec.php @@ -0,0 +1,56 @@ +beConstructedWith($eventBus); + } + + function it_is_initializable() + { + $this->shouldHaveType(ProductPublisher::class); + } + + function it_publishes_product_event(MessageBus $eventBus, LifecycleEventArgs $event, ProductInterface $product) + { + $product->isSimple()->willReturn(true); + $event->getEntity()->willReturn($product); + + $eventBus->handle(ProductCreated::occur($product->getWrappedObject()))->shouldBeCalled(); + + $this->postPersist($event); + } + + function it_does_not_publish_product_event_if_entity_is_not_a_product(MessageBus $eventBus, LifecycleEventArgs $event) + { + $event->getEntity()->willReturn(new \stdClass()); + + $eventBus->handle(Argument::any())->shouldNotBeCalled(); + + $this->postPersist($event); + } + + function it_does_not_publish_product_event_if_entity_is_not_a_simple_product( + MessageBus $eventBus, + LifecycleEventArgs $event, + ProductInterface $product + ) { + $product->isSimple()->willReturn(false); + $event->getEntity()->willReturn($product); + + $eventBus->handle(Argument::any())->shouldNotBeCalled(); + + $this->postPersist($event); + } +} diff --git a/spec/Exception/FilterSetConfigurationNotFoundExceptionSpec.php b/spec/Exception/FilterSetConfigurationNotFoundExceptionSpec.php index b4db631..648dabc 100644 --- a/spec/Exception/FilterSetConfigurationNotFoundExceptionSpec.php +++ b/spec/Exception/FilterSetConfigurationNotFoundExceptionSpec.php @@ -1,9 +1,9 @@ diff --git a/spec/Exception/MissingQueryParameterExceptionSpec.php b/spec/Exception/MissingQueryParameterExceptionSpec.php index c20a729..ca5bb5c 100644 --- a/spec/Exception/MissingQueryParameterExceptionSpec.php +++ b/spec/Exception/MissingQueryParameterExceptionSpec.php @@ -1,8 +1,8 @@ beConstructedWith($manager, $factory); + } + + function it_is_initializable() + { + $this->shouldHaveType(ProductProjector::class); + } + + function it_saves_product_document_if_product_has_channel_defined( + Manager $manager, + ProductFactoryInterface $factory, + ProductInterface $product, + LocaleInterface $locale, + ChannelInterface $channel + ) { + $channel->getLocales()->willReturn(new ArrayCollection([$locale->getWrappedObject()])); + $product->getChannels()->willReturn(new ArrayCollection([$channel->getWrappedObject()])); + + $productDocument = new Product(); + $factory->createFromSyliusSimpleProductModel($product, $locale, $channel)->willReturn($productDocument); + + $manager->persist($productDocument)->shouldBeCalled(); + $manager->commit()->shouldBeCalled(); + + $this->handleProductCreated(ProductCreated::occur($product->getWrappedObject())); + } + + function it_does_not_save_product_document_if_product_has_not_channel_defined( + Manager $manager, + ProductFactoryInterface $factory, + ProductInterface $product + ) { + $product->getChannels()->willReturn(new ArrayCollection([])); + $factory->createFromSyliusSimpleProductModel(Argument::any(), Argument::any(), Argument::any())->shouldNotBeCalled(); + + $manager->persist(Argument::any())->shouldNotBeCalled(); + $manager->commit()->shouldBeCalled(); + + $this->handleProductCreated(ProductCreated::occur($product->getWrappedObject())); + } + + function it_does_not_save_product_document_if_channel_has_not_locales_defined( + Manager $manager, + ProductFactoryInterface $factory, + ProductInterface $product, + ChannelInterface $channel + ) { + $channel->getLocales()->willReturn(new ArrayCollection([])); + $product->getChannels()->willReturn(new ArrayCollection([$channel->getWrappedObject()])); + + $factory->createFromSyliusSimpleProductModel(Argument::any(), Argument::any(), Argument::any())->shouldNotBeCalled(); + + $manager->persist(Argument::any())->shouldNotBeCalled(); + $manager->commit()->shouldBeCalled(); + + $this->handleProductCreated(ProductCreated::occur($product->getWrappedObject())); + } +} diff --git a/spec/Search/Criteria/CriteriaSpec.php b/spec/Search/Criteria/CriteriaSpec.php index b76cce6..76f515b 100644 --- a/spec/Search/Criteria/CriteriaSpec.php +++ b/spec/Search/Criteria/CriteriaSpec.php @@ -1,11 +1,11 @@ 'red', ]]); - $this->getResourceAlias()->shouldReturn('sylius.product'); - $this->getFiltering()->shouldBeLike(Filtering::fromQueryParameters([ + $this->documentClass()->shouldReturn('sylius.product'); + $this->filtering()->shouldBeLike(Filtering::fromQueryParameters([ 'page' => 2, 'per_page' => 50, 'sort' => '-price', 'option' => 'red', ])); - $this->getPaginating()->shouldBeLike(Paginating::fromQueryParameters([ + $this->paginating()->shouldBeLike(Paginating::fromQueryParameters([ 'page' => 2, 'per_page' => 50, 'sort' => '-price', 'option' => 'red', ])); - $this->getOrdering()->shouldBeLike(Ordering::fromQueryParameters([ + $this->ordering()->shouldBeLike(Ordering::fromQueryParameters([ 'page' => 2, 'per_page' => 50, 'sort' => '-price', diff --git a/spec/Search/Criteria/Filtering/ProductHasOptionCodesFilterSpec.php b/spec/Search/Criteria/Filtering/ProductHasOptionCodesFilterSpec.php deleted file mode 100644 index ce9f6c8..0000000 --- a/spec/Search/Criteria/Filtering/ProductHasOptionCodesFilterSpec.php +++ /dev/null @@ -1,27 +0,0 @@ - - */ -final class ProductHasOptionCodesFilterSpec extends ObjectBehavior -{ - function let() - { - $this->beConstructedWith(['mug_type_double', 'mug_type_small']); - } - - function it_is_initializable() - { - $this->shouldHaveType(ProductHasOptionCodesFilter::class); - } - - function it_has_immutable_option_codes() - { - $this->getCodes()->shouldReturn(['mug_type_double', 'mug_type_small']); - } -} diff --git a/spec/Search/Criteria/Filtering/ProductInChannelFilterSpec.php b/spec/Search/Criteria/Filtering/ProductInChannelFilterSpec.php deleted file mode 100644 index c8ae7bb..0000000 --- a/spec/Search/Criteria/Filtering/ProductInChannelFilterSpec.php +++ /dev/null @@ -1,27 +0,0 @@ - - */ -final class ProductInChannelFilterSpec extends ObjectBehavior -{ - function let() - { - $this->beConstructedWith('web_uk'); - } - - function it_is_initializable() - { - $this->shouldHaveType(ProductInChannelFilter::class); - } - - function it_has_immutable_channel_code() - { - $this->getChannelCode()->shouldReturn('web_uk'); - } -} diff --git a/spec/Search/Criteria/Filtering/ProductInPriceRangeFilterSpec.php b/spec/Search/Criteria/Filtering/ProductInPriceRangeFilterSpec.php deleted file mode 100644 index bdd9347..0000000 --- a/spec/Search/Criteria/Filtering/ProductInPriceRangeFilterSpec.php +++ /dev/null @@ -1,32 +0,0 @@ - - */ -final class ProductInPriceRangeFilterSpec extends ObjectBehavior -{ - function let() - { - $this->beConstructedWith(100, 300); - } - - function it_is_initializable() - { - $this->shouldHaveType(ProductInPriceRangeFilter::class); - } - - function it_has_immutable_grater_than() - { - $this->getGraterThan()->shouldReturn(100); - } - - function it_has_immutable_less_than() - { - $this->getLessThan()->shouldReturn(300); - } -} diff --git a/spec/Search/Criteria/Filtering/ProductInTaxonFilterSpec.php b/spec/Search/Criteria/Filtering/ProductInTaxonFilterSpec.php deleted file mode 100644 index 389f99e..0000000 --- a/spec/Search/Criteria/Filtering/ProductInTaxonFilterSpec.php +++ /dev/null @@ -1,27 +0,0 @@ - - */ -final class ProductInTaxonFilterSpec extends ObjectBehavior -{ - function let() - { - $this->beConstructedWith('mugs'); - } - - function it_is_initializable() - { - $this->shouldHaveType(ProductInTaxonFilter::class); - } - - function it_has_immutable_taxon_code() - { - $this->getTaxonCode()->shouldReturn('mugs'); - } -} diff --git a/spec/Search/Criteria/FilteringSpec.php b/spec/Search/Criteria/FilteringSpec.php index 060e528..37af4c8 100644 --- a/spec/Search/Criteria/FilteringSpec.php +++ b/spec/Search/Criteria/FilteringSpec.php @@ -1,8 +1,8 @@ 's', ]]); - $this->getFields()->shouldReturn(['option' => 'red', 'size' => 's',]); + $this->fields()->shouldReturn(['option' => 'red', 'size' => 's',]); } function it_removes_page_per_page_and_sort_attributes_from_query_parameters() @@ -32,9 +32,9 @@ function it_removes_page_per_page_and_sort_attributes_from_query_parameters() 'size' => 'm', 'sort' => 'name', 'page' => 10, - 'per_page' => 50, + 'limit' => 50, ]]); - $this->getFields()->shouldReturn(['option' => 'blue', 'size' => 'm',]); + $this->fields()->shouldReturn(['option' => 'blue', 'size' => 'm',]); } } diff --git a/spec/Search/Criteria/OrderingSpec.php b/spec/Search/Criteria/OrderingSpec.php index 139b806..55099c0 100644 --- a/spec/Search/Criteria/OrderingSpec.php +++ b/spec/Search/Criteria/OrderingSpec.php @@ -1,8 +1,8 @@ 'code', ]]); - $this->getField()->shouldReturn('code'); - $this->getDirection()->shouldReturn('asc'); + $this->field()->shouldReturn('code.raw'); + $this->direction()->shouldReturn('asc'); } function it_can_be_created_from_query_parameters() @@ -31,7 +31,7 @@ function it_can_be_created_from_query_parameters() 'sort' => '-code', ]]); - $this->getField()->shouldReturn('code'); - $this->getDirection()->shouldReturn('desc'); + $this->field()->shouldReturn('code.raw'); + $this->direction()->shouldReturn('desc'); } } diff --git a/spec/Search/Criteria/PaginatingSpec.php b/spec/Search/Criteria/PaginatingSpec.php index b44d0ec..6b72f7e 100644 --- a/spec/Search/Criteria/PaginatingSpec.php +++ b/spec/Search/Criteria/PaginatingSpec.php @@ -1,8 +1,8 @@ beConstructedThrough('fromQueryParameters', [[]]); - $this->getCurrentPage()->shouldReturn(1); - $this->getItemsPerPage()->shouldReturn(10); - $this->getOffset()->shouldReturn(0); + $this->currentPage()->shouldReturn(1); + $this->itemsPerPage()->shouldReturn(10); + $this->offset()->shouldReturn(0); } function it_can_be_created_from_query_parameters_with_default_values_if_parameters_are_not_valid() { $this->beConstructedThrough('fromQueryParameters', [[ 'page' => -1, - 'per_page' => -100, + 'limit' => -100, ]]); - $this->getCurrentPage()->shouldReturn(1); - $this->getItemsPerPage()->shouldReturn(10); - $this->getOffset()->shouldReturn(0); + $this->currentPage()->shouldReturn(1); + $this->itemsPerPage()->shouldReturn(10); + $this->offset()->shouldReturn(0); } function it_can_be_created_from_query_parameters() { $this->beConstructedThrough('fromQueryParameters', [[ 'page' => 2, - 'per_page' => 50, + 'limit' => 50, ]]); - $this->getCurrentPage()->shouldReturn(2); - $this->getItemsPerPage()->shouldReturn(50); - $this->getOffset()->shouldReturn(50); + $this->currentPage()->shouldReturn(2); + $this->itemsPerPage()->shouldReturn(50); + $this->offset()->shouldReturn(50); } } diff --git a/spec/Search/Criteria/SearchPhraseSpec.php b/spec/Search/Criteria/SearchPhraseSpec.php deleted file mode 100644 index 8019789..0000000 --- a/spec/Search/Criteria/SearchPhraseSpec.php +++ /dev/null @@ -1,27 +0,0 @@ - - */ -final class SearchPhraseSpec extends ObjectBehavior -{ - function let() - { - $this->beConstructedWith('Mug'); - } - - function it_is_initializable() - { - $this->shouldHaveType(SearchPhrase::class); - } - - function it_has_immutable_search_phrase() - { - $this->getPhrase()->shouldReturn('Mug'); - } -} diff --git a/spec/Search/Elastic/Applicator/Filter/ProductHasMultipleOptionCodesApplicatorSpec.php b/spec/Search/Elastic/Applicator/Filter/ProductHasMultipleOptionCodesApplicatorSpec.php deleted file mode 100644 index 090f963..0000000 --- a/spec/Search/Elastic/Applicator/Filter/ProductHasMultipleOptionCodesApplicatorSpec.php +++ /dev/null @@ -1,62 +0,0 @@ - - */ -final class ProductHasMultipleOptionCodesApplicatorSpec extends ObjectBehavior -{ - function let(QueryFactoryInterface $productHasOptionCodeQueryFactory) - { - $this->beConstructedWith($productHasOptionCodeQueryFactory); - } - - function it_is_initializable() - { - $this->shouldHaveType(ProductHasMultipleOptionCodesApplicator::class); - } - - function it_is_criteria_search_applicator() - { - $this->shouldImplement(SearchCriteriaApplicatorInterface::class); - } - - function it_applies_search_query_for_multiple_product_options( - QueryFactoryInterface $productHasOptionCodeQueryFactory, - TermQuery $mediumMugTermQuery, - TermQuery $stickerSizeTermQuery, - Search $search - ) { - $criteria = new ProductHasOptionCodesFilter(['medium_mug', 'sticker_size_1']); - $productHasOptionCodeQueryFactory->create(['option_value_code' => 'medium_mug'])->willReturn($mediumMugTermQuery); - $productHasOptionCodeQueryFactory->create(['option_value_code' => 'sticker_size_1'])->willReturn($stickerSizeTermQuery); - - $search->addFilter($mediumMugTermQuery, BoolQuery::SHOULD)->shouldBeCalled(); - $search->addFilter($stickerSizeTermQuery, BoolQuery::SHOULD)->shouldBeCalled(); - - $this->apply($criteria, $search); - } - - function it_applies_search_query_for_single_product_option( - QueryFactoryInterface $productHasOptionCodeQueryFactory, - TermQuery $mediumMugTermQuery, - Search $search - ) { - $criteria = new ProductHasOptionCodesFilter(['medium_mug']); - $productHasOptionCodeQueryFactory->create(['option_value_code' => 'medium_mug'])->willReturn($mediumMugTermQuery); - - $search->addFilter($mediumMugTermQuery, BoolQuery::SHOULD)->shouldBeCalled(); - - $this->apply($criteria, $search); - } -} diff --git a/spec/Search/Elastic/Applicator/Filter/ProductInChannelApplicatorSpec.php b/spec/Search/Elastic/Applicator/Filter/ProductInChannelApplicatorSpec.php index 173f207..7444143 100644 --- a/spec/Search/Elastic/Applicator/Filter/ProductInChannelApplicatorSpec.php +++ b/spec/Search/Elastic/Applicator/Filter/ProductInChannelApplicatorSpec.php @@ -1,13 +1,15 @@ create(['channel_code' => 'web'])->willReturn($nestedQuery); - $search->addFilter($nestedQuery, BoolQuery::MUST)->shouldBeCalled(); + $criteria = Criteria::fromQueryParameters(Product::class, ['channel_code' => 'web']); + $productInChannelQueryFactory->create($criteria->filtering()->fields())->willReturn($nestedQuery); + $search->addPostFilter($nestedQuery, BoolQuery::MUST)->shouldBeCalled(); $this->apply($criteria, $search); } + + function it_supports_channel_code_parameter() + { + $criteria = Criteria::fromQueryParameters(Product::class, ['channel_code' => 'web']); + + $this->supports($criteria)->shouldReturn(true); + + $criteria = Criteria::fromQueryParameters(Product::class, ['taxon_code' => 'tree']); + + $this->supports($criteria)->shouldReturn(false); + } } diff --git a/spec/Search/Elastic/Applicator/Filter/ProductInPriceRangeApplicatorSpec.php b/spec/Search/Elastic/Applicator/Filter/ProductInPriceRangeApplicatorSpec.php index 0809b2e..1b57b79 100644 --- a/spec/Search/Elastic/Applicator/Filter/ProductInPriceRangeApplicatorSpec.php +++ b/spec/Search/Elastic/Applicator/Filter/ProductInPriceRangeApplicatorSpec.php @@ -1,13 +1,15 @@ shouldImplement(SearchCriteriaApplicatorInterface::class); } - function it_applies_search_criteria_for_given_query(QueryFactoryInterface $productInPriceRangeQueryFactory, Search $search, NestedQuery $nestedQuery) + function it_applies_search_criteria_for_given_query(QueryFactoryInterface $productInPriceRangeQueryFactory, Search $search, TermQuery $termQuery) { - $criteria = new ProductInPriceRangeFilter(20, 50); - $productInPriceRangeQueryFactory->create(['product_price_range' => ['grater_than' => 20, 'less_than' => 50]])->willReturn($nestedQuery); - $search->addFilter($nestedQuery, BoolQuery::MUST)->shouldBeCalled(); + $criteria = Criteria::fromQueryParameters(Product::class, ['product_price_range' => ['grater_than' => 20, 'less_than' => 50]]); + $productInPriceRangeQueryFactory->create($criteria->filtering()->fields())->willReturn($termQuery); + $search->addPostFilter($termQuery, BoolQuery::MUST)->shouldBeCalled(); $this->apply($criteria, $search); } + + function it_supports_product_price_range_parameter() + { + $criteria = Criteria::fromQueryParameters(Product::class, ['product_price_range' => ['grater_than' => 20, 'less_than' => 50]]); + $this->supports($criteria)->shouldReturn(true); + + $criteria = Criteria::fromQueryParameters(Product::class, []); + $this->supports($criteria)->shouldReturn(false); + } } diff --git a/spec/Search/Elastic/Applicator/Filter/ProductInTaxonApplicatorSpec.php b/spec/Search/Elastic/Applicator/Filter/ProductInTaxonApplicatorSpec.php index 66b2447..2605ccd 100644 --- a/spec/Search/Elastic/Applicator/Filter/ProductInTaxonApplicatorSpec.php +++ b/spec/Search/Elastic/Applicator/Filter/ProductInTaxonApplicatorSpec.php @@ -1,15 +1,15 @@ beConstructedWith($productInMainTaxon, $productInProductTaxons); + $this->beConstructedWith($productInMainTaxon); } function it_is_initializable() @@ -35,19 +35,24 @@ function it_is_search_criteria_applicator() function it_applies_search_query_for_given_criteria( QueryFactoryInterface $productInMainTaxon, - QueryFactoryInterface $productInProductTaxons, - NestedQuery $nestedQuery, TermQuery $termQuery, Search $search ) { - $criteria = new ProductInTaxonFilter('mugs'); + $criteria = Criteria::fromQueryParameters(Product::class, ['taxon_code' => 'mugs']); - $productInMainTaxon->create(['taxon_code' => 'mugs'])->willReturn($termQuery); - $productInProductTaxons->create(['taxon_code' => 'mugs'])->willReturn($nestedQuery); + $productInMainTaxon->create($criteria->filtering()->fields())->willReturn($termQuery); - $search->addFilter($termQuery, BoolQuery::SHOULD)->shouldBeCalled(); - $search->addFilter($nestedQuery, BoolQuery::SHOULD)->shouldBeCalled(); + $search->addPostFilter($termQuery, BoolQuery::SHOULD)->shouldBeCalled(); $this->apply($criteria, $search); } + + function it_supports_taxon_code_paramter() + { + $criteria = Criteria::fromQueryParameters(Product::class, ['taxon_code' => 'mugs']); + $this->supports($criteria)->shouldReturn(true); + + $criteria = Criteria::fromQueryParameters(Product::class, []); + $this->supports($criteria)->shouldReturn(false); + } } diff --git a/spec/Search/Elastic/Applicator/Query/MatchProductByNameApplicatorSpec.php b/spec/Search/Elastic/Applicator/Query/MatchProductByNameApplicatorSpec.php index 3d979a7..93d4e8c 100644 --- a/spec/Search/Elastic/Applicator/Query/MatchProductByNameApplicatorSpec.php +++ b/spec/Search/Elastic/Applicator/Query/MatchProductByNameApplicatorSpec.php @@ -1,14 +1,15 @@ beConstructedWith($matchProductNameQueryFactory, $emptyQueryFactory); + $this->beConstructedWith($matchProductNameQueryFactory); } function it_is_initializable() @@ -37,22 +38,19 @@ function it_applies_match_product_by_name_query( MatchQuery $matchQuery, Search $search ) { - $criteria = new SearchPhrase('banana'); - $matchProductNameQueryFactory->create(['phrase' => 'banana'])->willReturn($matchQuery); + $criteria = Criteria::fromQueryParameters(Product::class, ['search' => 'banana']); + $matchProductNameQueryFactory->create($criteria->filtering()->fields())->willReturn($matchQuery); $search->addQuery($matchQuery)->shouldBeCalled(); $this->apply($criteria, $search); } - function it_applies_match_all_for_empty_search_phrase( - QueryFactoryInterface $emptyQueryFactory, - MatchAllQuery $matchAllQuery, - Search $search - ) { - $criteria = new SearchPhrase(''); - $emptyQueryFactory->create()->willReturn($matchAllQuery); - $search->addQuery($matchAllQuery)->shouldBeCalled(); + function it_supports_search_parameter() + { + $criteria = Criteria::fromQueryParameters(Product::class, ['search' => 'banana']); + $this->supports($criteria)->shouldReturn(true); - $this->apply($criteria, $search); + $criteria = Criteria::fromQueryParameters(Product::class, []); + $this->supports($criteria)->shouldReturn(false); } } diff --git a/spec/Search/Elastic/Applicator/Sort/SortByFieldApplicatorSpec.php b/spec/Search/Elastic/Applicator/Sort/SortByFieldApplicatorSpec.php index f4b2393..8395a8a 100644 --- a/spec/Search/Elastic/Applicator/Sort/SortByFieldApplicatorSpec.php +++ b/spec/Search/Elastic/Applicator/Sort/SortByFieldApplicatorSpec.php @@ -1,12 +1,12 @@ '-name']); - $sortByFieldSortFactory->create($criteria)->willReturn($fieldSort); + $criteria = Criteria::fromQueryParameters(Product::class, ['sort' => '-name']); + $sortByFieldSortFactory->create($criteria->ordering())->willReturn($fieldSort); $search->addSort($fieldSort)->shouldBeCalled(); $this->apply($criteria, $search); diff --git a/spec/Search/Elastic/ElasticSearchEngineSpec.php b/spec/Search/Elastic/ElasticSearchEngineSpec.php deleted file mode 100644 index d8a504a..0000000 --- a/spec/Search/Elastic/ElasticSearchEngineSpec.php +++ /dev/null @@ -1,134 +0,0 @@ - - */ -final class ElasticSearchEngineSpec extends ObjectBehavior -{ - function let(RepositoryManagerInterface $repositoryManager, SearchFactoryInterface $searchFactory) - { - $this->beConstructedWith($repositoryManager, $searchFactory); - } - - function it_is_initializable() - { - $this->shouldHaveType(ElasticSearchEngine::class); - } - - function it_is_search_engine() - { - $this->shouldImplement(SearchEngineInterface::class); - } - - function it_returns_paginator_with_default_query_if_there_is_no_applicators_registered( - RepositoryManagerInterface $repositoryManager, - SearchFactoryInterface $searchFactory, - Search $search, - Repository $repository, - SearchCriteriaApplicatorInterface $orderingApplicator, - PaginatorAdapterInterface $paginatorAdapter - ) { - $criteria = Criteria::fromQueryParameters('product', []); - $searchFactory->create()->willReturn($search); - $repositoryManager->getRepository('product')->willReturn($repository); - - $this->addSearchCriteriaApplicator($orderingApplicator, Ordering::class); - $orderingApplicator->apply($criteria->getOrdering(), $search)->shouldBeCalled(); - - $search->toArray()->willReturn([ - 'query' => [ - 'match_all' => new \stdClass(), - ] - ]); - - $repository->createPaginatorAdapter([ - 'query' => [ - 'match_all' => new \stdClass(), - ] - ])->willReturn($paginatorAdapter); - - $this->match($criteria)->shouldReturn($paginatorAdapter); - } - - function it_returns_resources_based_on_applicators_which_supports_given_criteria( - RepositoryManagerInterface $repositoryManager, - SearchFactoryInterface $searchFactory, - Search $search, - Repository $repository, - PaginatorAdapterInterface $paginatorAdapter, - SearchCriteriaApplicatorInterface $productHasOptionCodesApplicator, - SearchCriteriaApplicatorInterface $orderingApplicator - ) { - $productHasOptionCodesFilter = new ProductHasOptionCodesFilter(['mug_type']); - $criteria = Criteria::fromQueryParameters('product', [$productHasOptionCodesFilter]); - $this->addSearchCriteriaApplicator($productHasOptionCodesApplicator, ProductHasOptionCodesFilter::class); - $this->addSearchCriteriaApplicator($orderingApplicator, Ordering::class); - $searchFactory->create()->willReturn($search); - - $productHasOptionCodesApplicator->apply($productHasOptionCodesFilter, $search)->shouldBeCalled(); - $orderingApplicator->apply($criteria->getOrdering(), $search)->shouldBeCalled(); - - $repositoryManager->getRepository('product')->willReturn($repository); - $search->toArray()->willReturn([ - 'query' => [ - 'match_all' => new \stdClass(), - ] - ]); - - $repository->createPaginatorAdapter([ - 'query' => [ - 'match_all' => new \stdClass(), - ] - ])->willReturn($paginatorAdapter); - - $this->match($criteria)->shouldReturn($paginatorAdapter); - } - - function it_does_not_apply_none_object_filter( - RepositoryManagerInterface $repositoryManager, - SearchFactoryInterface $searchFactory, - Search $search, - Repository $repository, - PaginatorAdapterInterface $paginatorAdapter, - SearchCriteriaApplicatorInterface $productHasOptionCodesApplicator, - SearchCriteriaApplicatorInterface $orderingApplicator - ) { - $criteria = Criteria::fromQueryParameters('product', [null]); - $this->addSearchCriteriaApplicator($productHasOptionCodesApplicator, ProductHasOptionCodesFilter::class); - $this->addSearchCriteriaApplicator($orderingApplicator, Ordering::class); - $searchFactory->create()->willReturn($search); - - $productHasOptionCodesApplicator->apply(null, $search)->shouldNotBeCalled(); - $orderingApplicator->apply($criteria->getOrdering(), $search)->shouldBeCalled(); - - $repositoryManager->getRepository('product')->willReturn($repository); - $search->toArray()->willReturn([ - 'query' => [ - 'match_all' => new \stdClass(), - ] - ]); - - $repository->createPaginatorAdapter([ - 'query' => [ - 'match_all' => new \stdClass(), - ] - ])->willReturn($paginatorAdapter); - - $this->match($criteria)->shouldReturn($paginatorAdapter); - } -} diff --git a/spec/Search/Elastic/Factory/Query/EmptyCriteriaQueryFactorySpec.php b/spec/Search/Elastic/Factory/Query/EmptyCriteriaQueryFactorySpec.php index 476ebf2..15221e3 100644 --- a/spec/Search/Elastic/Factory/Query/EmptyCriteriaQueryFactorySpec.php +++ b/spec/Search/Elastic/Factory/Query/EmptyCriteriaQueryFactorySpec.php @@ -1,9 +1,9 @@ create(['phrase' => 'banana'])->shouldBeLike(new NestedQuery('translations', new MatchQuery('translations.name', 'banana'))); + $this->create(['search' => 'banana'])->shouldBeLike(new MatchQuery('name', 'banana')); } function it_cannot_be_created_without_search_parameter() diff --git a/spec/Search/Elastic/Factory/Query/ProductHasOptionCodeQueryFactorySpec.php b/spec/Search/Elastic/Factory/Query/ProductHasOptionCodeQueryFactorySpec.php index 4874c2e..a548152 100644 --- a/spec/Search/Elastic/Factory/Query/ProductHasOptionCodeQueryFactorySpec.php +++ b/spec/Search/Elastic/Factory/Query/ProductHasOptionCodeQueryFactorySpec.php @@ -1,12 +1,12 @@ create(['channel_code' => 'web'])->shouldBeLike( - new NestedQuery('channels', new TermQuery('channels.code', 'web')) - ); + $this->create(['channel_code' => 'web'])->shouldBeLike(new TermQuery('channel_code', 'web')); } function it_cannot_be_created_without_channel_code() diff --git a/spec/Search/Elastic/Factory/Query/ProductInMainTaxonQueryFactorySpec.php b/spec/Search/Elastic/Factory/Query/ProductInMainTaxonQueryFactorySpec.php index 9fe1cef..acba83f 100644 --- a/spec/Search/Elastic/Factory/Query/ProductInMainTaxonQueryFactorySpec.php +++ b/spec/Search/Elastic/Factory/Query/ProductInMainTaxonQueryFactorySpec.php @@ -1,11 +1,11 @@ create(['taxon_code' => 'mugs'])->shouldBeLike(new TermQuery('mainTaxon.code', 'mugs')); + $this->create(['taxon_code' => 'mugs'])->shouldBeLike(new TermQuery('main_taxon_code.value', 'mugs')); } function it_cannot_create_query_if_there_is_no_required_parameters() diff --git a/spec/Search/Elastic/Factory/Query/ProductInPriceRangeQueryFactorySpec.php b/spec/Search/Elastic/Factory/Query/ProductInPriceRangeQueryFactorySpec.php index 17961fb..95db118 100644 --- a/spec/Search/Elastic/Factory/Query/ProductInPriceRangeQueryFactorySpec.php +++ b/spec/Search/Elastic/Factory/Query/ProductInPriceRangeQueryFactorySpec.php @@ -1,12 +1,11 @@ create(['product_price_range' => ['grater_than' => 1000, 'less_than' => 2000]]) - ->shouldBeLike( - new NestedQuery('variants', - new NestedQuery('variants.channelPricings', - new RangeQuery('variants.channelPricings.price', ['gte' => 1000, 'lte' => 2000]) - ) - ) - ); + ->shouldBeLike(new RangeQuery('price.amount', ['gte' => 1000, 'lte' => 2000])); } function it_cannot_be_created_without_price_range_parameters() diff --git a/spec/Search/Elastic/Factory/Query/ProductInProductTaxonsQueryFactorySpec.php b/spec/Search/Elastic/Factory/Query/ProductInProductTaxonsQueryFactorySpec.php index 709f132..9d6e279 100644 --- a/spec/Search/Elastic/Factory/Query/ProductInProductTaxonsQueryFactorySpec.php +++ b/spec/Search/Elastic/Factory/Query/ProductInProductTaxonsQueryFactorySpec.php @@ -1,12 +1,12 @@ - */ -final class SearchFactorySpec extends ObjectBehavior -{ - function it_is_initializable() - { - $this->shouldHaveType(SearchFactory::class); - } - - function it_is_search_factory() - { - $this->shouldImplement(SearchFactoryInterface::class); - } - - function it_creates_new_empty_search_object() - { - $this->create()->shouldHaveType(Search::class); - } -} diff --git a/spec/Search/Elastic/Factory/Sort/SortByFieldQueryFactorySpec.php b/spec/Search/Elastic/Factory/Sort/SortByFieldQueryFactorySpec.php index 0a2e50b..0836fa4 100644 --- a/spec/Search/Elastic/Factory/Sort/SortByFieldQueryFactorySpec.php +++ b/spec/Search/Elastic/Factory/Sort/SortByFieldQueryFactorySpec.php @@ -1,10 +1,10 @@ '-price']); - $this->create($ordering)->shouldBeLike(new FieldSort('raw_price', 'desc')); + $this->create($ordering)->shouldBeLike(new FieldSort('price.raw', 'desc')); } function it_creates_ascending_field_sort_query() { $ordering = Ordering::fromQueryParameters(['sort' => 'price']); - $this->create($ordering)->shouldBeLike(new FieldSort('raw_price', 'asc')); + $this->create($ordering)->shouldBeLike(new FieldSort('price.raw', 'asc')); } function it_creates_ascending_by_name_field_sort_query_by_default() { $ordering = Ordering::fromQueryParameters([]); - $this->create($ordering)->shouldBeLike(new FieldSort('raw_name', 'asc')); + $this->create($ordering)->shouldBeLike(new FieldSort('name.raw', 'asc')); } } diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php index 5ea01ed..3926991 100644 --- a/src/Controller/SearchController.php +++ b/src/Controller/SearchController.php @@ -9,29 +9,23 @@ * file that was distributed with this source code. */ -namespace Lakion\SyliusElasticSearchBundle\Controller; +namespace Sylius\ElasticSearchPlugin\Controller; -use FOS\RestBundle\View\ConfigurableViewHandlerInterface; use FOS\RestBundle\View\View; -use Lakion\SyliusElasticSearchBundle\Form\Type\FilterSetType; -use Lakion\SyliusElasticSearchBundle\Search\Criteria\Criteria; -use Lakion\SyliusElasticSearchBundle\Search\Criteria\Filtering\ProductInChannelFilter; -use Lakion\SyliusElasticSearchBundle\Search\Criteria\Filtering\ProductInTaxonFilter; -use Lakion\SyliusElasticSearchBundle\Search\SearchEngineInterface; -use Sylius\Component\Core\Context\ShopperContextInterface; -use Sylius\Component\Taxonomy\Repository\TaxonRepositoryInterface; -use Symfony\Component\Form\FormFactoryInterface; +use FOS\RestBundle\View\ViewHandlerInterface; +use Sylius\ElasticSearchPlugin\Document\Product; +use Sylius\ElasticSearchPlugin\Search\Criteria\Criteria; +use Sylius\ElasticSearchPlugin\Search\SearchEngineInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Exception\HttpException; /** - * @author Arkadiusz Krakowiak + * @author Arkadiusz Krakowiak */ final class SearchController { /** - * @var ConfigurableViewHandlerInterface + * @var ViewHandlerInterface */ private $restViewHandler; @@ -41,39 +35,13 @@ final class SearchController private $searchEngine; /** - * @var FormFactoryInterface - */ - private $formFactory; - - /** - * @var TaxonRepositoryInterface - */ - private $taxonRepository; - - /** - * @var ShopperContextInterface - */ - private $shopperContext; - - /** - * @param ConfigurableViewHandlerInterface $restViewHandler + * @param ViewHandlerInterface $restViewHandler * @param SearchEngineInterface $searchEngine - * @param FormFactoryInterface $formFactory - * @param TaxonRepositoryInterface $taxonRepository - * @param ShopperContextInterface $shopperContext */ - public function __construct( - ConfigurableViewHandlerInterface $restViewHandler, - SearchEngineInterface $searchEngine, - FormFactoryInterface $formFactory, - TaxonRepositoryInterface $taxonRepository, - ShopperContextInterface $shopperContext - ) { + public function __construct(ViewHandlerInterface $restViewHandler, SearchEngineInterface $searchEngine) + { $this->restViewHandler = $restViewHandler; $this->searchEngine = $searchEngine; - $this->formFactory = $formFactory; - $this->taxonRepository = $taxonRepository; - $this->shopperContext = $shopperContext; } /** @@ -81,170 +49,19 @@ public function __construct( * * @return Response */ - public function filterAction(Request $request) + public function __invoke(Request $request) { - $view = View::create(); - if ($this->isHtmlRequest($request)) { - $view->setTemplate($this->getTemplateFromRequest($request)); - } - - $form = $this->formFactory->create( - FilterSetType::class, - Criteria::fromQueryParameters( - $this->getResourceClassFromRequest($request), - [ - 'per_page' => $request->get('per_page'), - new ProductInChannelFilter($this->shopperContext->getChannel()->getCode()) - ] - ), - ['filter_set' => $this->getFilterSetFromRequest($request)] - ); - $form->handleRequest($request); - - /** @var Criteria $criteria */ - $criteria = $form->getData(); - - $result = $this->searchEngine->match($criteria); - $partialResult = $result->getResults($criteria->getPaginating()->getOffset(), $criteria->getPaginating()->getItemsPerPage()); + $content = json_decode($request->getContent(), true); - $view->setData([ - 'products' => $partialResult->toArray(), - 'form' => $form->createView(), - 'criteria' => $criteria, - ]); - - return $this->restViewHandler->handle($view); - } - - /** - * @param string $slug - * @param Request $request - * - * @return Response - */ - public function filterByTaxonAction($slug, Request $request) - { - $view = View::create(); - if ($this->isHtmlRequest($request)) { - $view->setTemplate($this->getTemplateFromRequest($request)); + if (null === $content) { + $content = $request->query->all(); } - $locale = $this->shopperContext->getLocaleCode(); - $taxon = $this->taxonRepository->findOneBySlug($slug, $locale); - $form = $this->formFactory->create( - FilterSetType::class, - Criteria::fromQueryParameters( - $this->getResourceClassFromRequest($request), - ['per_page' => $request->get('per_page')] - ), - ['filter_set' => $taxon->getCode()] - ); - $form->handleRequest($request); - - /** @var Criteria $criteria */ - $criteria = $form->getData(); - $criteria = Criteria::fromQueryParameters( - $criteria->getResourceAlias(), - array_merge($criteria->getFiltering()->getFields(), [ - new ProductInTaxonFilter($taxon->getCode()), - new ProductInChannelFilter($this->shopperContext->getChannel()->getCode()) - ] - ) - ); + $criteria = Criteria::fromQueryParameters(Product::class, $content); $result = $this->searchEngine->match($criteria); - $partialResult = $result->getResults($criteria->getPaginating()->getOffset(), $criteria->getPaginating()->getItemsPerPage()); - - $view->setData([ - 'products' => $partialResult->toArray(), - 'form' => $form->createView(), - 'criteria' => $criteria, - ]); - - return $this->restViewHandler->handle($view); - } - - /** - * @param string $filterSetName - * - * @return Response - */ - public function renderFilterSetAction(Request $request, $filterSetName) - { - $view = View::create(); - $view->setTemplate($this->getTemplateFromRequest($request)); - if (!$this->isHtmlRequest($request)) { - throw new HttpException(Response::HTTP_BAD_REQUEST); - } - - $form = $this->formFactory->create( - FilterSetType::class, - null, - ['filter_set' => $filterSetName] - ); - - $view->setData([ - 'form' => $form->createView(), - ]); - - return $this->restViewHandler->handle($view); - } - - /** - * @param Request $request - * - * @return string - */ - private function getTemplateFromRequest(Request $request) - { - $syliusAttributes = $request->attributes->get('_sylius'); - - if (!isset($syliusAttributes['template'])) { - throw new HttpException(Response::HTTP_BAD_REQUEST, 'You need to configure template in routing!'); - } - - return $syliusAttributes['template']; - } - - /** - * @param Request $request - * - * @return string - */ - private function getResourceClassFromRequest(Request $request) - { - $syliusAttributes = $request->attributes->get('_sylius'); - - if (!isset($syliusAttributes['resource_class'])) { - throw new HttpException(Response::HTTP_BAD_REQUEST, 'You need to configure resource class in routing!'); - } - - return $syliusAttributes['resource_class']; - } - - /** - * @param Request $request - * - * @return bool - */ - private function isHtmlRequest(Request $request) - { - return !$request->isXmlHttpRequest() && 'html' === $request->getRequestFormat(); - } - - /** - * @param Request $request - * - * @return string - */ - private function getFilterSetFromRequest(Request $request) - { - $syliusAttributes = $request->attributes->get('_sylius'); - - if (!isset($syliusAttributes['filter_set'])) { - throw new HttpException(Response::HTTP_BAD_REQUEST, 'You need to configure filter set in routing!'); - } + $page = $result->take($criteria->paginating()->offset(), $criteria->paginating()->itemsPerPage()); - return $syliusAttributes['filter_set']; + return $this->restViewHandler->handle(View::create($page, Response::HTTP_OK)); } } diff --git a/src/DependencyInjection/Compiler/RegisterFilterTypePass.php b/src/DependencyInjection/Compiler/RegisterFilterTypePass.php index d8b8b89..2609d7d 100644 --- a/src/DependencyInjection/Compiler/RegisterFilterTypePass.php +++ b/src/DependencyInjection/Compiler/RegisterFilterTypePass.php @@ -9,14 +9,14 @@ * file that was distributed with this source code. */ -namespace Lakion\SyliusElasticSearchBundle\DependencyInjection\Compiler; +namespace Sylius\ElasticSearchPlugin\DependencyInjection\Compiler; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; /** - * @author Arkadiusz Krakowiak + * @author Arkadiusz Krakowiak */ final class RegisterFilterTypePass implements CompilerPassInterface { @@ -25,11 +25,11 @@ final class RegisterFilterTypePass implements CompilerPassInterface */ public function process(ContainerBuilder $container) { - if (!$container->hasDefinition('lakion_sylius_elastic_search.form_registry.filters')) { + if (!$container->hasDefinition('sylius_elastic_search.form_registry.filters')) { return; } - $registry = $container->getDefinition('lakion_sylius_elastic_search.form_registry.filters'); + $registry = $container->getDefinition('sylius_elastic_search.form_registry.filters'); foreach ($container->findTaggedServiceIds('filter.type') as $filterTypeServiceId => $filterTypeTag) { diff --git a/src/DependencyInjection/Compiler/RegisterSearchCriteriaApplicatorPass.php b/src/DependencyInjection/Compiler/RegisterSearchCriteriaApplicatorPass.php index 40fc11f..06cc4d3 100644 --- a/src/DependencyInjection/Compiler/RegisterSearchCriteriaApplicatorPass.php +++ b/src/DependencyInjection/Compiler/RegisterSearchCriteriaApplicatorPass.php @@ -9,14 +9,14 @@ * file that was distributed with this source code. */ -namespace Lakion\SyliusElasticSearchBundle\DependencyInjection\Compiler; +namespace Sylius\ElasticSearchPlugin\DependencyInjection\Compiler; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; /** - * @author Arkadiusz Krakowiak + * @author Arkadiusz Krakowiak */ final class RegisterSearchCriteriaApplicatorPass implements CompilerPassInterface { @@ -25,17 +25,13 @@ final class RegisterSearchCriteriaApplicatorPass implements CompilerPassInterfac */ public function process(ContainerBuilder $container) { - if (!$container->hasDefinition('lakion_sylius_elastic_search.search.elastic_engine')) { + if (!$container->hasDefinition('sylius_elastic_search.search.elastic_engine')) { return; } foreach ($container->findTaggedServiceIds('search_criteria_applicator') as $taggedServiceId => $taggedServiceConfig) { - if (!isset($taggedServiceConfig[0]['applies'])) { - throw new \InvalidArgumentException(sprintf('Applicator "%s" does not have applies attribute', $taggedServiceId)); - } - - $engineDefinition = $container->getDefinition('lakion_sylius_elastic_search.search.elastic_engine'); - $engineDefinition->addMethodCall('addSearchCriteriaApplicator', [new Reference($taggedServiceId), $taggedServiceConfig[0]['applies']]); + $engineDefinition = $container->getDefinition('sylius_elastic_search.search.elastic_engine'); + $engineDefinition->addMethodCall('addSearchCriteriaApplicator', [new Reference($taggedServiceId)]); } } } diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index d8d826d..c9912b6 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -9,14 +9,14 @@ * file that was distributed with this source code. */ -namespace Lakion\SyliusElasticSearchBundle\DependencyInjection; +namespace Sylius\ElasticSearchPlugin\DependencyInjection; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; /** - * @author Arkadiusz Krakowiak + * @author Arkadiusz Krakowiak */ final class Configuration implements ConfigurationInterface { @@ -26,7 +26,7 @@ final class Configuration implements ConfigurationInterface public function getConfigTreeBuilder() { $treeBuilder = new TreeBuilder(); - $rootNode = $treeBuilder->root('lakion_sylius_elastic_search'); + $rootNode = $treeBuilder->root('sylius_elastic_search'); $this->buildFilterSetNode($rootNode); diff --git a/src/DependencyInjection/LakionSyliusElasticSearchExtension.php b/src/DependencyInjection/LakionSyliusElasticSearchExtension.php deleted file mode 100644 index 677ca09..0000000 --- a/src/DependencyInjection/LakionSyliusElasticSearchExtension.php +++ /dev/null @@ -1,36 +0,0 @@ -load('services.xml'); - $this->createFilterSetsParameter($config, $container); - } - - /** - * @param array $config - * @param ContainerBuilder $container - */ - private function createFilterSetsParameter(array $config, ContainerBuilder $container) - { - $container->setParameter('lakion_sylius_elastic_search.filter_sets', reset($config)['filter_sets']); - } -} diff --git a/src/DependencyInjection/SyliusElasticSearchExtension.php b/src/DependencyInjection/SyliusElasticSearchExtension.php new file mode 100644 index 0000000..c3419ea --- /dev/null +++ b/src/DependencyInjection/SyliusElasticSearchExtension.php @@ -0,0 +1,23 @@ +processConfiguration($this->getConfiguration([], $container), $configs); + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + + $loader->load('services.xml'); + $container->setParameter('sylius_elastic_search.filter_sets', $config['filter_sets']); + } +} diff --git a/src/Document/Attribute.php b/src/Document/Attribute.php new file mode 100644 index 0000000..c3bc094 --- /dev/null +++ b/src/Document/Attribute.php @@ -0,0 +1,57 @@ +code; + } + + /** + * @param string $code + */ + public function setCode($code) + { + $this->code = $code; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @param string $name + */ + public function setName($name) + { + $this->name = $name; + } +} diff --git a/src/Document/AttributeValue.php b/src/Document/AttributeValue.php new file mode 100644 index 0000000..75c4dc1 --- /dev/null +++ b/src/Document/AttributeValue.php @@ -0,0 +1,80 @@ +code; + } + + /** + * @param string $code + */ + public function setCode($code) + { + $this->code = $code; + } + + /** + * @return string + */ + public function getValue() + { + return $this->value; + } + + /** + * @param string $value + */ + public function setValue($value) + { + $this->value = $value; + } + + /** + * @return Attribute + */ + public function getAttribute() + { + return $this->attribute; + } + + /** + * @param Attribute $attribute + */ + public function setAttribute(Attribute $attribute) + { + $this->attribute = $attribute; + } +} diff --git a/src/Document/Price.php b/src/Document/Price.php new file mode 100644 index 0000000..d1aa991 --- /dev/null +++ b/src/Document/Price.php @@ -0,0 +1,57 @@ +amount; + } + + /** + * @param int $amount + */ + public function setAmount($amount) + { + $this->amount = $amount; + } + + /** + * @return string + */ + public function getCurrency() + { + return $this->currency; + } + + /** + * @param string $currency + */ + public function setCurrency($currency) + { + $this->currency = $currency; + } +} diff --git a/src/Document/Product.php b/src/Document/Product.php new file mode 100644 index 0000000..eb435b3 --- /dev/null +++ b/src/Document/Product.php @@ -0,0 +1,258 @@ +attributeValues = new Collection(); + $this->taxonCodes = new Collection(); + } + + /** + * @return string + */ + public function getCode() + { + return $this->code; + } + + /** + * @param string $code + */ + public function setCode($code) + { + $this->code = $code; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @param string $name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * @return string + */ + public function getChannelCode() + { + return $this->channelCode; + } + + /** + * @param string $channelCode + */ + public function setChannelCode($channelCode) + { + $this->channelCode = $channelCode; + } + + /** + * @return string + */ + public function getLocaleCode() + { + return $this->localeCode; + } + + /** + * @param string $localeCode + */ + public function setLocaleCode($localeCode) + { + $this->localeCode = $localeCode; + } + + /** + * @return string + */ + public function getDescription() + { + return $this->description; + } + + /** + * @param string $description + */ + public function setDescription($description) + { + $this->description = $description; + } + + /** + * @return Price + */ + public function getPrice() + { + return $this->price; + } + + /** + * @param Price $price + */ + public function setPrice(Price $price) + { + $this->price = $price; + } + + /** + * @return TaxonCode + */ + public function getMainTaxonCode() + { + return $this->mainTaxonCode; + } + + /** + * @param TaxonCode $mainTaxonCode + */ + public function setMainTaxonCode(TaxonCode $mainTaxonCode) + { + $this->mainTaxonCode = $mainTaxonCode; + } + + /** + * @return Collection + */ + public function getTaxonCodes() + { + return $this->taxonCodes; + } + + /** + * @param Collection $taxonCodes + */ + public function setTaxonCodes(Collection $taxonCodes) + { + $this->taxonCodes = $taxonCodes; + } + + /** + * @return Collection + */ + public function getAttributeValues() + { + return $this->attributeValues; + } + + /** + * @param Collection $attributeValues + */ + public function setAttributeValues(Collection $attributeValues) + { + $this->attributeValues = $attributeValues; + } + + /** + * @return \DateTime + */ + public function getCreatedAt() + { + return $this->createdAt; + } + + /** + * @param \DateTime $createdAt + */ + public function setCreatedAt(\DateTime $createdAt) + { + $this->createdAt = $createdAt; + } +} diff --git a/src/Document/TaxonCode.php b/src/Document/TaxonCode.php new file mode 100644 index 0000000..744a16d --- /dev/null +++ b/src/Document/TaxonCode.php @@ -0,0 +1,34 @@ +value = $value; + } + + /** + * @return string + */ + public function getValue() + { + return $this->value; + } +} diff --git a/src/Event/ProductCreated.php b/src/Event/ProductCreated.php new file mode 100644 index 0000000..d395d97 --- /dev/null +++ b/src/Event/ProductCreated.php @@ -0,0 +1,39 @@ +product = $product; + } + + /** + * @param ProductInterface $product + * + * @return self + */ + public static function occur(ProductInterface $product) + { + return new self($product); + } + + /** + * @return ProductInterface + */ + public function product() + { + return $this->product; + } +} diff --git a/src/EventListener/ProductPublisher.php b/src/EventListener/ProductPublisher.php new file mode 100644 index 0000000..1fcc351 --- /dev/null +++ b/src/EventListener/ProductPublisher.php @@ -0,0 +1,37 @@ +eventBus = $eventBus; + } + + /** + * @param LifecycleEventArgs $event + */ + public function postPersist(LifecycleEventArgs $event) + { + $product = $event->getEntity(); + if ($product instanceof ProductInterface) { + if ($product->isSimple()) { + $this->eventBus->handle(ProductCreated::occur($product)); + } + } + } +} diff --git a/src/Exception/FilterSetConfigurationNotFoundException.php b/src/Exception/FilterSetConfigurationNotFoundException.php index 1cc8fc6..81b4335 100644 --- a/src/Exception/FilterSetConfigurationNotFoundException.php +++ b/src/Exception/FilterSetConfigurationNotFoundException.php @@ -1,9 +1,9 @@ + * @author Arkadiusz Krakowiak */ final class FilterSetConfigurationNotFoundException extends \RuntimeException { diff --git a/src/Exception/MissingQueryParameterException.php b/src/Exception/MissingQueryParameterException.php index b8a7bcf..dc49b9f 100644 --- a/src/Exception/MissingQueryParameterException.php +++ b/src/Exception/MissingQueryParameterException.php @@ -9,10 +9,10 @@ * file that was distributed with this source code. */ -namespace Lakion\SyliusElasticSearchBundle\Exception; +namespace Sylius\ElasticSearchPlugin\Exception; /** - * @author Arkadiusz Krakowiak + * @author Arkadiusz Krakowiak */ final class MissingQueryParameterException extends \RuntimeException { diff --git a/src/Factory/ProductFactory.php b/src/Factory/ProductFactory.php new file mode 100644 index 0000000..65fc4c6 --- /dev/null +++ b/src/Factory/ProductFactory.php @@ -0,0 +1,99 @@ +isSimple()) { + throw new \InvalidArgumentException(sprintf( + 'Cannot create elastic search model from configurable product "%s" via this method.', + $syliusProduct->getCode() + )); + } + + /** @var ProductVariantInterface $productVariant */ + $productVariant = $syliusProduct->getVariants()->first(); + + /** @var ProductTranslationInterface|TranslationInterface $productTranslation */ + $productTranslation = $syliusProduct->getTranslation($locale->getCode()); + $channelPrice = $productVariant->getChannelPricingForChannel($channel); + $syliusProductAttributes = $syliusProduct->getAttributesByLocale( + $locale->getCode(), + $channel->getDefaultLocale()->getCode() + ); + $syliusProductTaxons = $syliusProduct->getProductTaxons(); + + $product = new Product(); + $price = new Price(); + $taxonCode = new TaxonCode(); + $taxonCode->setValue($syliusProduct->getMainTaxon()->getCode()); + + $price->setAmount($channelPrice->getPrice()); + $price->setCurrency($channel->getBaseCurrency()->getCode()); + + $product->setLocaleCode($locale->getCode()); + $product->setName($productTranslation->getName()); + $product->setDescription($productTranslation->getDescription()); + $product->setChannelCode($channel->getCode()); + $product->setPrice($price); + $product->setCode($syliusProduct->getCode()); + $product->setCreatedAt($syliusProduct->getCreatedAt()); + $product->setMainTaxonCode($taxonCode); + + $productTaxonCodes = []; + foreach ($syliusProductTaxons as $syliusProductTaxon) { + $productTaxonCode = new TaxonCode(); + $productTaxonCode->setValue($syliusProductTaxon->getTaxon()->getCode()); + $productTaxonCodes[] = $productTaxonCode; + } + $product->setTaxonCodes(new Collection($productTaxonCodes)); + + $productAttributeValues = []; + foreach ($syliusProductAttributes as $syliusProductAttributeValue) { + $productAttributeValue = new AttributeValue(); + $productAttributeValue->setCode($syliusProductAttributeValue->getCode()); + $productAttributeValue->setValue($syliusProductAttributeValue->getValue()); + + $attribute = new Attribute(); + $attribute->setCode($syliusProductAttributeValue->getAttribute()->getCode()); + $attribute->setName($syliusProductAttributeValue->getAttribute()->getName()); + $productAttributeValue->setAttribute($attribute); + + $productAttributeValues[] = $productAttributeValue; + } + $productAttributeValues = new Collection($productAttributeValues); + $product->setAttributeValues($productAttributeValues); + + return $product; + } +} diff --git a/src/Factory/ProductFactoryInterface.php b/src/Factory/ProductFactoryInterface.php new file mode 100644 index 0000000..01281ec --- /dev/null +++ b/src/Factory/ProductFactoryInterface.php @@ -0,0 +1,29 @@ + + * @author Arkadiusz Krakowiak */ final class Filter { diff --git a/src/Form/Configuration/FilterSet.php b/src/Form/Configuration/FilterSet.php index 95fea84..de35680 100644 --- a/src/Form/Configuration/FilterSet.php +++ b/src/Form/Configuration/FilterSet.php @@ -9,10 +9,10 @@ * file that was distributed with this source code. */ -namespace Lakion\SyliusElasticSearchBundle\Form\Configuration; +namespace Sylius\ElasticSearchPlugin\Form\Configuration; /** - * @author Arkadiusz Krakowiak + * @author Arkadiusz Krakowiak */ final class FilterSet { diff --git a/src/Form/Configuration/Provider/FilterSetProvider.php b/src/Form/Configuration/Provider/FilterSetProvider.php index 29bc8b6..44f018f 100644 --- a/src/Form/Configuration/Provider/FilterSetProvider.php +++ b/src/Form/Configuration/Provider/FilterSetProvider.php @@ -1,17 +1,17 @@ + * @author Arkadiusz Krakowiak */ final class FilterSetProvider implements FilterSetProviderInterface { /** - * @var FilterSetProviderInterface[] + * @var PriorityQueue */ private $filterSetProviders; @@ -34,6 +34,7 @@ public function addFilterSetProvider(FilterSetProviderInterface $filterSetProvid */ public function getFilterSetConfiguration($filterSetName) { + /** @var FilterSetProviderInterface $filterSetProvider */ foreach ($this->filterSetProviders as $filterSetProvider) { try { return $filterSetProvider->getFilterSetConfiguration($filterSetName); diff --git a/src/Form/Configuration/Provider/FilterSetProviderInterface.php b/src/Form/Configuration/Provider/FilterSetProviderInterface.php index 1946e6d..c33cc80 100644 --- a/src/Form/Configuration/Provider/FilterSetProviderInterface.php +++ b/src/Form/Configuration/Provider/FilterSetProviderInterface.php @@ -9,13 +9,13 @@ * file that was distributed with this source code. */ -namespace Lakion\SyliusElasticSearchBundle\Form\Configuration\Provider; +namespace Sylius\ElasticSearchPlugin\Form\Configuration\Provider; -use Lakion\SyliusElasticSearchBundle\Exception\FilterSetConfigurationNotFoundException; -use Lakion\SyliusElasticSearchBundle\Form\Configuration\FilterSet; +use Sylius\ElasticSearchPlugin\Exception\FilterSetConfigurationNotFoundException; +use Sylius\ElasticSearchPlugin\Form\Configuration\FilterSet; /** - * @author Arkadiusz Krakowiak + * @author Arkadiusz Krakowiak */ interface FilterSetProviderInterface { diff --git a/src/Form/Configuration/Provider/FromArrayFilterSetProvider.php b/src/Form/Configuration/Provider/FromArrayFilterSetProvider.php index 27e2b80..53299ed 100644 --- a/src/Form/Configuration/Provider/FromArrayFilterSetProvider.php +++ b/src/Form/Configuration/Provider/FromArrayFilterSetProvider.php @@ -9,13 +9,13 @@ * file that was distributed with this source code. */ -namespace Lakion\SyliusElasticSearchBundle\Form\Configuration\Provider; +namespace Sylius\ElasticSearchPlugin\Form\Configuration\Provider; -use Lakion\SyliusElasticSearchBundle\Exception\FilterSetConfigurationNotFoundException; -use Lakion\SyliusElasticSearchBundle\Form\Configuration\FilterSet; +use Sylius\ElasticSearchPlugin\Exception\FilterSetConfigurationNotFoundException; +use Sylius\ElasticSearchPlugin\Form\Configuration\FilterSet; /** - * @author Arkadiusz Krakowiak + * @author Arkadiusz Krakowiak */ final class FromArrayFilterSetProvider implements FilterSetProviderInterface { diff --git a/src/Form/DataMapper/CriteriaDataMapper.php b/src/Form/DataMapper/CriteriaDataMapper.php deleted file mode 100644 index 2166ffa..0000000 --- a/src/Form/DataMapper/CriteriaDataMapper.php +++ /dev/null @@ -1,51 +0,0 @@ - - */ -final class CriteriaDataMapper implements DataMapperInterface -{ - /** - * {@inheritdoc} - */ - public function mapDataToForms($data, $forms) - { - } - - /** - * {@inheritdoc} - */ - public function mapFormsToData($forms, &$data) - { - $forms = iterator_to_array($forms); - $filtering = $data instanceof Criteria ? ['per_page' => $data->getPaginating()->getItemsPerPage()] : []; - - foreach ($forms as $form) { - if (null !== $form->getData()) { - $filtering[] = $form->getData(); - } - } - - $data = Criteria::fromQueryParameters( - $data->getResourceAlias(), - array_merge( - $filtering, - $data instanceof Criteria ? $data->getFiltering()->getFields() : [] - ) - ); - } -} diff --git a/src/Form/Type/FilterSetType.php b/src/Form/Type/FilterSetType.php deleted file mode 100644 index 85bf19a..0000000 --- a/src/Form/Type/FilterSetType.php +++ /dev/null @@ -1,82 +0,0 @@ - - */ -final class FilterSetType extends AbstractType -{ - /** - * @var FormTypeRegistryInterface - */ - private $filterTypeRegistry; - - /** - * @var FilterSetProviderInterface - */ - private $filterSetConfigurationProvider; - - /** - * @param FormTypeRegistryInterface $filterTypeRegistry - * @param FilterSetProviderInterface $filterSetConfigurationProvider - */ - public function __construct( - FormTypeRegistryInterface $filterTypeRegistry, - FilterSetProviderInterface $filterSetConfigurationProvider - ) { - $this->filterTypeRegistry = $filterTypeRegistry; - $this->filterSetConfigurationProvider = $filterSetConfigurationProvider; - } - - /** - * {@inheritdoc} - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - try { - $filters = $this->filterSetConfigurationProvider->getFilterSetConfiguration($options['filter_set'])->getFilters(); - } catch (FilterSetConfigurationNotFoundException $configurationNotFoundException) { - $filters = $this->filterSetConfigurationProvider->getFilterSetConfiguration('default')->getFilters(); - } - - foreach ($filters as $filter) { - $builder->add( - $filter->getName(), - $this->filterTypeRegistry->get('default', $filter->getType()), - $filter->getOptions() - ); - } - - $builder->setDataMapper(new CriteriaDataMapper()); - } - - /** - * {@inheritdoc} - */ - public function configureOptions(OptionsResolver $resolver) - { - $resolver - ->setDefault('csrf_protection', false) - ->setRequired('filter_set') - ->setAllowedTypes('filter_set', 'string') - ; - } -} diff --git a/src/Form/Type/OptionCodeFilterType.php b/src/Form/Type/OptionCodeFilterType.php deleted file mode 100644 index de43926..0000000 --- a/src/Form/Type/OptionCodeFilterType.php +++ /dev/null @@ -1,172 +0,0 @@ - - */ -final class OptionCodeFilterType extends AbstractType implements DataTransformerInterface -{ - /** - * @var RepositoryManagerInterface - */ - private $repositoryManager; - - /** - * @var QueryFactoryInterface - */ - private $productHasOptionCodeQueryFactory; - - /** - * @var string - */ - private $productModelClass; - - /** - * @var SearchFactoryInterface - */ - private $searchFactory; - - /** - * @param RepositoryManagerInterface $repositoryManager - * @param QueryFactoryInterface $productHasOptionCodeQueryFactory - * @param SearchFactoryInterface $searchFactory - */ - public function __construct( - RepositoryManagerInterface $repositoryManager, - QueryFactoryInterface $productHasOptionCodeQueryFactory, - SearchFactoryInterface $searchFactory, - $productModelClass - ) { - $this->repositoryManager = $repositoryManager; - $this->productHasOptionCodeQueryFactory = $productHasOptionCodeQueryFactory; - $this->searchFactory = $searchFactory; - $this->productModelClass = $productModelClass; - } - - /** - * {@inheritdoc} - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - $builder->add('code', EntityType::class, [ - 'class' => $options['class'], - 'choice_value' => function (ProductOptionValue $productOptionValue) { - return $productOptionValue->getCode(); - }, - 'query_builder' => function (EntityRepository $repository) use ($options) { - $queryBuilder = $repository->createQueryBuilder('o'); - - return $queryBuilder - ->leftJoin('o.option', 'option') - ->andWhere('option.code = :optionCode') - ->setParameter('optionCode', $options['option_code']); - }, - 'choice_label' => function (ProductOptionValue $productOptionValue) use ($options) { - - /** @var Repository $repository */ - $repository = $this->repositoryManager->getRepository($this->productModelClass); - $query = $this->buildAggregation($productOptionValue->getCode())->toArray(); - $result = $repository->createPaginatorAdapter($query); - $aggregation = $result->getAggregations(); - $count = $aggregation[$productOptionValue->getCode()]['buckets'][$productOptionValue->getCode()]['doc_count']; - - return sprintf('%s (%s)', $productOptionValue->getValue(), $count); - }, - 'multiple' => true, - 'expanded' => true, - ]); - - $builder->addModelTransformer($this); - } - - /** - * {@inheritdoc} - */ - public function transform($value) - { - if (null === $value) { - return null; - } - - return $value; - } - - /** - * {@inheritdoc} - */ - public function reverseTransform($value) - { - if ($value['code'] instanceof Collection) { - $productOptionCodes = $value['code']->map(function (ProductOptionValueInterface $productOptionValue) { - return $productOptionValue->getCode(); - }); - - if ($productOptionCodes->isEmpty()) { - return null; - } - - return new ProductHasOptionCodesFilter($productOptionCodes->toArray()); - } - - return null; - } - - /** - * {@inheritdoc} - */ - public function configureOptions(OptionsResolver $resolver) - { - $resolver - ->setDefault('class', ProductOptionValue::class) - ->setRequired('option_code') - ->setAllowedTypes('option_code', 'string') - ; - } - - /** - * @param string $code - * - * @return Search - */ - private function buildAggregation($code) - { - $hasOptionValueAggregation = new FiltersAggregation($code); - - $hasOptionValueAggregation->addFilter( - $this->productHasOptionCodeQueryFactory->create(['option_value_code' => $code]), - $code - ); - - $aggregationSearch = $this->searchFactory->create(); - $aggregationSearch->addAggregation($hasOptionValueAggregation); - - return $aggregationSearch; - } -} diff --git a/src/Form/Type/ProductPriceRangeFilterType.php b/src/Form/Type/ProductPriceRangeFilterType.php deleted file mode 100644 index 1d94ffc..0000000 --- a/src/Form/Type/ProductPriceRangeFilterType.php +++ /dev/null @@ -1,62 +0,0 @@ - - */ -final class ProductPriceRangeFilterType extends AbstractType implements DataTransformerInterface -{ - /** - * {@inheritdoc} - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - $builder - ->add('grater_than', MoneyType::class) - ->add('less_than', MoneyType::class) - ->addModelTransformer($this) - ; - } - - /** - * {@inheritdoc} - */ - public function transform($value) - { - if (null === $value) { - return null; - } - - return $value; - } - - /** - * {@inheritdoc} - */ - public function reverseTransform($value) - { - if (null === $value['grater_than'] || null === $value['less_than']) { - return null; - } - - return new ProductInPriceRangeFilter($value['grater_than'], $value['less_than']); - } -} diff --git a/src/Form/Type/SearchType.php b/src/Form/Type/SearchType.php deleted file mode 100644 index f69ee3f..0000000 --- a/src/Form/Type/SearchType.php +++ /dev/null @@ -1,66 +0,0 @@ - - */ -final class SearchType extends AbstractType implements DataTransformerInterface -{ - /** - * {@inheritdoc} - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - $builder - ->addModelTransformer($this) - ; - } - - /** - * {@inheritdoc} - */ - public function getParent() - { - return TextType::class; - } - - /** - * {@inheritdoc} - */ - public function transform($value) - { - if (null === $value) { - return null; - } - - return $value; - } - - /** - * {@inheritdoc} - */ - public function reverseTransform($value) - { - if (null === $value) { - return null; - } - - return new SearchPhrase($value); - } -} diff --git a/src/Projection/ProductProjector.php b/src/Projection/ProductProjector.php new file mode 100644 index 0000000..1b0aacf --- /dev/null +++ b/src/Projection/ProductProjector.php @@ -0,0 +1,56 @@ +manager = $manager; + $this->productDocumentFactory = $productDocumentFactory; + } + + /** + * @param ProductCreated $event + */ + public function handleProductCreated(ProductCreated $event) + { + $product = $event->product(); + $channels = $product->getChannels(); + + /** @var ChannelInterface $channel */ + foreach ($channels as $channel) { + $locales = $channel->getLocales(); + foreach ($locales as $locale) { + $productDocument = $this->productDocumentFactory->createFromSyliusSimpleProductModel( + $product, + $locale, + $channel + ); + + $this->manager->persist($productDocument); + } + } + + $this->manager->commit(); + } +} diff --git a/src/Resources/config/app/config.yml b/src/Resources/config/app/config.yml index 9474c28..856662a 100644 --- a/src/Resources/config/app/config.yml +++ b/src/Resources/config/app/config.yml @@ -1,87 +1,7 @@ -fos_elastica: - clients: +ongr_elasticsearch: + managers: default: - servers: - - { host: 127.0.0.1, port: 9200 } - indexes: - sylius: - client: default - finder: ~ - settings: - analysis: - analyzer: - sylius_code_analyzer: - type: custom - tokenizer: whitespace - filter: [lowercase] - types: - product: - mappings: - id: { type: integer} - code: { type: string, analyzer: sylius_code_analyzer } - name: { type: string } - raw_name: { type: string, index: not_analyzed, property_path: name } - description: { type: string } - shortDescription: { type: string } - slug: { type: string } - translations: - type: "nested" - properties: - locale: { type: string } - name: { type: string } - metaKeywords: { type: string } - metaDescription: { type: string } - variants: - type: "nested" - properties: - code: { type: string } - name: { type: string } - onHand: { type: integer } - onHold: { type: integer } - channelPricings: - type: "nested" - properties: - price: { type: integer } - channelCode: { type: string } - optionValues: - type: "nested" - properties: - name: { type: string } - value: { type: string } - code: { type: string } - attributes: - type: "nested" - properties: - value: { type: string } - channels: - type: "nested" - properties: - code: { type: string, analyzer: sylius_code_analyzer } - productTaxons: - type: "nested" - properties: - taxon: - type: "object" - properties: - code: { type: string } - name: { type: string } - position: { type: integer } - mainTaxon: - type: "object" - properties: - slug: { type: string } - code: { type: string } - name: { type: string } - images: - type: "nested" - properties: - path: { type: string } - type: { type: string } - enabled: { type: boolean } - isSimple: { type: boolean } - persistence: - driver: orm - model: "%sylius.model.product.class%" - provider: ~ - listener: ~ - finder: ~ + index: + index_name: sylius + mappings: + - SyliusElasticSearchPlugin diff --git a/src/Resources/config/routing.yml b/src/Resources/config/routing.yml index d25146e..cb9fb34 100644 --- a/src/Resources/config/routing.yml +++ b/src/Resources/config/routing.yml @@ -1,34 +1,6 @@ -lakion_elastic_search_shop_product_index: - path: /products +sylius_shop_search: + path: /search methods: [GET] defaults: - _controller: lakion_sylius_elastic_search.controller.search:filterAction - _sylius: - template: "@LakionSyliusElasticSearch/Product/index.html.twig" - resource_class: "%sylius.model.product.class%" - filter_set: mug_type_and_stickers_set - requirements: - slug: .+ - -lakion_elastic_search_shop_global_search: - path: /render-filter-set/{filterSetName} - methods: [GET] - defaults: - _controller: lakion_sylius_elastic_search.controller.search:renderFilterSetAction - _sylius: - template: "@LakionSyliusElasticSearch/Product/_globalSearch.html.twig" - resource_class: "%sylius.model.product.class%" - requirements: - slug: .+ - -sylius_shop_product_index: - path: /taxons/{slug} - methods: [GET] - defaults: - _controller: lakion_sylius_elastic_search.controller.search:filterByTaxonAction - _sylius: - template: "@LakionSyliusElasticSearch/Product/indexByTaxon.html.twig" - resource_class: "%sylius.model.product.class%" - filter_set: mug_type_and_stickers_set - requirements: - slug: .+ + _controller: sylius_elastic_search.controller.search + _format: json diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index 5a64c6b..f7c4c70 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -7,13 +7,22 @@ - - - + + + + + + + + + + + + diff --git a/src/Resources/config/services/controller.xml b/src/Resources/config/services/controller.xml index 9a6860b..abfbc3b 100644 --- a/src/Resources/config/services/controller.xml +++ b/src/Resources/config/services/controller.xml @@ -5,12 +5,9 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - + - - - - + diff --git a/src/Resources/config/services/filter_set_configuration_provider.xml b/src/Resources/config/services/filter_set_configuration_provider.xml index 1fc755c..68df48b 100644 --- a/src/Resources/config/services/filter_set_configuration_provider.xml +++ b/src/Resources/config/services/filter_set_configuration_provider.xml @@ -5,11 +5,11 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - + - - %lakion_sylius_elastic_search.filter_sets% + + %sylius_elastic_search.filter_sets% 1 diff --git a/src/Resources/config/services/form.xml b/src/Resources/config/services/form.xml deleted file mode 100644 index 4fa2ba7..0000000 --- a/src/Resources/config/services/form.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - %sylius.model.product.class% - - - - - - - - - diff --git a/src/Resources/config/services/query_factory.xml b/src/Resources/config/services/query_factory.xml index b4e02ea..132a6ae 100644 --- a/src/Resources/config/services/query_factory.xml +++ b/src/Resources/config/services/query_factory.xml @@ -5,13 +5,13 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - - - - - - + + + + + + + + diff --git a/src/Resources/config/services/search_criteria_applicator.xml b/src/Resources/config/services/search_criteria_applicator.xml index e266b71..f8a3bfe 100644 --- a/src/Resources/config/services/search_criteria_applicator.xml +++ b/src/Resources/config/services/search_criteria_applicator.xml @@ -5,31 +5,26 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - + + + + - - - - + + + - - - + + + - - - + + + - - - - - - - - + + + diff --git a/src/Resources/config/services/search_engine.xml b/src/Resources/config/services/search_engine.xml index 9e4be62..b5ea5c5 100644 --- a/src/Resources/config/services/search_engine.xml +++ b/src/Resources/config/services/search_engine.xml @@ -5,9 +5,9 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - + + + diff --git a/src/Resources/views/Product/_globalSearch.html.twig b/src/Resources/views/Product/_globalSearch.html.twig deleted file mode 100644 index 1999514..0000000 --- a/src/Resources/views/Product/_globalSearch.html.twig +++ /dev/null @@ -1,4 +0,0 @@ -{{ form_start(form, { 'action': path('lakion_elastic_search_shop_product_index'), 'method': 'GET'}) }} -{{ form_rest(form) }} - -{{ form_end(form) }} diff --git a/src/Resources/views/Product/index.html.twig b/src/Resources/views/Product/index.html.twig deleted file mode 100644 index bdeec38..0000000 --- a/src/Resources/views/Product/index.html.twig +++ /dev/null @@ -1,36 +0,0 @@ -{% extends '@SyliusShop/layout.html.twig' %} - -{% import '@SyliusUi/Macro/messages.html.twig' as messages %} - -{% block content %} -
-
- {{ form_start(form, {'method': 'GET'}) }} - - {{ form_row(form.search) }} - - {{ form_row(form) }} - - {{ form_end(form) }} -
-
- - {% if products|length > 0 %} -
- {% for product in products %} -
- {% include '@SyliusShop/Product/_box.html.twig' %} -
- {% endfor %} -
- - {% else %} - {{ messages.info('sylius.ui.no_results_to_display') }} - {% endif %} -
-
-{% endblock %} diff --git a/src/Resources/views/Product/indexByTaxon.html.twig b/src/Resources/views/Product/indexByTaxon.html.twig deleted file mode 100644 index c6d25a1..0000000 --- a/src/Resources/views/Product/indexByTaxon.html.twig +++ /dev/null @@ -1,36 +0,0 @@ -{% extends '@SyliusShop/layout.html.twig' %} - -{% import '@SyliusUi/Macro/messages.html.twig' as messages %} - -{% block content %} - {% include '@SyliusShop/Product/Index/_header.html.twig' %} -
-
- {% include '@SyliusShop/Product/Index/_sidebar.html.twig' %} - {{ form_start(form, {'method' : 'GET'}) }} - - {{ form_rest(form) }} - - {{ form_end(form) }} -
-
- - {% if products|length > 0 %} -
- {% for product in products %} -
- {% include '@SyliusShop/Product/_box.html.twig' %} -
- {% endfor %} -
- - {% else %} - {{ messages.info('sylius.ui.no_results_to_display') }} - {% endif %} -
-
-{% endblock %} diff --git a/src/Search/Criteria/Criteria.php b/src/Search/Criteria/Criteria.php index defefae..e0f95ba 100644 --- a/src/Search/Criteria/Criteria.php +++ b/src/Search/Criteria/Criteria.php @@ -1,16 +1,16 @@ + * @author Arkadiusz Krakowiak */ final class Criteria { /** * @var string */ - private $resourceAlias; + private $documentClass; /** * @var Paginating @@ -28,46 +28,46 @@ final class Criteria private $filtering; /** - * @param string $resourceAlias + * @param string $documentClass * @param Paginating $paginating * @param Ordering $ordering * @param Filtering $filtering */ - private function __construct($resourceAlias, Paginating $paginating, Ordering $ordering, Filtering $filtering) + private function __construct($documentClass, Paginating $paginating, Ordering $ordering, Filtering $filtering) { - $this->resourceAlias = $resourceAlias; + $this->documentClass = $documentClass; $this->paginating = $paginating; $this->ordering = $ordering; $this->filtering = $filtering; } /** - * @param $resourceAlias + * @param string $documentClass * @param array $parameters * * @return Criteria */ - public static function fromQueryParameters($resourceAlias, array $parameters) + public static function fromQueryParameters($documentClass, array $parameters) { $paginating = Paginating::fromQueryParameters($parameters); $ordering = Ordering::fromQueryParameters($parameters); $filtering = Filtering::fromQueryParameters($parameters); - return new self($resourceAlias, $paginating, $ordering, $filtering); + return new self($documentClass, $paginating, $ordering, $filtering); } /** * @return string */ - public function getResourceAlias() + public function documentClass() { - return $this->resourceAlias; + return $this->documentClass; } /** * @return Paginating */ - public function getPaginating() + public function paginating() { return $this->paginating; } @@ -75,7 +75,7 @@ public function getPaginating() /** * @return Ordering */ - public function getOrdering() + public function ordering() { return $this->ordering; } @@ -83,7 +83,7 @@ public function getOrdering() /** * @return Filtering */ - public function getFiltering() + public function filtering() { return $this->filtering; } diff --git a/src/Search/Criteria/Filtering.php b/src/Search/Criteria/Filtering.php index eb0baac..b372504 100644 --- a/src/Search/Criteria/Filtering.php +++ b/src/Search/Criteria/Filtering.php @@ -1,9 +1,9 @@ + * @author Arkadiusz Krakowiak */ final class Filtering { @@ -30,7 +30,7 @@ public static function fromQueryParameters(array $queryParameters) $fields = $queryParameters; unset($fields['page']); - unset($fields['per_page']); + unset($fields['limit']); unset($fields['sort']); return new self($fields); @@ -39,7 +39,7 @@ public static function fromQueryParameters(array $queryParameters) /** * @return array */ - public function getFields() + public function fields() { return $this->fields; } diff --git a/src/Search/Criteria/Filtering/ProductHasOptionCodesFilter.php b/src/Search/Criteria/Filtering/ProductHasOptionCodesFilter.php deleted file mode 100644 index 845dab9..0000000 --- a/src/Search/Criteria/Filtering/ProductHasOptionCodesFilter.php +++ /dev/null @@ -1,39 +0,0 @@ - - */ -final class ProductHasOptionCodesFilter -{ - /** - * @var array - */ - private $codes; - - /** - * @param array $codes - */ - public function __construct(array $codes) - { - $this->codes = $codes; - } - - /** - * @return array - */ - public function getCodes() - { - return $this->codes; - } -} diff --git a/src/Search/Criteria/Filtering/ProductInChannelFilter.php b/src/Search/Criteria/Filtering/ProductInChannelFilter.php deleted file mode 100644 index b48f480..0000000 --- a/src/Search/Criteria/Filtering/ProductInChannelFilter.php +++ /dev/null @@ -1,30 +0,0 @@ - - */ -final class ProductInChannelFilter -{ - /** - * @var string - */ - private $channelCode; - - /** - * @param string $channelCode - */ - public function __construct($channelCode) - { - $this->channelCode = $channelCode; - } - - /** - * @return string - */ - public function getChannelCode() - { - return $this->channelCode; - } -} diff --git a/src/Search/Criteria/Filtering/ProductInPriceRangeFilter.php b/src/Search/Criteria/Filtering/ProductInPriceRangeFilter.php deleted file mode 100644 index 15e6ba2..0000000 --- a/src/Search/Criteria/Filtering/ProductInPriceRangeFilter.php +++ /dev/null @@ -1,54 +0,0 @@ - - */ -final class ProductInPriceRangeFilter -{ - /** - * @var int - */ - private $graterThan; - - /** - * @var int - */ - private $lessThan; - - /** - * @param int $graterThan - * @param int $lessThan - */ - public function __construct($graterThan, $lessThan) - { - $this->graterThan = $graterThan; - $this->lessThan = $lessThan; - } - - /** - * @return int - */ - public function getGraterThan() - { - return $this->graterThan; - } - - /** - * @return int - */ - public function getLessThan() - { - return $this->lessThan; - } -} diff --git a/src/Search/Criteria/Filtering/ProductInTaxonFilter.php b/src/Search/Criteria/Filtering/ProductInTaxonFilter.php deleted file mode 100644 index 0ed28f2..0000000 --- a/src/Search/Criteria/Filtering/ProductInTaxonFilter.php +++ /dev/null @@ -1,30 +0,0 @@ - - */ -final class ProductInTaxonFilter -{ - /** - * @var string - */ - private $taxonCode; - - /** - * @param string $taxonCode - */ - public function __construct($taxonCode) - { - $this->taxonCode = $taxonCode; - } - - /** - * @return string - */ - public function getTaxonCode() - { - return $this->taxonCode; - } -} diff --git a/src/Search/Criteria/Ordering.php b/src/Search/Criteria/Ordering.php index e54e8a3..ac0e884 100644 --- a/src/Search/Criteria/Ordering.php +++ b/src/Search/Criteria/Ordering.php @@ -1,9 +1,9 @@ + * @author Arkadiusz Krakowiak */ final class Ordering { @@ -28,7 +28,7 @@ final class Ordering */ private function __construct($field, $direction) { - $this->field = $field; + $this->field = $field.'.raw'; $this->direction = $direction; } @@ -53,7 +53,7 @@ public static function fromQueryParameters(array $parameters) /** * @return string */ - public function getField() + public function field() { return $this->field; } @@ -61,7 +61,7 @@ public function getField() /** * @return string */ - public function getDirection() + public function direction() { return $this->direction; } diff --git a/src/Search/Criteria/Paginating.php b/src/Search/Criteria/Paginating.php index 8b5917a..78cdfce 100644 --- a/src/Search/Criteria/Paginating.php +++ b/src/Search/Criteria/Paginating.php @@ -1,14 +1,14 @@ + * @author Arkadiusz Krakowiak */ final class Paginating { const DEFAULT_CURRENT_PAGE = 1; - const DEFAULT_ITEMS_PER_PAGE = 10; + const DEFAULT_ITEMS_LIMIT = 10; /** * @var int @@ -38,7 +38,7 @@ private function __construct($currentPage, $itemsPerPage) $this->itemsPerPage = (int) $itemsPerPage; if (0 >= $itemsPerPage) { - $this->itemsPerPage = self::DEFAULT_ITEMS_PER_PAGE; + $this->itemsPerPage = self::DEFAULT_ITEMS_LIMIT; } $this->offset = $this->currentPage * $this->itemsPerPage - $this->itemsPerPage; @@ -52,7 +52,7 @@ private function __construct($currentPage, $itemsPerPage) public static function fromQueryParameters(array $parameters) { $currentPage = isset($parameters['page']) ? $parameters['page'] : self::DEFAULT_CURRENT_PAGE; - $itemsPerPage = isset($parameters['per_page']) ? $parameters['per_page'] : self::DEFAULT_ITEMS_PER_PAGE; + $itemsPerPage = isset($parameters['limit']) ? $parameters['limit'] : self::DEFAULT_ITEMS_LIMIT; return new self($currentPage, $itemsPerPage); } @@ -60,7 +60,7 @@ public static function fromQueryParameters(array $parameters) /** * @return int */ - public function getCurrentPage() + public function currentPage() { return $this->currentPage; } @@ -68,7 +68,7 @@ public function getCurrentPage() /** * @return int */ - public function getItemsPerPage() + public function itemsPerPage() { return $this->itemsPerPage; } @@ -76,7 +76,7 @@ public function getItemsPerPage() /** * @return int */ - public function getOffset() + public function offset() { return $this->offset; } diff --git a/src/Search/Criteria/SearchPhrase.php b/src/Search/Criteria/SearchPhrase.php deleted file mode 100644 index 56f08b0..0000000 --- a/src/Search/Criteria/SearchPhrase.php +++ /dev/null @@ -1,39 +0,0 @@ - - */ -final class SearchPhrase -{ - /** - * @var string - */ - private $phrase; - - /** - * @param string $phrase - */ - public function __construct($phrase) - { - $this->phrase = $phrase; - } - - /** - * @return string - */ - public function getPhrase() - { - return $this->phrase; - } -} diff --git a/src/Search/Elastic/Applicator/Filter/ProductHasMultipleOptionCodesApplicator.php b/src/Search/Elastic/Applicator/Filter/ProductHasMultipleOptionCodesApplicator.php deleted file mode 100644 index 2469e37..0000000 --- a/src/Search/Elastic/Applicator/Filter/ProductHasMultipleOptionCodesApplicator.php +++ /dev/null @@ -1,42 +0,0 @@ - - */ -final class ProductHasMultipleOptionCodesApplicator extends SearchCriteriaApplicator -{ - /** - * @var QueryFactoryInterface - */ - private $productHasOptionCodeQueryFactory; - - /** - * {@inheritdoc} - */ - public function __construct(QueryFactoryInterface $productHasOptionCodeQueryFactory) - { - $this->productHasOptionCodeQueryFactory = $productHasOptionCodeQueryFactory; - } - - /** - * @param ProductHasOptionCodesFilter $codesFilter - * @param Search $search - */ - public function applyProductHasOptionCodesFilter(ProductHasOptionCodesFilter $codesFilter, Search $search) - { - foreach ($codesFilter->getCodes() as $code) { - $search->addFilter( - $this->productHasOptionCodeQueryFactory->create(['option_value_code' => $code]), - BoolQuery::SHOULD - ); - } - } -} diff --git a/src/Search/Elastic/Applicator/Filter/ProductInChannelApplicator.php b/src/Search/Elastic/Applicator/Filter/ProductInChannelApplicator.php index 127527e..962ff49 100644 --- a/src/Search/Elastic/Applicator/Filter/ProductInChannelApplicator.php +++ b/src/Search/Elastic/Applicator/Filter/ProductInChannelApplicator.php @@ -1,17 +1,17 @@ + * @author Arkadiusz Krakowiak */ -final class ProductInChannelApplicator extends SearchCriteriaApplicator +final class ProductInChannelApplicator implements SearchCriteriaApplicatorInterface { /** * @var QueryFactoryInterface @@ -29,8 +29,19 @@ public function __construct(QueryFactoryInterface $productInChannelQueryFactory) /** * {@inheritdoc} */ - public function applyProductInChannelFilter(ProductInChannelFilter $inChannelFilter, Search $search) + public function apply(Criteria $criteria, Search $search) { - $search->addFilter($this->productInChannelQueryFactory->create(['channel_code' => $inChannelFilter->getChannelCode()]), BoolQuery::MUST); + $search->addPostFilter($this->productInChannelQueryFactory->create($criteria->filtering()->fields()), BoolQuery::MUST); + } + + /** + * {@inheritdoc} + */ + public function supports(Criteria $criteria) + { + return + array_key_exists('channel_code', $criteria->filtering()->fields()) && + null !== $criteria->filtering()->fields()['channel_code'] + ; } } diff --git a/src/Search/Elastic/Applicator/Filter/ProductInPriceRangeApplicator.php b/src/Search/Elastic/Applicator/Filter/ProductInPriceRangeApplicator.php index 0766e16..de9256d 100644 --- a/src/Search/Elastic/Applicator/Filter/ProductInPriceRangeApplicator.php +++ b/src/Search/Elastic/Applicator/Filter/ProductInPriceRangeApplicator.php @@ -1,17 +1,17 @@ + * @author Arkadiusz Krakowiak */ -final class ProductInPriceRangeApplicator extends SearchCriteriaApplicator +final class ProductInPriceRangeApplicator implements SearchCriteriaApplicatorInterface { /** * @var QueryFactoryInterface @@ -29,16 +29,25 @@ public function __construct(QueryFactoryInterface $productInPriceRangeQueryFacto /** * {@inheritdoc} */ - public function applyProductInPriceRangeFilter(ProductInPriceRangeFilter $inPriceRangeFilter, Search $search) + public function apply(Criteria $criteria, Search $search) { - $search->addFilter( - $this->productInPriceRangeQueryFactory->create([ - 'product_price_range' => [ - 'grater_than' => $inPriceRangeFilter->getGraterThan(), - 'less_than' => $inPriceRangeFilter->getLessThan() - ] - ]), + $search->addPostFilter( + $this->productInPriceRangeQueryFactory->create($criteria->filtering()->fields()), BoolQuery::MUST ); } + + /** + * {@inheritdoc} + */ + public function supports(Criteria $criteria) + { + return + array_key_exists('product_price_range', $criteria->filtering()->fields()) && + array_key_exists('grater_than', $criteria->filtering()->fields()['product_price_range']) && + array_key_exists('less_than', $criteria->filtering()->fields()['product_price_range']) && + null != $criteria->filtering()->fields()['product_price_range']['grater_than'] && + null != $criteria->filtering()->fields()['product_price_range']['less_than'] + ; + } } diff --git a/src/Search/Elastic/Applicator/Filter/ProductInTaxonApplicator.php b/src/Search/Elastic/Applicator/Filter/ProductInTaxonApplicator.php index 6c86b1a..0f9c9cb 100644 --- a/src/Search/Elastic/Applicator/Filter/ProductInTaxonApplicator.php +++ b/src/Search/Elastic/Applicator/Filter/ProductInTaxonApplicator.php @@ -1,19 +1,17 @@ + * @author Arkadiusz Krakowiak */ -final class ProductInTaxonApplicator extends SearchCriteriaApplicator +final class ProductInTaxonApplicator implements SearchCriteriaApplicatorInterface { /** * @var QueryFactoryInterface @@ -21,28 +19,32 @@ final class ProductInTaxonApplicator extends SearchCriteriaApplicator private $productInMainTaxonQueryFactory; /** - * @var QueryFactoryInterface + * @param QueryFactoryInterface $productInMainTaxonQueryFactory */ - private $productInProductTaxonsQueryFactory; + public function __construct(QueryFactoryInterface $productInMainTaxonQueryFactory) + { + $this->productInMainTaxonQueryFactory = $productInMainTaxonQueryFactory; + } /** - * @param QueryFactoryInterface $productInMainTaxonQueryFactory - * @param QueryFactoryInterface $productInProductTaxonsQueryFactory + * {@inheritdoc} */ - public function __construct( - QueryFactoryInterface $productInMainTaxonQueryFactory, - QueryFactoryInterface $productInProductTaxonsQueryFactory - ) { - $this->productInMainTaxonQueryFactory = $productInMainTaxonQueryFactory; - $this->productInProductTaxonsQueryFactory = $productInProductTaxonsQueryFactory; + public function apply(Criteria $criteria, Search $search) + { + $search->addPostFilter($this->productInMainTaxonQueryFactory->create( + $criteria->filtering()->fields()), + BoolQuery::SHOULD + ); } /** * {@inheritdoc} */ - public function applyProductInTaxonFilter(ProductInTaxonFilter $inTaxonFilter, Search $search) + public function supports(Criteria $criteria) { - $search->addFilter($this->productInMainTaxonQueryFactory->create(['taxon_code' => $inTaxonFilter->getTaxonCode()]), BoolQuery::SHOULD); - $search->addFilter($this->productInProductTaxonsQueryFactory->create(['taxon_code' => $inTaxonFilter->getTaxonCode()]), BoolQuery::SHOULD); + return + array_key_exists('taxon_code', $criteria->filtering()->fields()) && + null != $criteria->filtering()->fields()['taxon_code'] + ; } } diff --git a/src/Search/Elastic/Applicator/Query/MatchProductByNameApplicator.php b/src/Search/Elastic/Applicator/Query/MatchProductByNameApplicator.php index af64fbd..f156977 100644 --- a/src/Search/Elastic/Applicator/Query/MatchProductByNameApplicator.php +++ b/src/Search/Elastic/Applicator/Query/MatchProductByNameApplicator.php @@ -1,52 +1,46 @@ + * @author Arkadiusz Krakowiak */ -final class MatchProductByNameApplicator extends SearchCriteriaApplicator +final class MatchProductByNameApplicator implements SearchCriteriaApplicatorInterface { /** * @var QueryFactoryInterface */ private $matchProductNameQueryFactory; - /** - * @var QueryFactoryInterface - */ - private $emptyCriteriaQueryFactory; - /** * @param QueryFactoryInterface $matchProductNameQueryFactory - * @param QueryFactoryInterface $emptyCriteriaQueryFactory */ - public function __construct( - QueryFactoryInterface $matchProductNameQueryFactory, - QueryFactoryInterface $emptyCriteriaQueryFactory - ) { + public function __construct(QueryFactoryInterface $matchProductNameQueryFactory) + { $this->matchProductNameQueryFactory = $matchProductNameQueryFactory; - $this->emptyCriteriaQueryFactory = $emptyCriteriaQueryFactory; } /** * {@inheritdoc} */ - public function applySearchPhrase(SearchPhrase $searchPhrase, Search $search) + public function apply(Criteria $criteria, Search $search) { - if (null != $searchPhrase->getPhrase()) { - $search->addQuery($this->matchProductNameQueryFactory->create(['phrase' => $searchPhrase->getPhrase()])); - - return; - } + $search->addQuery($this->matchProductNameQueryFactory->create($criteria->filtering()->fields())); + } - $search->addQuery($this->emptyCriteriaQueryFactory->create()); + /** + * {@inheritdoc} + */ + public function supports(Criteria $criteria) + { + return + array_key_exists('search', $criteria->filtering()->fields()) && + null != $criteria->filtering()->fields()['search'] + ; } } diff --git a/src/Search/Elastic/Applicator/SearchCriteriaApplicator.php b/src/Search/Elastic/Applicator/SearchCriteriaApplicator.php deleted file mode 100644 index 38723b9..0000000 --- a/src/Search/Elastic/Applicator/SearchCriteriaApplicator.php +++ /dev/null @@ -1,52 +0,0 @@ - - */ -abstract class SearchCriteriaApplicator implements SearchCriteriaApplicatorInterface -{ - /** - * {@inheritDoc} - */ - public function apply($criteria, Search $search) - { - $method = $this->getApplyMethod($criteria); - - if (!method_exists($this, $method)) { - throw new \RuntimeException( - sprintf('Method "%s" does not exist for "%s".', $method, get_class($criteria)) - ); - } - - $this->$method($criteria, $search); - } - - /** - * @param mixed $criteria - * - * @return string - */ - private function getApplyMethod($criteria) - { - if (!is_object($criteria)) { - throw new \RuntimeException(sprintf('It should be query object, got "%s"', gettype($criteria))); - } - - $classParts = explode('\\', get_class($criteria)); - - return 'apply' . end($classParts); - } -} diff --git a/src/Search/Elastic/Applicator/SearchCriteriaApplicatorInterface.php b/src/Search/Elastic/Applicator/SearchCriteriaApplicatorInterface.php index 2d5fa54..e56c68f 100644 --- a/src/Search/Elastic/Applicator/SearchCriteriaApplicatorInterface.php +++ b/src/Search/Elastic/Applicator/SearchCriteriaApplicatorInterface.php @@ -9,19 +9,26 @@ * file that was distributed with this source code. */ -namespace Lakion\SyliusElasticSearchBundle\Search\Elastic\Applicator; +namespace Sylius\ElasticSearchPlugin\Search\Elastic\Applicator; -use Lakion\SyliusElasticSearchBundle\Search\Criteria\Criteria; use ONGR\ElasticsearchDSL\Search; +use Sylius\ElasticSearchPlugin\Search\Criteria\Criteria; /** - * @author Arkadiusz Krakowiak + * @author Arkadiusz Krakowiak */ interface SearchCriteriaApplicatorInterface { /** - * @param mixed $criteria + * @param Criteria $criteria * @param Search $search */ - public function apply($criteria, Search $search); + public function apply(Criteria $criteria, Search $search); + + /** + * @param Criteria $criteria + * + * @return bool + */ + public function supports(Criteria $criteria); } diff --git a/src/Search/Elastic/Applicator/Sort/SortByFieldApplicator.php b/src/Search/Elastic/Applicator/Sort/SortByFieldApplicator.php index a448286..3c97f09 100644 --- a/src/Search/Elastic/Applicator/Sort/SortByFieldApplicator.php +++ b/src/Search/Elastic/Applicator/Sort/SortByFieldApplicator.php @@ -1,18 +1,16 @@ + * @author Arkadiusz Krakowiak */ -final class SortByFieldApplicator extends SearchCriteriaApplicator +final class SortByFieldApplicator implements SearchCriteriaApplicatorInterface { /** * @var SortFactoryInterface @@ -30,8 +28,16 @@ public function __construct(SortFactoryInterface $sortByFieldQueryFactory) /** * {@inheritdoc} */ - public function applyOrdering(Ordering $ordering, Search $search) + public function apply(Criteria $criteria, Search $search) { - $search->addSort($this->sortByFieldQueryFactory->create($ordering)); + $search->addSort($this->sortByFieldQueryFactory->create($criteria->ordering())); + } + + /** + * {@inheritdoc} + */ + public function supports(Criteria $criteria) + { + return null != $criteria->ordering()->field() && null != $criteria->ordering()->direction(); } } diff --git a/src/Search/Elastic/ElasticSearchEngine.php b/src/Search/Elastic/ElasticSearchEngine.php index 51908c0..72e49dd 100644 --- a/src/Search/Elastic/ElasticSearchEngine.php +++ b/src/Search/Elastic/ElasticSearchEngine.php @@ -1,28 +1,22 @@ + * @author Arkadiusz Krakowiak */ final class ElasticSearchEngine implements SearchEngineInterface { /** - * @var RepositoryManagerInterface + * @var Manager */ - private $repositoryManager; - - /** - * @var SearchFactoryInterface - */ - private $searchFactory; + private $manager; /** * @var SearchCriteriaApplicatorInterface[] @@ -30,24 +24,20 @@ final class ElasticSearchEngine implements SearchEngineInterface private $searchCriteriaApplicators = []; /** - * @param RepositoryManagerInterface $repositoryManager - * @param SearchFactoryInterface $searchFactory + * @param Manager $manager + * @param SearchCriteriaApplicatorInterface $sortingApplicator */ - public function __construct(RepositoryManagerInterface $repositoryManager, SearchFactoryInterface $searchFactory) + public function __construct(Manager $manager, SearchCriteriaApplicatorInterface $sortingApplicator) { - $this->repositoryManager = $repositoryManager; - $this->searchFactory = $searchFactory; + $this->manager = $manager; } /** * @param SearchCriteriaApplicatorInterface $searchCriteriaApplicator - * @param string $criteriaClass */ - public function addSearchCriteriaApplicator( - SearchCriteriaApplicatorInterface $searchCriteriaApplicator, - $criteriaClass - ) { - $this->searchCriteriaApplicators[$criteriaClass] = $searchCriteriaApplicator; + public function addSearchCriteriaApplicator(SearchCriteriaApplicatorInterface $searchCriteriaApplicator) + { + $this->searchCriteriaApplicators[] = $searchCriteriaApplicator; } /** @@ -55,22 +45,15 @@ public function addSearchCriteriaApplicator( */ public function match(Criteria $criteria) { - $search = $this->searchFactory->create(); - $filters = array_merge($criteria->getFiltering()->getFields(), [$criteria->getOrdering()]); + $repository = $this->manager->getRepository($criteria->documentClass()); - foreach ($filters as $filter) { - if (!is_object($filter)) { - continue; - } - - if (isset($this->searchCriteriaApplicators[get_class($filter)])) { - $this->searchCriteriaApplicators[get_class($filter)]->apply($filter, $search); + $search = $repository->createSearch(); + foreach ($this->searchCriteriaApplicators as $searchCriteriaApplicator) { + if ($searchCriteriaApplicator->supports($criteria)) { + $searchCriteriaApplicator->apply($criteria, $search); } } - /** @var Repository $repository */ - $repository = $this->repositoryManager->getRepository($criteria->getResourceAlias()); - - return $repository->createPaginatorAdapter($search->toArray()); + return new ArrayResult(iterator_to_array($repository->findArray($search))); } } diff --git a/src/Search/Elastic/Factory/Query/EmptyCriteriaQueryFactory.php b/src/Search/Elastic/Factory/Query/EmptyCriteriaQueryFactory.php index affe87e..cc8b305 100644 --- a/src/Search/Elastic/Factory/Query/EmptyCriteriaQueryFactory.php +++ b/src/Search/Elastic/Factory/Query/EmptyCriteriaQueryFactory.php @@ -1,11 +1,11 @@ + * @author Arkadiusz Krakowiak */ final class EmptyCriteriaQueryFactory implements QueryFactoryInterface { diff --git a/src/Search/Elastic/Factory/Query/MatchProductNameQueryFactory.php b/src/Search/Elastic/Factory/Query/MatchProductNameQueryFactory.php index c693118..7c26db4 100644 --- a/src/Search/Elastic/Factory/Query/MatchProductNameQueryFactory.php +++ b/src/Search/Elastic/Factory/Query/MatchProductNameQueryFactory.php @@ -1,13 +1,13 @@ + * @author Arkadiusz Krakowiak */ final class MatchProductNameQueryFactory implements QueryFactoryInterface { @@ -16,10 +16,10 @@ final class MatchProductNameQueryFactory implements QueryFactoryInterface */ public function create(array $parameters = []) { - if (!isset($parameters['phrase']) || null == $parameters['phrase']) { + if (!isset($parameters['search']) || null == $parameters['search']) { throw new MissingQueryParameterException('search', get_class($this)); } - return new NestedQuery('translations', new MatchQuery('translations.name', $parameters['phrase'])); + return new MatchQuery('name', $parameters['search']); } } diff --git a/src/Search/Elastic/Factory/Query/ProductHasOptionCodeQueryFactory.php b/src/Search/Elastic/Factory/Query/ProductHasOptionCodeQueryFactory.php index daa8f72..68787d1 100644 --- a/src/Search/Elastic/Factory/Query/ProductHasOptionCodeQueryFactory.php +++ b/src/Search/Elastic/Factory/Query/ProductHasOptionCodeQueryFactory.php @@ -1,13 +1,13 @@ + * @author Arkadiusz Krakowiak */ final class ProductHasOptionCodeQueryFactory implements QueryFactoryInterface { diff --git a/src/Search/Elastic/Factory/Query/ProductInChannelQueryFactory.php b/src/Search/Elastic/Factory/Query/ProductInChannelQueryFactory.php index 6dd6422..2cee10b 100644 --- a/src/Search/Elastic/Factory/Query/ProductInChannelQueryFactory.php +++ b/src/Search/Elastic/Factory/Query/ProductInChannelQueryFactory.php @@ -1,13 +1,13 @@ + * @author Arkadiusz Krakowiak */ final class ProductInChannelQueryFactory implements QueryFactoryInterface { @@ -20,6 +20,6 @@ public function create(array $parameters = []) throw new MissingQueryParameterException('channel_code', get_class($this)); } - return new NestedQuery('channels', new TermQuery('channels.code', strtolower($parameters['channel_code']))); + return new TermQuery('channel_code', strtolower($parameters['channel_code'])); } } diff --git a/src/Search/Elastic/Factory/Query/ProductInMainTaxonQueryFactory.php b/src/Search/Elastic/Factory/Query/ProductInMainTaxonQueryFactory.php index 152df5a..326348a 100644 --- a/src/Search/Elastic/Factory/Query/ProductInMainTaxonQueryFactory.php +++ b/src/Search/Elastic/Factory/Query/ProductInMainTaxonQueryFactory.php @@ -1,12 +1,12 @@ + * @author Arkadiusz Krakowiak */ final class ProductInMainTaxonQueryFactory implements QueryFactoryInterface { @@ -19,6 +19,6 @@ public function create(array $parameters = []) throw new MissingQueryParameterException('taxon_code', get_class($this)); } - return new TermQuery('mainTaxon.code', strtolower($parameters['taxon_code'])); + return new TermQuery('main_taxon_code.value', strtolower($parameters['taxon_code'])); } } diff --git a/src/Search/Elastic/Factory/Query/ProductInPriceRangeQueryFactory.php b/src/Search/Elastic/Factory/Query/ProductInPriceRangeQueryFactory.php index bc4b9f4..4a48bd3 100644 --- a/src/Search/Elastic/Factory/Query/ProductInPriceRangeQueryFactory.php +++ b/src/Search/Elastic/Factory/Query/ProductInPriceRangeQueryFactory.php @@ -1,13 +1,13 @@ + * @author Arkadiusz Krakowiak */ final class ProductInPriceRangeQueryFactory implements QueryFactoryInterface { @@ -21,13 +21,7 @@ public function create(array $parameters = []) $graterThan = $parameters['product_price_range']['grater_than']; $lessThan = $parameters['product_price_range']['less_than']; - return new NestedQuery( - 'variants', - new NestedQuery( - 'variants.channelPricings', - new RangeQuery('variants.channelPricings.price', ['gte' => $graterThan, 'lte' => $lessThan]) - ) - ); + return new RangeQuery('price.amount', ['gte' => $graterThan, 'lte' => $lessThan]); } /** diff --git a/src/Search/Elastic/Factory/Query/ProductInProductTaxonsQueryFactory.php b/src/Search/Elastic/Factory/Query/ProductInProductTaxonsQueryFactory.php index 0673f32..3a44a4d 100644 --- a/src/Search/Elastic/Factory/Query/ProductInProductTaxonsQueryFactory.php +++ b/src/Search/Elastic/Factory/Query/ProductInProductTaxonsQueryFactory.php @@ -1,13 +1,13 @@ + * @author Arkadiusz Krakowiak */ final class ProductInProductTaxonsQueryFactory implements QueryFactoryInterface { diff --git a/src/Search/Elastic/Factory/Query/QueryFactoryInterface.php b/src/Search/Elastic/Factory/Query/QueryFactoryInterface.php index 50cacbf..7eb9ec6 100644 --- a/src/Search/Elastic/Factory/Query/QueryFactoryInterface.php +++ b/src/Search/Elastic/Factory/Query/QueryFactoryInterface.php @@ -9,13 +9,13 @@ * file that was distributed with this source code. */ -namespace Lakion\SyliusElasticSearchBundle\Search\Elastic\Factory\Query; +namespace Sylius\ElasticSearchPlugin\Search\Elastic\Factory\Query; -use Lakion\SyliusElasticSearchBundle\Exception\MissingQueryParameterException; +use Sylius\ElasticSearchPlugin\Exception\MissingQueryParameterException; use ONGR\ElasticsearchDSL\BuilderInterface; /** - * @author Arkadiusz Krakowiak + * @author Arkadiusz Krakowiak */ interface QueryFactoryInterface { diff --git a/src/Search/Elastic/Factory/Search/SearchFactory.php b/src/Search/Elastic/Factory/Search/SearchFactory.php deleted file mode 100644 index dea70e8..0000000 --- a/src/Search/Elastic/Factory/Search/SearchFactory.php +++ /dev/null @@ -1,19 +0,0 @@ - - */ -final class SearchFactory implements SearchFactoryInterface -{ - /** - * {@inheritdoc} - */ - public function create() - { - return new Search(); - } -} diff --git a/src/Search/Elastic/Factory/Search/SearchFactoryInterface.php b/src/Search/Elastic/Factory/Search/SearchFactoryInterface.php deleted file mode 100644 index a745695..0000000 --- a/src/Search/Elastic/Factory/Search/SearchFactoryInterface.php +++ /dev/null @@ -1,25 +0,0 @@ - - */ -interface SearchFactoryInterface -{ - /** - * @return Search - */ - public function create(); -} diff --git a/src/Search/Elastic/Factory/Sort/SortByFieldQueryFactory.php b/src/Search/Elastic/Factory/Sort/SortByFieldQueryFactory.php index 3a9454e..421a076 100644 --- a/src/Search/Elastic/Factory/Sort/SortByFieldQueryFactory.php +++ b/src/Search/Elastic/Factory/Sort/SortByFieldQueryFactory.php @@ -1,12 +1,12 @@ + * @author Arkadiusz Krakowiak */ final class SortByFieldQueryFactory implements SortFactoryInterface { @@ -15,6 +15,6 @@ final class SortByFieldQueryFactory implements SortFactoryInterface */ public function create(Ordering $ordering) { - return new FieldSort('raw_' . $ordering->getField(), $ordering->getDirection()); + return new FieldSort($ordering->field(), $ordering->direction()); } } diff --git a/src/Search/Elastic/Factory/Sort/SortFactoryInterface.php b/src/Search/Elastic/Factory/Sort/SortFactoryInterface.php index 6c451c6..ddf069e 100644 --- a/src/Search/Elastic/Factory/Sort/SortFactoryInterface.php +++ b/src/Search/Elastic/Factory/Sort/SortFactoryInterface.php @@ -9,13 +9,13 @@ * file that was distributed with this source code. */ -namespace Lakion\SyliusElasticSearchBundle\Search\Elastic\Factory\Sort; +namespace Sylius\ElasticSearchPlugin\Search\Elastic\Factory\Sort; -use Lakion\SyliusElasticSearchBundle\Search\Criteria\Ordering; +use Sylius\ElasticSearchPlugin\Search\Criteria\Ordering; use ONGR\ElasticsearchDSL\BuilderInterface; /** - * @author Arkadiusz Krakowiak + * @author Arkadiusz Krakowiak */ interface SortFactoryInterface { diff --git a/src/Search/SearchEngineInterface.php b/src/Search/SearchEngineInterface.php index af0006b..6dc7dbe 100644 --- a/src/Search/SearchEngineInterface.php +++ b/src/Search/SearchEngineInterface.php @@ -9,20 +9,21 @@ * file that was distributed with this source code. */ -namespace Lakion\SyliusElasticSearchBundle\Search; +namespace Sylius\ElasticSearchPlugin\Search; -use FOS\ElasticaBundle\Paginator\PaginatorAdapterInterface; -use Lakion\SyliusElasticSearchBundle\Search\Criteria\Criteria; +use ONGR\ElasticsearchBundle\Result\DocumentIterator; +use Porpaginas\Result; +use Sylius\ElasticSearchPlugin\Search\Criteria\Criteria; /** - * @author Arkadiusz Krakowiak + * @author Arkadiusz Krakowiak */ interface SearchEngineInterface { /** * @param Criteria $criteria * - * @return PaginatorAdapterInterface + * @return Result */ public function match(Criteria $criteria); } diff --git a/src/LakionSyliusElasticSearchBundle.php b/src/SyliusElasticSearchPlugin.php similarity index 53% rename from src/LakionSyliusElasticSearchBundle.php rename to src/SyliusElasticSearchPlugin.php index c9ffcb3..1c22d28 100644 --- a/src/LakionSyliusElasticSearchBundle.php +++ b/src/SyliusElasticSearchPlugin.php @@ -1,14 +1,17 @@ - */ final class ProductContext implements Context { /** @@ -53,50 +48,12 @@ public function __construct(SearchEngineInterface $searchEngine, SharedStorageIn $this->sharedStorage = $sharedStorage; } - /** - * @When I filter them by :mugTypeValue mug type - */ - public function iFilterThemByDoubleMugType($mugTypeValue) - { - $criteria = Criteria::fromQueryParameters(Product::class, [ - new ProductHasOptionCodesFilter([sprintf('mug_type_%s', $mugTypeValue)]), - ]); - - $this->match($criteria); - } - - /** - * @When I filter them by :mugTypeValue mug type or sticker size :stickerSizeValue - */ - public function iFilterThemByDoubleMugTypeAndStickerSize($mugTypeValue, $stickerSizeValue) - { - $criteria = Criteria::fromQueryParameters(Product::class, [ - new ProductHasOptionCodesFilter([sprintf('mug_type_%s', $mugTypeValue)]), - new ProductHasOptionCodesFilter([sprintf('sticker_size_%s', $stickerSizeValue)]), - ]); - - $this->match($criteria); - } - - /** - * @When I filter them by stickier size :stickerSizeValue - */ - public function iFilterThemByStickierSize($stickerSizeValue) - { - $criteria = Criteria::fromQueryParameters(Product::class, [ - new ProductHasOptionCodesFilter([sprintf('sticker_size_%s', $stickerSizeValue)]), - ]); - - $this->match($criteria); - } - /** * @When /^I filter them by price between ("[^"]+") and ("[^"]+")$/ */ public function iFilterThemByPriceBetweenAnd($graterThan, $lessThan) { - sleep(3); - $criteria = Criteria::fromQueryParameters(Product::class, [new ProductInPriceRangeFilter($graterThan, $lessThan)]); + $criteria = Criteria::fromQueryParameters(Product::class, ['product_price_range' => ['grater_than' => $graterThan, 'less_than' => $lessThan]]); $this->match($criteria); } @@ -110,35 +67,33 @@ public function iViewTheListOfTheProductsWithoutFiltering() } /** - * @When /^I filter them by (channel "[^"]+")$/ + * @When I filter them by channel :channelCode */ - public function iFilterThemByChannel(ChannelInterface $channel) + public function iFilterThemByChannel($channelCode) { - sleep(3); - $criteria = Criteria::fromQueryParameters(Product::class, [new ProductInChannelFilter($channel->getCode())]); + $criteria = Criteria::fromQueryParameters(Product::class, ['channel_code' => $channelCode]); $this->match($criteria); } /** - * @When /^I filter them by (channel "[^"]+") and price between ("[^"]+") and ("[^"]+")$/ + * @When /^I filter them by channel "([^"]+)" and price between ("[^"]+") and ("[^"]+")$/ */ - public function iFilterThemByChannelAndPriceBetweenAnd(ChannelInterface $channel, $graterThan, $lessThan) + public function iFilterThemByChannelAndPriceBetweenAnd($channelCode, $graterThan, $lessThan) { - sleep(3); $criteria = Criteria::fromQueryParameters(Product::class, [ - new ProductInChannelFilter($channel->getCode()), - new ProductInPriceRangeFilter($graterThan, $lessThan), + 'channel_code' => $channelCode, + 'product_price_range' => ['grater_than' => $graterThan, 'less_than' => $lessThan], ]); $this->match($criteria); } /** - * @When I filter them by :taxon taxon + * @When I filter them by :taxonCode taxon */ - public function iFilterThemByTaxon(TaxonInterface $taxon) + public function iFilterThemByTaxon($taxonCode) { - $criteria = Criteria::fromQueryParameters(Product::class, [new ProductInTaxonFilter($taxon->getCode())]); + $criteria = Criteria::fromQueryParameters(Product::class, ['taxon_code' => $taxonCode]); $this->match($criteria); } @@ -147,7 +102,6 @@ public function iFilterThemByTaxon(TaxonInterface $taxon) */ public function iSortThemByNameInAscendingOrder($field, $order) { - sleep(3); if ('descending' === $order) { $field = '-' . $field; } @@ -161,9 +115,7 @@ public function iSortThemByNameInAscendingOrder($field, $order) */ public function iSearchForProductsWithName($name) { - sleep(3); - - $criteria = Criteria::fromQueryParameters(Product::class, [new SearchPhrase($name)]); + $criteria = Criteria::fromQueryParameters(Product::class, ['search' => $name]); $this->match($criteria); } @@ -172,10 +124,10 @@ public function iSearchForProductsWithName($name) */ public function iShouldSeeProductsOnTheList($numberOfProducts) { - /** @var PaginatorAdapterInterface $result */ + /** @var Result $result */ $result = $this->sharedStorage->get('search_result'); - Assert::eq($result->getTotalHits(), $numberOfProducts); + Assert::eq($result->count(), $numberOfProducts); } /** @@ -183,24 +135,21 @@ public function iShouldSeeProductsOnTheList($numberOfProducts) */ public function iShouldSeeProductsInOrderLike(...$productNames) { - /** @var PaginatorAdapterInterface $result */ + /** @var Result $searchResult */ $searchResult = $this->sharedStorage->get('search_result'); - /** @var PartialResultsInterface $partialResult */ - $partialResult = $searchResult->getResults(0, 100); - /** * @var int $position - * @var ProductInterface $result + * @var Product $product */ - foreach ($partialResult->toArray() as $position => $result) { - if ($result->getName() !== $productNames[$position]) { + foreach ($searchResult as $position => $product) { + if ($product['name'] !== $productNames[$position]) { throw new \RuntimeException( sprintf( 'Sorting failed at position "%s" expected value was "%s", but got "%s"', $position + 1, $productNames[$position], - $result + $product['name'] ) ); } @@ -215,31 +164,12 @@ public function iShouldSeeProductsInOrderLike(...$productNames) */ public function itShouldBe(...$expectedProductNames) { - /** @var PaginatorAdapterInterface $result */ + /** @var Result $searchResult */ $searchResult = $this->sharedStorage->get('search_result'); - /** @var PartialResultsInterface $partialResult */ - $partialResult = $searchResult->getResults(0, 100); - - $resultProductNames = array_map(function (ProductInterface $product) { - return $product->getName(); - }, $partialResult->toArray()); - - $expectedProductCount = count($expectedProductNames); - $resultProductCount = count($resultProductNames); - - Assert::same($expectedProductCount, $resultProductCount); - - foreach ($expectedProductNames as $expectedProductName) { - Assert::oneOf( - $expectedProductName, - $resultProductNames, - sprintf( - 'Expected product with name "%s", does not exist in search result. Got "%s"', - $expectedProductName, - implode(',', $resultProductNames) - ) - ); + /** @var Product $product */ + foreach ($searchResult as $product) { + Assert::oneOf($product['name'], $expectedProductNames); } } diff --git a/tests/Behat/Context/Hook/ElasticSearchContext.php b/tests/Behat/Context/Hook/ElasticSearchContext.php index 41a77dd..b426e32 100644 --- a/tests/Behat/Context/Hook/ElasticSearchContext.php +++ b/tests/Behat/Context/Hook/ElasticSearchContext.php @@ -1,42 +1,30 @@ - */ final class ElasticSearchContext implements Context { /** - * @var Resetter + * @var Manager */ - private $resetter; + private $manager; /** - * @param Resetter $resetter + * @param Manager $manager */ - public function __construct(Resetter $resetter) + public function __construct(Manager $manager) { - $this->resetter = $resetter; + $this->manager = $manager; } /** * @BeforeScenario */ - public function resetElasticSearch() + public function purge() { - $this->resetter->resetAllIndexes(); + $this->manager->dropAndCreateIndex(); } } diff --git a/tests/Behat/Context/Setup/ProductContext.php b/tests/Behat/Context/Setup/ProductContext.php index dc874d4..6c5512a 100644 --- a/tests/Behat/Context/Setup/ProductContext.php +++ b/tests/Behat/Context/Setup/ProductContext.php @@ -9,64 +9,97 @@ * file that was distributed with this source code. */ -namespace Tests\Lakion\SyliusElasticSearchBundle\Behat\Context\Setup; +namespace Tests\Sylius\ElasticSearchPlugin\Behat\Context\Setup; use Behat\Behat\Context\Context; -use Tests\Lakion\SyliusElasticSearchBundle\Behat\Services\SuspenderInterface; -use Sylius\Bundle\FixturesBundle\Fixture\FixtureInterface; +use Doctrine\ORM\Id\UuidGenerator; +use ONGR\ElasticsearchBundle\Service\Manager; +use Sylius\ElasticSearchPlugin\Document\Price; +use Sylius\ElasticSearchPlugin\Document\Product; +use Sylius\ElasticSearchPlugin\Document\TaxonCode; -/** - * @author Arkadiusz Krakowiak - */ final class ProductContext implements Context { /** - * @var FixtureInterface + * @var Manager */ - private $bookProductFixture; + private $manager; /** - * @var FixtureInterface + * @param Manager $manager */ - private $mugProductFixture; + public function __construct(Manager $manager) + { + $this->manager = $manager; + } /** - * @var FixtureInterface + * @Given the store has :mugsNumber Mugs, :stickersNumber Stickers and :booksNumber Books */ - private $stickerProductFixture; + public function theStoreHasAboutMugsAndStickers($mugsNumber, $stickersNumber, $booksNumber) + { + $mugsTaxonCode = new TaxonCode(); + $mugsTaxonCode->setValue('mugs'); + + $stickersTaxonCode = new TaxonCode(); + $stickersTaxonCode->setValue('stickers'); + + $booksTaxonCode = new TaxonCode(); + $booksTaxonCode->setValue('books'); + + $this->generateProductsInTaxon($mugsNumber, $mugsTaxonCode); + $this->generateProductsInTaxon($stickersNumber, $stickersTaxonCode); + $this->generateProductsInTaxon($booksNumber, $booksTaxonCode); + } /** - * @var SuspenderInterface + * @Given the store has a product :productName + * @Given the store has a :productName product + * @Given I added a product :productName + * @Given /^the store(?:| also) has a product "([^"]+)" priced at ("[^"]+")$/ + * @Given /^the store(?:| also) has a product "([^"]+)" priced at ("[^"]+") in "([^"]+)" channel$/ */ - private $elasticSearchSuspender; + public function storeHasAProductPricedAt($productName, $price = 100, $channelCode = null) + { + $this->manager->persist($this->createProduct($productName, $price, $channelCode)); + $this->manager->commit(); + } /** - * @param FixtureInterface $bookProductFixture - * @param FixtureInterface $mugProductFixture - * @param FixtureInterface $stickerProductFixture - * @param SuspenderInterface $elasticSearchSuspender + * @param int $howMany + * @param TaxonCode $taxonCode */ - public function __construct( - FixtureInterface $bookProductFixture, - FixtureInterface $mugProductFixture, - FixtureInterface $stickerProductFixture, - SuspenderInterface $elasticSearchSuspender - ) { - $this->bookProductFixture = $bookProductFixture; - $this->mugProductFixture = $mugProductFixture; - $this->stickerProductFixture = $stickerProductFixture; - $this->elasticSearchSuspender = $elasticSearchSuspender; + private function generateProductsInTaxon($howMany, TaxonCode $taxonCode) + { + for ($i = 0; $i < $howMany; $i++) { + $product = new Product(); + $product->setMainTaxonCode($taxonCode); + $product->setCode(uniqid()); + $this->manager->persist($product); + } + + $this->manager->commit(); } /** - * @Given the store has :mugsNumber Mugs, :stickersNumber Stickers and :booksNumber Books + * @param string $productName + * @param int $priceAmount + * @param string $channelCode + * + * @return Product */ - public function theStoreHasAboutMugsAndStickers($mugsNumber, $stickersNumber, $booksNumber) + private function createProduct($productName, $priceAmount, $channelCode) { - $this->mugProductFixture->load(['amount' => (int) $mugsNumber]); - $this->stickerProductFixture->load(['amount' => (int) $stickersNumber]); - $this->bookProductFixture->load(['amount' => (int) $booksNumber]); + $price = new Price(); + $price->setCurrency('USD'); + $price->setAmount($priceAmount); + + $product = new Product(); + $product->setCode(uniqid()); + $product->setPrice($price); + $product->setChannelCode($channelCode); + $product->setName($productName); - $this->elasticSearchSuspender->waitForLoadingNumberOfData((int) $mugsNumber + (int) $stickersNumber + (int) $booksNumber, 5); + return $product; } } diff --git a/tests/Behat/Context/Ui/Shop/ProductContext.php b/tests/Behat/Context/Ui/Shop/ProductContext.php deleted file mode 100644 index 3e20b58..0000000 --- a/tests/Behat/Context/Ui/Shop/ProductContext.php +++ /dev/null @@ -1,163 +0,0 @@ - - */ -final class ProductContext implements Context -{ - /** - * @var IndexPageInterface - */ - private $indexPage; - - /** - * @param IndexPageInterface $indexPage - */ - public function __construct(IndexPageInterface $indexPage) - { - $this->indexPage = $indexPage; - } - - /** - * @When I filter them by :mugTypeValue mug type - */ - public function iFilterThemByDoubleMugType($mugTypeValue) - { - $this->indexPage->open(); - $this->indexPage->setPaginating(100); - $this->indexPage->filterByProductOptions(Criteria::fromQueryParameters(Product::class, ['mug_type' => $mugTypeValue])); - $this->indexPage->filter(); - } - - /** - * @When I filter them by :mugTypeValue mug type or sticker size :stickerSizeValue - */ - public function iFilterThemByDoubleMugTypeAndStickerSize($mugTypeValue, $stickerSizeValue) - { - $this->indexPage->open(); - $this->indexPage->setPaginating(100); - $this->indexPage->filterByProductOptions(Criteria::fromQueryParameters(Product::class, ['mug_type' => $mugTypeValue, 'sticker_size' => $stickerSizeValue])); - $this->indexPage->filter(); - } - - /** - * @When I filter them by stickier size :stickerSizeValue - */ - public function iFilterThemByStickierSize($stickerSizeValue) - { - $this->indexPage->open(); - $this->indexPage->setPaginating(100); - $this->indexPage->filterByProductOptions(Criteria::fromQueryParameters(Product::class, ['sticker_size' => $stickerSizeValue])); - $this->indexPage->filter(); - } - - /** - * @When /^I filter them by price between ("[^"]+") and ("[^"]+")$/ - */ - public function iFilterThemByPriceBetweenAnd($graterThan, $lessThan) - { - sleep(3); - $this->indexPage->open(); - $this->indexPage->setPaginating(100); - $this->indexPage->filterByPriceRange($graterThan, $lessThan); - $this->indexPage->filter(); - } - - /** - * @When I search for products with name :name - */ - public function iSearchForProductsWithName($name) - { - sleep(3); - $this->indexPage->open(); - $this->indexPage->setPaginating(100); - $this->indexPage->search($name); - } - - /** - * @When I view the list of the products without filtering - */ - public function iViewTheListOfTheProductsWithoutFiltering() - { - $this->indexPage->open(['per_page' => 100]); - } - - /** - * @Then I should see :numberOfProducts products on the list - */ - public function iShouldSeeProductsOnTheList($numberOfProducts) - { - Assert::eq(count($this->indexPage->getAllProducts()), $numberOfProducts); - } - - /** - * @Then /^I should see products in order like "([^"]+)", "([^"]+)", "([^"]+)", "([^"]+)", "([^"]+)", "([^"]+)"$/ - */ - public function iShouldSeeProductsInOrderLike(...$productNames) - { - foreach ($this->indexPage->getAllProducts() as $position => $result) { - if ($result->getText() !== $productNames[$position]) { - throw new \RuntimeException( - sprintf( - 'Sorting failed at position "%s" expected value was "%s", but got "%s"', - $position+1, - $productNames[$position], - $result - ) - ); - } - } - } - - /** - * @Then /^It should be "([^"]+)"$/ - * @Then /^It should be "([^"]+)", "([^"]+)"$/ - * @Then /^It should be "([^"]+)", "([^"]+)", "([^"]+)"$/ - * @Then /^It should be "([^"]+)", "([^"]+)", "([^"]+)", "([^"]+)"$/ - */ - public function itShouldBe(...$expectedProductNames) - { - $resultProductNames = array_map(function (NodeElement $productElement) { - return $productElement->getText(); - }, $this->indexPage->getAllProducts()); - - $expectedProductCount = count($expectedProductNames); - $resultProductCount = count($resultProductNames); - - if ($expectedProductCount !== $resultProductCount) { - throw new \RuntimeException( - sprintf('Expected product count was "%s", got "%s"', $expectedProductCount, $resultProductCount) - ); - } - - foreach ($expectedProductNames as $expectedProductName) { - if (!in_array($expectedProductName, $resultProductNames)) { - throw new \RuntimeException(sprintf( - 'Expected product with name "%s", does not exist in search result. Got "%s"', - $expectedProductName, - implode(',', $resultProductNames) - )); - } - } - } -} diff --git a/tests/Behat/Page/Product/IndexPage.php b/tests/Behat/Page/Product/IndexPage.php deleted file mode 100644 index 3dd59b1..0000000 --- a/tests/Behat/Page/Product/IndexPage.php +++ /dev/null @@ -1,101 +0,0 @@ - - */ -final class IndexPage extends SymfonyPage implements IndexPageInterface -{ - /** - * {@inheritdoc} - */ - public function filterByProductOptions(Criteria $criteria) - { - foreach ($criteria->getFiltering()->getFields() as $type => $value) { - $filterType = $this->getElement('filter_option', [ - '%filter_type%' => $type, - '%filter_value%' => sprintf('%s_%s', $type, $value) - ]); - - $filterType->check(); - } - } - - /** - * {@inheritdoc} - */ - public function filterByPriceRange($graterThan, $lessThan) - { - $this->getElement('filter_price_range_grater_than')->setValue($graterThan / 100); - $this->getElement('filter_price_range_less_than')->setValue($lessThan / 100); - } - - public function filter() - { - $this->getDocument()->pressButton('Filter'); - } - - /** - * {@inheritdoc} - */ - public function setPaginating($perPage) - { - $this->getElement('pagination')->selectOption($perPage); - } - - /** - * {@inheritdoc} - */ - public function search($phrase) - { - $this->getElement('search')->setValue($phrase); - $this->getElement('search_submit')->press(); - } - - /** - * {@inheritdoc} - */ - public function getAllProducts() - { - $productElements = $this->getElement('products')->findAll('css', 'div .column > div .content > a'); - - return $productElements; - } - - /** - * {@inheritdoc} - */ - public function getRouteName() - { - return 'lakion_elastic_search_shop_product_index'; - } - - /** - * {@inheritdoc} - */ - protected function getDefinedElements() - { - return array_merge(parent::getDefinedElements(), [ - 'filter_option' => '#filter_set_%filter_type%_code_%filter_value%', - 'search' => '#filter_set_search', - 'search_submit' => '#search_submit', - 'filter_price_range_grater_than' => '#filter_set_product_price_grater_than', - 'filter_price_range_less_than' => '#filter_set_product_price_less_than', - 'products' => '#products', - 'pagination' => '#pagination', - ]); - } -} diff --git a/tests/Behat/Page/Product/IndexPageInterface.php b/tests/Behat/Page/Product/IndexPageInterface.php deleted file mode 100644 index b76abae..0000000 --- a/tests/Behat/Page/Product/IndexPageInterface.php +++ /dev/null @@ -1,50 +0,0 @@ - - */ -interface IndexPageInterface extends SymfonyPageInterface -{ - /** - * @param Criteria $criteria - */ - public function filterByProductOptions(Criteria $criteria); - - /** - * @param int $graterThan - * @param int $lessThan - */ - public function filterByPriceRange($graterThan, $lessThan); - - public function filter(); - - /** - * @param int $perPage - */ - public function setPaginating($perPage); - - /** - * @param $phrase - */ - public function search($phrase); - - /** - * @return NodeElement[] - */ - public function getAllProducts(); -} diff --git a/tests/Behat/Resources/services.xml b/tests/Behat/Resources/services.xml index 6f5df13..755b7a1 100644 --- a/tests/Behat/Resources/services.xml +++ b/tests/Behat/Resources/services.xml @@ -7,14 +7,4 @@ - - - - - - - - - - diff --git a/tests/Behat/Resources/services/searching_products.xml b/tests/Behat/Resources/services/searching_products.xml index 0a237bb..51636f7 100644 --- a/tests/Behat/Resources/services/searching_products.xml +++ b/tests/Behat/Resources/services/searching_products.xml @@ -5,25 +5,20 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - - - + + - - + + - - + + - - diff --git a/tests/Behat/Resources/suites.yml b/tests/Behat/Resources/suites.yml index fba5c5d..3ae4415 100644 --- a/tests/Behat/Resources/suites.yml +++ b/tests/Behat/Resources/suites.yml @@ -1,3 +1,2 @@ imports: - suites/shop/domain/product/searching_products.yml - - suites/shop/ui/product/searching_products.yml diff --git a/tests/Behat/Resources/suites/shop/domain/product/searching_products.yml b/tests/Behat/Resources/suites/shop/domain/product/searching_products.yml index 4850d05..fa85c0b 100644 --- a/tests/Behat/Resources/suites/shop/domain/product/searching_products.yml +++ b/tests/Behat/Resources/suites/shop/domain/product/searching_products.yml @@ -2,18 +2,12 @@ default: suites: domain_shop_searching_product: contexts_services: - - lakion_sylius_elastic_search.behat.context.hook.elastic_search - - sylius.behat.context.hook.doctrine_orm + - sylius_elastic_search.behat.context.hook.elastic_search - - sylius.behat.context.transform.channel - sylius.behat.context.transform.lexical - - sylius.behat.context.transform.taxon - - lakion_sylius_elastic_search.behat.context.setup.product - - sylius.behat.context.setup.channel - - sylius.behat.context.setup.taxonomy - - sylius.behat.context.setup.product + - sylius_elastic_search.behat.context.setup.product - - lakion_sylius_elastic_search.behat.context.domain.shop.product + - sylius_elastic_search.behat.context.domain.shop.product filters: tags: "@searching_products && @domain" diff --git a/tests/Behat/Resources/suites/shop/ui/product/searching_products.yml b/tests/Behat/Resources/suites/shop/ui/product/searching_products.yml deleted file mode 100644 index 2ce1721..0000000 --- a/tests/Behat/Resources/suites/shop/ui/product/searching_products.yml +++ /dev/null @@ -1,19 +0,0 @@ -default: - suites: - ui_shop_searching_product: - contexts_services: - - lakion_sylius_elastic_search.behat.context.hook.elastic_search - - sylius.behat.context.hook.doctrine_orm - - - sylius.behat.context.transform.channel - - sylius.behat.context.transform.lexical - - sylius.behat.context.transform.taxon - - - lakion_sylius_elastic_search.behat.context.setup.product - - sylius.behat.context.setup.channel - - sylius.behat.context.setup.taxonomy - - sylius.behat.context.setup.product - - - lakion_sylius_elastic_search.behat.context.ui.shop.product - filters: - tags: "@searching_products && @ui" diff --git a/tests/Behat/Services/ElasticSearchSuspender.php b/tests/Behat/Services/ElasticSearchSuspender.php deleted file mode 100644 index eac2644..0000000 --- a/tests/Behat/Services/ElasticSearchSuspender.php +++ /dev/null @@ -1,58 +0,0 @@ - - */ -final class ElasticSearchSuspender implements SuspenderInterface -{ - /** - * @var Client - */ - private $client; - - /** - * @param Client $client - */ - public function __construct(Client $client) - { - $this->client = $client; - } - - /** - * {@inheritdoc} - */ - public function waitForLoadingNumberOfData($number, $timeoutSeconds) - { - $start = microtime(true); - $end = $start + $timeoutSeconds; - - do { - $response = $this->refreshResponse(); - $count = !isset($response->getData()['count']) ? 0 : $response->getData()['count']; - - sleep(1); - } while ($count !== $number && microtime(true) < $end); - } - - /** - * @return Response - */ - private function refreshResponse() - { - return $this->client->request('_count'); - } -} diff --git a/tests/Behat/Services/SuspenderInterface.php b/tests/Behat/Services/SuspenderInterface.php deleted file mode 100644 index 275cc99..0000000 --- a/tests/Behat/Services/SuspenderInterface.php +++ /dev/null @@ -1,26 +0,0 @@ - - */ -interface SuspenderInterface -{ - /** - * @param int $number - * @param int $timeoutSeconds - * - * @return bool - */ - public function waitForLoadingNumberOfData($number, $timeoutSeconds); -} diff --git a/tests/DependencyInjection/Compiler/RegisterFilterTypePassTest.php b/tests/DependencyInjection/Compiler/RegisterFilterTypePassTest.php index 7464731..1a9fdd0 100644 --- a/tests/DependencyInjection/Compiler/RegisterFilterTypePassTest.php +++ b/tests/DependencyInjection/Compiler/RegisterFilterTypePassTest.php @@ -9,9 +9,9 @@ * file that was distributed with this source code. */ -namespace Tests\Lakion\SyliusElasticSearchBundle\DependencyInjection\Compiler; +namespace Tests\Sylius\ElasticSearchPlugin\DependencyInjection\Compiler; -use Lakion\SyliusElasticSearchBundle\DependencyInjection\Compiler\RegisterFilterTypePass; +use Sylius\ElasticSearchPlugin\DependencyInjection\Compiler\RegisterFilterTypePass; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\DefinitionHasMethodCallConstraint; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -28,16 +28,16 @@ final class RegisterFilterTypePassTest extends AbstractCompilerPassTestCase */ public function it_collects_tagged_filter_types() { - $this->setDefinition('lakion_sylius_elastic_search.form_registry.filters', new Definition()); + $this->setDefinition('sylius_elastic_search.form_registry.filters', new Definition()); $this->setDefinition( - 'lakion_sylius_elastic_search.form_type.filter', + 'sylius_elastic_search.form_type.filter', (new Definition(\stdClass::class))->addTag('form.type')->addTag('filter.type', ['type' => 'option']) ); $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'lakion_sylius_elastic_search.form_registry.filters', + 'sylius_elastic_search.form_registry.filters', 'add', ['default', 'option', \stdClass::class] ); @@ -50,7 +50,7 @@ public function it_does_nothing_if_there_is_no_filter_registry() { $this->compile(); - $this->assertContainerBuilderNotHasService('lakion_sylius_elastic_search.form_registry.filters'); + $this->assertContainerBuilderNotHasService('sylius_elastic_search.form_registry.filters'); } /** @@ -58,16 +58,16 @@ public function it_does_nothing_if_there_is_no_filter_registry() */ public function it_does_nothing_if_there_is_no_tagged_filters() { - $this->setDefinition('lakion_sylius_elastic_search.form_registry.filters', new Definition()); + $this->setDefinition('sylius_elastic_search.form_registry.filters', new Definition()); $this->setDefinition( - 'lakion_sylius_elastic_search.form_type.filter', + 'sylius_elastic_search.form_type.filter', (new Definition())->addTag('tag', ['type' => 'option']) ); $this->compile(); $this->assertContainerBuilderNotHasServiceDefinitionWithMethodCall( - 'lakion_sylius_elastic_search.form_registry.filters', + 'sylius_elastic_search.form_registry.filters', 'add', ['default', 'option', 'class'] ); @@ -78,9 +78,9 @@ public function it_does_nothing_if_there_is_no_tagged_filters() */ public function tagged_filter_types_must_have_type_configured() { - $this->setDefinition('lakion_sylius_elastic_search.form_registry.filters', new Definition()); + $this->setDefinition('sylius_elastic_search.form_registry.filters', new Definition()); $this->setDefinition( - 'lakion_sylius_elastic_search.form_type.filter', + 'sylius_elastic_search.form_type.filter', (new Definition())->addTag('form.type')->addTag('filter.type') ); diff --git a/tests/DependencyInjection/Compiler/RegisterSearchCriteriaApplicatorPassTest.php b/tests/DependencyInjection/Compiler/RegisterSearchCriteriaApplicatorPassTest.php index 5a086e5..c165141 100644 --- a/tests/DependencyInjection/Compiler/RegisterSearchCriteriaApplicatorPassTest.php +++ b/tests/DependencyInjection/Compiler/RegisterSearchCriteriaApplicatorPassTest.php @@ -9,9 +9,9 @@ * file that was distributed with this source code. */ -namespace Tests\Lakion\SyliusElasticSearchBundle\DependencyInjection\Compiler; +namespace Tests\Sylius\ElasticSearchPlugin\DependencyInjection\Compiler; -use Lakion\SyliusElasticSearchBundle\DependencyInjection\Compiler\RegisterSearchCriteriaApplicatorPass; +use Sylius\ElasticSearchPlugin\DependencyInjection\Compiler\RegisterSearchCriteriaApplicatorPass; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\DefinitionHasMethodCallConstraint; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -28,20 +28,19 @@ final class RegisterSearchCriteriaApplicatorPassTest extends AbstractCompilerPas */ public function it_collects_tagged_search_criteria_applicators() { - $this->setDefinition('lakion_sylius_elastic_search.search.elastic_engine', new Definition()); + $this->setDefinition('sylius_elastic_search.search.elastic_engine', new Definition()); $this->setDefinition( - 'lakion_sylius_elastic_search.search_criteria_applicator.product_has_multiple_option_codes', - (new Definition(\stdClass::class))->addTag('search_criteria_applicator', ['applies' => 'productHasOption']) + 'sylius_elastic_search.search_criteria_applicator.product_has_multiple_option_codes', + (new Definition(\stdClass::class))->addTag('search_criteria_applicator') ); $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'lakion_sylius_elastic_search.search.elastic_engine', + 'sylius_elastic_search.search.elastic_engine', 'addSearchCriteriaApplicator', [ - new Reference('lakion_sylius_elastic_search.search_criteria_applicator.product_has_multiple_option_codes'), - 'productHasOption' + new Reference('sylius_elastic_search.search_criteria_applicator.product_has_multiple_option_codes') ] ); } @@ -53,22 +52,7 @@ public function it_does_nothing_if_there_is_no_search_criteria_applicators() { $this->compile(); - $this->assertContainerBuilderNotHasService('lakion_sylius_elastic_search.search.elastic_engine'); - } - - /** - * @test - */ - public function tagged_applicators_must_have_applies_attribute_configured() - { - $this->setDefinition('lakion_sylius_elastic_search.search.elastic_engine', new Definition()); - $this->setDefinition( - 'lakion_sylius_elastic_search.search_criteria_applicator.product_has_multiple_option_codes', - (new Definition(\stdClass::class))->addTag('search_criteria_applicator') - ); - - $this->expectException(\InvalidArgumentException::class); - $this->compile(); + $this->assertContainerBuilderNotHasService('sylius_elastic_search.search.elastic_engine'); } /** @@ -76,18 +60,18 @@ public function tagged_applicators_must_have_applies_attribute_configured() */ public function it_does_nothing_if_there_is_no_tagged_applicators() { - $this->setDefinition('lakion_sylius_elastic_search.search.elastic_engine', new Definition()); + $this->setDefinition('sylius_elastic_search.search.elastic_engine', new Definition()); $this->setDefinition( - 'lakion_sylius_elastic_search.search_criteria_applicator.product_has_multiple_option_codes', + 'sylius_elastic_search.search_criteria_applicator.product_has_multiple_option_codes', new Definition(\stdClass::class) ); $this->compile(); $this->assertContainerBuilderNotHasServiceDefinitionWithMethodCall( - 'lakion_sylius_elastic_search.search.elastic_engine', + 'sylius_elastic_search.search.elastic_engine', 'addSearchCriteriaApplicator', - [new Reference('lakion_sylius_elastic_search.search_criteria_applicator.product_has_multiple_option_codes')] + [new Reference('sylius_elastic_search.search_criteria_applicator.product_has_multiple_option_codes')] ); } diff --git a/tests/DependencyInjection/ConfigurationTest.php b/tests/DependencyInjection/ConfigurationTest.php index ec3982d..caab2f3 100644 --- a/tests/DependencyInjection/ConfigurationTest.php +++ b/tests/DependencyInjection/ConfigurationTest.php @@ -9,9 +9,9 @@ * file that was distributed with this source code. */ -namespace Tests\Lakion\SyliusElasticSearchBundle\DependencyInjection; +namespace Tests\Sylius\ElasticSearchPlugin\DependencyInjection; -use Lakion\SyliusElasticSearchBundle\DependencyInjection\Configuration; +use Sylius\ElasticSearchPlugin\DependencyInjection\Configuration; use Matthias\SymfonyConfigTest\PhpUnit\ConfigurationTestCaseTrait; /** diff --git a/tests/Event/ProductCreatedTest.php b/tests/Event/ProductCreatedTest.php new file mode 100644 index 0000000..65246e8 --- /dev/null +++ b/tests/Event/ProductCreatedTest.php @@ -0,0 +1,21 @@ +prophesize(ProductInterface::class); + $event = ProductCreated::occur($product->reveal()); + + $this->assertEquals($product->reveal(), $event->product()); + } +} diff --git a/tests/Factory/ProductFactoryTest.php b/tests/Factory/ProductFactoryTest.php new file mode 100644 index 0000000..f0b2668 --- /dev/null +++ b/tests/Factory/ProductFactoryTest.php @@ -0,0 +1,158 @@ +create(); + + $this->assertEquals(null, $product->getCode()); + $this->assertEquals(null, $product->getName()); + $this->assertEquals(null, $product->getLocaleCode()); + $this->assertEquals(new Collection, $product->getAttributeValues()); + $this->assertEquals(null, $product->getPrice()); + $this->assertEquals(null, $product->getChannelCode()); + $this->assertEquals(null, $product->getCreatedAt()); + $this->assertEquals(null, $product->getDescription()); + $this->assertEquals(new Collection, $product->getTaxonCodes()); + } + + /** + * @test + */ + public function it_creates_product_document_from_sylius_product_model() + { + $createdAt = \DateTime::createFromFormat(\DateTime::W3C, '2017-04-18T16:12:55+02:00'); + $syliusProductAttributeValue = new ProductAttributeValue(); + $syliusProductAttribute = new ProductAttribute(); + $syliusProductAttribute->setCurrentLocale('en_US'); + $syliusProductAttribute->setCode('red'); + $syliusProductAttribute->setName('Color red'); + $syliusProductAttributeValue->setLocaleCode('en_US'); + $syliusProductAttribute->setType(TextAttributeType::TYPE); + $syliusProductAttribute->setStorageType(TextAttributeType::TYPE); + $syliusProductAttributeValue->setAttribute($syliusProductAttribute); + $syliusProductAttributeValue->setValue('red'); + + $syliusTaxon = new Taxon(); + $syliusTaxon->setCode('tree'); + $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->setDescription('Lorem ipsum'); + $syliusProduct->setCode('banana'); + $syliusProduct->addAttribute($syliusProductAttributeValue); + + $factory = new ProductFactory(); + /** @var Product $product */ + $product = $factory->createFromSyliusSimpleProductModel( + $syliusProduct, + $syliusLocale, + $syliusChannel + ); + + $taxonCode = new TaxonCode(); + $taxonCode->setValue('tree'); + + $productTaxonCode = new TaxonCode(); + $productTaxonCode->setValue('tree'); + + $productAttribute = new Attribute(); + $productAttribute->setCode('red'); + $productAttribute->setName('Color red'); + + $productAttributeValue = new AttributeValue(); + $productAttributeValue->setValue('red'); + $productAttributeValue->setCode('red'); + $productAttributeValue->setAttribute($productAttribute); + + $this->assertEquals('banana', $product->getCode()); + $this->assertEquals('Banana', $product->getName()); + $this->assertEquals('en_US', $product->getLocaleCode()); + $this->assertEquals( + new Collection([ + $productAttributeValue + ]), + $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($createdAt, $product->getCreatedAt()); + $this->assertEquals('Lorem ipsum', $product->getDescription()); + $this->assertEquals($taxonCode, $product->getMainTaxonCode()); + $this->assertEquals(new Collection([$productTaxonCode]), $product->getTaxonCodes()); + } + + /** + * @test + * + * @expectedException \InvalidArgumentException + */ + public function it_cannot_create_product_document_from_configurable_product() + { + $factory = new ProductFactory(); + + $syliusProduct = new SyliusProduct(); + $syliusProduct->addVariant(new ProductVariant()); + $syliusProduct->addVariant(new ProductVariant()); + $syliusLocale = new Locale(); + $syliusChannel = new Channel(); + + $factory->createFromSyliusSimpleProductModel($syliusProduct, $syliusLocale, $syliusChannel); + } +}