From 0f83a8ba97127bf3f9feb16183e48f5211f2a61d Mon Sep 17 00:00:00 2001 From: Arminek Date: Mon, 29 May 2017 08:15:41 +0200 Subject: [PATCH] [ElasticSearch] Define api end point --- .travis.yml | 3 +- README.md | 35 +++-------- composer.json | 3 +- ...iltering_list_of_products_by_taxon.feature | 10 +-- spec/EventListener/ProductPublisherSpec.php | 14 +++++ spec/Search/Criteria/CriteriaSpec.php | 8 +-- .../ProductHasOptionCodesFilterSpec.php | 27 -------- .../Filtering/ProductInChannelFilterSpec.php | 27 -------- .../ProductInPriceRangeFilterSpec.php | 32 ---------- .../Filtering/ProductInTaxonFilterSpec.php | 27 -------- spec/Search/Criteria/FilteringSpec.php | 4 +- spec/Search/Criteria/OrderingSpec.php | 8 +-- spec/Search/Criteria/PaginatingSpec.php | 18 +++--- spec/Search/Criteria/SearchPhraseSpec.php | 27 -------- ...ctHasMultipleOptionCodesApplicatorSpec.php | 62 ------------------ .../Filter/ProductInChannelApplicatorSpec.php | 17 ++++- .../ProductInPriceRangeApplicatorSpec.php | 15 ++++- .../Filter/ProductInTaxonApplicatorSpec.php | 15 ++++- .../MatchProductByNameApplicatorSpec.php | 25 ++++---- .../Sort/SortByFieldApplicatorSpec.php | 7 ++- .../MatchProductNameQueryFactorySpec.php | 2 +- src/Controller/SearchController.php | 24 ++++--- .../RegisterSearchCriteriaApplicatorPass.php | 6 +- src/EventListener/ProductPublisher.php | 4 +- src/Resources/config/routing.yml | 6 ++ src/Resources/config/services/controller.xml | 3 - .../services/search_criteria_applicator.xml | 15 ++--- src/Search/Criteria/Criteria.php | 24 +++---- src/Search/Criteria/Filtering.php | 2 +- .../Filtering/ProductHasOptionCodesFilter.php | 39 ------------ .../Filtering/ProductInChannelFilter.php | 30 --------- .../Filtering/ProductInPriceRangeFilter.php | 54 ---------------- .../Filtering/ProductInTaxonFilter.php | 30 --------- src/Search/Criteria/Ordering.php | 4 +- src/Search/Criteria/Paginating.php | 6 +- src/Search/Criteria/SearchPhrase.php | 39 ------------ ...roductHasMultipleOptionCodesApplicator.php | 42 ------------- .../Filter/ProductInChannelApplicator.php | 21 +++++-- .../Filter/ProductInPriceRangeApplicator.php | 29 ++++++--- .../Filter/ProductInTaxonApplicator.php | 24 +++++-- .../Query/MatchProductByNameApplicator.php | 39 +++++------- .../Applicator/SearchCriteriaApplicator.php | 52 --------------- .../SearchCriteriaApplicatorInterface.php | 12 +++- .../Applicator/Sort/SortByFieldApplicator.php | 18 ++++-- src/Search/Elastic/ElasticSearchEngine.php | 33 +++------- .../Query/MatchProductNameQueryFactory.php | 4 +- .../Factory/Sort/SortByFieldQueryFactory.php | 2 +- src/Search/SearchEngineInterface.php | 3 +- tests/Application/app/config/routing.yml | 2 +- .../Context/Domain/Shop/ProductContext.php | 63 +++++-------------- ...gisterSearchCriteriaApplicatorPassTest.php | 20 +----- 51 files changed, 278 insertions(+), 758 deletions(-) delete mode 100644 spec/Search/Criteria/Filtering/ProductHasOptionCodesFilterSpec.php delete mode 100644 spec/Search/Criteria/Filtering/ProductInChannelFilterSpec.php delete mode 100644 spec/Search/Criteria/Filtering/ProductInPriceRangeFilterSpec.php delete mode 100644 spec/Search/Criteria/Filtering/ProductInTaxonFilterSpec.php delete mode 100644 spec/Search/Criteria/SearchPhraseSpec.php delete mode 100644 spec/Search/Elastic/Applicator/Filter/ProductHasMultipleOptionCodesApplicatorSpec.php delete mode 100644 src/Search/Criteria/Filtering/ProductHasOptionCodesFilter.php delete mode 100644 src/Search/Criteria/Filtering/ProductInChannelFilter.php delete mode 100644 src/Search/Criteria/Filtering/ProductInPriceRangeFilter.php delete mode 100644 src/Search/Criteria/Filtering/ProductInTaxonFilter.php delete mode 100644 src/Search/Criteria/SearchPhrase.php delete mode 100644 src/Search/Elastic/Applicator/Filter/ProductHasMultipleOptionCodesApplicator.php delete mode 100644 src/Search/Elastic/Applicator/SearchCriteriaApplicator.php diff --git a/.travis.yml b/.travis.yml index e0911d0..0e50c11 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ language: php php: - 7.1 - - 5.6 jdk: - oraclejdk8 @@ -13,7 +12,7 @@ addons: - elasticsearch-5.x packages: - elasticsearch - - oracle-java8-installer + - oracle-java8-set-default services: - elasticsearch diff --git a/README.md b/README.md index b312c52..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 - 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 2ea254d..f374e9d 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,8 @@ "sylius/sylius": "dev-master", "ongr/elasticsearch-dsl": "^5.0", "ongr/elasticsearch-bundle": "^5.0", - "simple-bus/symfony-bridge": "^4.1" + "simple-bus/symfony-bridge": "^4.1", + "beberlei/porpaginas": "^1.0" }, "require-dev": { "behat/behat": "^3.2", 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 cf1defb..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,24 +5,24 @@ Feature: Filtering list of products by taxon I want to be able to filter the products Background: - Given 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 + 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 + 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 + 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/spec/EventListener/ProductPublisherSpec.php b/spec/EventListener/ProductPublisherSpec.php index 2bf64a8..366ec20 100644 --- a/spec/EventListener/ProductPublisherSpec.php +++ b/spec/EventListener/ProductPublisherSpec.php @@ -24,6 +24,7 @@ function it_is_initializable() 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(); @@ -39,4 +40,17 @@ function it_does_not_publish_product_event_if_entity_is_not_a_product(MessageBus $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/Search/Criteria/CriteriaSpec.php b/spec/Search/Criteria/CriteriaSpec.php index d1a56eb..76f515b 100644 --- a/spec/Search/Criteria/CriteriaSpec.php +++ b/spec/Search/Criteria/CriteriaSpec.php @@ -27,20 +27,20 @@ function it_is_created_from_query_parameters_and_resource_alias() 'option' => '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 a03866d..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 e04d6bb..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 c3bd395..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 c8d222a..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 f407335..37af4c8 100644 --- a/spec/Search/Criteria/FilteringSpec.php +++ b/spec/Search/Criteria/FilteringSpec.php @@ -22,7 +22,7 @@ function it_can_be_created_from_query_parameters() 'size' => '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() @@ -35,6 +35,6 @@ function it_removes_page_per_page_and_sort_attributes_from_query_parameters() '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 2545a5d..55099c0 100644 --- a/spec/Search/Criteria/OrderingSpec.php +++ b/spec/Search/Criteria/OrderingSpec.php @@ -21,8 +21,8 @@ function it_can_be_created_from_query_parameters_with_default_direction() 'sort' => 'code', ]]); - $this->getField()->shouldReturn('code.raw'); - $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.raw'); - $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 c3f7d8f..6b72f7e 100644 --- a/spec/Search/Criteria/PaginatingSpec.php +++ b/spec/Search/Criteria/PaginatingSpec.php @@ -19,9 +19,9 @@ function it_can_be_created_form_query_parameters_with_default_values_if_paramete { $this->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() @@ -31,9 +31,9 @@ function it_can_be_created_from_query_parameters_with_default_values_if_paramete '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() @@ -43,8 +43,8 @@ function it_can_be_created_from_query_parameters() '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 8731b41..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 3e87e91..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->addPostFilter($mediumMugTermQuery, BoolQuery::SHOULD)->shouldBeCalled(); - $search->addPostFilter($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->addPostFilter($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 0a5c868..7444143 100644 --- a/spec/Search/Elastic/Applicator/Filter/ProductInChannelApplicatorSpec.php +++ b/spec/Search/Elastic/Applicator/Filter/ProductInChannelApplicatorSpec.php @@ -4,6 +4,8 @@ use ONGR\ElasticsearchDSL\Query\Compound\BoolQuery; use ONGR\ElasticsearchDSL\Query\Joining\NestedQuery; +use Sylius\ElasticSearchPlugin\Document\Product; +use Sylius\ElasticSearchPlugin\Search\Criteria\Criteria; use Sylius\ElasticSearchPlugin\Search\Criteria\Filtering\ProductInChannelFilter; use Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\Filter\ProductInChannelApplicator; use Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\SearchCriteriaApplicatorInterface; @@ -36,10 +38,21 @@ function it_applies_search_criteria_with_channel_code( Search $search, NestedQuery $nestedQuery ) { - $criteria = new ProductInChannelFilter('web'); - $productInChannelQueryFactory->create(['channel_code' => 'web'])->willReturn($nestedQuery); + $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 46011c9..1b57b79 100644 --- a/spec/Search/Elastic/Applicator/Filter/ProductInPriceRangeApplicatorSpec.php +++ b/spec/Search/Elastic/Applicator/Filter/ProductInPriceRangeApplicatorSpec.php @@ -4,6 +4,8 @@ use ONGR\ElasticsearchDSL\Query\Compound\BoolQuery; use ONGR\ElasticsearchDSL\Query\TermLevel\TermQuery; +use Sylius\ElasticSearchPlugin\Document\Product; +use Sylius\ElasticSearchPlugin\Search\Criteria\Criteria; use Sylius\ElasticSearchPlugin\Search\Criteria\Filtering\ProductInPriceRangeFilter; use Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\Filter\ProductInPriceRangeApplicator; use Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\SearchCriteriaApplicatorInterface; @@ -33,10 +35,19 @@ function it_is_search_criteria_applicator() 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($termQuery); + $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 a4e7439..2605ccd 100644 --- a/spec/Search/Elastic/Applicator/Filter/ProductInTaxonApplicatorSpec.php +++ b/spec/Search/Elastic/Applicator/Filter/ProductInTaxonApplicatorSpec.php @@ -4,6 +4,8 @@ use ONGR\ElasticsearchDSL\Query\Compound\BoolQuery; use ONGR\ElasticsearchDSL\Query\TermLevel\TermQuery; +use Sylius\ElasticSearchPlugin\Document\Product; +use Sylius\ElasticSearchPlugin\Search\Criteria\Criteria; use Sylius\ElasticSearchPlugin\Search\Criteria\Filtering\ProductInTaxonFilter; use Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\Filter\ProductInTaxonApplicator; use Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\SearchCriteriaApplicatorInterface; @@ -36,12 +38,21 @@ function it_applies_search_query_for_given_criteria( TermQuery $termQuery, Search $search ) { - $criteria = new ProductInTaxonFilter('mugs'); + $criteria = Criteria::fromQueryParameters(Product::class, ['taxon_code' => 'mugs']); - $productInMainTaxon->create(['taxon_code' => 'mugs'])->willReturn($termQuery); + $productInMainTaxon->create($criteria->filtering()->fields())->willReturn($termQuery); $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 3b0356f..93d4e8c 100644 --- a/spec/Search/Elastic/Applicator/Query/MatchProductByNameApplicatorSpec.php +++ b/spec/Search/Elastic/Applicator/Query/MatchProductByNameApplicatorSpec.php @@ -3,6 +3,8 @@ namespace spec\Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\Query; use ONGR\ElasticsearchDSL\Query\FullText\MatchQuery; +use Sylius\ElasticSearchPlugin\Document\Product; +use Sylius\ElasticSearchPlugin\Search\Criteria\Criteria; use Sylius\ElasticSearchPlugin\Search\Criteria\SearchPhrase; use Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\Query\MatchProductByNameApplicator; use Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\SearchCriteriaApplicatorInterface; @@ -16,9 +18,9 @@ */ final class MatchProductByNameApplicatorSpec extends ObjectBehavior { - function let(QueryFactoryInterface $matchProductNameQueryFactory, QueryFactoryInterface $emptyQueryFactory) + function let(QueryFactoryInterface $matchProductNameQueryFactory) { - $this->beConstructedWith($matchProductNameQueryFactory, $emptyQueryFactory); + $this->beConstructedWith($matchProductNameQueryFactory); } function it_is_initializable() @@ -36,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 ba69640..8395a8a 100644 --- a/spec/Search/Elastic/Applicator/Sort/SortByFieldApplicatorSpec.php +++ b/spec/Search/Elastic/Applicator/Sort/SortByFieldApplicatorSpec.php @@ -2,7 +2,8 @@ namespace spec\Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\Sort; -use Sylius\ElasticSearchPlugin\Search\Criteria\Ordering; +use Sylius\ElasticSearchPlugin\Document\Product; +use Sylius\ElasticSearchPlugin\Search\Criteria\Criteria; use Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\SearchCriteriaApplicatorInterface; use Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\Sort\SortByFieldApplicator; use Sylius\ElasticSearchPlugin\Search\Elastic\Factory\Sort\SortFactoryInterface; @@ -35,8 +36,8 @@ function it_applies_sort_query_to_search_with_given_sorting( Search $search, FieldSort $fieldSort ) { - $criteria = Ordering::fromQueryParameters(['sort' => '-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/Factory/Query/MatchProductNameQueryFactorySpec.php b/spec/Search/Elastic/Factory/Query/MatchProductNameQueryFactorySpec.php index 77200bd..48f2bca 100644 --- a/spec/Search/Elastic/Factory/Query/MatchProductNameQueryFactorySpec.php +++ b/spec/Search/Elastic/Factory/Query/MatchProductNameQueryFactorySpec.php @@ -25,7 +25,7 @@ function it_is_query_factory() function it_creates_match_query_with_name_field_by_default() { - $this->create(['phrase' => 'banana'])->shouldBeLike(new MatchQuery('name', 'banana')); + $this->create(['search' => 'banana'])->shouldBeLike(new MatchQuery('name', 'banana')); } function it_cannot_be_created_without_search_parameter() diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php index 32b8e91..3926991 100644 --- a/src/Controller/SearchController.php +++ b/src/Controller/SearchController.php @@ -11,8 +11,8 @@ namespace Sylius\ElasticSearchPlugin\Controller; -use FOS\RestBundle\View\ConfigurableViewHandlerInterface; use FOS\RestBundle\View\View; +use FOS\RestBundle\View\ViewHandlerInterface; use Sylius\ElasticSearchPlugin\Document\Product; use Sylius\ElasticSearchPlugin\Search\Criteria\Criteria; use Sylius\ElasticSearchPlugin\Search\SearchEngineInterface; @@ -25,7 +25,7 @@ final class SearchController { /** - * @var ConfigurableViewHandlerInterface + * @var ViewHandlerInterface */ private $restViewHandler; @@ -35,10 +35,10 @@ final class SearchController private $searchEngine; /** - * @param ConfigurableViewHandlerInterface $restViewHandler + * @param ViewHandlerInterface $restViewHandler * @param SearchEngineInterface $searchEngine */ - public function __construct(ConfigurableViewHandlerInterface $restViewHandler, SearchEngineInterface $searchEngine) + public function __construct(ViewHandlerInterface $restViewHandler, SearchEngineInterface $searchEngine) { $this->restViewHandler = $restViewHandler; $this->searchEngine = $searchEngine; @@ -49,15 +49,19 @@ public function __construct(ConfigurableViewHandlerInterface $restViewHandler, S * * @return Response */ - public function searchAction(Request $request) + public function __invoke(Request $request) { - $content = $request->getContent(); - $criteria = Criteria::fromQueryParameters(Product::class, json_decode($content, true)); + $content = json_decode($request->getContent(), true); - $result = $this->searchEngine->match($criteria); + if (null === $content) { + $content = $request->query->all(); + } + + $criteria = Criteria::fromQueryParameters(Product::class, $content); - $view = View::create($result); + $result = $this->searchEngine->match($criteria); + $page = $result->take($criteria->paginating()->offset(), $criteria->paginating()->itemsPerPage()); - return $this->restViewHandler->handle($view); + return $this->restViewHandler->handle(View::create($page, Response::HTTP_OK)); } } diff --git a/src/DependencyInjection/Compiler/RegisterSearchCriteriaApplicatorPass.php b/src/DependencyInjection/Compiler/RegisterSearchCriteriaApplicatorPass.php index 0d27e1d..06cc4d3 100644 --- a/src/DependencyInjection/Compiler/RegisterSearchCriteriaApplicatorPass.php +++ b/src/DependencyInjection/Compiler/RegisterSearchCriteriaApplicatorPass.php @@ -30,12 +30,8 @@ public function process(ContainerBuilder $container) } 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('sylius_elastic_search.search.elastic_engine'); - $engineDefinition->addMethodCall('addSearchCriteriaApplicator', [new Reference($taggedServiceId), $taggedServiceConfig[0]['applies']]); + $engineDefinition->addMethodCall('addSearchCriteriaApplicator', [new Reference($taggedServiceId)]); } } } diff --git a/src/EventListener/ProductPublisher.php b/src/EventListener/ProductPublisher.php index 7b6b5ce..1fcc351 100644 --- a/src/EventListener/ProductPublisher.php +++ b/src/EventListener/ProductPublisher.php @@ -29,7 +29,9 @@ public function postPersist(LifecycleEventArgs $event) { $product = $event->getEntity(); if ($product instanceof ProductInterface) { - $this->eventBus->handle(ProductCreated::occur($product)); + if ($product->isSimple()) { + $this->eventBus->handle(ProductCreated::occur($product)); + } } } } diff --git a/src/Resources/config/routing.yml b/src/Resources/config/routing.yml index e69de29..cb9fb34 100644 --- a/src/Resources/config/routing.yml +++ b/src/Resources/config/routing.yml @@ -0,0 +1,6 @@ +sylius_shop_search: + path: /search + methods: [GET] + defaults: + _controller: sylius_elastic_search.controller.search + _format: json diff --git a/src/Resources/config/services/controller.xml b/src/Resources/config/services/controller.xml index 75272b8..abfbc3b 100644 --- a/src/Resources/config/services/controller.xml +++ b/src/Resources/config/services/controller.xml @@ -8,9 +8,6 @@ - - - diff --git a/src/Resources/config/services/search_criteria_applicator.xml b/src/Resources/config/services/search_criteria_applicator.xml index a80f4c9..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/Search/Criteria/Criteria.php b/src/Search/Criteria/Criteria.php index ad4032a..e0f95ba 100644 --- a/src/Search/Criteria/Criteria.php +++ b/src/Search/Criteria/Criteria.php @@ -10,7 +10,7 @@ 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 26152df..b372504 100644 --- a/src/Search/Criteria/Filtering.php +++ b/src/Search/Criteria/Filtering.php @@ -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 8033095..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 699cc6f..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 b84a908..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 ebb828d..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 5d01ccd..ac0e884 100644 --- a/src/Search/Criteria/Ordering.php +++ b/src/Search/Criteria/Ordering.php @@ -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 7ed0687..78cdfce 100644 --- a/src/Search/Criteria/Paginating.php +++ b/src/Search/Criteria/Paginating.php @@ -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 828d201..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 38efc25..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->addPostFilter( - $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 8a8978b..962ff49 100644 --- a/src/Search/Elastic/Applicator/Filter/ProductInChannelApplicator.php +++ b/src/Search/Elastic/Applicator/Filter/ProductInChannelApplicator.php @@ -3,15 +3,15 @@ namespace Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\Filter; use ONGR\ElasticsearchDSL\Query\Compound\BoolQuery; -use Sylius\ElasticSearchPlugin\Search\Criteria\Filtering\ProductInChannelFilter; -use Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\SearchCriteriaApplicator; +use Sylius\ElasticSearchPlugin\Search\Criteria\Criteria; +use Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\SearchCriteriaApplicatorInterface; use Sylius\ElasticSearchPlugin\Search\Elastic\Factory\Query\QueryFactoryInterface; use ONGR\ElasticsearchDSL\Search; /** * @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->addPostFilter($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 532ccd4..de9256d 100644 --- a/src/Search/Elastic/Applicator/Filter/ProductInPriceRangeApplicator.php +++ b/src/Search/Elastic/Applicator/Filter/ProductInPriceRangeApplicator.php @@ -3,15 +3,15 @@ namespace Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\Filter; use ONGR\ElasticsearchDSL\Query\Compound\BoolQuery; -use Sylius\ElasticSearchPlugin\Search\Criteria\Filtering\ProductInPriceRangeFilter; -use Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\SearchCriteriaApplicator; +use Sylius\ElasticSearchPlugin\Search\Criteria\Criteria; +use Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\SearchCriteriaApplicatorInterface; use Sylius\ElasticSearchPlugin\Search\Elastic\Factory\Query\QueryFactoryInterface; use ONGR\ElasticsearchDSL\Search; /** * @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->addPostFilter( - $this->productInPriceRangeQueryFactory->create([ - 'product_price_range' => [ - 'grater_than' => $inPriceRangeFilter->getGraterThan(), - 'less_than' => $inPriceRangeFilter->getLessThan() - ] - ]), + $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 315ec02..0f9c9cb 100644 --- a/src/Search/Elastic/Applicator/Filter/ProductInTaxonApplicator.php +++ b/src/Search/Elastic/Applicator/Filter/ProductInTaxonApplicator.php @@ -3,15 +3,15 @@ namespace Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\Filter; use ONGR\ElasticsearchDSL\Query\Compound\BoolQuery; -use Sylius\ElasticSearchPlugin\Search\Criteria\Filtering\ProductInTaxonFilter; -use Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\SearchCriteriaApplicator; +use Sylius\ElasticSearchPlugin\Search\Criteria\Criteria; +use Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\SearchCriteriaApplicatorInterface; use Sylius\ElasticSearchPlugin\Search\Elastic\Factory\Query\QueryFactoryInterface; use ONGR\ElasticsearchDSL\Search; /** * @author Arkadiusz Krakowiak */ -final class ProductInTaxonApplicator extends SearchCriteriaApplicator +final class ProductInTaxonApplicator implements SearchCriteriaApplicatorInterface { /** * @var QueryFactoryInterface @@ -29,8 +29,22 @@ public function __construct(QueryFactoryInterface $productInMainTaxonQueryFactor /** * {@inheritdoc} */ - public function applyProductInTaxonFilter(ProductInTaxonFilter $inTaxonFilter, Search $search) + public function apply(Criteria $criteria, Search $search) { - $search->addPostFilter($this->productInMainTaxonQueryFactory->create(['taxon_code' => $inTaxonFilter->getTaxonCode()]), BoolQuery::SHOULD); + $search->addPostFilter($this->productInMainTaxonQueryFactory->create( + $criteria->filtering()->fields()), + BoolQuery::SHOULD + ); + } + + /** + * {@inheritdoc} + */ + public function supports(Criteria $criteria) + { + 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 9bdb32f..f156977 100644 --- a/src/Search/Elastic/Applicator/Query/MatchProductByNameApplicator.php +++ b/src/Search/Elastic/Applicator/Query/MatchProductByNameApplicator.php @@ -2,50 +2,45 @@ namespace Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\Query; -use Sylius\ElasticSearchPlugin\Search\Criteria\SearchPhrase; -use Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\SearchCriteriaApplicator; +use Sylius\ElasticSearchPlugin\Search\Criteria\Criteria; +use Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\SearchCriteriaApplicatorInterface; use Sylius\ElasticSearchPlugin\Search\Elastic\Factory\Query\QueryFactoryInterface; use ONGR\ElasticsearchDSL\Search; /** * @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 ca507be..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 4e80dcd..e56c68f 100644 --- a/src/Search/Elastic/Applicator/SearchCriteriaApplicatorInterface.php +++ b/src/Search/Elastic/Applicator/SearchCriteriaApplicatorInterface.php @@ -12,6 +12,7 @@ namespace Sylius\ElasticSearchPlugin\Search\Elastic\Applicator; use ONGR\ElasticsearchDSL\Search; +use Sylius\ElasticSearchPlugin\Search\Criteria\Criteria; /** * @author Arkadiusz Krakowiak @@ -19,8 +20,15 @@ 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 ca5d055..3c97f09 100644 --- a/src/Search/Elastic/Applicator/Sort/SortByFieldApplicator.php +++ b/src/Search/Elastic/Applicator/Sort/SortByFieldApplicator.php @@ -2,15 +2,15 @@ namespace Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\Sort; -use Sylius\ElasticSearchPlugin\Search\Criteria\Ordering; -use Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\SearchCriteriaApplicator; +use Sylius\ElasticSearchPlugin\Search\Criteria\Criteria; +use Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\SearchCriteriaApplicatorInterface; use Sylius\ElasticSearchPlugin\Search\Elastic\Factory\Sort\SortFactoryInterface; use ONGR\ElasticsearchDSL\Search; /** * @author Arkadiusz Krakowiak */ -final class SortByFieldApplicator extends SearchCriteriaApplicator +final class SortByFieldApplicator implements SearchCriteriaApplicatorInterface { /** * @var SortFactoryInterface @@ -28,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 cc00cc0..72e49dd 100644 --- a/src/Search/Elastic/ElasticSearchEngine.php +++ b/src/Search/Elastic/ElasticSearchEngine.php @@ -3,6 +3,7 @@ namespace Sylius\ElasticSearchPlugin\Search\Elastic; use ONGR\ElasticsearchBundle\Service\Manager; +use Porpaginas\Arrays\ArrayResult; use Sylius\ElasticSearchPlugin\Search\Criteria\Criteria; use Sylius\ElasticSearchPlugin\Search\Elastic\Applicator\SearchCriteriaApplicatorInterface; use Sylius\ElasticSearchPlugin\Search\SearchEngineInterface; @@ -22,11 +23,6 @@ final class ElasticSearchEngine implements SearchEngineInterface */ private $searchCriteriaApplicators = []; - /** - * @var SearchCriteriaApplicatorInterface - */ - private $sortingApplicator; - /** * @param Manager $manager * @param SearchCriteriaApplicatorInterface $sortingApplicator @@ -34,18 +30,14 @@ final class ElasticSearchEngine implements SearchEngineInterface public function __construct(Manager $manager, SearchCriteriaApplicatorInterface $sortingApplicator) { $this->manager = $manager; - $this->sortingApplicator = $sortingApplicator; } /** * @param SearchCriteriaApplicatorInterface $searchCriteriaApplicator - * @param string $criteriaClass */ - public function addSearchCriteriaApplicator( - SearchCriteriaApplicatorInterface $searchCriteriaApplicator, - $criteriaClass - ) { - $this->searchCriteriaApplicators[$criteriaClass] = $searchCriteriaApplicator; + public function addSearchCriteriaApplicator(SearchCriteriaApplicatorInterface $searchCriteriaApplicator) + { + $this->searchCriteriaApplicators[] = $searchCriteriaApplicator; } /** @@ -53,22 +45,15 @@ public function addSearchCriteriaApplicator( */ public function match(Criteria $criteria) { - $repository = $this->manager->getRepository($criteria->getResourceAlias()); + $repository = $this->manager->getRepository($criteria->documentClass()); $search = $repository->createSearch(); - - foreach ($criteria->getFiltering()->getFields() as $filter) { - if (!is_object($filter)) { - continue; - } - - if (isset($this->searchCriteriaApplicators[get_class($filter)])) { - $this->searchCriteriaApplicators[get_class($filter)]->apply($filter, $search); + foreach ($this->searchCriteriaApplicators as $searchCriteriaApplicator) { + if ($searchCriteriaApplicator->supports($criteria)) { + $searchCriteriaApplicator->apply($criteria, $search); } } - $this->sortingApplicator->applyOrdering($criteria->getOrdering(), $search); - - return $repository->findDocuments($search); + return new ArrayResult(iterator_to_array($repository->findArray($search))); } } diff --git a/src/Search/Elastic/Factory/Query/MatchProductNameQueryFactory.php b/src/Search/Elastic/Factory/Query/MatchProductNameQueryFactory.php index d52b9a9..7c26db4 100644 --- a/src/Search/Elastic/Factory/Query/MatchProductNameQueryFactory.php +++ b/src/Search/Elastic/Factory/Query/MatchProductNameQueryFactory.php @@ -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 MatchQuery('name', $parameters['phrase']); + return new MatchQuery('name', $parameters['search']); } } diff --git a/src/Search/Elastic/Factory/Sort/SortByFieldQueryFactory.php b/src/Search/Elastic/Factory/Sort/SortByFieldQueryFactory.php index f2531d8..421a076 100644 --- a/src/Search/Elastic/Factory/Sort/SortByFieldQueryFactory.php +++ b/src/Search/Elastic/Factory/Sort/SortByFieldQueryFactory.php @@ -15,6 +15,6 @@ final class SortByFieldQueryFactory implements SortFactoryInterface */ public function create(Ordering $ordering) { - return new FieldSort($ordering->getField(), $ordering->getDirection()); + return new FieldSort($ordering->field(), $ordering->direction()); } } diff --git a/src/Search/SearchEngineInterface.php b/src/Search/SearchEngineInterface.php index ce0d20e..6dc7dbe 100644 --- a/src/Search/SearchEngineInterface.php +++ b/src/Search/SearchEngineInterface.php @@ -12,6 +12,7 @@ namespace Sylius\ElasticSearchPlugin\Search; use ONGR\ElasticsearchBundle\Result\DocumentIterator; +use Porpaginas\Result; use Sylius\ElasticSearchPlugin\Search\Criteria\Criteria; /** @@ -22,7 +23,7 @@ interface SearchEngineInterface /** * @param Criteria $criteria * - * @return DocumentIterator + * @return Result */ public function match(Criteria $criteria); } diff --git a/tests/Application/app/config/routing.yml b/tests/Application/app/config/routing.yml index 8344829..079afc3 100644 --- a/tests/Application/app/config/routing.yml +++ b/tests/Application/app/config/routing.yml @@ -17,5 +17,5 @@ sylius_api: prefix: /api sylius_search: - resource: "@LakionSyliusElasticSearchBundle/Resources/config/routing.yml" + resource: "@SyliusElasticSearchPlugin/Resources/config/routing.yml" prefix: / diff --git a/tests/Behat/Context/Domain/Shop/ProductContext.php b/tests/Behat/Context/Domain/Shop/ProductContext.php index 836872c..44d0178 100644 --- a/tests/Behat/Context/Domain/Shop/ProductContext.php +++ b/tests/Behat/Context/Domain/Shop/ProductContext.php @@ -13,6 +13,8 @@ use Behat\Behat\Context\Context; use ONGR\ElasticsearchBundle\Result\DocumentIterator; +use Porpaginas\Page; +use Porpaginas\Result; use Sylius\ElasticSearchPlugin\Document\Product; use Sylius\ElasticSearchPlugin\Search\Criteria\Criteria; use Sylius\ElasticSearchPlugin\Search\Criteria\Filtering\ProductHasOptionCodesFilter; @@ -46,49 +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) { - $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); } @@ -106,7 +71,7 @@ public function iViewTheListOfTheProductsWithoutFiltering() */ public function iFilterThemByChannel($channelCode) { - $criteria = Criteria::fromQueryParameters(Product::class, [new ProductInChannelFilter($channelCode)]); + $criteria = Criteria::fromQueryParameters(Product::class, ['channel_code' => $channelCode]); $this->match($criteria); } @@ -116,8 +81,8 @@ public function iFilterThemByChannel($channelCode) public function iFilterThemByChannelAndPriceBetweenAnd($channelCode, $graterThan, $lessThan) { $criteria = Criteria::fromQueryParameters(Product::class, [ - new ProductInChannelFilter($channelCode), - new ProductInPriceRangeFilter($graterThan, $lessThan), + 'channel_code' => $channelCode, + 'product_price_range' => ['grater_than' => $graterThan, 'less_than' => $lessThan], ]); $this->match($criteria); @@ -128,7 +93,7 @@ public function iFilterThemByChannelAndPriceBetweenAnd($channelCode, $graterThan */ public function iFilterThemByTaxon($taxonCode) { - $criteria = Criteria::fromQueryParameters(Product::class, [new ProductInTaxonFilter($taxonCode)]); + $criteria = Criteria::fromQueryParameters(Product::class, ['taxon_code' => $taxonCode]); $this->match($criteria); } @@ -150,7 +115,7 @@ public function iSortThemByNameInAscendingOrder($field, $order) */ public function iSearchForProductsWithName($name) { - $criteria = Criteria::fromQueryParameters(Product::class, [new SearchPhrase($name)]); + $criteria = Criteria::fromQueryParameters(Product::class, ['search' => $name]); $this->match($criteria); } @@ -159,7 +124,7 @@ public function iSearchForProductsWithName($name) */ public function iShouldSeeProductsOnTheList($numberOfProducts) { - /** @var DocumentIterator $result */ + /** @var Result $result */ $result = $this->sharedStorage->get('search_result'); Assert::eq($result->count(), $numberOfProducts); @@ -170,7 +135,7 @@ public function iShouldSeeProductsOnTheList($numberOfProducts) */ public function iShouldSeeProductsInOrderLike(...$productNames) { - /** @var DocumentIterator $searchResult */ + /** @var Result $searchResult */ $searchResult = $this->sharedStorage->get('search_result'); /** @@ -178,13 +143,13 @@ public function iShouldSeeProductsInOrderLike(...$productNames) * @var Product $product */ foreach ($searchResult as $position => $product) { - if ($product->getName() !== $productNames[$position]) { + 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], - $product->getName() + $product['name'] ) ); } @@ -199,12 +164,12 @@ public function iShouldSeeProductsInOrderLike(...$productNames) */ public function itShouldBe(...$expectedProductNames) { - /** @var DocumentIterator $searchResult */ + /** @var Result $searchResult */ $searchResult = $this->sharedStorage->get('search_result'); /** @var Product $product */ foreach ($searchResult as $product) { - Assert::oneOf($product->getName(), $expectedProductNames); + Assert::oneOf($product['name'], $expectedProductNames); } } diff --git a/tests/DependencyInjection/Compiler/RegisterSearchCriteriaApplicatorPassTest.php b/tests/DependencyInjection/Compiler/RegisterSearchCriteriaApplicatorPassTest.php index dd6ecb6..c165141 100644 --- a/tests/DependencyInjection/Compiler/RegisterSearchCriteriaApplicatorPassTest.php +++ b/tests/DependencyInjection/Compiler/RegisterSearchCriteriaApplicatorPassTest.php @@ -31,7 +31,7 @@ public function it_collects_tagged_search_criteria_applicators() $this->setDefinition('sylius_elastic_search.search.elastic_engine', new Definition()); $this->setDefinition( 'sylius_elastic_search.search_criteria_applicator.product_has_multiple_option_codes', - (new Definition(\stdClass::class))->addTag('search_criteria_applicator', ['applies' => 'productHasOption']) + (new Definition(\stdClass::class))->addTag('search_criteria_applicator') ); $this->compile(); @@ -40,8 +40,7 @@ public function it_collects_tagged_search_criteria_applicators() 'sylius_elastic_search.search.elastic_engine', 'addSearchCriteriaApplicator', [ - new Reference('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') ] ); } @@ -56,21 +55,6 @@ public function it_does_nothing_if_there_is_no_search_criteria_applicators() $this->assertContainerBuilderNotHasService('sylius_elastic_search.search.elastic_engine'); } - /** - * @test - */ - public function tagged_applicators_must_have_applies_attribute_configured() - { - $this->setDefinition('sylius_elastic_search.search.elastic_engine', new Definition()); - $this->setDefinition( - '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(); - } - /** * @test */