From 97fab0cd0bd69eacd6bef0163916d71c4297f319 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Thu, 30 Sep 2021 14:48:30 +0200 Subject: [PATCH 001/142] refactor: Prepare the search plugin v2 --- .idea/.gitignore | 8 + .idea/SyliusSearchPlugin.iml | 12 + .idea/inspectionProfiles/Project_Default.xml | 6 + .idea/modules.xml | 8 + .idea/php.xml | 4 + .idea/vcs.xml | 6 + README.md | 474 +----------------- TESTING.md | 6 +- composer.json | 65 +-- .../monsieurbiz_sylius_search_plugin.yaml | 0 .../monsieurbiz_sylius_search_plugin.yaml | 0 recipe/{dev => 1.0}/manifest.json | 0 recipe/2.0-dev | 1 + screenshot_instant.jpg | Bin 724385 -> 0 bytes screenshot_search.jpg | Bin 674830 -> 0 bytes screenshot_taxon.jpg | Bin 618703 -> 0 bytes src/Adapter/ResultSetAdapter.php | 59 --- src/Command/PopulateCommand.php | 67 --- src/Context/RequestTaxonContext.php | 57 --- src/Context/TaxonContextInterface.php | 21 - src/Controller/SearchController.php | 198 -------- src/DependencyInjection/Configuration.php | 82 +-- .../MonsieurBizSyliusSearchExtension.php | 3 +- src/Entity/Product/FilterableInterface.php | 21 - src/EventListener/DocumentListener.php | 65 --- src/Exception/MissingAttributeException.php | 20 - src/Exception/MissingConfigFileException.php | 20 - src/Exception/MissingLocaleException.php | 20 - src/Exception/MissingParamException.php | 20 - src/Exception/MissingPriceException.php | 20 - src/Exception/NotSupportedTypeException.php | 20 - src/Exception/ReadFileException.php | 20 - src/Exception/ReadOnlyIndexException.php | 20 - src/Exception/TaxonNotFoundException.php | 24 - src/Exception/UnknownGridConfigType.php | 28 -- .../Factory/FilterableFixtureFactory.php | 101 ---- .../FilterableFixtureFactoryInterface.php | 20 - src/Fixture/FilterableFixture.php | 56 --- src/Fixture/FilterableFixtureInterface.php | 20 - .../ProductAttributeTypeExtension.php | 39 -- .../Extension/ProductOptionTypeExtension.php | 39 -- src/Form/Type/SearchType.php | 54 -- src/Helper/AggregationHelper.php | 135 ----- src/Helper/FilterHelper.php | 218 -------- src/Helper/RenderDocumentUrlHelper.php | 32 -- src/Helper/SlugHelper.php | 27 - src/Helper/SortHelper.php | 75 --- src/Model/ArrayObject.php | 22 - src/Model/Config/FilesConfig.php | 62 --- src/Model/Config/GridConfig.php | 339 ------------- src/Model/Document/Filter.php | 92 ---- src/Model/Document/FilterValue.php | 71 --- src/Model/Document/Index/AbstractIndex.php | 81 --- src/Model/Document/Index/Indexer.php | 191 ------- src/Model/Document/Index/Search.php | 280 ----------- src/Model/Document/RangeFilter.php | 115 ----- src/Model/Document/Result.php | 217 -------- src/Model/Document/ResultInterface.php | 126 ----- src/Model/Document/ResultSet.php | 335 ------------- .../Documentable/DocumentableInterface.php | 23 - .../Documentable/DocumentableProductTrait.php | 239 --------- src/Model/Product/FilterableTrait.php | 41 -- src/Provider/DocumentRepositoryProvider.php | 47 -- src/Provider/SearchQueryProvider.php | 91 ---- src/Provider/UrlParamsProvider.php | 67 --- .../views/ProductAttribute/_form.html.twig | 26 - .../views/ProductOption/_form.html.twig | 18 - src/Resources/config/config.yaml | 2 - .../elasticsearch/mappings/analyzers.yaml | 14 - .../mappings/documents-de_de_mapping.yaml | 72 --- .../mappings/documents-en_mapping.yaml | 72 --- .../mappings/documents-en_us_mapping.yaml | 72 --- .../mappings/documents-es_es_mapping.yaml | 72 --- .../mappings/documents-es_mx_mapping.yaml | 72 --- .../mappings/documents-fr_fr_mapping.yaml | 72 --- .../mappings/documents-fr_mapping.yaml | 72 --- .../mappings/documents-it_it_mapping.yaml | 72 --- .../mappings/documents-pl_pl_mapping.yaml | 72 --- .../mappings/documents-pt_pt_mapping.yaml | 72 --- .../mappings/documents-zh_cn_mapping.yaml | 72 --- .../config/elasticsearch/queries/instant.yaml | 56 --- .../config/elasticsearch/queries/search.yaml | 56 --- .../config/elasticsearch/queries/taxon.yaml | 18 - src/Resources/config/jane/document.json | 118 ----- src/Resources/config/jane/dto-config.php | 20 - src/Resources/config/routing.yaml | 5 - src/Resources/config/routing/shop.yaml | 27 - src/Resources/config/services.yaml | 69 --- src/Resources/config/sylius.yaml | 8 - src/Resources/config/twig.yaml | 3 - src/Resources/public/js/app.js | 74 --- src/Resources/translations/messages.en.yml | 30 -- src/Resources/translations/messages.fr.yml | 30 -- src/Resources/translations/messages.it.yml | 30 -- src/Resources/views/.gitkeep | 0 src/Resources/views/Common/_box.html.twig | 27 - src/Resources/views/Common/_filter.html.twig | 28 -- src/Resources/views/Common/_filters.html.twig | 50 -- .../views/Common/_pagination.html.twig | 16 - .../views/Common/_rangeFilter.html.twig | 25 - src/Resources/views/Common/_sorting.html.twig | 52 -- src/Resources/views/Event/_sonata.html.twig | 1 - src/Resources/views/Event/_sylius.html.twig | 1 - src/Resources/views/Event/event.html.twig | 5 - src/Resources/views/Header/form.html.twig | 1 - src/Resources/views/Instant/_box.html.twig | 27 - src/Resources/views/Instant/result.html.twig | 9 - src/Resources/views/Search/_box.html.twig | 1 - src/Resources/views/Search/_filters.html.twig | 1 - src/Resources/views/Search/_header.html.twig | 7 - .../views/Search/_pagination.html.twig | 1 - src/Resources/views/Search/_sidebar.html.twig | 1 - src/Resources/views/Search/_sorting.html.twig | 1 - src/Resources/views/Search/result.html.twig | 44 -- src/Resources/views/Taxon/_box.html.twig | 1 - src/Resources/views/Taxon/_filters.html.twig | 1 - .../views/Taxon/_pagination.html.twig | 1 - src/Resources/views/Taxon/_sidebar.html.twig | 6 - src/Resources/views/Taxon/_sorting.html.twig | 1 - src/Resources/views/Taxon/_tree.html.twig | 11 - src/Resources/views/Taxon/result.html.twig | 44 -- src/Resources/views/form.html.twig | 9 - src/Resources/views/js.html.twig | 17 - src/Twig/Extension/CheckMethodExists.php | 43 -- src/Twig/Extension/RenderDocumentUrl.php | 44 -- src/Twig/Extension/RenderSearchForm.php | 61 --- src/generated/Model/Attributes.php | 142 ------ src/generated/Model/Document.php | 331 ------------ src/generated/Model/Price.php | 88 ---- src/generated/Model/Taxon.php | 142 ------ .../Normalizer/AttributesNormalizer.php | 112 ----- .../Normalizer/DocumentNormalizer.php | 228 --------- .../Normalizer/JaneObjectNormalizer.php | 48 -- .../Normalizer/NormalizerFactory.php | 18 - src/generated/Normalizer/PriceNormalizer.php | 80 --- src/generated/Normalizer/TaxonNormalizer.php | 104 ---- submit_filters.png | Bin 42313 -> 0 bytes tests/Application/config/bootstrap.php | 2 +- tests/Application/config/bundles.php | 4 +- .../dev/monsieurbiz_sylius_search_plugin.yaml | 10 - .../monsieurbiz_sylius_search_plugin.yaml | 1 - .../monsieurbiz_sylius_search_plugin.yaml | 10 - .../src/Entity/Product/Product.php | 35 -- .../src/Entity/Product/ProductAttribute.php | 35 -- .../src/Entity/Product/ProductOption.php | 35 -- .../src/Migrations/Version20201016105958.php | 37 -- .../templates/bundles/SyliusAdminBundle | 1 - 147 files changed, 88 insertions(+), 7938 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/SyliusSearchPlugin.iml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/php.xml create mode 100644 .idea/vcs.xml rename recipe/{dev => 1.0}/config/packages/monsieurbiz_sylius_search_plugin.yaml (100%) rename recipe/{dev => 1.0}/config/routes/monsieurbiz_sylius_search_plugin.yaml (100%) rename recipe/{dev => 1.0}/manifest.json (100%) create mode 120000 recipe/2.0-dev delete mode 100644 screenshot_instant.jpg delete mode 100644 screenshot_search.jpg delete mode 100644 screenshot_taxon.jpg delete mode 100644 src/Adapter/ResultSetAdapter.php delete mode 100644 src/Command/PopulateCommand.php delete mode 100644 src/Context/RequestTaxonContext.php delete mode 100644 src/Context/TaxonContextInterface.php delete mode 100644 src/Controller/SearchController.php delete mode 100644 src/Entity/Product/FilterableInterface.php delete mode 100644 src/EventListener/DocumentListener.php delete mode 100644 src/Exception/MissingAttributeException.php delete mode 100644 src/Exception/MissingConfigFileException.php delete mode 100644 src/Exception/MissingLocaleException.php delete mode 100644 src/Exception/MissingParamException.php delete mode 100644 src/Exception/MissingPriceException.php delete mode 100644 src/Exception/NotSupportedTypeException.php delete mode 100644 src/Exception/ReadFileException.php delete mode 100644 src/Exception/ReadOnlyIndexException.php delete mode 100644 src/Exception/TaxonNotFoundException.php delete mode 100644 src/Exception/UnknownGridConfigType.php delete mode 100644 src/Fixture/Factory/FilterableFixtureFactory.php delete mode 100644 src/Fixture/Factory/FilterableFixtureFactoryInterface.php delete mode 100644 src/Fixture/FilterableFixture.php delete mode 100644 src/Fixture/FilterableFixtureInterface.php delete mode 100644 src/Form/Extension/ProductAttributeTypeExtension.php delete mode 100644 src/Form/Extension/ProductOptionTypeExtension.php delete mode 100644 src/Form/Type/SearchType.php delete mode 100644 src/Helper/AggregationHelper.php delete mode 100644 src/Helper/FilterHelper.php delete mode 100644 src/Helper/RenderDocumentUrlHelper.php delete mode 100644 src/Helper/SlugHelper.php delete mode 100644 src/Helper/SortHelper.php delete mode 100644 src/Model/ArrayObject.php delete mode 100644 src/Model/Config/FilesConfig.php delete mode 100644 src/Model/Config/GridConfig.php delete mode 100644 src/Model/Document/Filter.php delete mode 100644 src/Model/Document/FilterValue.php delete mode 100644 src/Model/Document/Index/AbstractIndex.php delete mode 100644 src/Model/Document/Index/Indexer.php delete mode 100644 src/Model/Document/Index/Search.php delete mode 100644 src/Model/Document/RangeFilter.php delete mode 100644 src/Model/Document/Result.php delete mode 100644 src/Model/Document/ResultInterface.php delete mode 100644 src/Model/Document/ResultSet.php delete mode 100644 src/Model/Documentable/DocumentableInterface.php delete mode 100644 src/Model/Documentable/DocumentableProductTrait.php delete mode 100644 src/Model/Product/FilterableTrait.php delete mode 100644 src/Provider/DocumentRepositoryProvider.php delete mode 100644 src/Provider/SearchQueryProvider.php delete mode 100644 src/Provider/UrlParamsProvider.php delete mode 100644 src/Resources/SyliusAdminBundle/views/ProductAttribute/_form.html.twig delete mode 100644 src/Resources/SyliusAdminBundle/views/ProductOption/_form.html.twig delete mode 100644 src/Resources/config/elasticsearch/mappings/analyzers.yaml delete mode 100644 src/Resources/config/elasticsearch/mappings/documents-de_de_mapping.yaml delete mode 100644 src/Resources/config/elasticsearch/mappings/documents-en_mapping.yaml delete mode 100644 src/Resources/config/elasticsearch/mappings/documents-en_us_mapping.yaml delete mode 100644 src/Resources/config/elasticsearch/mappings/documents-es_es_mapping.yaml delete mode 100644 src/Resources/config/elasticsearch/mappings/documents-es_mx_mapping.yaml delete mode 100644 src/Resources/config/elasticsearch/mappings/documents-fr_fr_mapping.yaml delete mode 100644 src/Resources/config/elasticsearch/mappings/documents-fr_mapping.yaml delete mode 100644 src/Resources/config/elasticsearch/mappings/documents-it_it_mapping.yaml delete mode 100644 src/Resources/config/elasticsearch/mappings/documents-pl_pl_mapping.yaml delete mode 100644 src/Resources/config/elasticsearch/mappings/documents-pt_pt_mapping.yaml delete mode 100644 src/Resources/config/elasticsearch/mappings/documents-zh_cn_mapping.yaml delete mode 100644 src/Resources/config/elasticsearch/queries/instant.yaml delete mode 100644 src/Resources/config/elasticsearch/queries/search.yaml delete mode 100644 src/Resources/config/elasticsearch/queries/taxon.yaml delete mode 100644 src/Resources/config/jane/document.json delete mode 100644 src/Resources/config/jane/dto-config.php delete mode 100644 src/Resources/config/routing.yaml delete mode 100644 src/Resources/config/routing/shop.yaml delete mode 100644 src/Resources/config/sylius.yaml delete mode 100644 src/Resources/config/twig.yaml delete mode 100644 src/Resources/public/js/app.js delete mode 100644 src/Resources/translations/messages.en.yml delete mode 100644 src/Resources/translations/messages.fr.yml delete mode 100644 src/Resources/translations/messages.it.yml delete mode 100644 src/Resources/views/.gitkeep delete mode 100644 src/Resources/views/Common/_box.html.twig delete mode 100644 src/Resources/views/Common/_filter.html.twig delete mode 100644 src/Resources/views/Common/_filters.html.twig delete mode 100644 src/Resources/views/Common/_pagination.html.twig delete mode 100644 src/Resources/views/Common/_rangeFilter.html.twig delete mode 100644 src/Resources/views/Common/_sorting.html.twig delete mode 100644 src/Resources/views/Event/_sonata.html.twig delete mode 100644 src/Resources/views/Event/_sylius.html.twig delete mode 100644 src/Resources/views/Event/event.html.twig delete mode 100644 src/Resources/views/Header/form.html.twig delete mode 100644 src/Resources/views/Instant/_box.html.twig delete mode 100644 src/Resources/views/Instant/result.html.twig delete mode 100644 src/Resources/views/Search/_box.html.twig delete mode 100644 src/Resources/views/Search/_filters.html.twig delete mode 100644 src/Resources/views/Search/_header.html.twig delete mode 100644 src/Resources/views/Search/_pagination.html.twig delete mode 100644 src/Resources/views/Search/_sidebar.html.twig delete mode 100644 src/Resources/views/Search/_sorting.html.twig delete mode 100644 src/Resources/views/Search/result.html.twig delete mode 100644 src/Resources/views/Taxon/_box.html.twig delete mode 100644 src/Resources/views/Taxon/_filters.html.twig delete mode 100644 src/Resources/views/Taxon/_pagination.html.twig delete mode 100644 src/Resources/views/Taxon/_sidebar.html.twig delete mode 100644 src/Resources/views/Taxon/_sorting.html.twig delete mode 100644 src/Resources/views/Taxon/_tree.html.twig delete mode 100644 src/Resources/views/Taxon/result.html.twig delete mode 100644 src/Resources/views/form.html.twig delete mode 100644 src/Resources/views/js.html.twig delete mode 100644 src/Twig/Extension/CheckMethodExists.php delete mode 100644 src/Twig/Extension/RenderDocumentUrl.php delete mode 100644 src/Twig/Extension/RenderSearchForm.php delete mode 100644 src/generated/Model/Attributes.php delete mode 100644 src/generated/Model/Document.php delete mode 100644 src/generated/Model/Price.php delete mode 100644 src/generated/Model/Taxon.php delete mode 100644 src/generated/Normalizer/AttributesNormalizer.php delete mode 100644 src/generated/Normalizer/DocumentNormalizer.php delete mode 100644 src/generated/Normalizer/JaneObjectNormalizer.php delete mode 100644 src/generated/Normalizer/NormalizerFactory.php delete mode 100644 src/generated/Normalizer/PriceNormalizer.php delete mode 100644 src/generated/Normalizer/TaxonNormalizer.php delete mode 100644 submit_filters.png delete mode 100644 tests/Application/config/packages/dev/monsieurbiz_sylius_search_plugin.yaml delete mode 120000 tests/Application/config/packages/monsieurbiz_sylius_search_plugin.yaml delete mode 100644 tests/Application/config/packages/test/monsieurbiz_sylius_search_plugin.yaml delete mode 100644 tests/Application/src/Entity/Product/Product.php delete mode 100644 tests/Application/src/Entity/Product/ProductAttribute.php delete mode 100644 tests/Application/src/Entity/Product/ProductOption.php delete mode 100644 tests/Application/src/Migrations/Version20201016105958.php delete mode 120000 tests/Application/templates/bundles/SyliusAdminBundle diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..13566b81 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/SyliusSearchPlugin.iml b/.idea/SyliusSearchPlugin.iml new file mode 100644 index 00000000..c2fc8eb8 --- /dev/null +++ b/.idea/SyliusSearchPlugin.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000..03d9549e --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..2d734ccb --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml new file mode 100644 index 00000000..73416885 --- /dev/null +++ b/.idea/php.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..94a25f7f --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 7ecec0d0..aa737950 100644 --- a/README.md +++ b/README.md @@ -15,449 +15,9 @@ [![Search Plugin license](https://img.shields.io/github/license/monsieurbiz/SyliusSearchPlugin?public)](https://github.com/monsieurbiz/SyliusSearchPlugin/blob/master/LICENSE.txt) [![Build Status](https://img.shields.io/github/workflow/status/monsieurbiz/SyliusSearchPlugin/PHP%20Composer)](https://github.com/monsieurbiz/SyliusSearchPlugin/actions?query=workflow%3A%22PHP+Composer%22) -A search plugin for Sylius using [Jane](https://github.com/janephp/janephp) and [Elastically](https://github.com/jolicode/elastically). +A search plugin for Sylius using [Elastically](https://github.com/jolicode/elastically) and [Jane](https://github.com/janephp/janephp). -## Features - -### Search with filters, sort and limits - -![Search results](screenshot_search.jpg) - -### Taxon view with filters, sort and limits - -![Taxon view](screenshot_taxon.jpg) - - -### Instant search while you're typing - -![Instant search](screenshot_instant.jpg) - -## Installation - -Require the plugin : -`composer require monsieurbiz/sylius-search-plugin="^1.0@RC"` - -> If you are using Symfony Flex, the recipe will automatically do the actions below. - -Then create the config file in `config/packages/monsieurbiz_search_plugin.yaml` with the [default configuration](#configuration). - -Import routes in `config/routes.yaml` : -```yaml -monsieurbiz_search_plugin: - resource: "@MonsieurBizSyliusSearchPlugin/Resources/config/routing.yaml" -``` - -Modify `config/bundles.php` to add this line at the end : -``` - MonsieurBiz\SyliusSearchPlugin\MonsieurBizSyliusSearchPlugin::class => ['all' => true], -``` - -Finally configure plugin in your `.env` file by adding these lines at the end : -``` -###> MonsieurBizSearchPlugin ### -MONSIEURBIZ_SEARCHPLUGIN_ES_HOST=localhost -MONSIEURBIZ_SEARCHPLUGIN_ES_PORT=9200 -###< MonsieurBizSearchPlugin ### -``` - -## Installation - -1. Install Elasticsearch 💪. See [Infrastructure](#infrastructure) below. -2. Your `Product` entity needs to implement the [DocumentableInterface](#documentable-objects) interface and use the `\MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableProductTrait` trait. - -2. Your `ProductAttribute` and `ProductOption` entities need to implement the `\MonsieurBiz\SyliusSearchPlugin\Entity\Product\FilterableInterface` interface and use the `\MonsieurBiz\SyliusSearchPlugin\Model\Product\FilterableTrait` trait. - -3. You need to run a diff of your doctrine's migrations: `console doctrine:migrations:diff`. Don't forget to run it! (`console doctrine:migrations:migrate`) - -4. Copy the templates: (we update the `ProductAttribute` and `ProductOption` forms) - - ```bash - mkdir -p templates/bundles/SyliusAdminBundle - cp -Rv vendor/monsieurbiz/sylius-search-plugin/src/Resources/SyliusAdminBundle/views/ templates/bundles/SyliusAdminBundle/ - ``` - -5. Run the [populate command](#Command). - -## Infrastructure - -The plugin was developed for Elasticsearch 7.2.x versions. -You need to have `analysis-icu` and `analysis-phonetic` elasticsearch plugin installed. - -### Development - -Elasticsearch is available on `9200` port : http://127.0.0.1:9200/ -Cerebro on port `9000` : http://127.0.0.1:9000/#/overview?host=http:%2F%2Felasticsearch:9200 -Kibana on port `5601` : http://127.0.0.1:5601/ - -On your machine, Elasticsearch is available at http://127.0.0.1:9200/ -In docker, Elasticsearch is available at http://elasticsearch:9200/ -This is the second URL you have to put on Cerebro, Kibana and Elasticsearch if you want to connect to the cluster. - -For a development infrastructure with docker, you can check the [Monsieur Biz Sylius infra](https://github.com/monsieurbiz/sylius-infra/) - -## Configuration - -The default module configuration is : - -```yaml -imports: - - { resource: "@MonsieurBizSyliusSearchPlugin/Resources/config/config.yaml" } - -monsieur_biz_sylius_search: - files: - search: '%kernel.project_dir%/vendor/monsieurbiz/sylius-search-plugin/src/Resources/config/elasticsearch/queries/search.yaml' - instant: '%kernel.project_dir%/vendor/monsieurbiz/sylius-search-plugin/src/Resources/config/elasticsearch/queries/instant.yaml' - taxon: '%kernel.project_dir%/vendor/monsieurbiz/sylius-search-plugin/src/Resources/config/elasticsearch/queries/taxon.yaml' - documentable_classes : - - 'App\Entity\Product\Product' - grid: - limits: - taxon: [9, 18, 27] - search: [9, 18, 27] - default_limit: - taxon: 9 - search: 9 - instant: 10 - sorting: - taxon: ['name', 'price', 'created_at'] - search: ['name', 'price', 'created_at'] - filters: - apply_manually: false # Will refresh the filters depending on applied filters after you apply it manually - use_main_taxon: true # Use main taxon for the taxon filter, else use the taxons - -``` - -You can customize it in `config/packages/monsieurbiz_sylius_search_plugin.yaml`. - -`monsieur_biz_sylius_search.files.search` is the query used to perform the search. -`monsieur_biz_sylius_search.files.instant` is the query used to perform the instant search. -`monsieur_biz_sylius_search.files.taxon` is the query used to perform the taxon view. - -The `{{QUERY}}` string inside is replaced in PHP by the query typed by the user. - -`documentable_classes` is an array of entities which can be indexed in Elasticsearch. - -You can also change available sortings and limits. - -You can decide to load filters before their application or after : -```yaml -monsieur_biz_sylius_search: - grid: - filters: - apply_manually: false # Will refresh the filters depending on applied filters after you apply it manually -``` - -For example, if you choose a `L` size and the config is `true`, you will have only filters available for this size. -You will also have a button to apply the desired filters : - -![Submit filters button](submit_filters.png) - - -If it's set to false, you will have all available filters for the products and they will be applied on each change -automatically. - - -You can decide to use the `Categories` filter with main taxon or taxons : -```yaml -monsieur_biz_sylius_search: - grid: - filters: - use_main_taxon: true # Use main taxon for the taxon filter, else use the taxons -``` - -## Documentable objects - -If you want to index an object in the search index, your entity have to implements `MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface` interface : - -```php -interface DocumentableInterface -{ - public function getDocumentType(): string; - public function convertToDocument(string $locale): Result; -} -``` - -Here is an example for the product conversion using the plugin Trait to convert products : - -```php -baseConvertion($locale); - - /* Adding additional attributes */ -// $document->addAttribute('my_attribute', 'My attribute', [$this->getMyAttribute()], $locale, 75); - - return $document; - } -} -``` - -Then, replace `MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableProductTrait` in your Product with your new Trait and done! - - -```php -customProperty; - } - - /** - * @param null|string $customString - * - * @return ResultInterface - */ - public function setCustomerProperty($customString): ResultInterface - { - $this->customProperty = $customString; - return $this; - } -} -``` - -Then, create a new Trait. This is the same as in "Adding additional attributes" explained. Except, we also overwrite the createResult method to use our own Result we just created. - -```php -baseConvertion($locale); - - /* Setting additonal properties */ -// $document->setCustomProperty('myCustomValue'); - - return $document; - } -} -``` - -As a final step, overwrite the `JoliCode\Elastically\Client` config in your `config/services.yaml` to use your new Result entity. - -```yaml -services: - ... - - # Change monsieurbiz/sylius-search-plugin services - JoliCode\Elastically\Client: - arguments: - $config: - host: '%env(MONSIEURBIZ_SEARCHPLUGIN_ES_HOST)%' - port: '%env(MONSIEURBIZ_SEARCHPLUGIN_ES_PORT)%' - elastically_mappings_directory: '%kernel.project_dir%/vendor/monsieurbiz/sylius-search-plugin/src/Resources/config/elasticsearch/mappings' - elastically_index_class_mapping: - documents-it_it: \App\SearchPlugin\Model\Result - documents-fr_fr: \App\SearchPlugin\Model\Result - documents-fr: \App\SearchPlugin\Model\Result - documents-en: \App\SearchPlugin\Model\Result - documents-en_us: \App\SearchPlugin\Model\Result - elastically_bulk_size: 100 -``` - -## Score by attribute - -Each document attribute can have a `score`. It means it can be more important than another. -For example, the product name in the exemple above has a score of `50`, and the description a score of `10` : -```php -$document->addAttribute('name', 'Name', [$this->getTranslation($locale)->getName()], $locale, 50); -$document->addAttribute('description', 'Description', [$this->getTranslation($locale)->getDescription()], $locale, 10); -``` - -## Improve search accuracy - -You can customize the search with your custom query files and modifying : - -```yaml -monsieur_biz_sylius_search: - files: - search: '%kernel.project_dir%/vendor/monsieurbiz/sylius-search-plugin/src/Resources/config/elasticsearch/queries/search.yaml' - instant: '%kernel.project_dir%/vendor/monsieurbiz/sylius-search-plugin/src/Resources/config/elasticsearch/queries/instant.yaml' - taxon: '%kernel.project_dir%/vendor/monsieurbiz/sylius-search-plugin/src/Resources/config/elasticsearch/queries/taxon.yaml' -``` - -## Indexed Documents - -Indexed documents are all entities defined in `monsieur_biz_search.documentable_classes` which implement `DocumentableInterface`. - -```yaml -monsieur_biz_sylius_search: - documentable_classes : - - 'App\Entity\Product\Product' -``` - -## Command - -A symfony command is available to populate index : `console monsieurbiz:search:populate` - -## Index on save - -For product entity, we have a listener to add / update / delete document on save. -It is the `MonsieurBiz\SyliusSearchPlugin\EventListener\DocumentListener` class which : -- `saveDocument` on `post_create` dans `post_update` -- `removeDocument` on `pre_delete` - -If your entity implements `DocumentableInterface`, you can add listeners to manage entities modifications (Replace `` with your) : -```yaml - app.event_listener.document_listener: - class: MonsieurBiz\SyliusSearchPlugin\EventListener\DocumentListener - arguments: - - '@MonsieurBiz\SyliusSearchPlugin\Model\Document\Index\Indexer' - tags: - - { name: kernel.event_listener, event: sylius..post_create, method: saveDocument } - - { name: kernel.event_listener, event: sylius..post_update, method: saveDocument } - - { name: kernel.event_listener, event: sylius..pre_delete, method: deleteDocument } -``` - -## Url Params - -If you add a new entity in search index. You have to be able to generate an URL when you display it. -In order to do that, you can customize the `RenderDocumentUrl` twig extension : -```php -public function getUrlParams(Result $document): UrlParamsProvider { - switch ($document->getType()) { - case "product" : - return new UrlParamsProvider('sylius_shop_product_show', ['slug' => $document->getSlug(), '_locale' => $document->getLocale()]); - break; - - // Add new case ! - } - - throw new NotSupportedTypeException(sprintf('Object type "%s" not supported to get URL', $this->getType())); -} -``` - -## Display search form in front - -A Twig method is available to display the form : `search_form()`. You can pass a parameter to specify a custom template. -By default, the form is displayed on `sonata.block.event.sylius.shop.layout.header` event. - -## Front customization - -You can override all templates in your theme. - -The bundle's templates are : -- Search results display page (`src/MonsieurBizSearchPlugin/Resources/views/Search/`) -- Instant search display block (`src/MonsieurBizSearchPlugin/Resources/views/Instant/`) -- Taxon results display page (`src/MonsieurBizSearchPlugin/Resources/views/Taxon/`) -- Smaller components (`src/MonsieurBizSearchPlugin/Resources/views/Common/`) -- JS parameters (`src/MonsieurBizSearchPlugin/Resources/views/js.html.twig`) - -Sylius documentation to customize these templates is available [here](https://docs.sylius.com/en/latest/customization/template.html). +TODO ## Jane @@ -483,33 +43,3 @@ You can also find YAML used by plugin to perform the search on Elasticsearch : - `src/MonsieurBizSearchPlugin/Resources/config/elasticsearch/queries/taxon.yaml` These queries can be customized in another folder if you change the plugin config. - -## Fixtures - -You can use fixtures to define filterable and non-filterable options and attributes : - -```yaml - -sylius_fixtures: - suites: - default: - fixtures: - monsieurbiz_sylius_search_filterable: - options: - custom: - cap_collection: - attribute: 'cap_collection' - filterable: false - - dress_collection: - attribute: 'dress_collection' - filterable: false - - dress_height: - option: 'dress_height' - filterable: false - - dress_size: - option: 'dress_size' - filterable: true -``` diff --git a/TESTING.md b/TESTING.md index 80d8b411..269aa464 100644 --- a/TESTING.md +++ b/TESTING.md @@ -59,8 +59,8 @@ and `tests/Application/.env.test`. You can also add custom configuration in `tes - Using `dev` environment: ```bash - $ (cd tests/Application && bin/console sylius:fixtures:load -e dev) - $ (cd tests/Application && bin/console server:run -d public -e dev) + $ (cd tests/Application && bin/console sylius:fixtures:load -e 1.0-dev) + $ (cd tests/Application && bin/console server:run -d public -e 1.0-dev) ``` ### Reindex Elasticsearch @@ -74,5 +74,5 @@ and `tests/Application/.env.test`. You can also add custom configuration in `tes - Using `dev` environment: ```bash - $ (cd tests/Application && bin/console monsieurbiz:search:populate -e dev) + $ (cd tests/Application && bin/console monsieurbiz:search:populate -e 1.0-dev) ``` diff --git a/composer.json b/composer.json index a35b577e..bede7f0f 100644 --- a/composer.json +++ b/composer.json @@ -5,48 +5,45 @@ "description": "A search plugin using Elasticsearch for Sylius.", "license": "MIT", "require": { - "php": "^7.2 || ^8.0", - "sylius/sylius": "^1.4", + "php": "^7.4 || ^8.0", + "sylius/sylius": "^1.9", "jolicode/elastically": "^1.0.0" }, "require-dev": { - "behat/behat": "^3.4", - "behat/mink": "^1.7@dev", - "behat/mink-browserkit-driver": "^1.3", - "behat/mink-extension": "^2.2", - "behat/mink-selenium2-driver": "^1.3", + "behat/behat": "^3.6.1", + "behat/mink-selenium2-driver": "^1.4", + "dmore/behat-chrome-extension": "^1.3", + "dmore/chrome-mink-driver": "^2.7", + "friends-of-behat/mink": "^1.8", + "friends-of-behat/mink-browserkit-driver": "^1.4", + "friends-of-behat/mink-debug-extension": "^2.0.0", + "friends-of-behat/mink-extension": "^2.4", "friends-of-behat/page-object-extension": "^0.3", "friends-of-behat/suite-settings-extension": "^1.0", - "friends-of-behat/symfony-extension": "^2.0", - "friends-of-behat/variadic-extension": "^1.1", - "friends-of-behat/mink-debug-extension": "^2.0", - "phpspec/phpspec": "^6.3", + "friends-of-behat/symfony-extension": "^2.1", + "friends-of-behat/variadic-extension": "^1.3", + "friendsofsymfony/oauth-server-bundle": ">2.0.0-alpha.0 ^2.0@dev", + "phpspec/phpspec": "^7.0", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "0.12.85", "phpstan/phpstan-doctrine": "0.12.33", - "phpstan/phpstan-webmozart-assert": "^0.12.8", - "phpunit/phpunit": "^8.5.12", - "sensiolabs/security-checker": "^5.0", - "sylius-labs/coding-standard": "^3.2.2", - "symfony/browser-kit": "^3.4|^4.1", - "symfony/debug-bundle": "^3.4|^4.1", - "symfony/dotenv": "^4.2", - "symfony/intl": "^3.4|^4.1", - "symfony/web-profiler-bundle": "^3.4|^4.1", - "symfony/web-server-bundle": "^3.4|^4.1", - "jane-php/json-schema": "^5.2" - }, - "conflict": { - "symfony/symfony": "4.1.8", - "symfony/browser-kit": "4.1.8", - "symfony/dependency-injection": "4.1.8", - "symfony/dom-crawler": "4.1.8", - "symfony/routing": "4.1.8" + "phpstan/phpstan-strict-rules": "^0.12.0", + "phpstan/phpstan-webmozart-assert": "0.12.12", + "phpunit/phpunit": "^9.5", + "sensiolabs/security-checker": "^6.0", + "sylius-labs/coding-standard": "^4.0", + "symfony/browser-kit": "^4.4 || ^5.2", + "symfony/debug-bundle": "^4.4 || ^5.2", + "symfony/dotenv": "^4.4 || ^5.2", + "symfony/intl": "^4.4 || ^5.2", + "symfony/web-profiler-bundle": "^4.4 || ^5.2", + "vimeo/psalm": "4.7.1" }, "prefer-stable": true, "autoload": { "psr-4": { "MonsieurBiz\\SyliusSearchPlugin\\": "src/", - "Tests\\MonsieurBiz\\SyliusSearchPlugin\\": "tests/", - "Tests\\MonsieurBiz\\SyliusSearchPlugin\\App\\": "tests/Application/src" + "Tests\\MonsieurBiz\\SyliusSearchPlugin\\": "tests/" } }, "autoload-dev": { @@ -55,9 +52,13 @@ "scripts": { "phpcs": "php-cs-fixer fix --using-cache=false" }, + "config": { + "sort-packages": true + }, "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.0-dev", + "dev-2.0.x": "2.0-dev" } } } diff --git a/recipe/dev/config/packages/monsieurbiz_sylius_search_plugin.yaml b/recipe/1.0/config/packages/monsieurbiz_sylius_search_plugin.yaml similarity index 100% rename from recipe/dev/config/packages/monsieurbiz_sylius_search_plugin.yaml rename to recipe/1.0/config/packages/monsieurbiz_sylius_search_plugin.yaml diff --git a/recipe/dev/config/routes/monsieurbiz_sylius_search_plugin.yaml b/recipe/1.0/config/routes/monsieurbiz_sylius_search_plugin.yaml similarity index 100% rename from recipe/dev/config/routes/monsieurbiz_sylius_search_plugin.yaml rename to recipe/1.0/config/routes/monsieurbiz_sylius_search_plugin.yaml diff --git a/recipe/dev/manifest.json b/recipe/1.0/manifest.json similarity index 100% rename from recipe/dev/manifest.json rename to recipe/1.0/manifest.json diff --git a/recipe/2.0-dev b/recipe/2.0-dev new file mode 120000 index 00000000..c3318381 --- /dev/null +++ b/recipe/2.0-dev @@ -0,0 +1 @@ +2.0/ \ No newline at end of file diff --git a/screenshot_instant.jpg b/screenshot_instant.jpg deleted file mode 100644 index 295722e3f7191c788219c3cd6c7cd6038bddb0e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 724385 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 tLA>lWc&P7H<~k%oB>tlT2}sN)H9np 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�$NU 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ʪ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
Qc1Bri`Ul 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ⓈZdBG`7RCo^1`oZ3SZ6+fqX$>|bPs!id$z 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 z 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 C-(9-^ diff --git a/screenshot_search.jpg b/screenshot_search.jpg deleted file mode 100644 index 2d24a7f16521327dd8e8c52b23ff734a81e01a14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 674830 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 z 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⋙O#4Yqj0NWSnGu`WaQ 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 VM|Yp%Vm3)2~vO_sI$RON0O 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

#om8d=eZ||CYNqpU3sA^6vvp`bLjYO)Ljx&T zmN_^K4S*38orvR>g%t8==!de7SiIwsrr!PqCh57$!f;2P zC+kit8B(9f#tT5M{CK{#Vnrea=R*abUA%IF+oj(#u~>~KaT>;K%mgSCu7FNq-wh?{ zrLDc$su-nf6+2e)kGJ7MFrZQJEWHT#dBE@btU6PEixh9(YNm_MrhE0~8e+>FrCY6! zX=v8&e*veku_fuL1<9n24Lu$5vDo%D`XqC?g1DCh{>N;!ok3@%QBp$=l18^8WlFDr zkELL4rJo3<0vc|F1G%D5k(!AzP$?uCb09t|>2)c?LXO0(eL?bAV$fN!3+$!LN3D&~ z)PNMoV~J1;QGT}J*2ZkQr�IXU)T$wvy*ONP0oSIVolR#>G*-J+4?yLec)$Z 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 diff --git a/screenshot_taxon.jpg b/screenshot_taxon.jpg deleted file mode 100644 index 42d7463281a96901d6b52cca0a54b25d9c880876..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 618703 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

YJc=UTG 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Μ^#nUoo!LY0)s6LX_OSd;ThG$X|r=f77JL1MTv2 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 z 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

VZPVe?#-I z^Zkku389e`AiVJr0AD^jDZmlBf^QO}IF`1v{UMHAXN+sJv6rrk=rWeZ3UROwIkhx= zqhxha(%!3N$qdRRVmA<~Jyx6Le9YddzUyui+7N{^n!wL}8oNoCPlSp|@Yjel5G*l~ zmcAF6o>XzDg#fi%=xR>cCaXKd1G!{Mnm@72zi)J;YvnvR3K>YlTGCe{oLs=0gMQK* zV^4nj$?`7KVN%}qE6s#?Y*%8>EN$}5H(B97`t!#5cYZ7;G+x~AI{Icp^{0b`{9?%> z@EZ+SRs%@qn=ZdhMqT+;@cWojv55nJeicOd3&5U2xjcU2_q3fr^eFDh8mDHyHr|;kPj-~}tss|LZQ1V^EOK%Ad#Ie*L zh`Xq9d1LYCO^ca~FgNz^?Fo|KsE|pAS|SS{^+RgiB7b-f=Dv~Bp_AxJqMS~$+M z?Au)PsGZV(DAKx~#C5%}Hy8s0QBl1MCV*O8B!Gj7MlDJKnfEPK&Si4idgbBJ#4CZ}TV#H_`S}u$U@5A`gtv0wN zN@gpfnhRrGIm8bveZ4xF+X7x{)-%yFSO%FxQv@S*>^24EyS1XIs)KnULbX%WNLEJ_ zXR zV;C8@`bSN5V`Ak6-33V@Oe>OjbAnbcAxy7VBkS? zNj+{TFvDL}fbvdpu(V~e|Y$9vQS zZ0v^Pok_yLEri3CE#im6!Yw<7$1;dlqhWSM$d6;6dcQUoJGs+1oPH>1*s87nrjcDWzru;S<#9j>L}{+C+A34Az^WU|<{;d3u|gT16Z@?%yzI3GPyc z{qXhN$cM!pC8fz+Sv=sv41Bvy9wU!~_xF{Mk8*Y!^@>(eCwf*4o-I>xLz-GniVKP| zTw-?VP)P(%cMUaYC8vwEV*u!iiYel$*ZhOn2WxiHNs8#=fIcNJB3oZhjA}9*#d@nB zm>FaKT)j<+bneYuVSDW_|x`^HHB&nU@=s_vW^TzCs*B

F=4OP| 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%0D#f~ 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 diff --git a/src/Adapter/ResultSetAdapter.php b/src/Adapter/ResultSetAdapter.php deleted file mode 100644 index dfc1f528..00000000 --- a/src/Adapter/ResultSetAdapter.php +++ /dev/null @@ -1,59 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Adapter; - -use MonsieurBiz\SyliusSearchPlugin\Model\Document\ResultSet; -use Pagerfanta\Adapter\AdapterInterface; - -class ResultSetAdapter implements AdapterInterface -{ - /** @var ResultSet */ - private $resultSet; - - /** - * Constructor. - * - * @param ResultSet $resultSet - */ - public function __construct(ResultSet $resultSet) - { - $this->resultSet = $resultSet; - } - - /** - * Returns the array. - * - * @return ResultSet - */ - public function getResultSet() - { - return $this->resultSet; - } - - /** - * {@inheritdoc} - */ - public function getNbResults() - { - return $this->resultSet->getTotalHits(); - } - - /** - * {@inheritdoc} - */ - public function getSlice($offset, $length) - { - return \array_slice($this->resultSet->getResults(), $offset, $length); - } -} diff --git a/src/Command/PopulateCommand.php b/src/Command/PopulateCommand.php deleted file mode 100644 index a213368b..00000000 --- a/src/Command/PopulateCommand.php +++ /dev/null @@ -1,67 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Command; - -use MonsieurBiz\SyliusSearchPlugin\Exception\ReadOnlyIndexException; -use MonsieurBiz\SyliusSearchPlugin\Model\Document\Index\Indexer; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; - -class PopulateCommand extends Command -{ - /** - * @var string - */ - protected static $defaultName = 'monsieurbiz:search:populate'; - - /** - * @var Indexer - */ - protected $documentIndexer; - - /** - * PopulateCommand constructor. - * - * @param Indexer $documentIndexer - */ - public function __construct(Indexer $documentIndexer) - { - $this->documentIndexer = $documentIndexer; - parent::__construct(static::$defaultName); - } - - /** - * Populate ES. - * - * @param InputInterface $input - * @param OutputInterface $output - * - * @return int 0 if everything went fine, or an exit code - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $output->writeln(sprintf('Generating index')); - try { - $this->documentIndexer->indexAll(); - } catch (ReadOnlyIndexException $exception) { - $output->writeln('Cannot purge old index. Please to do it manually if needed.'); - // it's better to use return Command::FAILURE; in Symfony 5 - return 1; - } - $output->writeln(sprintf('Generated index')); - // it's better to use return Command::SUCCESS; in Symfony 5 - return 0; - } -} diff --git a/src/Context/RequestTaxonContext.php b/src/Context/RequestTaxonContext.php deleted file mode 100644 index b0ce4138..00000000 --- a/src/Context/RequestTaxonContext.php +++ /dev/null @@ -1,57 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Context; - -use MonsieurBiz\SyliusSearchPlugin\Exception\TaxonNotFoundException; -use Sylius\Component\Core\Model\TaxonInterface; -use Sylius\Component\Locale\Context\LocaleContextInterface; -use Sylius\Component\Taxonomy\Repository\TaxonRepositoryInterface; -use Symfony\Component\HttpFoundation\RequestStack; - -final class RequestTaxonContext implements TaxonContextInterface -{ - /** @var RequestStack */ - private $requestStack; - - /** @var TaxonRepositoryInterface */ - private $taxonRepository; - - /** @var LocaleContextInterface */ - private $localeContext; - - public function __construct( - RequestStack $requestStack, - TaxonRepositoryInterface $taxonRepository, - LocaleContextInterface $localeContext - ) { - $this->requestStack = $requestStack; - $this->taxonRepository = $taxonRepository; - $this->localeContext = $localeContext; - } - - public function getTaxon(): TaxonInterface - { - $slug = htmlspecialchars($this->requestStack->getCurrentRequest()->get('slug')); - $localeCode = $this->localeContext->getLocaleCode(); - - /** @var TaxonInterface $taxon */ - $taxon = $this->taxonRepository->findOneBySlug($slug, $localeCode); - - if (null === $slug || null === $taxon) { - throw new TaxonNotFoundException(); - } - - return $taxon; - } -} diff --git a/src/Context/TaxonContextInterface.php b/src/Context/TaxonContextInterface.php deleted file mode 100644 index 237733af..00000000 --- a/src/Context/TaxonContextInterface.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Context; - -use Sylius\Component\Core\Model\TaxonInterface; - -interface TaxonContextInterface -{ - public function getTaxon(): TaxonInterface; -} diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php deleted file mode 100644 index 883f82eb..00000000 --- a/src/Controller/SearchController.php +++ /dev/null @@ -1,198 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Controller; - -use MonsieurBiz\SyliusSearchPlugin\Context\TaxonContextInterface; -use MonsieurBiz\SyliusSearchPlugin\Exception\MissingLocaleException; -use MonsieurBiz\SyliusSearchPlugin\Exception\NotSupportedTypeException; -use MonsieurBiz\SyliusSearchPlugin\Helper\RenderDocumentUrlHelper; -use MonsieurBiz\SyliusSearchPlugin\Model\Config\GridConfig; -use MonsieurBiz\SyliusSearchPlugin\Model\Document\Index\Search; -use MonsieurBiz\SyliusSearchPlugin\Model\Document\Result; -use MonsieurBiz\SyliusSearchPlugin\Model\Document\ResultSet; -use Sylius\Component\Channel\Context\ChannelContextInterface; -use Sylius\Component\Currency\Context\CurrencyContextInterface; -use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; -use Symfony\Component\HttpFoundation\RedirectResponse; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; -use Twig\Environment; - -class SearchController extends AbstractController -{ - public const SORT_ASC = 'asc'; - public const SORT_DESC = 'desc'; - - /** @var Environment */ - private $templatingEngine; - - /** @var Search */ - private $documentSearch; - - /** @var ChannelContextInterface */ - private $channelContext; - - /** @var CurrencyContextInterface */ - private $currencyContext; - - /** @var TaxonContextInterface */ - private $taxonContext; - - /** @var GridConfig */ - private $gridConfig; - - /** @var RenderDocumentUrlHelper */ - private $renderDocumentUrlHelper; - - public function __construct( - Environment $templatingEngine, - Search $documentSearch, - ChannelContextInterface $channelContext, - CurrencyContextInterface $currencyContext, - TaxonContextInterface $taxonContext, - GridConfig $gridConfig, - RenderDocumentUrlHelper $renderDocumentUrlHelper - ) { - $this->templatingEngine = $templatingEngine; - $this->documentSearch = $documentSearch; - $this->channelContext = $channelContext; - $this->currencyContext = $currencyContext; - $this->taxonContext = $taxonContext; - $this->gridConfig = $gridConfig; - $this->renderDocumentUrlHelper = $renderDocumentUrlHelper; - } - - /** - * Post search. - * - * @param Request $request - * - * @return RedirectResponse - */ - public function postAction(Request $request) - { - $query = $request->request->get('monsieurbiz_searchplugin_search')['query'] ?? null; - - return new RedirectResponse( - $this->generateUrl('monsieurbiz_sylius_search_search', - ['query' => urlencode($query)]) - ); - } - - /** - * Perform the search action & display results. User can add page, limit or sorting. - * - * @param Request $request - * - * @return Response - */ - public function searchAction(Request $request): Response - { - // Init grid config depending on request - $this->gridConfig->init(GridConfig::SEARCH_TYPE, $request); - - // Perform search - /** @var ResultSet $resultSet */ - $resultSet = $this->documentSearch->search($this->gridConfig); - - // Redirect to document if only one result and no filter applied - $appliedFilters = $this->gridConfig->getAppliedFilters(); - if (1 === $resultSet->getTotalHits() && empty($appliedFilters)) { - /** @var Result $document */ - $document = current($resultSet->getResults()); - try { - $urlParams = $this->renderDocumentUrlHelper->getUrlParams($document); - - return new RedirectResponse($this->generateUrl($urlParams->getPath(), $urlParams->getParams())); - } catch (NotSupportedTypeException $e) { - // Return list of results if cannot redirect, so ignore Exception - } catch (MissingLocaleException $e) { - // Return list of results if locale is missing - } - } - - // Get number formatter for currency - $currencyCode = $this->currencyContext->getCurrencyCode(); - $formatter = new \NumberFormatter($request->getLocale() . '@currency=' . $currencyCode, \NumberFormatter::CURRENCY); - - // Display result list - return new Response($this->templatingEngine->render('@MonsieurBizSyliusSearchPlugin/Search/result.html.twig', [ - 'query' => $this->gridConfig->getQuery(), - 'limits' => $this->gridConfig->getLimits(), - 'resultSet' => $resultSet, - 'channel' => $this->channelContext->getChannel(), - 'currencyCode' => $this->currencyContext->getCurrencyCode(), - 'moneySymbol' => $formatter->getSymbol(\NumberFormatter::CURRENCY_SYMBOL), - 'gridConfig' => $this->gridConfig, - ])); - } - - /** - * Perform the instant search action & display results. - * - * @param Request $request - * - * @return Response - */ - public function instantAction(Request $request): Response - { - // Init grid config depending on request - $this->gridConfig->init(GridConfig::INSTANT_TYPE, $request); - - // Perform instant search - /** @var ResultSet $resultSet */ - $resultSet = $this->documentSearch->instant($this->gridConfig); - - // Display instant result list - return new Response($this->templatingEngine->render('@MonsieurBizSyliusSearchPlugin/Instant/result.html.twig', [ - 'query' => $this->gridConfig->getQuery(), - 'resultSet' => $resultSet, - 'channel' => $this->channelContext->getChannel(), - 'currencyCode' => $this->currencyContext->getCurrencyCode(), - 'gridConfig' => $this->gridConfig, - ])); - } - - /** - * Perform the taxon action & display results. - * - * @param Request $request - * - * @return Response - */ - public function taxonAction(Request $request): Response - { - // Init grid config depending on request - $this->gridConfig->init(GridConfig::TAXON_TYPE, $request, $this->taxonContext->getTaxon()); - - // Perform search - /** @var ResultSet $resultSet */ - $resultSet = $this->documentSearch->taxon($this->gridConfig); - - // Get number formatter for currency - $currencyCode = $this->currencyContext->getCurrencyCode(); - $formatter = new \NumberFormatter($request->getLocale() . '@currency=' . $currencyCode, \NumberFormatter::CURRENCY); - - // Display result list - return new Response($this->templatingEngine->render('@MonsieurBizSyliusSearchPlugin/Taxon/result.html.twig', [ - 'taxon' => $this->gridConfig->getTaxon(), - 'limits' => $this->gridConfig->getLimits(), - 'resultSet' => $resultSet, - 'channel' => $this->channelContext->getChannel(), - 'currencyCode' => $this->currencyContext->getCurrencyCode(), - 'moneySymbol' => $formatter->getSymbol(\NumberFormatter::CURRENCY_SYMBOL), - 'gridConfig' => $this->gridConfig, - ])); - } -} diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 72e2c0ce..361af12d 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -24,87 +24,7 @@ final class Configuration implements ConfigurationInterface public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder('monsieur_biz_sylius_search'); - if (method_exists($treeBuilder, 'getRootNode')) { - $rootNode = $treeBuilder->getRootNode(); - } else { - // BC layer for symfony/config 4.1 and older - $rootNode = $treeBuilder->root('monsieur_biz_sylius_search'); - } - - $rootNode - ->children() - // Files - ->arrayNode('files') - ->children() - ->scalarNode('search')->isRequired()->end() - ->scalarNode('taxon')->isRequired()->end() - ->scalarNode('instant')->isRequired()->end() - ->end() - ->end() - - // Documentable classes - ->variableNode('documentable_classes')->end() - - // Grid - ->arrayNode('grid') - ->children() - - // Limits - ->arrayNode('limits') - ->children() - ->arrayNode('taxon') - ->performNoDeepMerging() - ->integerPrototype()->end() - ->isRequired() - ->defaultValue([10, 25, 50]) - ->end() - ->arrayNode('search') - ->performNoDeepMerging() - ->integerPrototype()->end() - ->isRequired() - ->defaultValue([10, 25, 50]) - ->end() - ->end() - ->end() - - // Default limit - ->arrayNode('default_limit') - ->children() - ->integerNode('taxon')->isRequired()->defaultValue(10)->end() - ->integerNode('search')->isRequired()->defaultValue(10)->end() - ->integerNode('instant')->isRequired()->defaultValue(10)->end() - ->end() - ->end() - - // Sorting - ->arrayNode('sorting') - ->children() - ->arrayNode('taxon') - ->performNoDeepMerging() - ->scalarPrototype()->end() - ->isRequired() - ->defaultValue(['name']) - ->end() - ->arrayNode('search') - ->performNoDeepMerging() - ->scalarPrototype()->end() - ->isRequired() - ->defaultValue(['name']) - ->end() - ->end() - ->end() - - // Filters - ->arrayNode('filters') - ->children() - ->booleanNode('apply_manually')->isRequired()->defaultValue(false)->end() - ->booleanNode('use_main_taxon')->isRequired()->defaultValue(false)->end() - ->end() - ->end() - - ->end() - ->end() - ; + $rootNode = $treeBuilder->getRootNode(); return $treeBuilder; } diff --git a/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php b/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php index 2b3602ff..a7aec66d 100644 --- a/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php +++ b/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php @@ -22,8 +22,7 @@ final class MonsieurBizSyliusSearchExtension extends Extension public function load(array $configs, ContainerBuilder $container): void { - $configuration = new Configuration(); - $config = $this->processConfiguration($configuration, $configs); + $config = $this->processConfiguration($this->getConfiguration([], $container), $configs); foreach ($config as $name => $value) { $container->setParameter(self::EXTENSION_CONFIG_NAME . '.' . $name, $value); } diff --git a/src/Entity/Product/FilterableInterface.php b/src/Entity/Product/FilterableInterface.php deleted file mode 100644 index a57cb315..00000000 --- a/src/Entity/Product/FilterableInterface.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Entity\Product; - -interface FilterableInterface -{ - public function isFilterable(): bool; - - public function setFilterable(bool $filterable): void; -} diff --git a/src/EventListener/DocumentListener.php b/src/EventListener/DocumentListener.php deleted file mode 100644 index 950ba4d0..00000000 --- a/src/EventListener/DocumentListener.php +++ /dev/null @@ -1,65 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\EventListener; - -use MonsieurBiz\SyliusSearchPlugin\Model\Document\Index\Indexer; -use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; -use Symfony\Component\EventDispatcher\GenericEvent; -use Webmozart\Assert\Assert; - -final class DocumentListener -{ - /** @var Indexer */ - private $documentIndexer; - - /** - * DocumentListener constructor. - * - * @param Indexer $documentIndexer - */ - public function __construct(Indexer $documentIndexer) - { - $this->documentIndexer = $documentIndexer; - } - - /** - * Save document to search index, update if exists. - * - * @param GenericEvent $event - * - * @throws \Exception - */ - public function saveDocument(GenericEvent $event): void - { - $subject = $event->getSubject(); - Assert::isInstanceOf($subject, DocumentableInterface::class); - - $this->documentIndexer->indexOne($subject); - } - - /** - * Delete document in search index. - * - * @param GenericEvent $event - * - * @throws \Exception - */ - public function deleteDocument(GenericEvent $event): void - { - $subject = $event->getSubject(); - Assert::isInstanceOf($subject, DocumentableInterface::class); - - $this->documentIndexer->removeOne($subject); - } -} diff --git a/src/Exception/MissingAttributeException.php b/src/Exception/MissingAttributeException.php deleted file mode 100644 index 93eb4d7d..00000000 --- a/src/Exception/MissingAttributeException.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Exception; - -use Exception; - -class MissingAttributeException extends Exception -{ -} diff --git a/src/Exception/MissingConfigFileException.php b/src/Exception/MissingConfigFileException.php deleted file mode 100644 index e40285d4..00000000 --- a/src/Exception/MissingConfigFileException.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Exception; - -use Exception; - -class MissingConfigFileException extends Exception -{ -} diff --git a/src/Exception/MissingLocaleException.php b/src/Exception/MissingLocaleException.php deleted file mode 100644 index f0cd340b..00000000 --- a/src/Exception/MissingLocaleException.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Exception; - -use Exception; - -class MissingLocaleException extends Exception -{ -} diff --git a/src/Exception/MissingParamException.php b/src/Exception/MissingParamException.php deleted file mode 100644 index f0126db3..00000000 --- a/src/Exception/MissingParamException.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Exception; - -use Exception; - -class MissingParamException extends Exception -{ -} diff --git a/src/Exception/MissingPriceException.php b/src/Exception/MissingPriceException.php deleted file mode 100644 index 9912d06a..00000000 --- a/src/Exception/MissingPriceException.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Exception; - -use Exception; - -class MissingPriceException extends Exception -{ -} diff --git a/src/Exception/NotSupportedTypeException.php b/src/Exception/NotSupportedTypeException.php deleted file mode 100644 index f1f174c2..00000000 --- a/src/Exception/NotSupportedTypeException.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Exception; - -use Exception; - -class NotSupportedTypeException extends Exception -{ -} diff --git a/src/Exception/ReadFileException.php b/src/Exception/ReadFileException.php deleted file mode 100644 index 8d931002..00000000 --- a/src/Exception/ReadFileException.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Exception; - -use Exception; - -class ReadFileException extends Exception -{ -} diff --git a/src/Exception/ReadOnlyIndexException.php b/src/Exception/ReadOnlyIndexException.php deleted file mode 100644 index e24c964d..00000000 --- a/src/Exception/ReadOnlyIndexException.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Exception; - -use Exception; - -class ReadOnlyIndexException extends Exception -{ -} diff --git a/src/Exception/TaxonNotFoundException.php b/src/Exception/TaxonNotFoundException.php deleted file mode 100644 index 6c651d37..00000000 --- a/src/Exception/TaxonNotFoundException.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Exception; - -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; - -final class TaxonNotFoundException extends NotFoundHttpException -{ - public function __construct() - { - parent::__construct('Taxon cannot be found.'); - } -} diff --git a/src/Exception/UnknownGridConfigType.php b/src/Exception/UnknownGridConfigType.php deleted file mode 100644 index f6aa90a4..00000000 --- a/src/Exception/UnknownGridConfigType.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Exception; - -use MonsieurBiz\SyliusSearchPlugin\Model\Config\GridConfig; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; - -final class UnknownGridConfigType extends NotFoundHttpException -{ - public function __construct() - { - parent::__construct(sprintf( - 'Unknown GridConfig type, available are "%s", "%s" and "%s"', - GridConfig::SEARCH_TYPE, GridConfig::TAXON_TYPE, GridConfig::INSTANT_TYPE - )); - } -} diff --git a/src/Fixture/Factory/FilterableFixtureFactory.php b/src/Fixture/Factory/FilterableFixtureFactory.php deleted file mode 100644 index 045c70d8..00000000 --- a/src/Fixture/Factory/FilterableFixtureFactory.php +++ /dev/null @@ -1,101 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Fixture\Factory; - -use MonsieurBiz\SyliusSearchPlugin\Entity\Product\FilterableInterface; -use Sylius\Bundle\CoreBundle\Fixture\Factory\AbstractExampleFactory; -use Sylius\Bundle\CoreBundle\Fixture\OptionsResolver\LazyOption; -use Sylius\Component\Product\Model\ProductAttributeInterface; -use Sylius\Component\Product\Model\ProductOptionInterface; -use Sylius\Component\Resource\Repository\RepositoryInterface; -use Symfony\Component\OptionsResolver\OptionsResolver; - -class FilterableFixtureFactory extends AbstractExampleFactory implements FilterableFixtureFactoryInterface -{ - /** - * @var RepositoryInterface - */ - protected $productAttributeRepository; - - /** - * @var RepositoryInterface - */ - protected $productOptionRepository; - - /** - * @var OptionsResolver - */ - private $optionsResolver; - - /** - * FilterableFixtureFactory constructor. - * - * @param RepositoryInterface $productAttributeRepository - * @param RepositoryInterface $productOptionRepository - */ - public function __construct( - RepositoryInterface $productAttributeRepository, - RepositoryInterface $productOptionRepository - ) { - $this->productAttributeRepository = $productAttributeRepository; - $this->productOptionRepository = $productOptionRepository; - $this->optionsResolver = new OptionsResolver(); - $this->configureOptions($this->optionsResolver); - } - - /** - * {@inheritdoc} - */ - protected function configureOptions(OptionsResolver $resolver): void - { - $resolver - ->setDefault('attribute', null) - ->setAllowedTypes('attribute', ['null', 'string', ProductAttributeInterface::class]) - ->setNormalizer('attribute', LazyOption::findOneBy($this->productAttributeRepository, 'code')) - ->setDefault('option', null) - ->setAllowedTypes('option', ['null', 'string', ProductOptionInterface::class]) - ->setNormalizer('option', LazyOption::findOneBy($this->productOptionRepository, 'code')) - ->setDefault('filterable', true) - ; - } - - /** - * @param array $options - * - * @throws \Exception - * - * @return object - */ - public function create(array $options = []) - { - $options = $this->optionsResolver->resolve($options); - - if (isset($options['attribute']) && !empty($options['attribute'])) { - $object = $options['attribute']; - } elseif (isset($options['option']) && !empty($options['option'])) { - $object = $options['option']; - } else { - throw new \Exception('You need to specify an attribute or an option to be filterable.'); - } - - if (!$object instanceof FilterableInterface) { - throw new \Exception(sprintf('Your class "%s" is not an instance of %s', \get_class($object), FilterableInterface::class)); - } - - /** @var FilterableInterface $object */ - $object->setFilterable(((bool) $options['filterable']) ?? false); - - return $object; - } -} diff --git a/src/Fixture/Factory/FilterableFixtureFactoryInterface.php b/src/Fixture/Factory/FilterableFixtureFactoryInterface.php deleted file mode 100644 index 21c64c8a..00000000 --- a/src/Fixture/Factory/FilterableFixtureFactoryInterface.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Fixture\Factory; - -use Sylius\Bundle\CoreBundle\Fixture\Factory\ExampleFactoryInterface; - -interface FilterableFixtureFactoryInterface extends ExampleFactoryInterface -{ -} diff --git a/src/Fixture/FilterableFixture.php b/src/Fixture/FilterableFixture.php deleted file mode 100644 index e3315cfe..00000000 --- a/src/Fixture/FilterableFixture.php +++ /dev/null @@ -1,56 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Fixture; - -use Doctrine\ORM\EntityManagerInterface; -use MonsieurBiz\SyliusSearchPlugin\Fixture\Factory\FilterableFixtureFactoryInterface; -use Sylius\Bundle\CoreBundle\Fixture\AbstractResourceFixture; -use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; - -class FilterableFixture extends AbstractResourceFixture implements FilterableFixtureInterface -{ - /** - * FilterableFixture constructor. - * - * @param EntityManagerInterface $productManager - * @param FilterableFixtureFactoryInterface $exampleFactory - */ - public function __construct( - EntityManagerInterface $productManager, - FilterableFixtureFactoryInterface $exampleFactory - ) { - parent::__construct($productManager, $exampleFactory); - } - - /** - * {@inheritdoc} - */ - public function getName(): string - { - return 'monsieurbiz_sylius_search_filterable'; - } - - /** - * {@inheritdoc} - */ - protected function configureResourceNode(ArrayNodeDefinition $resourceNode): void - { - $resourceNode - ->children() - ->scalarNode('attribute')->end() - ->scalarNode('option')->end() - ->booleanNode('filterable')->defaultValue(true)->end() - ; - } -} diff --git a/src/Fixture/FilterableFixtureInterface.php b/src/Fixture/FilterableFixtureInterface.php deleted file mode 100644 index 7218e7a2..00000000 --- a/src/Fixture/FilterableFixtureInterface.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Fixture; - -use Sylius\Bundle\FixturesBundle\Fixture\FixtureInterface; - -interface FilterableFixtureInterface extends FixtureInterface -{ -} diff --git a/src/Form/Extension/ProductAttributeTypeExtension.php b/src/Form/Extension/ProductAttributeTypeExtension.php deleted file mode 100644 index b83b30e2..00000000 --- a/src/Form/Extension/ProductAttributeTypeExtension.php +++ /dev/null @@ -1,39 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Form\Extension; - -use Sylius\Bundle\ProductBundle\Form\Type\ProductAttributeType; -use Symfony\Component\Form\AbstractTypeExtension; -use Symfony\Component\Form\Extension\Core\Type\CheckboxType; -use Symfony\Component\Form\FormBuilderInterface; - -final class ProductAttributeTypeExtension extends AbstractTypeExtension -{ - public function buildForm(FormBuilderInterface $builder, array $options): void - { - $builder - ->add('filterable', CheckboxType::class, [ - 'label' => 'monsieurbiz_searchplugin.admin.product_attribute.form.filterable', - 'required' => true, - ]) - ; - } - - public static function getExtendedTypes(): array - { - return [ - ProductAttributeType::class, - ]; - } -} diff --git a/src/Form/Extension/ProductOptionTypeExtension.php b/src/Form/Extension/ProductOptionTypeExtension.php deleted file mode 100644 index 2d7ec32b..00000000 --- a/src/Form/Extension/ProductOptionTypeExtension.php +++ /dev/null @@ -1,39 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Form\Extension; - -use Sylius\Bundle\ProductBundle\Form\Type\ProductOptionType; -use Symfony\Component\Form\AbstractTypeExtension; -use Symfony\Component\Form\Extension\Core\Type\CheckboxType; -use Symfony\Component\Form\FormBuilderInterface; - -final class ProductOptionTypeExtension extends AbstractTypeExtension -{ - public function buildForm(FormBuilderInterface $builder, array $options): void - { - $builder - ->add('filterable', CheckboxType::class, [ - 'label' => 'monsieurbiz_searchplugin.admin.product_option.form.filterable', - 'required' => true, - ]) - ; - } - - public static function getExtendedTypes(): array - { - return [ - ProductOptionType::class, - ]; - } -} diff --git a/src/Form/Type/SearchType.php b/src/Form/Type/SearchType.php deleted file mode 100644 index c04ba720..00000000 --- a/src/Form/Type/SearchType.php +++ /dev/null @@ -1,54 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Form\Type; - -use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\Extension\Core\Type\SubmitType; -use Symfony\Component\Form\Extension\Core\Type\TextType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Validator\Constraints\NotBlank; -use Symfony\Component\Validator\Constraints\Required; - -class SearchType extends AbstractType -{ - /** - * @param FormBuilderInterface $builder - * @param array $options - */ - public function buildForm(FormBuilderInterface $builder, array $options): void - { - $builder - ->add('query', TextType::class, [ - 'required' => true, - 'label' => 'monsieurbiz_searchplugin.form.query', - 'constraints' => [ - new NotBlank(), - new Required(), - ], - ]) - ->add('submit', SubmitType::class, [ - 'attr' => ['class' => 'submit'], - 'label' => 'monsieurbiz_searchplugin.form.submit', - ]) - ; - } - - /** - * {@inheritdoc} - */ - public function getBlockPrefix() - { - return 'monsieurbiz_searchplugin_search'; - } -} diff --git a/src/Helper/AggregationHelper.php b/src/Helper/AggregationHelper.php deleted file mode 100644 index bf13d1ab..00000000 --- a/src/Helper/AggregationHelper.php +++ /dev/null @@ -1,135 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Helper; - -class AggregationHelper -{ - public const MAX_AGGREGATED_ATTRIBUTES_INFO = 100; - public const MAX_AGGREGATED_TAXON_INFO = 500; - - /** - * Build sort array to add in query. - * - * @param string $field - * - * @return array - */ - public static function buildAggregation(string $field): array - { - return [ - 'filter' => [ - 'bool' => [ - 'must' => [ - [ - 'term' => ['attributes.code' => $field], - ], - ], - ], - ], - 'aggs' => [ - 'values' => [ - 'terms' => ['field' => 'attributes.value.keyword', 'size' => self::MAX_AGGREGATED_ATTRIBUTES_INFO], // Retrieve all attributes info - ], - ], - ]; - } - - /** - * Build sort array to add in query. - * - * @param array $filters - * - * @return array - */ - public static function buildAggregations(array $filters): array - { - $attributeAggregations = []; - foreach ($filters as $field) { - $attributeAggregations[$field] = self::buildAggregation($field); - } - - $aggregations = [ - 'attributes' => [ - 'nested' => ['path' => 'attributes'], - 'aggs' => [ - 'codes' => [ - 'terms' => ['field' => 'attributes.code', 'size' => self::MAX_AGGREGATED_ATTRIBUTES_INFO] // Retrieve all attributes info - , - 'aggs' => [ - 'names' => [ - 'terms' => ['field' => 'attributes.name.keyword'], - ], - ], - ], - ], - ], - // Get taxon info to be able to retrieve the attribute name from code, we also need the level - 'taxons' => [ - 'nested' => ['path' => 'taxon'], - 'aggs' => [ - 'codes' => [ - 'terms' => ['field' => 'taxon.code', 'size' => self::MAX_AGGREGATED_TAXON_INFO], // Retrieve all taxon info - 'aggs' => [ - 'levels' => [ - 'terms' => ['field' => 'taxon.level'], - 'aggs' => [ - 'names' => [ - 'terms' => ['field' => 'taxon.name'], - ], - ], - ], - ], - ], - ], - ], - // Get main taxon info to be able to retrieve the attribute name from code, we also need the level - 'mainTaxon' => [ - 'nested' => ['path' => 'mainTaxon'], - 'aggs' => [ - 'codes' => [ - 'terms' => ['field' => 'mainTaxon.code', 'size' => self::MAX_AGGREGATED_TAXON_INFO], // Retrieve all taxon info - 'aggs' => [ - 'levels' => [ - 'terms' => ['field' => 'mainTaxon.level'], - 'aggs' => [ - 'names' => [ - 'terms' => ['field' => 'mainTaxon.name'], - ], - ], - ], - ], - ], - ], - ], - // Get attributes info to be able to retrieve the attribute name from code - 'price' => [ - 'nested' => ['path' => 'price'], - 'aggs' => [ - 'values' => [ - 'stats' => ['field' => 'price.value'], - ], - ], - ], - ]; - - if (!empty($attributeAggregations)) { - $aggregations['filters'] = [ - 'nested' => ['path' => 'attributes'], - 'aggs' => $attributeAggregations, - ]; - } - - return $aggregations; - } -} diff --git a/src/Helper/FilterHelper.php b/src/Helper/FilterHelper.php deleted file mode 100644 index 25f8ef39..00000000 --- a/src/Helper/FilterHelper.php +++ /dev/null @@ -1,218 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Helper; - -class FilterHelper -{ - public const MAIN_TAXON_FILTER = 'main_taxon'; - public const TAXON_FILTER = 'taxon'; - public const PRICE_FILTER = 'price'; - - /** - * Return an array with filters for query. - * - * @param array $appliedFilters - * - * @return array - */ - public static function buildFilters(array $appliedFilters): array - { - if (empty($appliedFilters)) { - return []; - } - - $filters = []; - foreach ($appliedFilters as $field => $values) { - if (self::TAXON_FILTER === $field) { - $filters[] = self::buildTaxonFilter($values); - } elseif (self::MAIN_TAXON_FILTER === $field) { - $filters[] = self::buildMainTaxonFilter($values); - } elseif (self::PRICE_FILTER === $field) { - if (isset($values['min']) && isset($values['max'])) { - $filters[] = self::buildPriceFilter((int) $values['min'], (int) $values['max']); - } - } else { - $filters[] = self::buildFilter($field, $values); - } - } - - return [ - 'bool' => [ - 'filter' => $filters, - ], - ]; - } - - /** - * Build filter array to add in query. - * - * @param string $field - * @param array $values - * - * @return array - */ - public static function buildFilter(string $field, array $values): array - { - $filterValues = []; - foreach ($values as $value) { - $filterValues[] = self::buildFilterValue($value); - } - - return [ - 'nested' => [ - 'path' => 'attributes', - 'query' => [ - 'bool' => [ - 'must' => [ - 'match' => [ - 'attributes.code' => $field, - ], - ], - 'should' => $filterValues, - 'minimum_should_match' => 1, - ], - ], - ], - ]; - } - - /** - * Build filter array for taxon to add in query. - * - * @param array $values - * - * @return array - */ - public static function buildTaxonFilter(array $values): array - { - $filterValues = []; - foreach ($values as $value) { - $filterValues[] = self::buildTaxonFilterValue($value); - } - - return [ - 'nested' => [ - 'path' => 'taxon', - 'query' => [ - 'bool' => [ - 'should' => $filterValues, - 'minimum_should_match' => 1, - ], - ], - ], - ]; - } - - /** - * Build filter array for main taxon to add in query. - * - * @param array $values - * - * @return array - */ - public static function buildMainTaxonFilter(array $values): array - { - $filterValues = []; - foreach ($values as $value) { - $filterValues[] = self::buildMainTaxonFilterValue($value); - } - - return [ - 'nested' => [ - 'path' => 'mainTaxon', - 'query' => [ - 'bool' => [ - 'should' => $filterValues, - 'minimum_should_match' => 1, - ], - ], - ], - ]; - } - - /** - * Build filter array for price to add in query. - * - * @param int $min - * @param int $max - * - * @return array - */ - public static function buildPriceFilter(int $min, int $max): array - { - return [ - 'nested' => [ - 'path' => 'price', - 'query' => [ - 'range' => [ - 'price.value' => [ - [ - 'gte' => $min * 100, - 'lte' => $max * 100, - ], - ], - ], - ], - ], - ]; - } - - /** - * Build filter value array to add in query. - * - * @param string $value - * - * @return array - */ - public static function buildFilterValue(string $value): array - { - return [ - 'term' => [ - 'attributes.value.keyword' => SlugHelper::toLabel($value), - ], - ]; - } - - /** - * Build filter value array to add in query. - * - * @param string $value - * - * @return array - */ - public static function buildTaxonFilterValue(string $value): array - { - return [ - 'term' => [ - 'taxon.name' => SlugHelper::toLabel($value), - ], - ]; - } - - /** - * Build filter value array to add in query. - * - * @param string $value - * - * @return array - */ - public static function buildMainTaxonFilterValue(string $value): array - { - return [ - 'term' => [ - 'mainTaxon.name' => SlugHelper::toLabel($value), - ], - ]; - } -} diff --git a/src/Helper/RenderDocumentUrlHelper.php b/src/Helper/RenderDocumentUrlHelper.php deleted file mode 100644 index f3524c92..00000000 --- a/src/Helper/RenderDocumentUrlHelper.php +++ /dev/null @@ -1,32 +0,0 @@ -getType()) { - case 'product': - return new UrlParamsProvider('sylius_shop_product_show', ['slug' => $document->getSlug(), '_locale' => $document->getLocale()]); - break; - } - - throw new NotSupportedTypeException(sprintf('Object type "%s" not supported to get URL', $document->getType())); - } -} diff --git a/src/Helper/SlugHelper.php b/src/Helper/SlugHelper.php deleted file mode 100644 index b3bb6205..00000000 --- a/src/Helper/SlugHelper.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Helper; - -class SlugHelper -{ - public static function toSlug($label): string - { - return urlencode($label); - } - - public static function toLabel($slug): string - { - return urldecode($slug); - } -} diff --git a/src/Helper/SortHelper.php b/src/Helper/SortHelper.php deleted file mode 100644 index 17060905..00000000 --- a/src/Helper/SortHelper.php +++ /dev/null @@ -1,75 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Helper; - -class SortHelper -{ - /** - * Get query's sort array depending on sorted field. - * - * @param string $field - * @param string $channel - * @param string $order - * @param string $taxon - * - * @return array - */ - public static function getSortParamByField(string $field, string $channel, string $order = 'asc', $taxon = ''): array - { - switch ($field) { - case 'name': - return self::buildSort('attributes.value.keyword', $order, 'attributes', 'attributes.code', $field); - case 'created_at': - return self::buildSort('attributes.value.keyword', $order, 'attributes', 'attributes.code', $field); - case 'price': - return self::buildSort('price.value', $order, 'price', 'price.channel', $channel); - case 'position': - return self::buildSort('taxon.productPosition', $order, 'taxon', 'taxon.code', $taxon); - default: - // Dummy value to have null sorting in ES and keep ES results sorting - return self::buildSort('attributes.value.keyword', $order, 'attributes', 'attributes.code', 'dummy'); - } - } - - /** - * Build sort array to add in query. - * - * @param string $field - * @param string $order - * @param string $nestedPath - * @param string $sortFilterField - * @param string $sortFilterValue - * - * @return array - */ - public static function buildSort( - string $field, - string $order, - string $nestedPath, - string $sortFilterField, - string $sortFilterValue - ): array { - return [ - $field => [ - 'order' => $order, - 'nested' => [ - 'path' => $nestedPath, - 'filter' => [ - 'term' => [$sortFilterField => $sortFilterValue], - ], - ], - ], - ]; - } -} diff --git a/src/Model/ArrayObject.php b/src/Model/ArrayObject.php deleted file mode 100644 index 418ff6d8..00000000 --- a/src/Model/ArrayObject.php +++ /dev/null @@ -1,22 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Model; - -class ArrayObject extends \ArrayObject -{ - public function toArray(): array - { - return $this->getArrayCopy(); - } -} diff --git a/src/Model/Config/FilesConfig.php b/src/Model/Config/FilesConfig.php deleted file mode 100644 index 73c4aa32..00000000 --- a/src/Model/Config/FilesConfig.php +++ /dev/null @@ -1,62 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Model\Config; - -use MonsieurBiz\SyliusSearchPlugin\Exception\MissingConfigFileException; - -class FilesConfig -{ - /** @var string */ - private $searchPath; - - /** @var string */ - private $instantPath; - - /** @var string */ - private $taxonPath; - - public function __construct(array $files) - { - if (!isset($files['search']) || !isset($files['instant']) || !isset($files['taxon'])) { - throw new MissingConfigFileException('You need to have 3 config files : search, instant and taxon'); - } - $this->searchPath = $files['search']; - $this->instantPath = $files['instant']; - $this->taxonPath = $files['taxon']; - } - - /** - * @return string - */ - public function getSearchPath(): string - { - return $this->searchPath; - } - - /** - * @return string - */ - public function getInstantPath(): string - { - return $this->instantPath; - } - - /** - * @return string - */ - public function getTaxonPath(): string - { - return $this->taxonPath; - } -} diff --git a/src/Model/Config/GridConfig.php b/src/Model/Config/GridConfig.php deleted file mode 100644 index 30c8b551..00000000 --- a/src/Model/Config/GridConfig.php +++ /dev/null @@ -1,339 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Model\Config; - -use MonsieurBiz\SyliusSearchPlugin\Exception\UnknownGridConfigType; -use Sylius\Component\Core\Model\TaxonInterface; -use Sylius\Component\Product\Model\ProductAttribute; -use Sylius\Component\Product\Model\ProductOption; -use Sylius\Component\Resource\Repository\RepositoryInterface; -use Symfony\Component\HttpFoundation\Request; - -class GridConfig -{ - public const SEARCH_TYPE = 'search'; - public const TAXON_TYPE = 'taxon'; - public const INSTANT_TYPE = 'instant'; - - public const SORT_ASC = 'asc'; - public const SORT_DESC = 'desc'; - - public const FALLBACK_LIMIT = 10; - - /** @var array */ - private $config; - - /** @var string[] */ - private $isInitialized = false; - - /** @var string */ - private $type; - - /** @var string */ - private $locale; - - /** @var string */ - private $query; - - /** @var int */ - private $page; - - /** @var int[] */ - private $limits; - - /** @var int */ - private $limit; - - /** @var string[] */ - private $sorting; - - /** @var TaxonInterface|null */ - private $taxon; - - /** @var array */ - private $appliedFilters; - - /** - * @var array|null - */ - private $filterableAttributes; - - /** - * @var array|null - */ - private $filterableOptions; - - /** - * @var RepositoryInterface - */ - private $productAttributeRepository; - - /** - * @var RepositoryInterface - */ - private $productOptionRepository; - - /** - * GridConfig constructor. - * - * @param array $config - * @param RepositoryInterface $productAttributeRepository - * @param RepositoryInterface $productOptionRepository - */ - public function __construct(array $config, RepositoryInterface $productAttributeRepository, RepositoryInterface $productOptionRepository) - { - $this->config = $config; - $this->productAttributeRepository = $productAttributeRepository; - $this->productOptionRepository = $productOptionRepository; - } - - /** - * @param string $type - * @param Request $request - * @param TaxonInterface|null $taxon - */ - public function init(string $type, Request $request, ?TaxonInterface $taxon = null): void - { - if ($this->isInitialized) { - return; - } - - switch ($type) { - case self::SEARCH_TYPE: - // Set type, locale, page and query - $this->type = $type; - $this->locale = $request->getLocale(); - $this->page = max(1, (int) $request->get('page')); - $this->query = htmlspecialchars(urldecode($request->get('query'))); - - // Set sorting - $availableSorting = $this->config['sorting']['search'] ?? []; - $this->sorting = $this->cleanSorting($request->get('sorting'), $availableSorting); - - // Set limit - $this->limit = max(1, (int) $request->get('limit')); - $this->limits = $this->config['limits']['search'] ?? []; - if (!\in_array($this->limit, $this->limits, true)) { - $this->limit = $this->config['default_limit']['search'] ?? self::FALLBACK_LIMIT; - } - - // Set applied filters - $this->appliedFilters = $request->get('attribute') ?? []; - if ($priceFilter = $request->get('price')) { - $this->appliedFilters['price'] = $priceFilter; - } - - $this->isInitialized = true; - break; - case self::TAXON_TYPE: - // Set type, locale, page and taxon - $this->type = $type; - $this->locale = $request->getLocale(); - $this->page = max(1, (int) $request->get('page')); - $this->taxon = $taxon; - - // Set sorting - $availableSorting = $this->config['sorting']['taxon'] ?? []; - $this->sorting = $this->cleanSorting($request->get('sorting'), $availableSorting); - if (!\is_array($this->sorting) || empty($this->sorting)) { - $this->sorting['position'] = self::SORT_ASC; - } - - // Set applied filters - $this->appliedFilters = $request->get('attribute') ?? []; - if ($priceFilter = $request->get('price')) { - $this->appliedFilters['price'] = $priceFilter; - } - - // Set limit - $this->limit = max(1, (int) $request->get('limit')); - $this->limits = $this->config['limits']['taxon'] ?? []; - if (!\in_array($this->limit, $this->limits, true)) { - $this->limit = $this->config['default_limit']['taxon'] ?? self::FALLBACK_LIMIT; - } - $this->isInitialized = true; - break; - case self::INSTANT_TYPE: - // Set type, locale, page and query - $this->type = $type; - $this->locale = $request->getLocale(); - $this->page = 1; - $this->query = htmlspecialchars(urldecode($request->get('query'))); - - // Set limit - $this->limit = $this->config['default_limit']['instant'] ?? self::FALLBACK_LIMIT; - $this->isInitialized = true; - break; - default: - throw new UnknownGridConfigType(); - } - } - - /** - * @return string - */ - public function getType(): string - { - return $this->type; - } - - /** - * @return string - */ - public function getLocale(): string - { - return $this->locale; - } - - /** - * @return string - */ - public function getQuery(): string - { - return $this->query; - } - - /** - * @return int - */ - public function getPage(): int - { - return $this->page; - } - - /** - * @return int[] - */ - public function getLimits(): array - { - return $this->limits; - } - - /** - * @return int - */ - public function getLimit(): int - { - return $this->limit; - } - - /** - * @return string[] - */ - public function getSorting(): array - { - return $this->sorting; - } - - /** - * @return string[] - */ - public function getAttributeFilters(): array - { - if (null === $this->filterableAttributes) { - $attributes = $this->productAttributeRepository->findBy([ - 'filterable' => true, - ]); - $this->filterableAttributes = []; - /** @var ProductAttribute $attribute */ - foreach ($attributes as $attribute) { - $this->filterableAttributes[] = $attribute->getCode(); - } - } - - return $this->filterableAttributes; - } - - /** - * @return string[] - */ - public function getOptionFilters(): array - { - if (null === $this->filterableOptions) { - $options = $this->productOptionRepository->findBy([ - 'filterable' => true, - ]); - $this->filterableOptions = []; - /** @var ProductOption $option */ - foreach ($options as $option) { - $this->filterableOptions[] = $option->getCode(); - } - } - - return $this->filterableOptions; - } - - /** - * @return bool - */ - public function haveToApplyManuallyFilters(): bool - { - return $this->config['filters']['apply_manually'] ?? false; - } - - /** - * @return bool - */ - public function useMainTaxonForFilter(): bool - { - return $this->config['filters']['use_main_taxon'] ?? false; - } - - /** - * @return string[] - */ - public function getFilters(): array - { - return array_merge($this->getAttributeFilters(), $this->getOptionFilters()); - } - - /** - * @return array - */ - public function getAppliedFilters(): array - { - return $this->appliedFilters; - } - - /** - * @return TaxonInterface|null - */ - public function getTaxon(): ?TaxonInterface - { - return $this->taxon; - } - - /** - * Be sure given sort in available. - * - * @param $sorting - * @param $availableSorting - * - * @return array - */ - private function cleanSorting(?array $sorting, array $availableSorting): array - { - if (!\is_array($sorting)) { - return []; - } - - foreach ($sorting as $field => $order) { - if (!\in_array($field, $availableSorting, true) || !\in_array($order, [self::SORT_ASC, self::SORT_DESC], true)) { - unset($sorting[$field]); - } - } - - return $sorting; - } -} diff --git a/src/Model/Document/Filter.php b/src/Model/Document/Filter.php deleted file mode 100644 index 86bd2a55..00000000 --- a/src/Model/Document/Filter.php +++ /dev/null @@ -1,92 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Model\Document; - -class Filter -{ - /** - * @var string - */ - private $code; - - /** - * @var string - */ - private $label; - - /** - * @var FilterValue[] - */ - private $values = []; - - /** - * @var int - */ - private $count; - - /** - * Filter constructor. - * - * @param string $code - * @param string $label - * @param int $count - */ - public function __construct(string $code, string $label, int $count) - { - $this->code = $code; - $this->label = $label; - $this->count = $count; - } - - /** - * @return string - */ - public function getCode(): string - { - return $this->code; - } - - /** - * @return string - */ - public function getLabel(): string - { - return $this->label; - } - - /** - * @return FilterValue[] - */ - public function getValues(): array - { - return $this->values; - } - - /** - * @param $value - * @param $count - */ - public function addValue($value, $count): void - { - $this->values[] = new FilterValue($value, $count); - } - - /** - * @return int - */ - public function getCount(): int - { - return $this->count; - } -} diff --git a/src/Model/Document/FilterValue.php b/src/Model/Document/FilterValue.php deleted file mode 100644 index 3192a5ce..00000000 --- a/src/Model/Document/FilterValue.php +++ /dev/null @@ -1,71 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Model\Document; - -use MonsieurBiz\SyliusSearchPlugin\Helper\SlugHelper; - -class FilterValue -{ - /** - * @var string - */ - private $slug; - - /** - * @var string - */ - private $label; - - /** - * @var int - */ - private $count; - - /** - * Filter constructor. - * - * @param string $label - * @param int $count - */ - public function __construct(string $label, int $count) - { - $this->slug = SlugHelper::toSlug($label); - $this->label = $label; - $this->count = $count; - } - - /** - * @return string - */ - public function getSlug(): string - { - return $this->slug; - } - - /** - * @return string - */ - public function getLabel(): string - { - return $this->label; - } - - /** - * @return int - */ - public function getCount(): int - { - return $this->count; - } -} diff --git a/src/Model/Document/Index/AbstractIndex.php b/src/Model/Document/Index/AbstractIndex.php deleted file mode 100644 index 53c9d75d..00000000 --- a/src/Model/Document/Index/AbstractIndex.php +++ /dev/null @@ -1,81 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Model\Document\Index; - -use JoliCode\Elastically\Client; -use JoliCode\Elastically\IndexBuilder; -use JoliCode\Elastically\Indexer; -use MonsieurBiz\SyliusSearchPlugin\Provider\DocumentRepositoryProvider; - -abstract class AbstractIndex -{ - public const DOCUMENT_INDEX_NAME = 'documents'; - - /** - * @var DocumentRepositoryProvider - */ - protected $documentRepositoryProvider; - - /** @var Client */ - private $client; - - /** - * PopulateCommand constructor. - * - * @param Client $client - */ - public function __construct( - Client $client - ) { - $this->client = $client; - } - - /** - * Get the client. - * - * @return Client - */ - protected function getClient(): Client - { - return $this->client; - } - - /** - * Retrieve the index name. - * - * @param string $locale - * - * @return string - */ - protected function getIndexName(string $locale): string - { - return self::DOCUMENT_INDEX_NAME . '-' . strtolower($locale); - } - - /** - * @return IndexBuilder - */ - protected function getIndexBuilder(): IndexBuilder - { - return $this->client->getIndexBuilder(); - } - - /** - * @return Indexer - */ - protected function getIndexer(): Indexer - { - return $this->client->getIndexer(); - } -} diff --git a/src/Model/Document/Index/Indexer.php b/src/Model/Document/Index/Indexer.php deleted file mode 100644 index e13a5f84..00000000 --- a/src/Model/Document/Index/Indexer.php +++ /dev/null @@ -1,191 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Model\Document\Index; - -use Elastica\Document; -use Elastica\Exception\ResponseException; -use JoliCode\Elastically\Client; -use MonsieurBiz\SyliusSearchPlugin\Exception\MissingParamException; -use MonsieurBiz\SyliusSearchPlugin\Exception\ReadOnlyIndexException; -use MonsieurBiz\SyliusSearchPlugin\Model\Document\Result; -use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; -use MonsieurBiz\SyliusSearchPlugin\Provider\DocumentRepositoryProvider; -use MonsieurBiz\SyliusSearchPlugin\Provider\SearchQueryProvider; -use Psr\Log\LoggerInterface; -use Sylius\Component\Locale\Model\LocaleInterface; -use Sylius\Component\Resource\Repository\RepositoryInterface; -use Webmozart\Assert\Assert; - -class Indexer extends AbstractIndex -{ - /** - * @var DocumentRepositoryProvider - */ - protected $documentRepositoryProvider; - - /** @var RepositoryInterface */ - private $localeRepository; - - /** @var array */ - private $locales = []; - - /** - * PopulateCommand constructor. - * - * @param Client $client - * @param DocumentRepositoryProvider $documentRepositoryProvider - * @param RepositoryInterface $localeRepository - * @param SearchQueryProvider $searchQueryProvider - * @param LoggerInterface $logger - */ - public function __construct( - Client $client, - DocumentRepositoryProvider $documentRepositoryProvider, - RepositoryInterface $localeRepository - ) { - parent::__construct($client); - $this->documentRepositoryProvider = $documentRepositoryProvider; - $this->localeRepository = $localeRepository; - } - - /** - * Retrieve all available locales. - * - * @return array - */ - public function getLocales(): array - { - if (empty($this->locales)) { - $locales = $this->localeRepository->findAll(); - $this->locales = array_map( - function(LocaleInterface $locale) { - return $locale->getCode(); - }, - $locales - ); - } - - return $this->locales; - } - - /** - * Index all documents in all locales. - * - * @throws \Exception - */ - public function indexAll(): void - { - foreach ($this->getLocales() as $locale) { - $this->indexAllByLocale($locale); - } - } - - /** - * Index all document for a locale. - * - * @param string $locale - * - * @throws \Exception - */ - public function indexAllByLocale(string $locale): void - { - $indexName = $this->getIndexName($locale); - $newIndex = $this->getIndexBuilder()->createIndex($indexName); - $this->getIndexBuilder()->markAsLive( - $newIndex, - $indexName - ); - - $repositories = $this->documentRepositoryProvider->getRepositories(); - foreach ($repositories as $repository) { - $documents = $repository->findAll(); - /** @var DocumentableInterface $document */ - foreach ($documents as $document) { - Assert::isInstanceOf($document, DocumentableInterface::class); - $this->indexOneByLocale($document->convertToDocument($locale), $locale); - } - } - - $this->getIndexer()->flush(); - - $this->getIndexer()->refresh($indexName); - try { - $this->getIndexBuilder()->purgeOldIndices($indexName); - } catch (ResponseException $exception) { - throw new ReadOnlyIndexException($exception->getMessage()); - } - } - - /** - * Index a document for all locales. - * - * @param DocumentableInterface $subject - * - * @throws \Exception - */ - public function indexOne(DocumentableInterface $subject): void - { - foreach ($this->getLocales() as $locale) { - $this->indexOneByLocale($subject->convertToDocument($locale), $locale); - $this->getIndexer()->flush(); - } - } - - /** - * Index a document for one locale. - * - * @param Result $document - * @param string $locale - * - * @throws MissingParamException - */ - public function indexOneByLocale(Result $document, string $locale): void - { - $this->getIndexer()->scheduleIndex( - $this->getClient()->getIndex($this->getIndexName($locale)), - new Document($document->getUniqId(), $document) - ); - } - - /** - * Remove a document for all locales. - * - * @param DocumentableInterface $subject - * - * @throws \Exception - */ - public function removeOne(DocumentableInterface $subject): void - { - foreach ($this->getLocales() as $locale) { - $this->removeOneByLocale($subject->convertToDocument($locale), $locale); - $this->getIndexer()->flush(); - } - } - - /** - * Remove a document for all locales. - * - * @param Result $document - * @param string $locale - * - * @throws MissingParamException - */ - public function removeOneByLocale(Result $document, string $locale): void - { - $this->getIndexer()->scheduleDelete( - $this->getClient()->getIndex($this->getIndexName($locale)), - $document->getUniqId() - ); - } -} diff --git a/src/Model/Document/Index/Search.php b/src/Model/Document/Index/Search.php deleted file mode 100644 index 6db1ee29..00000000 --- a/src/Model/Document/Index/Search.php +++ /dev/null @@ -1,280 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Model\Document\Index; - -use Elastica\Exception\Connection\HttpException; -use Elastica\Exception\ResponseException; -use Elastica\ResultSet as ElasticaResultSet; -use JoliCode\Elastically\Client; -use MonsieurBiz\SyliusSearchPlugin\Exception\ReadFileException; -use MonsieurBiz\SyliusSearchPlugin\Helper\AggregationHelper; -use MonsieurBiz\SyliusSearchPlugin\Helper\FilterHelper; -use MonsieurBiz\SyliusSearchPlugin\Helper\SortHelper; -use MonsieurBiz\SyliusSearchPlugin\Model\ArrayObject; -use MonsieurBiz\SyliusSearchPlugin\Model\Config\GridConfig; -use MonsieurBiz\SyliusSearchPlugin\Model\Document\ResultSet; -use MonsieurBiz\SyliusSearchPlugin\Provider\SearchQueryProvider; -use Psr\Log\LoggerInterface; -use Sylius\Component\Channel\Context\ChannelContextInterface; -use Symfony\Component\Yaml\Yaml; - -class Search extends AbstractIndex -{ - /** @var SearchQueryProvider */ - private $searchQueryProvider; - - /** @var LoggerInterface */ - private $logger; - - /** @var ChannelContextInterface */ - private $channelContext; - - /** - * PopulateCommand constructor. - * - * @param Client $client - * @param SearchQueryProvider $searchQueryProvider - * @param ChannelContextInterface $channelContext - * @param LoggerInterface $logger - */ - public function __construct( - Client $client, - SearchQueryProvider $searchQueryProvider, - ChannelContextInterface $channelContext, - LoggerInterface $logger - ) { - parent::__construct($client); - $this->searchQueryProvider = $searchQueryProvider; - $this->channelContext = $channelContext; - $this->logger = $logger; - } - - /** - * Search documents for a given locale, search terms, max number items and page. - * - * @param GridConfig $gridConfig - * - * @return ResultSet - */ - public function search(GridConfig $gridConfig): ResultSet - { - try { - return $this->query($gridConfig, $this->getSearchQuery($gridConfig)); - } catch (ReadFileException $exception) { - $this->logger->critical($exception->getMessage()); - - return new ResultSet($gridConfig->getLimit(), $gridConfig->getPage()); - } - } - - /** - * Instant search documents for a given locale, query and a max number items. - * - * @param GridConfig $gridConfig - * - * @return ResultSet - */ - public function instant(GridConfig $gridConfig): ResultSet - { - try { - return $this->query($gridConfig, $this->getInstantQuery($gridConfig)); - } catch (ReadFileException $exception) { - $this->logger->critical($exception->getMessage()); - - return new ResultSet($gridConfig->getLimit(), $gridConfig->getPage()); - } - } - - /** - * Taxon search documents for a given locale, taxon code, max number items and page. - * - * @param GridConfig $gridConfig - * - * @return ResultSet - */ - public function taxon(GridConfig $gridConfig): ResultSet - { - try { - return $this->query($gridConfig, $this->getTaxonQuery($gridConfig)); - } catch (ReadFileException $exception) { - $this->logger->critical($exception->getMessage()); - - return new ResultSet($gridConfig->getLimit(), $gridConfig->getPage()); - } - } - - /** - * Perform search for a given query. - * - * @param GridConfig $gridConfig - * @param array $query - * - * @return ResultSet - */ - private function query(GridConfig $gridConfig, array $query) - { - try { - /** @var ElasticaResultSet $results */ - $results = $this->getClient()->getIndex($this->getIndexName($gridConfig->getLocale()))->search( - $query, $gridConfig->getLimit() - ); - } catch (HttpException $exception) { - $this->logger->critical($exception->getMessage()); - - return new ResultSet($gridConfig->getLimit(), $gridConfig->getPage()); - } catch (ResponseException $exception) { - $this->logger->critical($exception->getMessage()); - - return new ResultSet($gridConfig->getLimit(), $gridConfig->getPage()); - } - - return new ResultSet($gridConfig->getLimit(), $gridConfig->getPage(), $results, $gridConfig->getTaxon()); - } - - /** - * Retrieve the query to send to Elasticsearch for search. - * - * @param GridConfig $gridConfig - * - * @throws ReadFileException - * - * @return array - */ - private function getSearchQuery(GridConfig $gridConfig): array - { - $query = $this->searchQueryProvider->getSearchQuery(); - - // Replace params - $query = str_replace('{{QUERY}}', $gridConfig->getQuery(), $query); - $query = str_replace('{{CHANNEL}}', $this->channelContext->getChannel()->getCode(), $query); - - // Convert query to array - $query = $this->parseQuery($query); - - $appliedFilters = FilterHelper::buildFilters($gridConfig->getAppliedFilters()); - if ($gridConfig->haveToApplyManuallyFilters() && isset($appliedFilters['bool']['filter'])) { - // Will retrieve filters after we applied the current ones - $query['query']['bool']['filter'] = array_merge( - $query['query']['bool']['filter'], $appliedFilters['bool']['filter'] - ); - } elseif (!empty($appliedFilters)) { - // Will retrieve filters before we applied the current ones - $query['post_filter'] = new ArrayObject($appliedFilters); // Use custom ArrayObject because Elastica make `toArray` on it. - } - - // Manage limits - $from = ($gridConfig->getPage() - 1) * $gridConfig->getLimit(); - $query['from'] = max(0, $from); - $query['size'] = max(1, $gridConfig->getLimit()); - - // Manage sorting - $channelCode = $this->channelContext->getChannel()->getCode(); - foreach ($gridConfig->getSorting() as $field => $order) { - $query['sort'][] = SortHelper::getSortParamByField($field, $channelCode, $order); - break; // only 1 - } - - // Manage filters - $aggs = AggregationHelper::buildAggregations($gridConfig->getFilters()); - if (!empty($aggs)) { - $query['aggs'] = AggregationHelper::buildAggregations($gridConfig->getFilters()); - } - - return $query; - } - - /** - * Retrieve the query to send to Elasticsearch for instant search. - * - * @param GridConfig $gridConfig - * - * @throws ReadFileException - * - * @return array - */ - private function getInstantQuery(GridConfig $gridConfig): array - { - $query = $this->searchQueryProvider->getInstantQuery(); - - // Replace params - $query = str_replace('{{QUERY}}', $gridConfig->getQuery(), $query); - $query = str_replace('{{CHANNEL}}', $this->channelContext->getChannel()->getCode(), $query); - - // Convert query to array - return $this->parseQuery($query); - } - - /** - * Retrieve the query to send to Elasticsearch for taxon search. - * - * @param GridConfig $gridConfig - * - * @throws ReadFileException - * - * @return array - */ - private function getTaxonQuery(GridConfig $gridConfig): array - { - $query = $this->searchQueryProvider->getTaxonQuery(); - - // Replace params - $query = str_replace('{{TAXON}}', $gridConfig->getTaxon()->getCode(), $query); - $query = str_replace('{{CHANNEL}}', $this->channelContext->getChannel()->getCode(), $query); - - // Convert query to array - $query = $this->parseQuery($query); - - // Apply filters - $appliedFilters = FilterHelper::buildFilters($gridConfig->getAppliedFilters()); - if ($gridConfig->haveToApplyManuallyFilters() && isset($appliedFilters['bool']['filter'])) { - // Will retrieve filters after we applied the current ones - $query['query']['bool']['filter'] = array_merge( - $query['query']['bool']['filter'], $appliedFilters['bool']['filter'] - ); - } elseif (!empty($appliedFilters)) { - // Will retrieve filters before we applied the current ones - $query['post_filter'] = new ArrayObject($appliedFilters); // Use custom ArrayObject because Elastica make `toArray` on it. - } - - // Manage limits - $from = ($gridConfig->getPage() - 1) * $gridConfig->getLimit(); - $query['from'] = max(0, $from); - $query['size'] = max(1, $gridConfig->getLimit()); - - // Manage sorting - $channelCode = $this->channelContext->getChannel()->getCode(); - foreach ($gridConfig->getSorting() as $field => $order) { - $query['sort'][] = SortHelper::getSortParamByField($field, $channelCode, $order, $gridConfig->getTaxon()->getCode()); - break; // only 1 - } - - // Manage filters - $aggs = AggregationHelper::buildAggregations($gridConfig->getFilters()); - if (!empty($aggs)) { - $query['aggs'] = AggregationHelper::buildAggregations($gridConfig->getFilters()); - } - - return $query; - } - - /** - * @param string $query - * - * @return array - */ - private function parseQuery(string $query): array - { - return Yaml::parse($query); - } -} diff --git a/src/Model/Document/RangeFilter.php b/src/Model/Document/RangeFilter.php deleted file mode 100644 index 2b58e956..00000000 --- a/src/Model/Document/RangeFilter.php +++ /dev/null @@ -1,115 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Model\Document; - -class RangeFilter -{ - /** - * @var string - */ - private $code; - - /** - * @var string - */ - private $label; - - /** - * @var string - */ - private $minLabel; - - /** - * @var string - */ - private $maxLabel; - - /** - * @var int - */ - private $min; - - /** - * @var int - */ - private $max; - - /** - * Filter constructor. - * - * @param string $code - * @param string $label - * @param string $minLabel - * @param string $maxLabel - * @param int $min - * @param int $max - */ - public function __construct(string $code, string $label, string $minLabel, string $maxLabel, int $min, int $max) - { - $this->code = $code; - $this->label = $label; - $this->minLabel = $minLabel; - $this->maxLabel = $maxLabel; - $this->min = $min; - $this->max = $max; - } - - /** - * @return string - */ - public function getCode(): string - { - return $this->code; - } - - /** - * @return string - */ - public function getLabel(): string - { - return $this->label; - } - - /** - * @return string - */ - public function getMinLabel(): string - { - return $this->minLabel; - } - - /** - * @return string - */ - public function getMaxLabel(): string - { - return $this->maxLabel; - } - - /** - * @return int - */ - public function getMin(): int - { - return $this->min; - } - - /** - * @return int - */ - public function getMax(): int - { - return $this->max; - } -} diff --git a/src/Model/Document/Result.php b/src/Model/Document/Result.php deleted file mode 100644 index 64034517..00000000 --- a/src/Model/Document/Result.php +++ /dev/null @@ -1,217 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Model\Document; - -use MonsieurBiz\SyliusSearchPlugin\Exception\MissingLocaleException; -use MonsieurBiz\SyliusSearchPlugin\Exception\MissingParamException; -use MonsieurBiz\SyliusSearchPlugin\Exception\MissingPriceException; -use MonsieurBiz\SyliusSearchPlugin\Exception\NotSupportedTypeException; -use MonsieurBiz\SyliusSearchPlugin\generated\Model\Attributes; -use MonsieurBiz\SyliusSearchPlugin\generated\Model\Document; -use MonsieurBiz\SyliusSearchPlugin\generated\Model\Price; -use MonsieurBiz\SyliusSearchPlugin\generated\Model\Taxon; -use MonsieurBiz\SyliusSearchPlugin\Provider\UrlParamsProvider; - -class Result extends Document implements ResultInterface -{ - /** - * Document ID in elasticsearch. - * - * @throws MissingParamException - * - * @return string - */ - public function getUniqId(): string - { - if (!$this->getType()) { - throw new MissingParamException('Missing "type" for document'); - } - if (!$this->getId()) { - throw new MissingParamException('Missing "ID" for document'); - } - - return sprintf('%s-%d', $this->getType(), $this->getId()); - } - - /** - * @param string $code - * - * @return Attributes - */ - public function getAttribute(string $code): ?Attributes - { - foreach ($this->getAttributes() as $attribute) { - if ($attribute->getCode() === $code) { - return $attribute; - } - } - - return null; - } - - /** - * @param string $channelCode - * @param string $currencyCode - * - * @throws MissingPriceException - * - * @return Price|null - */ - public function getPriceByChannelAndCurrency(string $channelCode, string $currencyCode): ?Price - { - if (null === $this->getPrice()) { - return null; - } - foreach ($this->getPrice() as $price) { - if ($price->getChannel() === $channelCode && $price->getCurrency() === $currencyCode) { - return $price; - } - } - throw new MissingPriceException(sprintf('Price not found for channel "%s" and currency "%s"', $channelCode, $currencyCode)); - } - - /** - * @param string $channelCode - * @param string $currencyCode - * - * @return Price|null - */ - public function getOriginalPriceByChannelAndCurrency(string $channelCode, string $currencyCode): ?Price - { - if (null === $this->getOriginalPrice()) { - return null; - } - - foreach ($this->getOriginalPrice() as $price) { - if ($price->getChannel() === $channelCode && $price->getCurrency() === $currencyCode) { - return $price; - } - } - - return null; - } - - /** - * @throws MissingLocaleException - * - * @return string - */ - public function getLocale(): string - { - foreach ($this->getAttributes() as $attribute) { - if ($attribute->getLocale()) { - return $attribute->getLocale(); - } - } - - throw new MissingLocaleException('Locale not found in document'); - } - - /** - * @throws MissingLocaleException - * @throws NotSupportedTypeException - * - * @return UrlParamsProvider - */ - public function getUrlParams(): UrlParamsProvider - { - switch ($this->getType()) { - case 'product': - return new UrlParamsProvider('sylius_shop_product_show', ['slug' => $this->getSlug(), '_locale' => $this->getLocale()]); - break; - } - - throw new NotSupportedTypeException(sprintf('Object type "%s" not supported to get URL', $this->getType())); - } - - /** - * @param string $channel - * - * @return Result - */ - public function addChannel(string $channel): self - { - $this->setChannel($this->getChannel() ? array_unique(array_merge($this->getChannel(), [$channel])) : [$channel]); - - return $this; - } - - /** - * @param string $code - * @param string $name - * @param int $position - * @param int $level - * @param int $productPosition - * - * @return Result - */ - public function addTaxon(string $code, string $name, int $position, int $level, int $productPosition): ResultInterface - { - $taxon = new Taxon(); - $taxon->setCode($code)->setPosition($position)->setName($name)->setLevel($level)->setProductPosition($productPosition); - $this->setTaxon($this->getTaxon() ? array_merge($this->getTaxon(), [$taxon]) : [$taxon]); - - return $this; - } - - /** - * @param string $channel - * @param string $currency - * @param int $value - * - * @return Result - */ - public function addPrice(string $channel, string $currency, int $value): ResultInterface - { - $price = new Price(); - $price->setChannel($channel)->setCurrency($currency)->setValue($value); - $this->setPrice($this->getPrice() ? array_merge($this->getPrice(), [$price]) : [$price]); - - return $this; - } - - /** - * @param string $channel - * @param string $currency - * @param int $value - * - * @return Result - */ - public function addOriginalPrice(string $channel, string $currency, int $value): ResultInterface - { - $price = new Price(); - $price->setChannel($channel)->setCurrency($currency)->setValue($value); - $this->setOriginalPrice($this->getOriginalPrice() ? array_merge($this->getOriginalPrice(), [$price]) : [$price]); - - return $this; - } - - /** - * @param string $code - * @param string $name - * @param array $value - * @param string $locale - * @param int $score - * - * @return Result - */ - public function addAttribute(string $code, string $name, array $value, string $locale, int $score): ResultInterface - { - $attribute = new Attributes(); - $attribute->setCode($code)->setName($name)->setValue($value)->setLocale($locale)->setScore($score); - $this->setAttributes($this->getAttributes() ? array_merge($this->getAttributes(), [$attribute]) : [$attribute]); - - return $this; - } -} diff --git a/src/Model/Document/ResultInterface.php b/src/Model/Document/ResultInterface.php deleted file mode 100644 index a6ec4f4f..00000000 --- a/src/Model/Document/ResultInterface.php +++ /dev/null @@ -1,126 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Model\Document; - -use MonsieurBiz\SyliusSearchPlugin\Exception\MissingLocaleException; -use MonsieurBiz\SyliusSearchPlugin\Exception\MissingParamException; -use MonsieurBiz\SyliusSearchPlugin\Exception\MissingPriceException; -use MonsieurBiz\SyliusSearchPlugin\Exception\NotSupportedTypeException; -use MonsieurBiz\SyliusSearchPlugin\generated\Model\Attributes; -use MonsieurBiz\SyliusSearchPlugin\generated\Model\Document; -use MonsieurBiz\SyliusSearchPlugin\generated\Model\Price; -use MonsieurBiz\SyliusSearchPlugin\generated\Model\Taxon; -use MonsieurBiz\SyliusSearchPlugin\Provider\UrlParamsProvider; - -interface ResultInterface -{ - /** - * Document ID in elasticsearch. - * - * @throws MissingParamException - * - * @return string - */ - public function getUniqId(): string; - - - /** - * @param string $code - * - * @return Attributes - */ - public function getAttribute(string $code): ?Attributes; - - /** - * @param string $channelCode - * @param string $currencyCode - * - * @throws MissingPriceException - * - * @return Price|null - */ - public function getPriceByChannelAndCurrency(string $channelCode, string $currencyCode): ?Price; - - /** - * @param string $channelCode - * @param string $currencyCode - * - * @throws MissingPriceException - * - * @return Price|null - */ - public function getOriginalPriceByChannelAndCurrency(string $channelCode, string $currencyCode): ?Price; - - /** - * @throws MissingLocaleException - * - * @return string - */ - public function getLocale(): string; - - /** - * @throws MissingLocaleException - * @throws NotSupportedTypeException - * - * @return UrlParamsProvider - */ - public function getUrlParams(): UrlParamsProvider; - - /** - * @param string $channel - * - * @return ResultInterface - */ - public function addChannel(string $channel): self; - - /** - * @param string $code - * @param string $name - * @param int $position - * @param int $level - * @param int $productPosition - * - * @return ResultInterface - */ - public function addTaxon(string $code, string $name, int $position, int $level, int $productPosition): self; - - /** - * @param string $channel - * @param string $currency - * @param int $value - * - * @return ResultInterface - */ - public function addPrice(string $channel, string $currency, int $value): self; - - /** - * @param string $channel - * @param string $currency - * @param int $value - * - * @return ResultInterface - */ - public function addOriginalPrice(string $channel, string $currency, int $value): self; - - /** - * @param string $code - * @param string $name - * @param array $value - * @param string $locale - * @param int $score - * - * @return ResultInterface - */ - public function addAttribute(string $code, string $name, array $value, string $locale, int $score): self; -} diff --git a/src/Model/Document/ResultSet.php b/src/Model/Document/ResultSet.php deleted file mode 100644 index 3cb19121..00000000 --- a/src/Model/Document/ResultSet.php +++ /dev/null @@ -1,335 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Model\Document; - -use Elastica\ResultSet as ElasticaResultSet; -use JoliCode\Elastically\Result; -use MonsieurBiz\SyliusSearchPlugin\Adapter\ResultSetAdapter; -use MonsieurBiz\SyliusSearchPlugin\generated\Model\Taxon; -use Pagerfanta\Pagerfanta; -use Sylius\Component\Core\Model\TaxonInterface; - -class ResultSet -{ - /** @var Result[] */ - private $results = []; - - /** @var int */ - private $totalHits; - - /** @var int */ - private $maxItems; - - /** @var int */ - private $page; - - /** @var Filter[] */ - private $filters = []; - - /** @var RangeFilter|null */ - private $priceFilter; - - /** @var Filter|null */ - private $taxonFilter; - - /** @var Filter|null */ - private $mainTaxonFilter; - - /** @var Pagerfanta */ - private $pager; - - /** - * SearchResults constructor. - * - * @param int $maxItems - * @param int $page - * @param ElasticaResultSet|null $resultSet - * @param TaxonInterface|null $taxon - */ - public function __construct(int $maxItems, int $page, ?ElasticaResultSet $resultSet = null, ?TaxonInterface $taxon = null) - { - $this->maxItems = $maxItems; - $this->page = $page; - - // Empty result set - if (null === $resultSet) { - $this->totalHits = 0; - $this->results = []; - $this->filters = []; - } else { - /** @var Result $result */ - foreach ($resultSet as $result) { - $this->results[] = $result->getModel(); - } - $this->totalHits = $resultSet->getTotalHits(); - $this->initFilters($resultSet, $taxon); - } - - $this->initPager(); - } - - /** - * Init pager with Pager Fanta. - */ - private function initPager(): void - { - $adapter = new ResultSetAdapter($this); - $this->pager = new Pagerfanta($adapter); - $this->pager->setMaxPerPage($this->maxItems); - $this->pager->setCurrentPage($this->page); - } - - /** - * Init filters array depending on result aggregations. - * - * @param ElasticaResultSet $resultSet - * @param TaxonInterface|null $taxon - */ - private function initFilters(ElasticaResultSet $resultSet, ?TaxonInterface $taxon = null): void - { - $aggregations = $resultSet->getAggregations(); - // No aggregation so don't perform filters - if (empty($aggregations)) { - return; - } - - // Retrieve filters labels in aggregations - $attributes = []; - $attributeAggregations = $aggregations['attributes'] ?? []; - unset($attributeAggregations['doc_count']); - $attributeCodeBuckets = $attributeAggregations['codes']['buckets'] ?? []; - foreach ($attributeCodeBuckets as $attributeCodeBucket) { - $attributeCode = $attributeCodeBucket['key']; - $attributeNameBuckets = $attributeCodeBucket['names']['buckets'] ?? []; - foreach ($attributeNameBuckets as $attributeNameBucket) { - $attributeName = $attributeNameBucket['key']; - $attributes[$attributeCode] = $attributeName; - break; - } - } - - // Retrieve filters values in aggregations - $filterAggregations = $aggregations['filters'] ?? []; - unset($filterAggregations['doc_count']); - foreach ($filterAggregations as $field => $aggregation) { - if (0 === $aggregation['doc_count']) { - continue; - } - $filter = new Filter($field, $attributes[$field] ?? $field, $aggregation['doc_count']); - $buckets = $aggregation['values']['buckets'] ?? []; - foreach ($buckets as $bucket) { - if (isset($bucket['key']) && isset($bucket['doc_count'])) { - $filter->addValue($bucket['key'], $bucket['doc_count']); - } - } - $this->filters[] = $filter; - } - $this->sortFilters(); - - $this->addTaxonFilter($aggregations, $taxon); - $this->addMainTaxonFilter($aggregations, $taxon); - - $this->addPriceFilter($aggregations); - } - - /** - * @return Result[] - */ - public function getResults(): array - { - return $this->results; - } - - /** - * @return Filter[] - */ - public function getFilters(): array - { - return $this->filters; - } - - /** - * @return int - */ - public function getTotalHits(): int - { - return $this->totalHits; - } - - /** - * @return Pagerfanta - */ - public function getPager(): Pagerfanta - { - return $this->pager; - } - - /** - * @return Filter|null - */ - public function getTaxonFilter(): ?Filter - { - return $this->taxonFilter; - } - - /** - * @return Filter|null - */ - public function getMainTaxonFilter(): ?Filter - { - return $this->mainTaxonFilter; - } - - /** - * @return RangeFilter|null - */ - public function getPriceFilter(): ?RangeFilter - { - return $this->priceFilter; - } - - /** - * Sort filters. - */ - protected function sortFilters(): void - { - usort($this->filters, function($filter1, $filter2) { - /** @var Filter $filter1 */ - /** @var Filter $filter2 */ - - // If same count we display the filters with more values before - if ($filter1->getCount() === $filter2->getCount()) { - return \count($filter2->getValues()) > \count($filter1->getValues()); - } - - return $filter2->getCount() > $filter1->getCount(); - }); - } - - /** - * Add taxon filter depending on aggregations. - * - * @param array $aggregations - * @param TaxonInterface|null $taxon - */ - protected function addTaxonFilter(array $aggregations, ?TaxonInterface $taxon): void - { - $taxonAggregation = $aggregations['taxons'] ?? null; - if ($taxonAggregation && $taxonAggregation['doc_count'] > 0) { - // Get current taxon level to retrieve only greater levels, in search we will take only the first level - $currentTaxonLevel = $taxon ? $taxon->getLevel() : 0; - - // Get children taxon if we have current taxon - $childrenTaxon = []; - if ($taxon) { - foreach ($taxon->getChildren() as $child) { - $childrenTaxon[$child->getCode()] = $child->getLevel(); - } - } - - $filter = new Filter('taxon', 'monsieurbiz_searchplugin.filters.taxon_filter', $taxonAggregation['doc_count']); - - // Get taxon code in aggregation - $taxonCodeBuckets = $taxonAggregation['codes']['buckets'] ?? []; - foreach ($taxonCodeBuckets as $taxonCodeBucket) { - if (0 === $taxonCodeBucket['doc_count']) { - continue; - } - $taxonCode = $taxonCodeBucket['key']; - $taxonName = null; - - // Get taxon level in aggregation - $taxonLevelBuckets = $taxonCodeBucket['levels']['buckets'] ?? []; - foreach ($taxonLevelBuckets as $taxonLevelBucket) { - $level = $taxonLevelBucket['key']; - if ($level === ($currentTaxonLevel + 1) && (!$taxon || isset($childrenTaxon[$taxonCode]))) { - // Get taxon name in aggregation - $taxonNameBuckets = $taxonLevelBucket['names']['buckets'] ?? []; - foreach ($taxonNameBuckets as $taxonNameBucket) { - $taxonName = $taxonNameBucket['key']; - $filter->addValue($taxonName ?? $taxonCode, $taxonCodeBucket['doc_count']); - break 2; - } - } - } - } - - // Put taxon filter in first if contains value - if (\count($filter->getValues())) { - $this->taxonFilter = $filter; - } - } - } - - /** - * Add main taxon filter depending on aggregations. - * - * @param array $aggregations - * @param TaxonInterface|null $taxon - */ - protected function addMainTaxonFilter(array $aggregations, ?TaxonInterface $taxon): void - { - $taxonAggregation = $aggregations['mainTaxon'] ?? null; - if ($taxonAggregation && $taxonAggregation['doc_count'] > 0) { - $filter = new Filter('main_taxon', 'monsieurbiz_searchplugin.filters.taxon_filter', $taxonAggregation['doc_count']); - - // Get main taxon code in aggregation - $taxonCodeBuckets = $taxonAggregation['codes']['buckets'] ?? []; - foreach ($taxonCodeBuckets as $taxonCodeBucket) { - if (0 === $taxonCodeBucket['doc_count']) { - continue; - } - $taxonCode = $taxonCodeBucket['key']; - $taxonName = null; - - // Get main taxon level in aggregation - $taxonLevelBuckets = $taxonCodeBucket['levels']['buckets'] ?? []; - foreach ($taxonLevelBuckets as $taxonLevelBucket) { - // Get main taxon name in aggregation - $taxonNameBuckets = $taxonLevelBucket['names']['buckets'] ?? []; - foreach ($taxonNameBuckets as $taxonNameBucket) { - $taxonName = $taxonNameBucket['key']; - $filter->addValue($taxonName ?? $taxonCode, $taxonCodeBucket['doc_count']); - break 2; - } - } - } - - // Put taxon filter in first if contains value - if (\count($filter->getValues())) { - $this->mainTaxonFilter = $filter; - } - } - } - - /** - * Add price filter depending on aggregations. - * - * @param array $aggregations - */ - protected function addPriceFilter(array $aggregations): void - { - $priceAggregation = $aggregations['price'] ?? null; - if ($priceAggregation && $priceAggregation['doc_count'] > 0) { - $this->priceFilter = new RangeFilter( - 'price', - 'monsieurbiz_searchplugin.filters.price_filter', - 'monsieurbiz_searchplugin.filters.price_min', - 'monsieurbiz_searchplugin.filters.price_max', - (int) floor(($priceAggregation['values']['min'] ?? 0) / 100), - (int) ceil(($priceAggregation['values']['max'] ?? 0) / 100) - ); - } - } -} diff --git a/src/Model/Documentable/DocumentableInterface.php b/src/Model/Documentable/DocumentableInterface.php deleted file mode 100644 index 78dfee57..00000000 --- a/src/Model/Documentable/DocumentableInterface.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Model\Documentable; - -use MonsieurBiz\SyliusSearchPlugin\Model\Document\Result; - -interface DocumentableInterface -{ - public function getDocumentType(): string; - - public function convertToDocument(string $locale): Result; -} diff --git a/src/Model/Documentable/DocumentableProductTrait.php b/src/Model/Documentable/DocumentableProductTrait.php deleted file mode 100644 index 50365e00..00000000 --- a/src/Model/Documentable/DocumentableProductTrait.php +++ /dev/null @@ -1,239 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Model\Documentable; - -use MonsieurBiz\SyliusSearchPlugin\generated\Model\Taxon as DocumentTaxon; -use MonsieurBiz\SyliusSearchPlugin\Model\Document\Result; -use MonsieurBiz\SyliusSearchPlugin\Model\Document\ResultInterface; -use Sylius\Component\Attribute\Model\AttributeValueInterface; -use Sylius\Component\Core\Model\Channel; -use Sylius\Component\Core\Model\Image; -use Sylius\Component\Core\Model\ProductTaxonInterface; -use Sylius\Component\Core\Model\ProductVariant; -use Sylius\Component\Core\Model\TaxonInterface; -use Sylius\Component\Currency\Model\CurrencyInterface; - -trait DocumentableProductTrait -{ - /** - * @return string - */ - public function getDocumentType(): string - { - return 'product'; - } - - /** - * @return ResultInterface - */ - public function createResult(): ResultInterface - { - return new Result(); - } - - /** - * @param string $locale - * - * @return ResultInterface - */ - public function convertToDocument(string $locale): ResultInterface - { - $document = $this->createResult(); - - // Document data - $document->setType($this->getDocumentType()); - $document->setCode($this->getCode()); - $document->setId($this->getId()); - $document->setEnabled($this->isEnabled()); - $document->setSlug($this->getTranslation($locale)->getSlug()); - - $document = $this->addImagesInDocument($document); - $document = $this->addChannelsInDocument($document); - $document = $this->addPricesInDocument($document); - $document = $this->addTaxonsInDocument($document, $locale); - - $document->addAttribute('name', 'Name', [$this->getTranslation($locale)->getName()], $locale, 50); - $document->addAttribute('description', 'Description', [$this->getTranslation($locale)->getDescription()], $locale, 10); - $document->addAttribute('short_description', 'Short description', [$this->getTranslation($locale)->getShortDescription()], $locale, 10); - $document->addAttribute('created_at', 'Creation Date', [$this->getCreatedAt()], $locale, 1); - - $document = $this->addAttributesInDocument($document, $locale); - - return $this->addOptionsInDocument($document, $locale); - } - - /** - * @param ResultInterface $document - * - * @return ResultInterface - */ - protected function addImagesInDocument(ResultInterface $document): ResultInterface - { - /** @var Image $image */ - if ($image = $this->getImages()->first()) { - $document->setImage($image->getPath()); - } - - return $document; - } - - /** - * @param ResultInterface $document - * - * @return ResultInterface - */ - protected function addChannelsInDocument(ResultInterface $document): ResultInterface - { - /** @var Channel $channel */ - foreach ($this->getChannels() as $channel) { - $document->addChannel($channel->getCode()); - } - - return $document; - } - - /** - * @param ResultInterface $document - * - * @return ResultInterface - */ - protected function addPricesInDocument(ResultInterface $document): ResultInterface - { - /** @var Channel $channel */ - foreach ($this->getChannels() as $channel) { - /** @var ProductVariant $variant */ - if ($variant = $this->getCheapestVariantForChannel($channel)) { - $price = $variant->getChannelPricingForChannel($channel); - - /** @var CurrencyInterface $currency */ - foreach ($channel->getCurrencies() as $currency) { - $document->addPrice($channel->getCode(), $currency->getCode(), $price->getPrice()); - if ($originalPrice = $price->getOriginalPrice()) { - $document->addOriginalPrice($channel->getCode(), $currency->getCode(), $originalPrice); - } - } - } - } - - return $document; - } - - /** - * @param ResultInterface $document - * @param string $locale - * - * @return ResultInterface - */ - protected function addTaxonsInDocument(ResultInterface $document, string $locale): ResultInterface - { - /** @var TaxonInterface $mainTaxon */ - if ($mainTaxon = $this->getMainTaxon()) { - $taxon = new DocumentTaxon(); - $taxon - ->setName($mainTaxon->getTranslation($locale)->getName()) - ->setCode($mainTaxon->getCode()) - ->setPosition($mainTaxon->getPosition()) - ->setLevel($mainTaxon->getLevel()) - ; - $document->setMainTaxon($taxon); - } - - /** @var ProductTaxonInterface $productTaxon */ - foreach ($this->getProductTaxons() as $productTaxon) { - $document->addTaxon( - $productTaxon->getTaxon()->getCode(), - $productTaxon->getTaxon()->getTranslation($locale)->getName(), - $productTaxon->getTaxon()->getPosition(), - $productTaxon->getTaxon()->getLevel(), - $productTaxon->getPosition() - ); - } - - return $document; - } - - /** - * @param ResultInterface $document - * @param string $locale - * - * @return ResultInterface - */ - protected function addAttributesInDocument(ResultInterface $document, string $locale): ResultInterface - { - /** @var AttributeValueInterface $attribute */ - foreach ($this->getAttributesByLocale($locale, $locale) as $attribute) { - $attributeValues = []; - if (isset($attribute->getAttribute()->getConfiguration()['choices'])) { - foreach ($attribute->getValue() as $value) { - $attributeValues[] = $attribute->getAttribute()->getConfiguration()['choices'][$value][$locale]; - } - } else { - $attributeValues[] = $attribute->getValue(); - } - $document->addAttribute($attribute->getCode(), $attribute->getName(), $attributeValues, $attribute->getLocaleCode() ?? $locale, 1); - } - - return $document; - } - - /** - * @param Result $document - * @param string $locale - * - * @return Result - */ - protected function addOptionsInDocument(ResultInterface $document, string $locale): ResultInterface - { - $options = []; - foreach ($this->getVariants() as $variant) { - /** @var \App\Entity\Product\ProductVariant $variant */ - foreach ($variant->getOptionValues() as $val) { - if (!isset($options[$val->getOption()->getCode()])) { - $options[$val->getOption()->getCode()] = [ - 'name' => $val->getOption()->getTranslation($locale)->getName(), - 'values' => [], - ]; - } - $options[$val->getOption()->getCode()]['values'][$val->getCode()] = $val->getTranslation($locale)->getValue(); - } - } - - foreach ($options as $optionCode => $option) { - $document->addAttribute($optionCode, $option['name'], array_values($option['values']), $locale, 1); - } - - return $document; - } - - /** - * @param $channel - * - * @return null - */ - private function getCheapestVariantForChannel($channel) - { - $cheapestVariant = null; - $cheapestPrice = null; - $variants = $this->getVariants(); - foreach ($variants as $variant) { - $channelPrice = $variant->getChannelPricingForChannel($channel); - if (null === $cheapestPrice || $channelPrice->getPrice() < $cheapestPrice) { - $cheapestPrice = $channelPrice->getPrice(); - $cheapestVariant = $variant; - } - } - - return $cheapestVariant; - } -} diff --git a/src/Model/Product/FilterableTrait.php b/src/Model/Product/FilterableTrait.php deleted file mode 100644 index 2f1d51c1..00000000 --- a/src/Model/Product/FilterableTrait.php +++ /dev/null @@ -1,41 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Model\Product; - -use Doctrine\ORM\Mapping as ORM; - -trait FilterableTrait -{ - /** - * @var bool - * @ORM\Column(name="filterable", type="boolean", nullable=false, options={"default"=true}) - */ - protected $filterable = true; - - /** - * @return bool - */ - public function isFilterable(): bool - { - return $this->filterable; - } - - /** - * @param bool $filterable - */ - public function setFilterable(bool $filterable): void - { - $this->filterable = $filterable; - } -} diff --git a/src/Provider/DocumentRepositoryProvider.php b/src/Provider/DocumentRepositoryProvider.php deleted file mode 100644 index 4e5f5b60..00000000 --- a/src/Provider/DocumentRepositoryProvider.php +++ /dev/null @@ -1,47 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Provider; - -use Doctrine\ORM\EntityManagerInterface; - -class DocumentRepositoryProvider -{ - /** @var EntityManagerInterface */ - private $entityManager; - - /** @var string */ - private $documentableClasses; - - /** - * SearchQueryProvider constructor. - * - * @param EntityManagerInterface $entityManager - * @param array $documentableClasses - */ - public function __construct(EntityManagerInterface $entityManager, array $documentableClasses) - { - $this->entityManager = $entityManager; - $this->documentableClasses = $documentableClasses; - } - - public function getRepositories() - { - $repositories = []; - foreach ($this->documentableClasses as $class) { - $repositories[] = $this->entityManager->getRepository($class); - } - - return $repositories; - } -} diff --git a/src/Provider/SearchQueryProvider.php b/src/Provider/SearchQueryProvider.php deleted file mode 100644 index 71094673..00000000 --- a/src/Provider/SearchQueryProvider.php +++ /dev/null @@ -1,91 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Provider; - -use MonsieurBiz\SyliusSearchPlugin\Exception\MissingConfigFileException; -use MonsieurBiz\SyliusSearchPlugin\Exception\ReadFileException; -use MonsieurBiz\SyliusSearchPlugin\Model\Config\FilesConfig; - -class SearchQueryProvider -{ - /** @var FilesConfig */ - private $filesConfig; - - /** - * SearchQueryProvider constructor. - * - * @param array $files - * - * @throws MissingConfigFileException - */ - public function __construct(array $files) - { - $this->filesConfig = new FilesConfig($files); - } - - /** - * Get search query. - * - * @throws ReadFileException - * - * @return string - */ - public function getSearchQuery() - { - return $this->getQuery($this->filesConfig->getSearchPath()); - } - - /** - * Get instant query. - * - * @throws ReadFileException - * - * @return false|string - */ - public function getInstantQuery() - { - return $this->getQuery($this->filesConfig->getInstantPath()); - } - - /** - * Get taxon query. - * - * @throws ReadFileException - * - * @return false|string - */ - public function getTaxonQuery() - { - return $this->getQuery($this->filesConfig->getTaxonPath()); - } - - /** - * Get content from file. - * - * @param $path - * - * @throws ReadFileException - * - * @return false|string - */ - private function getQuery($path) - { - $query = @file_get_contents($path); - if (false === $query) { - throw new ReadFileException(sprintf('Error while opening file "%s".', $path)); - } - - return $query; - } -} diff --git a/src/Provider/UrlParamsProvider.php b/src/Provider/UrlParamsProvider.php deleted file mode 100644 index 1df1d80c..00000000 --- a/src/Provider/UrlParamsProvider.php +++ /dev/null @@ -1,67 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Provider; - -class UrlParamsProvider -{ - /** @var string */ - private $path; - - /** @var array */ - private $params; - - /** - * UrlParamsProvider constructor. - * - * @param string $path - * @param array $params - */ - public function __construct(string $path, array $params) - { - $this->path = $path; - $this->params = $params; - } - - /** - * @return string - */ - public function getPath(): string - { - return $this->path; - } - - /** - * @param string $path - */ - public function setPath(string $path): void - { - $this->path = $path; - } - - /** - * @return array - */ - public function getParams(): array - { - return $this->params; - } - - /** - * @param array $params - */ - public function setParams(array $params): void - { - $this->params = $params; - } -} diff --git a/src/Resources/SyliusAdminBundle/views/ProductAttribute/_form.html.twig b/src/Resources/SyliusAdminBundle/views/ProductAttribute/_form.html.twig deleted file mode 100644 index 6d75a0d8..00000000 --- a/src/Resources/SyliusAdminBundle/views/ProductAttribute/_form.html.twig +++ /dev/null @@ -1,26 +0,0 @@ -{% from '@SyliusAdmin/Macro/translationForm.html.twig' import translationForm %} - -{{ form_errors(form) }} - -

-
-

{{ 'monsieurbiz_searchplugin.admin.product_attribute.form.title'|trans }}

-
- {{ form_row(form.filterable) }} -
-
-{% if form.configuration is defined %} -
-

{{ 'sylius.ui.configuration'|trans }}

- {% for field in form.configuration %} - {{ form_row(field) }} - {% endfor %} -
-{% endif %} -{{ translationForm(form.translations) }} diff --git a/src/Resources/SyliusAdminBundle/views/ProductOption/_form.html.twig b/src/Resources/SyliusAdminBundle/views/ProductOption/_form.html.twig deleted file mode 100644 index bcc8013f..00000000 --- a/src/Resources/SyliusAdminBundle/views/ProductOption/_form.html.twig +++ /dev/null @@ -1,18 +0,0 @@ -{% from '@SyliusAdmin/Macro/translationForm.html.twig' import translationForm %} - -
- {{ form_errors(form) }} -
- {{ form_row(form.code) }} - {{ form_row(form.position) }} -
- {{ translationForm(form.translations) }} -
-
-

{{ 'monsieurbiz_searchplugin.admin.product_option.form.title'|trans }}

-
- {{ form_row(form.filterable) }} -
-
-

{{ 'sylius.ui.values'|trans }}

-{{ form_row(form.values) }} diff --git a/src/Resources/config/config.yaml b/src/Resources/config/config.yaml index ca0f321e..acdf743b 100644 --- a/src/Resources/config/config.yaml +++ b/src/Resources/config/config.yaml @@ -1,4 +1,2 @@ imports: - - { resource: "twig.yaml" } - - { resource: "sylius.yaml" } - { resource: "services.yaml" } diff --git a/src/Resources/config/elasticsearch/mappings/analyzers.yaml b/src/Resources/config/elasticsearch/mappings/analyzers.yaml deleted file mode 100644 index e9bdbc5e..00000000 --- a/src/Resources/config/elasticsearch/mappings/analyzers.yaml +++ /dev/null @@ -1,14 +0,0 @@ -filter: - search_autocomplete_filter: - type: 'edge_ngram' - min_gram: 1 - max_gram: 20 -analyzer: - search_autocomplete: - type: 'custom' - tokenizer: 'icu_tokenizer' - filter: [ 'lowercase', 'icu_folding', 'elision', 'search_autocomplete_filter' ] - search_standard: - type: 'custom' - tokenizer: 'icu_tokenizer' - filter: [ 'lowercase', 'icu_folding', 'elision' ] diff --git a/src/Resources/config/elasticsearch/mappings/documents-de_de_mapping.yaml b/src/Resources/config/elasticsearch/mappings/documents-de_de_mapping.yaml deleted file mode 100644 index 8676e36f..00000000 --- a/src/Resources/config/elasticsearch/mappings/documents-de_de_mapping.yaml +++ /dev/null @@ -1,72 +0,0 @@ -mappings: - dynamic: false - properties: - type: - type: keyword - code: - type: keyword - enabled: - type: boolean - channel: - type: keyword - mainTaxon: - type: nested - properties: - code: - type: keyword - name: - type: keyword - position: - type: integer - level: - type: integer - productPosition: - type: integer - taxon: - type: nested - properties: - code: - type: keyword - name: - type: keyword - position: - type: integer - level: - type: integer - productPosition: - type: integer - attributes: - type: nested - properties: - name: - type: text - analyzer: search_standard - fields: - keyword: - type: keyword - value: - type: text - analyzer: search_standard - fields: - keyword: - type: keyword - code: - type: keyword - score: - type: rank_feature - price: - type: nested - properties: - currency: - type: keyword - channel: - type: keyword - value: - type: integer - originalPrice: - type: nested - properties: - currency: - type: keyword - value: - type: integer diff --git a/src/Resources/config/elasticsearch/mappings/documents-en_mapping.yaml b/src/Resources/config/elasticsearch/mappings/documents-en_mapping.yaml deleted file mode 100644 index 8676e36f..00000000 --- a/src/Resources/config/elasticsearch/mappings/documents-en_mapping.yaml +++ /dev/null @@ -1,72 +0,0 @@ -mappings: - dynamic: false - properties: - type: - type: keyword - code: - type: keyword - enabled: - type: boolean - channel: - type: keyword - mainTaxon: - type: nested - properties: - code: - type: keyword - name: - type: keyword - position: - type: integer - level: - type: integer - productPosition: - type: integer - taxon: - type: nested - properties: - code: - type: keyword - name: - type: keyword - position: - type: integer - level: - type: integer - productPosition: - type: integer - attributes: - type: nested - properties: - name: - type: text - analyzer: search_standard - fields: - keyword: - type: keyword - value: - type: text - analyzer: search_standard - fields: - keyword: - type: keyword - code: - type: keyword - score: - type: rank_feature - price: - type: nested - properties: - currency: - type: keyword - channel: - type: keyword - value: - type: integer - originalPrice: - type: nested - properties: - currency: - type: keyword - value: - type: integer diff --git a/src/Resources/config/elasticsearch/mappings/documents-en_us_mapping.yaml b/src/Resources/config/elasticsearch/mappings/documents-en_us_mapping.yaml deleted file mode 100644 index 8676e36f..00000000 --- a/src/Resources/config/elasticsearch/mappings/documents-en_us_mapping.yaml +++ /dev/null @@ -1,72 +0,0 @@ -mappings: - dynamic: false - properties: - type: - type: keyword - code: - type: keyword - enabled: - type: boolean - channel: - type: keyword - mainTaxon: - type: nested - properties: - code: - type: keyword - name: - type: keyword - position: - type: integer - level: - type: integer - productPosition: - type: integer - taxon: - type: nested - properties: - code: - type: keyword - name: - type: keyword - position: - type: integer - level: - type: integer - productPosition: - type: integer - attributes: - type: nested - properties: - name: - type: text - analyzer: search_standard - fields: - keyword: - type: keyword - value: - type: text - analyzer: search_standard - fields: - keyword: - type: keyword - code: - type: keyword - score: - type: rank_feature - price: - type: nested - properties: - currency: - type: keyword - channel: - type: keyword - value: - type: integer - originalPrice: - type: nested - properties: - currency: - type: keyword - value: - type: integer diff --git a/src/Resources/config/elasticsearch/mappings/documents-es_es_mapping.yaml b/src/Resources/config/elasticsearch/mappings/documents-es_es_mapping.yaml deleted file mode 100644 index 8676e36f..00000000 --- a/src/Resources/config/elasticsearch/mappings/documents-es_es_mapping.yaml +++ /dev/null @@ -1,72 +0,0 @@ -mappings: - dynamic: false - properties: - type: - type: keyword - code: - type: keyword - enabled: - type: boolean - channel: - type: keyword - mainTaxon: - type: nested - properties: - code: - type: keyword - name: - type: keyword - position: - type: integer - level: - type: integer - productPosition: - type: integer - taxon: - type: nested - properties: - code: - type: keyword - name: - type: keyword - position: - type: integer - level: - type: integer - productPosition: - type: integer - attributes: - type: nested - properties: - name: - type: text - analyzer: search_standard - fields: - keyword: - type: keyword - value: - type: text - analyzer: search_standard - fields: - keyword: - type: keyword - code: - type: keyword - score: - type: rank_feature - price: - type: nested - properties: - currency: - type: keyword - channel: - type: keyword - value: - type: integer - originalPrice: - type: nested - properties: - currency: - type: keyword - value: - type: integer diff --git a/src/Resources/config/elasticsearch/mappings/documents-es_mx_mapping.yaml b/src/Resources/config/elasticsearch/mappings/documents-es_mx_mapping.yaml deleted file mode 100644 index 8676e36f..00000000 --- a/src/Resources/config/elasticsearch/mappings/documents-es_mx_mapping.yaml +++ /dev/null @@ -1,72 +0,0 @@ -mappings: - dynamic: false - properties: - type: - type: keyword - code: - type: keyword - enabled: - type: boolean - channel: - type: keyword - mainTaxon: - type: nested - properties: - code: - type: keyword - name: - type: keyword - position: - type: integer - level: - type: integer - productPosition: - type: integer - taxon: - type: nested - properties: - code: - type: keyword - name: - type: keyword - position: - type: integer - level: - type: integer - productPosition: - type: integer - attributes: - type: nested - properties: - name: - type: text - analyzer: search_standard - fields: - keyword: - type: keyword - value: - type: text - analyzer: search_standard - fields: - keyword: - type: keyword - code: - type: keyword - score: - type: rank_feature - price: - type: nested - properties: - currency: - type: keyword - channel: - type: keyword - value: - type: integer - originalPrice: - type: nested - properties: - currency: - type: keyword - value: - type: integer diff --git a/src/Resources/config/elasticsearch/mappings/documents-fr_fr_mapping.yaml b/src/Resources/config/elasticsearch/mappings/documents-fr_fr_mapping.yaml deleted file mode 100644 index 8676e36f..00000000 --- a/src/Resources/config/elasticsearch/mappings/documents-fr_fr_mapping.yaml +++ /dev/null @@ -1,72 +0,0 @@ -mappings: - dynamic: false - properties: - type: - type: keyword - code: - type: keyword - enabled: - type: boolean - channel: - type: keyword - mainTaxon: - type: nested - properties: - code: - type: keyword - name: - type: keyword - position: - type: integer - level: - type: integer - productPosition: - type: integer - taxon: - type: nested - properties: - code: - type: keyword - name: - type: keyword - position: - type: integer - level: - type: integer - productPosition: - type: integer - attributes: - type: nested - properties: - name: - type: text - analyzer: search_standard - fields: - keyword: - type: keyword - value: - type: text - analyzer: search_standard - fields: - keyword: - type: keyword - code: - type: keyword - score: - type: rank_feature - price: - type: nested - properties: - currency: - type: keyword - channel: - type: keyword - value: - type: integer - originalPrice: - type: nested - properties: - currency: - type: keyword - value: - type: integer diff --git a/src/Resources/config/elasticsearch/mappings/documents-fr_mapping.yaml b/src/Resources/config/elasticsearch/mappings/documents-fr_mapping.yaml deleted file mode 100644 index 0b9c8c1c..00000000 --- a/src/Resources/config/elasticsearch/mappings/documents-fr_mapping.yaml +++ /dev/null @@ -1,72 +0,0 @@ -mappings: - dynamic: false - properties: - type: - type: keyword - code: - type: keyword - enabled: - type: boolean - channel: - type: keyword - mainTaxon: - type: nested - properties: - code: - type: keyword - name: - type: keyword - position: - type: integer - level: - type: integer - productPosition: - type: integer - taxon: - type: nested - properties: - code: - type: keyword - name: - type: keyword - position: - type: integer - level: - type: integer - productPosition: - type: integer - attributes: - type: nested - properties: - name: - type: text - analyzer: search_standard - fields: - keyword: - type: keyword - value: - type: text - analyzer: search_standard - fields: - keyword: - type: keyword - code: - type: keyword - score: - type: rank_feature - price: - type: nested - properties: - currency: - type: keyword - channel: - type: keyword - value: - type: integer - originalPrice: - type: nested - properties: - currency: - type: keyword - value: - type: integer diff --git a/src/Resources/config/elasticsearch/mappings/documents-it_it_mapping.yaml b/src/Resources/config/elasticsearch/mappings/documents-it_it_mapping.yaml deleted file mode 100644 index 8676e36f..00000000 --- a/src/Resources/config/elasticsearch/mappings/documents-it_it_mapping.yaml +++ /dev/null @@ -1,72 +0,0 @@ -mappings: - dynamic: false - properties: - type: - type: keyword - code: - type: keyword - enabled: - type: boolean - channel: - type: keyword - mainTaxon: - type: nested - properties: - code: - type: keyword - name: - type: keyword - position: - type: integer - level: - type: integer - productPosition: - type: integer - taxon: - type: nested - properties: - code: - type: keyword - name: - type: keyword - position: - type: integer - level: - type: integer - productPosition: - type: integer - attributes: - type: nested - properties: - name: - type: text - analyzer: search_standard - fields: - keyword: - type: keyword - value: - type: text - analyzer: search_standard - fields: - keyword: - type: keyword - code: - type: keyword - score: - type: rank_feature - price: - type: nested - properties: - currency: - type: keyword - channel: - type: keyword - value: - type: integer - originalPrice: - type: nested - properties: - currency: - type: keyword - value: - type: integer diff --git a/src/Resources/config/elasticsearch/mappings/documents-pl_pl_mapping.yaml b/src/Resources/config/elasticsearch/mappings/documents-pl_pl_mapping.yaml deleted file mode 100644 index 8676e36f..00000000 --- a/src/Resources/config/elasticsearch/mappings/documents-pl_pl_mapping.yaml +++ /dev/null @@ -1,72 +0,0 @@ -mappings: - dynamic: false - properties: - type: - type: keyword - code: - type: keyword - enabled: - type: boolean - channel: - type: keyword - mainTaxon: - type: nested - properties: - code: - type: keyword - name: - type: keyword - position: - type: integer - level: - type: integer - productPosition: - type: integer - taxon: - type: nested - properties: - code: - type: keyword - name: - type: keyword - position: - type: integer - level: - type: integer - productPosition: - type: integer - attributes: - type: nested - properties: - name: - type: text - analyzer: search_standard - fields: - keyword: - type: keyword - value: - type: text - analyzer: search_standard - fields: - keyword: - type: keyword - code: - type: keyword - score: - type: rank_feature - price: - type: nested - properties: - currency: - type: keyword - channel: - type: keyword - value: - type: integer - originalPrice: - type: nested - properties: - currency: - type: keyword - value: - type: integer diff --git a/src/Resources/config/elasticsearch/mappings/documents-pt_pt_mapping.yaml b/src/Resources/config/elasticsearch/mappings/documents-pt_pt_mapping.yaml deleted file mode 100644 index 8676e36f..00000000 --- a/src/Resources/config/elasticsearch/mappings/documents-pt_pt_mapping.yaml +++ /dev/null @@ -1,72 +0,0 @@ -mappings: - dynamic: false - properties: - type: - type: keyword - code: - type: keyword - enabled: - type: boolean - channel: - type: keyword - mainTaxon: - type: nested - properties: - code: - type: keyword - name: - type: keyword - position: - type: integer - level: - type: integer - productPosition: - type: integer - taxon: - type: nested - properties: - code: - type: keyword - name: - type: keyword - position: - type: integer - level: - type: integer - productPosition: - type: integer - attributes: - type: nested - properties: - name: - type: text - analyzer: search_standard - fields: - keyword: - type: keyword - value: - type: text - analyzer: search_standard - fields: - keyword: - type: keyword - code: - type: keyword - score: - type: rank_feature - price: - type: nested - properties: - currency: - type: keyword - channel: - type: keyword - value: - type: integer - originalPrice: - type: nested - properties: - currency: - type: keyword - value: - type: integer diff --git a/src/Resources/config/elasticsearch/mappings/documents-zh_cn_mapping.yaml b/src/Resources/config/elasticsearch/mappings/documents-zh_cn_mapping.yaml deleted file mode 100644 index 8676e36f..00000000 --- a/src/Resources/config/elasticsearch/mappings/documents-zh_cn_mapping.yaml +++ /dev/null @@ -1,72 +0,0 @@ -mappings: - dynamic: false - properties: - type: - type: keyword - code: - type: keyword - enabled: - type: boolean - channel: - type: keyword - mainTaxon: - type: nested - properties: - code: - type: keyword - name: - type: keyword - position: - type: integer - level: - type: integer - productPosition: - type: integer - taxon: - type: nested - properties: - code: - type: keyword - name: - type: keyword - position: - type: integer - level: - type: integer - productPosition: - type: integer - attributes: - type: nested - properties: - name: - type: text - analyzer: search_standard - fields: - keyword: - type: keyword - value: - type: text - analyzer: search_standard - fields: - keyword: - type: keyword - code: - type: keyword - score: - type: rank_feature - price: - type: nested - properties: - currency: - type: keyword - channel: - type: keyword - value: - type: integer - originalPrice: - type: nested - properties: - currency: - type: keyword - value: - type: integer diff --git a/src/Resources/config/elasticsearch/queries/instant.yaml b/src/Resources/config/elasticsearch/queries/instant.yaml deleted file mode 100644 index 7c9cea61..00000000 --- a/src/Resources/config/elasticsearch/queries/instant.yaml +++ /dev/null @@ -1,56 +0,0 @@ ---- -# Instant search query, sorts and filters are managed dynamically -query: - bool: - filter: - - term: - enabled: true - - term: - channel: "{{CHANNEL}}" - must: - - bool: - should: - - term: - code: "{{QUERY}}" - - nested: - path: attributes - query: - - bool: - filter: - - multi_match: - query: "{{QUERY}}" - fields: - - attributes.value - fuzziness: 1 - should: - - rank_feature: - field: attributes.score - - multi_match: - query: "{{QUERY}}" - fields: - - attributes.value^2 - should: - - nested: - path: attributes - query: - - bool: - must: - - multi_match: - query: "{{QUERY}}" - fields: - - attributes.value^6 - - multi_match: - query: "name" - fields: - - attributes.code - - bool: - must: - - multi_match: - query: "{{QUERY}}" - fields: - - attributes.value^4 - fuzziness: 1 - - multi_match: - query: "name" - fields: - - attributes.code diff --git a/src/Resources/config/elasticsearch/queries/search.yaml b/src/Resources/config/elasticsearch/queries/search.yaml deleted file mode 100644 index f352cc29..00000000 --- a/src/Resources/config/elasticsearch/queries/search.yaml +++ /dev/null @@ -1,56 +0,0 @@ ---- -# Search query, sorts and filters are managed dynamically -query: - bool: - filter: - - term: - enabled: true - - term: - channel: "{{CHANNEL}}" - must: - - bool: - should: - - term: - code: "{{QUERY}}" - - nested: - path: attributes - query: - - bool: - filter: - - multi_match: - query: "{{QUERY}}" - fields: - - attributes.value - fuzziness: 1 - should: - - rank_feature: - field: attributes.score - - multi_match: - query: "{{QUERY}}" - fields: - - attributes.value^2 - should: - - nested: - path: attributes - query: - - bool: - must: - - multi_match: - query: "{{QUERY}}" - fields: - - attributes.value^6 - - multi_match: - query: "name" - fields: - - attributes.code - - bool: - must: - - multi_match: - query: "{{QUERY}}" - fields: - - attributes.value^4 - fuzziness: 1 - - multi_match: - query: "name" - fields: - - attributes.code diff --git a/src/Resources/config/elasticsearch/queries/taxon.yaml b/src/Resources/config/elasticsearch/queries/taxon.yaml deleted file mode 100644 index ed642dc2..00000000 --- a/src/Resources/config/elasticsearch/queries/taxon.yaml +++ /dev/null @@ -1,18 +0,0 @@ ---- -# Taxon query, sorts and filters are managed dynamically -query: - bool: - filter: - - term: - enabled: true - - term: - channel: "{{CHANNEL}}" - must: - nested: - path: taxon - query: - bool: - must: - term: - taxon.code: - value: "{{TAXON}}" diff --git a/src/Resources/config/jane/document.json b/src/Resources/config/jane/document.json deleted file mode 100644 index 823b2b2b..00000000 --- a/src/Resources/config/jane/document.json +++ /dev/null @@ -1,118 +0,0 @@ -{ - "$schema": "http://json-schema.org/2019-09/schema#", - "definitions": { - "Document": { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "code": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "enabled": { - "type": "boolean" - }, - "slug": { - "type": "string" - }, - "image": { - "type": "string" - }, - "channel": { - "type": "array", - "items": { - "type": "string" - } - }, - "main_taxon": { - "$ref": "#/definitions/Taxon" - }, - "taxon": { - "type": "array", - "items": { - "$ref": "#/definitions/Taxon" - } - }, - "attributes": { - "type": "array", - "items": { - "$ref": "#/definitions/Attributes" - } - }, - "price": { - "type": "array", - "items": { - "$ref": "#/definitions/Price" - } - }, - "original_price": { - "type": "array", - "items": { - "$ref": "#/definitions/Price" - } - } - } - }, - "Price": { - "type": "object", - "properties": { - "channel": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "value": { - "type": "integer" - } - } - }, - "Attributes": { - "type": "object", - "properties": { - "code": { - "type": "string" - }, - "name": { - "type": "string" - }, - "value": { - "type": "array", - "items": { - "type": "string" - } - }, - "locale": { - "type": "string" - }, - "score": { - "type": "integer" - } - } - }, - "Taxon": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "code": { - "type": "string" - }, - "position": { - "type": "integer" - }, - "level": { - "type": "integer" - }, - "product_position": { - "type": "integer" - } - } - } - } -} diff --git a/src/Resources/config/jane/dto-config.php b/src/Resources/config/jane/dto-config.php deleted file mode 100644 index ecd1112c..00000000 --- a/src/Resources/config/jane/dto-config.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -return [ - 'json-schema-file' => 'src/Resources/config/jane/document.json', - 'root-class' => 'Model', - 'namespace' => 'MonsieurBiz\SyliusSearchPlugin\generated', - 'directory' => 'src/generated', - 'strict' => false, -]; diff --git a/src/Resources/config/routing.yaml b/src/Resources/config/routing.yaml deleted file mode 100644 index f2a3d58b..00000000 --- a/src/Resources/config/routing.yaml +++ /dev/null @@ -1,5 +0,0 @@ -monsieurbiz_sylius_search_shop: - resource: "routing/shop.yaml" - prefix: /{_locale} - requirements: - _locale: ^[a-z]{2}(?:_[A-Z]{2})?$ diff --git a/src/Resources/config/routing/shop.yaml b/src/Resources/config/routing/shop.yaml deleted file mode 100644 index 7d645600..00000000 --- a/src/Resources/config/routing/shop.yaml +++ /dev/null @@ -1,27 +0,0 @@ -monsieurbiz_sylius_search_search: - path: /search/{query} - methods: [GET] - defaults: - _controller: MonsieurBiz\SyliusSearchPlugin\Controller\SearchController:searchAction - requirements: - query: .+ - -monsieurbiz_sylius_search_post: - path: /search - methods: [POST] - defaults: - _controller: MonsieurBiz\SyliusSearchPlugin\Controller\SearchController:postAction - -monsieurbiz_sylius_search_instant: - path: /instant - methods: [POST] - defaults: - _controller: MonsieurBiz\SyliusSearchPlugin\Controller\SearchController:instantAction - -monsieurbiz_sylius_search_taxon: - path: /taxons/{slug} - methods: [GET] - defaults: - _controller: MonsieurBiz\SyliusSearchPlugin\Controller\SearchController:taxonAction - requirements: - slug: .+ diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index 81895441..0466d0a9 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -5,10 +5,6 @@ services: autoconfigure: true public: false - _instanceof: - MonsieurBiz\SyliusSearchPlugin\Fixture\FilterableFixtureInterface: - tags: ['sylius_fixtures.fixture'] - MonsieurBiz\SyliusSearchPlugin\: resource: '../../*' exclude: '../../{Entity,Migrations,Tests,Kernel.php}' @@ -21,68 +17,3 @@ services: resource: '../../Form/Extension' tags: - { name: form.type_extension } - - # Client configuration. - JoliCode\Elastically\Client: - arguments: - $config: - host: '%env(MONSIEURBIZ_SEARCHPLUGIN_ES_HOST)%' - port: '%env(MONSIEURBIZ_SEARCHPLUGIN_ES_PORT)%' - elastically_mappings_directory: '%kernel.project_dir%/vendor/monsieurbiz/sylius-search-plugin/src/Resources/config/elasticsearch/mappings' - elastically_index_class_mapping: - # @TODO Add it in config - documents-it_it: \MonsieurBiz\SyliusSearchPlugin\Model\Document\Result - documents-fr_fr: \MonsieurBiz\SyliusSearchPlugin\Model\Document\Result - documents-fr: \MonsieurBiz\SyliusSearchPlugin\Model\Document\Result - documents-en: \MonsieurBiz\SyliusSearchPlugin\Model\Document\Result - documents-en_us: \MonsieurBiz\SyliusSearchPlugin\Model\Document\Result - elastically_bulk_size: 100 - - # Add JS for plugin - monsieurbiz_sylius_search.block_event_listener.layout.javascripts: - class: Sylius\Bundle\UiBundle\Block\BlockEventListener - arguments: - - '@@MonsieurBizSyliusSearchPlugin/js.html.twig' - tags: - - { name: kernel.event_listener, event: sonata.block.event.sylius.shop.layout.javascripts, method: onBlockEvent } - - # Add form search in header - monsieurbiz_sylius_search.block_event_listener.layout.header: - class: Sylius\Bundle\UiBundle\Block\BlockEventListener - arguments: - - '@@MonsieurBizSyliusSearchPlugin/Header/form.html.twig' - tags: - - { name: kernel.event_listener, event: sonata.block.event.sylius.shop.layout.header, method: onBlockEvent } - - # Event when a product is added / modified / deleted - monsieurbiz_sylius_search.event_listener.document_listener: - class: MonsieurBiz\SyliusSearchPlugin\EventListener\DocumentListener - arguments: - - '@MonsieurBiz\SyliusSearchPlugin\Model\Document\Index\Indexer' - tags: - - { name: kernel.event_listener, event: sylius.product.post_create, method: saveDocument } - - { name: kernel.event_listener, event: sylius.product.post_update, method: saveDocument } - - { name: kernel.event_listener, event: sylius.product.pre_delete, method: deleteDocument } - - MonsieurBiz\SyliusSearchPlugin\Provider\SearchQueryProvider: - arguments: - $files: '%monsieurbiz_sylius_search.files%' - - # Provider to retrieve repositories to index - MonsieurBiz\SyliusSearchPlugin\Provider\DocumentRepositoryProvider: - arguments: - $documentableClasses: '%monsieurbiz_sylius_search.documentable_classes%' - - MonsieurBiz\SyliusSearchPlugin\Model\Config\GridConfig: - arguments: - $config: '%monsieurbiz_sylius_search.grid%' - - # Helpers - MonsieurBiz\SyliusSearchPlugin\Helper\RenderDocumentUrlHelper: ~ - - # Twig extensions - MonsieurBiz\SyliusSearchPlugin\Twig\Extension\RenderDocumentUrl: - arguments: - $helper: '@MonsieurBiz\SyliusSearchPlugin\Helper\RenderDocumentUrlHelper' - tags: - - { name: twig.extension } diff --git a/src/Resources/config/sylius.yaml b/src/Resources/config/sylius.yaml deleted file mode 100644 index b98da91d..00000000 --- a/src/Resources/config/sylius.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# Add JS for plugin for Sylius 2.0 -#sylius_ui: -# events: -# sylius.shop.layout.javascripts: -# blocks: -# monsieur_biz_sylius_search_js: -# template: "@MonsieurBizSyliusSearchPlugin/js.html.twig" -# priority: 0 diff --git a/src/Resources/config/twig.yaml b/src/Resources/config/twig.yaml deleted file mode 100644 index fc4605fc..00000000 --- a/src/Resources/config/twig.yaml +++ /dev/null @@ -1,3 +0,0 @@ -twig: - globals: - monsieur_biz_sylius_search_grid_config: '@MonsieurBiz\SyliusSearchPlugin\Model\Config\GridConfig' diff --git a/src/Resources/public/js/app.js b/src/Resources/public/js/app.js deleted file mode 100644 index d03720fc..00000000 --- a/src/Resources/public/js/app.js +++ /dev/null @@ -1,74 +0,0 @@ -/** global: monsieurbizSearchPlugin */ -(function ($) { - 'use strict'; - $.fn.extend({ - instantSearch: function () { - // No instant if disabled - if (!monsieurbizSearchPlugin.instantEnabled) { - return; - } - $(monsieurbizSearchPlugin.searchInputSelector).prop('autocomplete', 'off'); - // Init a timeout variable to be used below - var instantSearchTimeout = null; - $(monsieurbizSearchPlugin.searchInputSelector).keyup(function() { - clearTimeout(instantSearchTimeout); - var query = $(this).val(); - var resultElement = $(this).closest(monsieurbizSearchPlugin.resultClosestSelector).find(monsieurbizSearchPlugin.resultFindSelector); - instantSearchTimeout = setTimeout(function () { - if (query.length >= monsieurbizSearchPlugin.minQueryLength) { - $.post(monsieurbizSearchPlugin.instantUrl, { query: query }) - .done(function( data ) { - resultElement.html(data); - resultElement.show(); - }); - } - }, monsieurbizSearchPlugin.keyUpTimeOut); - }); - - // Hide results when user leave the search field - $(monsieurbizSearchPlugin.searchInputSelector).focusout(function () { - var resultElement = $(this).closest(monsieurbizSearchPlugin.resultClosestSelector).find(monsieurbizSearchPlugin.resultFindSelector); - setTimeout(function () { - resultElement.hide(); - }, 100); // Add timeout to keep the click on the result - }); - }, - filterSearch: function () { - $(monsieurbizSearchPlugin.priceFilterSelector).prop('autocomplete', 'off'); - - // If only a button can submit filters - if (monsieurbizSearchPlugin.refreshWithButton) { - $(monsieurbizSearchPlugin.filterForm).submit(function(event) { - $(monsieurbizSearchPlugin.loaderSelector).dimmer('show'); - }); - return; - } - - // Init a timeout variable when typing a price - var priceFilterTimeout = null; - $(monsieurbizSearchPlugin.priceFilterSelector).keyup(function() { - clearTimeout(priceFilterTimeout); - var input = $(this); - priceFilterTimeout = setTimeout(function () { - $(this).applyFilter(input.attr('name'), input.val()); - }, monsieurbizSearchPlugin.keyUpTimeOut); - }); - - $(monsieurbizSearchPlugin.attributeFilterSelector).change(function() { - $(this).applyFilter($(this).attr('name'), $(this).val()); - }); - }, - applyFilter: function (field, value) { - // Changed field and value are available in case we need it - $(monsieurbizSearchPlugin.loaderSelector).dimmer('show'); - $(monsieurbizSearchPlugin.filterForm).submit(); - } - }); -})(jQuery); - -(function($) { - $(document).ready(function () { - $(this).instantSearch(); - $(this).filterSearch(); - }); -})(jQuery); diff --git a/src/Resources/translations/messages.en.yml b/src/Resources/translations/messages.en.yml deleted file mode 100644 index e772090b..00000000 --- a/src/Resources/translations/messages.en.yml +++ /dev/null @@ -1,30 +0,0 @@ -monsieurbiz_searchplugin: - admin: - product_attribute: - form: - title: Search - filterable: Filterable - product_option: - form: - title: Search - filterable: Filterable - form: - query: 'Search' - submit: 'Submit search' - search: - result: - search_result: 'Search results for "%query%" (%count%)' - no_result: 'No result.' - instant: - result: - search_result: 'Search results for "%query%" (%count%)' - no_result: 'No result.' - filters: - filter_results: 'Filter results' - apply_filters: 'Apply filters' - no_filter: 'No filter available' - taxon_filter: 'Categories' - price_filter: 'Price' - price_min: 'Min price' - price_max: 'Max price' - loading: 'We are looking for the best results according to your criteria' diff --git a/src/Resources/translations/messages.fr.yml b/src/Resources/translations/messages.fr.yml deleted file mode 100644 index 112acfaa..00000000 --- a/src/Resources/translations/messages.fr.yml +++ /dev/null @@ -1,30 +0,0 @@ -monsieurbiz_searchplugin: - admin: - product_attribute: - form: - title: Recherche - filterable: Filtrable - product_option: - form: - title: Recherche - filterable: Filtrable - form: - query: 'Recherche' - submit: 'Lancer la recherche' - search: - result: - search_result: 'Résultats de recherche pour "%query%" (%count%)' - no_result: 'Pas de résultat.' - instant: - result: - search_result: 'Résultats de recherche pour "%query%" (%count%)' - no_result: 'Pas de résultat.' - filters: - filter_results: 'Filtrer les résultats' - apply_filters: 'Appliquer les filtres' - no_filter: 'Aucun filtre disponible' - taxon_filter: 'Catégories' - price_filter: 'Prix' - price_min: 'Prix minimum' - price_max: 'Prix maximum' - loading: 'Nous recherchons les meilleurs résultats selon vos critères' diff --git a/src/Resources/translations/messages.it.yml b/src/Resources/translations/messages.it.yml deleted file mode 100644 index 95e7c38f..00000000 --- a/src/Resources/translations/messages.it.yml +++ /dev/null @@ -1,30 +0,0 @@ -monsieurbiz_searchplugin: - admin: - product_attribute: - form: - title: Ricerca - filterable: Filtrabile - product_option: - form: - title: Ricerca - filterable: Filtrabile - form: - query: 'Cerca' - submit: 'Cerca' - search: - result: - search_result: 'Risultati per la ricerca "%query%" (%count%)' - no_result: 'Nessun risultato.' - instant: - result: - search_result: 'Risultati per la ricerca "%query%" (%count%)' - no_result: 'Nessun risultato.' - filters: - filter_results: 'Filtra i risultati' - apply_filters: 'Applica i filtri' - no_filter: 'Nessun filtro disponibile' - taxon_filter: 'Categorie' - price_filter: 'Prezzo' - price_min: 'Prezzo Minimo' - price_max: 'Prezzo Massimo' - loading: 'Stiamo cercando i risultati migliori in base ai filtri che hai applicato' diff --git a/src/Resources/views/.gitkeep b/src/Resources/views/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/Resources/views/Common/_box.html.twig b/src/Resources/views/Common/_box.html.twig deleted file mode 100644 index 09111e04..00000000 --- a/src/Resources/views/Common/_box.html.twig +++ /dev/null @@ -1,27 +0,0 @@ -{% import "@SyliusShop/Common/Macro/money.html.twig" as money %} - -
diff --git a/src/Resources/views/Common/_filter.html.twig b/src/Resources/views/Common/_filter.html.twig deleted file mode 100644 index edff2c97..00000000 --- a/src/Resources/views/Common/_filter.html.twig +++ /dev/null @@ -1,28 +0,0 @@ -{% set appliedAttributes = app.request.query.get('attribute') %} -{% set currentValues = (appliedAttributes[filter.code] is defined) ? appliedAttributes[filter.code] : [] %} -{% if filter.values|length > 1 or currentValues is not empty %} -
-
-
{{ filter.label | trans }}
-
- {% for value in filter.values %} - {% set valueIsApplied = (value.slug in currentValues) %} -
-
- -
-
- {% endfor %} -
-
-
-{% endif %} diff --git a/src/Resources/views/Common/_filters.html.twig b/src/Resources/views/Common/_filters.html.twig deleted file mode 100644 index 53b78708..00000000 --- a/src/Resources/views/Common/_filters.html.twig +++ /dev/null @@ -1,50 +0,0 @@ -{% set taxonFilter = gridConfig.useMainTaxonForFilter() ? resultSet.mainTaxonFilter : resultSet.taxonFilter %} - - diff --git a/src/Resources/views/Common/_pagination.html.twig b/src/Resources/views/Common/_pagination.html.twig deleted file mode 100644 index c2a1f838..00000000 --- a/src/Resources/views/Common/_pagination.html.twig +++ /dev/null @@ -1,16 +0,0 @@ -{% set paginationLimits = (limits) %} - -
-
- -
-
diff --git a/src/Resources/views/Common/_rangeFilter.html.twig b/src/Resources/views/Common/_rangeFilter.html.twig deleted file mode 100644 index c6f312bf..00000000 --- a/src/Resources/views/Common/_rangeFilter.html.twig +++ /dev/null @@ -1,25 +0,0 @@ -
- {% set currentValue = app.request.query.get( filter.code ) %} - {% set minValue = (currentValue['min'] is defined) ? currentValue['min'] : filter.min %} - {% set maxValue = (currentValue['max'] is defined) ? currentValue['max'] : filter.max %} -
-
{{ filter.label | trans }}
-
- -
- -
-
{{ moneySymbol }}
-
-
- -
- -
-
{{ moneySymbol }}
-
-
- -
-
-
diff --git a/src/Resources/views/Common/_sorting.html.twig b/src/Resources/views/Common/_sorting.html.twig deleted file mode 100644 index 2d2da374..00000000 --- a/src/Resources/views/Common/_sorting.html.twig +++ /dev/null @@ -1,52 +0,0 @@ -{% if resultSet.totalHits > 0 %} - - {% set route = app.request.attributes.get('_route') %} - {% set route_parameters = app.request.attributes.get('_route_params')|merge(app.request.query.all) %} - - {% set criteria = app.request.query.get('criteria', {}) %} - - {% set default_path = path(route, route_parameters|merge({'sorting': null, 'criteria': criteria})) %} - {% set from_a_to_z_path = path(route, route_parameters|merge({'sorting': {'name': 'asc'}, 'criteria': criteria})) %} - {% set from_z_to_a_path = path(route, route_parameters|merge({'sorting': {'name': 'desc'}, 'criteria': criteria})) %} - {% set oldest_first_path = path(route, route_parameters|merge({'sorting': {'created_at': 'asc'}, 'criteria': criteria})) %} - {% set newest_first_path = path(route, route_parameters|merge({'sorting': {'created_at': 'desc'}, 'criteria': criteria})) %} - {% set cheapest_first_path = path(route, route_parameters|merge({'sorting': {'price': 'asc'}, 'criteria': criteria})) %} - {% set most_expensive_first_path = path(route, route_parameters|merge({'sorting': {'price': 'desc'}, 'criteria': criteria})) %} - - {% if app.request.query.get('sorting') is empty %} - {% set current_sorting_label = 'sylius.ui.by_position'|trans|lower %} - {% elseif app.request.query.get('sorting').name is defined and app.request.query.get('sorting').name == 'asc'%} - {% set current_sorting_label = 'sylius.ui.from_a_to_z'|trans|lower %} - {% elseif app.request.query.get('sorting').name is defined and app.request.query.get('sorting').name == 'desc'%} - {% set current_sorting_label = 'sylius.ui.from_z_to_a'|trans|lower %} - {% elseif app.request.query.get('sorting').created_at is defined and app.request.query.get('sorting').created_at == 'desc'%} - {% set current_sorting_label = 'sylius.ui.newest_first'|trans|lower %} - {% elseif app.request.query.get('sorting').created_at is defined and app.request.query.get('sorting').created_at == 'asc'%} - {% set current_sorting_label = 'sylius.ui.oldest_first'|trans|lower %} - {% elseif app.request.query.get('sorting').price is defined and app.request.query.get('sorting').price == 'asc'%} - {% set current_sorting_label = 'sylius.ui.cheapest_first'|trans|lower %} - {% elseif app.request.query.get('sorting').price is defined and app.request.query.get('sorting').price == 'desc' %} - {% set current_sorting_label = 'sylius.ui.most_expensive_first'|trans|lower %} - {% else %} - {% set current_sorting_label = 'sylius.ui.by_position'|trans|lower %} - {% endif %} - - -{% endif %} diff --git a/src/Resources/views/Event/_sonata.html.twig b/src/Resources/views/Event/_sonata.html.twig deleted file mode 100644 index f4308e68..00000000 --- a/src/Resources/views/Event/_sonata.html.twig +++ /dev/null @@ -1 +0,0 @@ -{{ sonata_block_render_event(eventName, eventData) }} diff --git a/src/Resources/views/Event/_sylius.html.twig b/src/Resources/views/Event/_sylius.html.twig deleted file mode 100644 index ab3779aa..00000000 --- a/src/Resources/views/Event/_sylius.html.twig +++ /dev/null @@ -1 +0,0 @@ -{{ sylius_template_event(eventName, eventData) }} diff --git a/src/Resources/views/Event/event.html.twig b/src/Resources/views/Event/event.html.twig deleted file mode 100644 index 55a25779..00000000 --- a/src/Resources/views/Event/event.html.twig +++ /dev/null @@ -1,5 +0,0 @@ -{% if bundle_exists('SonataCoreBundle') %} - {% include '@MonsieurBizSyliusSearchPlugin/Event/_sonata.html.twig' with {'eventName': eventName, 'eventData': eventData} %} -{% else %} - {% include '@MonsieurBizSyliusSearchPlugin/Event/_sylius.html.twig' with {'eventName': eventName, 'eventData': eventData} %} -{% endif %} diff --git a/src/Resources/views/Header/form.html.twig b/src/Resources/views/Header/form.html.twig deleted file mode 100644 index 9ecbbd9a..00000000 --- a/src/Resources/views/Header/form.html.twig +++ /dev/null @@ -1 +0,0 @@ -{{ search_form() }} diff --git a/src/Resources/views/Instant/_box.html.twig b/src/Resources/views/Instant/_box.html.twig deleted file mode 100644 index 5b2d76e5..00000000 --- a/src/Resources/views/Instant/_box.html.twig +++ /dev/null @@ -1,27 +0,0 @@ -{% import "@SyliusShop/Common/Macro/money.html.twig" as money %} - - diff --git a/src/Resources/views/Instant/result.html.twig b/src/Resources/views/Instant/result.html.twig deleted file mode 100644 index 2410c3f6..00000000 --- a/src/Resources/views/Instant/result.html.twig +++ /dev/null @@ -1,9 +0,0 @@ -{% block content %} - {% if resultSet.totalHits == 0 %} - {{ 'monsieurbiz_searchplugin.instant.result.no_result'|trans }} - {% else %} - {% for result in resultSet.results %} - {% include '@MonsieurBizSyliusSearchPlugin/Instant/_box.html.twig' with {'result': result} %} - {% endfor %} - {% endif %} -{% endblock %} diff --git a/src/Resources/views/Search/_box.html.twig b/src/Resources/views/Search/_box.html.twig deleted file mode 100644 index 01dd1460..00000000 --- a/src/Resources/views/Search/_box.html.twig +++ /dev/null @@ -1 +0,0 @@ -{% include '@MonsieurBizSyliusSearchPlugin/Common/_box.html.twig' with {'result': result} %} diff --git a/src/Resources/views/Search/_filters.html.twig b/src/Resources/views/Search/_filters.html.twig deleted file mode 100644 index 0b4d2940..00000000 --- a/src/Resources/views/Search/_filters.html.twig +++ /dev/null @@ -1 +0,0 @@ -{% include '@MonsieurBizSyliusSearchPlugin/Common/_filters.html.twig' %} diff --git a/src/Resources/views/Search/_header.html.twig b/src/Resources/views/Search/_header.html.twig deleted file mode 100644 index 90131d43..00000000 --- a/src/Resources/views/Search/_header.html.twig +++ /dev/null @@ -1,7 +0,0 @@ -{% include '@MonsieurBizSyliusSearchPlugin/Event/event.html.twig' with {'eventName': 'sylius.shop.search.header.before', 'eventData': {'query': query}} %} - -

- {{ 'monsieurbiz_searchplugin.search.result.search_result'|trans({'%query%': query, '%count%': resultSet.totalHits}) }} -

- -{% include '@MonsieurBizSyliusSearchPlugin/Event/event.html.twig' with {'eventName': 'sylius.shop.search.header.after', 'eventData': {'query': query}} %} diff --git a/src/Resources/views/Search/_pagination.html.twig b/src/Resources/views/Search/_pagination.html.twig deleted file mode 100644 index bd29502e..00000000 --- a/src/Resources/views/Search/_pagination.html.twig +++ /dev/null @@ -1 +0,0 @@ -{% include '@MonsieurBizSyliusSearchPlugin/Common/_pagination.html.twig' %} diff --git a/src/Resources/views/Search/_sidebar.html.twig b/src/Resources/views/Search/_sidebar.html.twig deleted file mode 100644 index 9c13626a..00000000 --- a/src/Resources/views/Search/_sidebar.html.twig +++ /dev/null @@ -1 +0,0 @@ -{% include '@MonsieurBizSyliusSearchPlugin/Search/_filters.html.twig' %} diff --git a/src/Resources/views/Search/_sorting.html.twig b/src/Resources/views/Search/_sorting.html.twig deleted file mode 100644 index a91f09ff..00000000 --- a/src/Resources/views/Search/_sorting.html.twig +++ /dev/null @@ -1 +0,0 @@ -{% include '@MonsieurBizSyliusSearchPlugin/Common/_sorting.html.twig' %} diff --git a/src/Resources/views/Search/result.html.twig b/src/Resources/views/Search/result.html.twig deleted file mode 100644 index be69a02d..00000000 --- a/src/Resources/views/Search/result.html.twig +++ /dev/null @@ -1,44 +0,0 @@ -{% extends '@SyliusShop/layout.html.twig' %} -{% import '@SyliusUi/Macro/pagination.html.twig' as pagination %} - -{% block content %} - {% include '@MonsieurBizSyliusSearchPlugin/Search/_header.html.twig' %} - -
-
-
-

{{ 'monsieurbiz_searchplugin.filters.loading' | trans }}

-
-
-
- {% include '@MonsieurBizSyliusSearchPlugin/Search/_sidebar.html.twig' %} -
-
- {% if resultSet.totalHits == 0 %} -
-
-
-

- {{ 'monsieurbiz_searchplugin.search.result.no_result'|trans }} -

-
-
-
- {% else %} - {% include '@MonsieurBizSyliusSearchPlugin/Search/_pagination.html.twig' %} - {% include '@MonsieurBizSyliusSearchPlugin/Search/_sorting.html.twig' %} - - -
- {% for result in resultSet.results %} - {% include '@MonsieurBizSyliusSearchPlugin/Search/_box.html.twig' with {'result': result} %} - {% endfor %} -
- - - - {{ pagination.simple(resultSet.pager) }} - {% endif %} -
-
-{% endblock %} diff --git a/src/Resources/views/Taxon/_box.html.twig b/src/Resources/views/Taxon/_box.html.twig deleted file mode 100644 index 01dd1460..00000000 --- a/src/Resources/views/Taxon/_box.html.twig +++ /dev/null @@ -1 +0,0 @@ -{% include '@MonsieurBizSyliusSearchPlugin/Common/_box.html.twig' with {'result': result} %} diff --git a/src/Resources/views/Taxon/_filters.html.twig b/src/Resources/views/Taxon/_filters.html.twig deleted file mode 100644 index 0b4d2940..00000000 --- a/src/Resources/views/Taxon/_filters.html.twig +++ /dev/null @@ -1 +0,0 @@ -{% include '@MonsieurBizSyliusSearchPlugin/Common/_filters.html.twig' %} diff --git a/src/Resources/views/Taxon/_pagination.html.twig b/src/Resources/views/Taxon/_pagination.html.twig deleted file mode 100644 index bd29502e..00000000 --- a/src/Resources/views/Taxon/_pagination.html.twig +++ /dev/null @@ -1 +0,0 @@ -{% include '@MonsieurBizSyliusSearchPlugin/Common/_pagination.html.twig' %} diff --git a/src/Resources/views/Taxon/_sidebar.html.twig b/src/Resources/views/Taxon/_sidebar.html.twig deleted file mode 100644 index 23d1eabe..00000000 --- a/src/Resources/views/Taxon/_sidebar.html.twig +++ /dev/null @@ -1,6 +0,0 @@ -{% include '@MonsieurBizSyliusSearchPlugin/Event/event.html.twig' with {'eventName': 'sylius.shop.product.index.before_vertical_menu', 'eventData': {'taxon': taxon}} %} - -{#{% include '@MonsieurBizSyliusSearchPlugin/Taxon/_tree.html.twig' %}#} -{% include '@MonsieurBizSyliusSearchPlugin/Taxon/_filters.html.twig' %} - -{% include '@MonsieurBizSyliusSearchPlugin/Event/event.html.twig' with {'eventName': 'sylius.shop.product.index.after_vertical_menu', 'eventData': {'taxon': taxon}} %} diff --git a/src/Resources/views/Taxon/_sorting.html.twig b/src/Resources/views/Taxon/_sorting.html.twig deleted file mode 100644 index a91f09ff..00000000 --- a/src/Resources/views/Taxon/_sorting.html.twig +++ /dev/null @@ -1 +0,0 @@ -{% include '@MonsieurBizSyliusSearchPlugin/Common/_sorting.html.twig' %} diff --git a/src/Resources/views/Taxon/_tree.html.twig b/src/Resources/views/Taxon/_tree.html.twig deleted file mode 100644 index e7ab2956..00000000 --- a/src/Resources/views/Taxon/_tree.html.twig +++ /dev/null @@ -1,11 +0,0 @@ - diff --git a/src/Resources/views/Taxon/result.html.twig b/src/Resources/views/Taxon/result.html.twig deleted file mode 100644 index ad745724..00000000 --- a/src/Resources/views/Taxon/result.html.twig +++ /dev/null @@ -1,44 +0,0 @@ -{% extends '@SyliusShop/layout.html.twig' %} -{% import '@SyliusUi/Macro/pagination.html.twig' as pagination %} - -{% block content %} - {% include '@SyliusShop/Product/Index/_header.html.twig' %} - -
-
-
-

{{ 'monsieurbiz_searchplugin.filters.loading' | trans }}

-
-
-
- {% include '@MonsieurBizSyliusSearchPlugin/Taxon/_sidebar.html.twig' %} -
-
- {% if resultSet.totalHits == 0 %} -
-
-
-

- {{ 'monsieurbiz_searchplugin.search.result.no_result'|trans }} -

-
-
-
- {% else %} - {% include '@MonsieurBizSyliusSearchPlugin/Taxon/_pagination.html.twig' %} - {% include '@MonsieurBizSyliusSearchPlugin/Taxon/_sorting.html.twig' %} - - -
- {% for result in resultSet.results %} - {% include '@MonsieurBizSyliusSearchPlugin/Taxon/_box.html.twig' with {'result': result} %} - {% endfor %} -
- - - - {{ pagination.simple(resultSet.pager) }} - {% endif %} -
-
-{% endblock %} diff --git a/src/Resources/views/form.html.twig b/src/Resources/views/form.html.twig deleted file mode 100644 index 3b861152..00000000 --- a/src/Resources/views/form.html.twig +++ /dev/null @@ -1,9 +0,0 @@ -{% form_theme form '@SyliusShop/Form/theme.html.twig' %} -
- {{ form_start(form, {'action': path('monsieurbiz_sylius_search_post'), 'method': 'POST', 'attr': {'class': 'ui search item autocomplete-search'}}) }} - {{ form_errors(form) }} - {{ form_row(form.query, {'value': query, 'label': false}) }} - {{ form_row(form.submit, {'attr': {'class': 'ui primary button'}}) }} -
- {{ form_end(form) }} -
diff --git a/src/Resources/views/js.html.twig b/src/Resources/views/js.html.twig deleted file mode 100644 index c14f2620..00000000 --- a/src/Resources/views/js.html.twig +++ /dev/null @@ -1,17 +0,0 @@ - -{% include '@SyliusUi/_javascripts.html.twig' with {'path': 'bundles/monsieurbizsyliussearchplugin/js/app.js'} %} diff --git a/src/Twig/Extension/CheckMethodExists.php b/src/Twig/Extension/CheckMethodExists.php deleted file mode 100644 index f9dcfc2a..00000000 --- a/src/Twig/Extension/CheckMethodExists.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Twig\Extension; - -use Symfony\Component\DependencyInjection\ContainerInterface; -use Twig\Extension\AbstractExtension; -use Twig\TwigFunction; - -class CheckMethodExists extends AbstractExtension -{ - private $container; - - public function __construct(ContainerInterface $container) - { - $this->container = $container; - } - - public function getFunctions() - { - return [ - new TwigFunction('bundle_exists', [$this, 'bundleExists']), - ]; - } - - public function bundleExists($bundle) - { - return \array_key_exists( - $bundle, - $this->container->getParameter('kernel.bundles') - ); - } -} diff --git a/src/Twig/Extension/RenderDocumentUrl.php b/src/Twig/Extension/RenderDocumentUrl.php deleted file mode 100644 index 2829e59b..00000000 --- a/src/Twig/Extension/RenderDocumentUrl.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Twig\Extension; - -use MonsieurBiz\SyliusSearchPlugin\Helper\RenderDocumentUrlHelper; -use Twig\Extension\AbstractExtension; -use Twig\TwigFunction; - -class RenderDocumentUrl extends AbstractExtension -{ - /** - * @var RenderDocumentUrlHelper - */ - private $helper; - - /** - * RenderDocumentUrl constructor. - * - * @param RenderDocumentUrlHelper $helper - */ - public function __construct( - RenderDocumentUrlHelper $helper - ) { - $this->helper = $helper; - } - - public function getFunctions() - { - return [ - new TwigFunction('search_result_url_param', [$this->helper, 'getUrlParams']), - ]; - } -} diff --git a/src/Twig/Extension/RenderSearchForm.php b/src/Twig/Extension/RenderSearchForm.php deleted file mode 100644 index 8bd60636..00000000 --- a/src/Twig/Extension/RenderSearchForm.php +++ /dev/null @@ -1,61 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Twig\Extension; - -use MonsieurBiz\SyliusSearchPlugin\Form\Type\SearchType; -use Symfony\Component\Form\FormFactoryInterface; -use Symfony\Component\HttpFoundation\RequestStack; -use Twig\Environment; -use Twig\Extension\AbstractExtension; -use Twig\Markup; -use Twig\TwigFunction; - -class RenderSearchForm extends AbstractExtension -{ - /** @var FormFactoryInterface */ - private $formFactory; - - /** @var Environment */ - private $templatingEngine; - - /** @var RequestStack */ - private $requestStack; - - public function __construct( - FormFactoryInterface $formFactory, - Environment $templatingEngine, - RequestStack $requestStack - ) { - $this->formFactory = $formFactory; - $this->templatingEngine = $templatingEngine; - $this->requestStack = $requestStack; - } - - public function getFunctions() - { - return [ - new TwigFunction('search_form', [$this, 'createForm']), - ]; - } - - public function createForm($template = null) - { - $template = $template ?? '@MonsieurBizSyliusSearchPlugin/form.html.twig'; - - return new Markup($this->templatingEngine->render($template, [ - 'form' => $this->formFactory->create(SearchType::class)->createView(), - 'query' => urldecode($this->requestStack->getCurrentRequest()->get('query') ?? ''), - ]), 'UTF-8'); - } -} diff --git a/src/generated/Model/Attributes.php b/src/generated/Model/Attributes.php deleted file mode 100644 index 49eb9ecc..00000000 --- a/src/generated/Model/Attributes.php +++ /dev/null @@ -1,142 +0,0 @@ -code; - } - /** - * - * - * @param string|null $code - * - * @return self - */ - public function setCode(?string $code) : self - { - $this->code = $code; - return $this; - } - /** - * - * - * @return string|null - */ - public function getName() : ?string - { - return $this->name; - } - /** - * - * - * @param string|null $name - * - * @return self - */ - public function setName(?string $name) : self - { - $this->name = $name; - return $this; - } - /** - * - * - * @return string[]|null - */ - public function getValue() : ?array - { - return $this->value; - } - /** - * - * - * @param string[]|null $value - * - * @return self - */ - public function setValue(?array $value) : self - { - $this->value = $value; - return $this; - } - /** - * - * - * @return string|null - */ - public function getLocale() : ?string - { - return $this->locale; - } - /** - * - * - * @param string|null $locale - * - * @return self - */ - public function setLocale(?string $locale) : self - { - $this->locale = $locale; - return $this; - } - /** - * - * - * @return int|null - */ - public function getScore() : ?int - { - return $this->score; - } - /** - * - * - * @param int|null $score - * - * @return self - */ - public function setScore(?int $score) : self - { - $this->score = $score; - return $this; - } -} \ No newline at end of file diff --git a/src/generated/Model/Document.php b/src/generated/Model/Document.php deleted file mode 100644 index 2e0bcda3..00000000 --- a/src/generated/Model/Document.php +++ /dev/null @@ -1,331 +0,0 @@ -type; - } - /** - * - * - * @param string|null $type - * - * @return self - */ - public function setType(?string $type) : self - { - $this->type = $type; - return $this; - } - /** - * - * - * @return string|null - */ - public function getCode() : ?string - { - return $this->code; - } - /** - * - * - * @param string|null $code - * - * @return self - */ - public function setCode(?string $code) : self - { - $this->code = $code; - return $this; - } - /** - * - * - * @return int|null - */ - public function getId() : ?int - { - return $this->id; - } - /** - * - * - * @param int|null $id - * - * @return self - */ - public function setId(?int $id) : self - { - $this->id = $id; - return $this; - } - /** - * - * - * @return bool|null - */ - public function getEnabled() : ?bool - { - return $this->enabled; - } - /** - * - * - * @param bool|null $enabled - * - * @return self - */ - public function setEnabled(?bool $enabled) : self - { - $this->enabled = $enabled; - return $this; - } - /** - * - * - * @return string|null - */ - public function getSlug() : ?string - { - return $this->slug; - } - /** - * - * - * @param string|null $slug - * - * @return self - */ - public function setSlug(?string $slug) : self - { - $this->slug = $slug; - return $this; - } - /** - * - * - * @return string|null - */ - public function getImage() : ?string - { - return $this->image; - } - /** - * - * - * @param string|null $image - * - * @return self - */ - public function setImage(?string $image) : self - { - $this->image = $image; - return $this; - } - /** - * - * - * @return string[]|null - */ - public function getChannel() : ?array - { - return $this->channel; - } - /** - * - * - * @param string[]|null $channel - * - * @return self - */ - public function setChannel(?array $channel) : self - { - $this->channel = $channel; - return $this; - } - /** - * - * - * @return Taxon|null - */ - public function getMainTaxon() : ?Taxon - { - return $this->mainTaxon; - } - /** - * - * - * @param Taxon|null $mainTaxon - * - * @return self - */ - public function setMainTaxon(?Taxon $mainTaxon) : self - { - $this->mainTaxon = $mainTaxon; - return $this; - } - /** - * - * - * @return Taxon[]|null - */ - public function getTaxon() : ?array - { - return $this->taxon; - } - /** - * - * - * @param Taxon[]|null $taxon - * - * @return self - */ - public function setTaxon(?array $taxon) : self - { - $this->taxon = $taxon; - return $this; - } - /** - * - * - * @return Attributes[]|null - */ - public function getAttributes() : ?array - { - return $this->attributes; - } - /** - * - * - * @param Attributes[]|null $attributes - * - * @return self - */ - public function setAttributes(?array $attributes) : self - { - $this->attributes = $attributes; - return $this; - } - /** - * - * - * @return Price[]|null - */ - public function getPrice() : ?array - { - return $this->price; - } - /** - * - * - * @param Price[]|null $price - * - * @return self - */ - public function setPrice(?array $price) : self - { - $this->price = $price; - return $this; - } - /** - * - * - * @return Price[]|null - */ - public function getOriginalPrice() : ?array - { - return $this->originalPrice; - } - /** - * - * - * @param Price[]|null $originalPrice - * - * @return self - */ - public function setOriginalPrice(?array $originalPrice) : self - { - $this->originalPrice = $originalPrice; - return $this; - } -} \ No newline at end of file diff --git a/src/generated/Model/Price.php b/src/generated/Model/Price.php deleted file mode 100644 index afaaf63b..00000000 --- a/src/generated/Model/Price.php +++ /dev/null @@ -1,88 +0,0 @@ -channel; - } - /** - * - * - * @param string|null $channel - * - * @return self - */ - public function setChannel(?string $channel) : self - { - $this->channel = $channel; - return $this; - } - /** - * - * - * @return string|null - */ - public function getCurrency() : ?string - { - return $this->currency; - } - /** - * - * - * @param string|null $currency - * - * @return self - */ - public function setCurrency(?string $currency) : self - { - $this->currency = $currency; - return $this; - } - /** - * - * - * @return int|null - */ - public function getValue() : ?int - { - return $this->value; - } - /** - * - * - * @param int|null $value - * - * @return self - */ - public function setValue(?int $value) : self - { - $this->value = $value; - return $this; - } -} \ No newline at end of file diff --git a/src/generated/Model/Taxon.php b/src/generated/Model/Taxon.php deleted file mode 100644 index a9166a8c..00000000 --- a/src/generated/Model/Taxon.php +++ /dev/null @@ -1,142 +0,0 @@ -name; - } - /** - * - * - * @param string|null $name - * - * @return self - */ - public function setName(?string $name) : self - { - $this->name = $name; - return $this; - } - /** - * - * - * @return string|null - */ - public function getCode() : ?string - { - return $this->code; - } - /** - * - * - * @param string|null $code - * - * @return self - */ - public function setCode(?string $code) : self - { - $this->code = $code; - return $this; - } - /** - * - * - * @return int|null - */ - public function getPosition() : ?int - { - return $this->position; - } - /** - * - * - * @param int|null $position - * - * @return self - */ - public function setPosition(?int $position) : self - { - $this->position = $position; - return $this; - } - /** - * - * - * @return int|null - */ - public function getLevel() : ?int - { - return $this->level; - } - /** - * - * - * @param int|null $level - * - * @return self - */ - public function setLevel(?int $level) : self - { - $this->level = $level; - return $this; - } - /** - * - * - * @return int|null - */ - public function getProductPosition() : ?int - { - return $this->productPosition; - } - /** - * - * - * @param int|null $productPosition - * - * @return self - */ - public function setProductPosition(?int $productPosition) : self - { - $this->productPosition = $productPosition; - return $this; - } -} \ No newline at end of file diff --git a/src/generated/Normalizer/AttributesNormalizer.php b/src/generated/Normalizer/AttributesNormalizer.php deleted file mode 100644 index 5eebd74d..00000000 --- a/src/generated/Normalizer/AttributesNormalizer.php +++ /dev/null @@ -1,112 +0,0 @@ -{'$ref'})) { - return new Reference($data->{'$ref'}, $context['document-origin']); - } - if (isset($data->{'$recursiveRef'})) { - return new Reference($data->{'$recursiveRef'}, $context['document-origin']); - } - $object = new \MonsieurBiz\SyliusSearchPlugin\generated\Model\Attributes(); - if (property_exists($data, 'code') && $data->{'code'} !== null) { - $object->setCode($data->{'code'}); - } - elseif (property_exists($data, 'code') && $data->{'code'} === null) { - $object->setCode(null); - } - if (property_exists($data, 'name') && $data->{'name'} !== null) { - $object->setName($data->{'name'}); - } - elseif (property_exists($data, 'name') && $data->{'name'} === null) { - $object->setName(null); - } - if (property_exists($data, 'value') && $data->{'value'} !== null) { - $values = array(); - foreach ($data->{'value'} as $value) { - $values[] = $value; - } - $object->setValue($values); - } - elseif (property_exists($data, 'value') && $data->{'value'} === null) { - $object->setValue(null); - } - if (property_exists($data, 'locale') && $data->{'locale'} !== null) { - $object->setLocale($data->{'locale'}); - } - elseif (property_exists($data, 'locale') && $data->{'locale'} === null) { - $object->setLocale(null); - } - if (property_exists($data, 'score') && $data->{'score'} !== null) { - $object->setScore($data->{'score'}); - } - elseif (property_exists($data, 'score') && $data->{'score'} === null) { - $object->setScore(null); - } - return $object; - } - public function normalize($object, $format = null, array $context = array()) - { - $data = new \stdClass(); - if (null !== $object->getCode()) { - $data->{'code'} = $object->getCode(); - } - else { - $data->{'code'} = null; - } - if (null !== $object->getName()) { - $data->{'name'} = $object->getName(); - } - else { - $data->{'name'} = null; - } - if (null !== $object->getValue()) { - $values = array(); - foreach ($object->getValue() as $value) { - $values[] = $value; - } - $data->{'value'} = $values; - } - else { - $data->{'value'} = null; - } - if (null !== $object->getLocale()) { - $data->{'locale'} = $object->getLocale(); - } - else { - $data->{'locale'} = null; - } - if (null !== $object->getScore()) { - $data->{'score'} = $object->getScore(); - } - else { - $data->{'score'} = null; - } - return $data; - } -} \ No newline at end of file diff --git a/src/generated/Normalizer/DocumentNormalizer.php b/src/generated/Normalizer/DocumentNormalizer.php deleted file mode 100644 index 6ce12a0d..00000000 --- a/src/generated/Normalizer/DocumentNormalizer.php +++ /dev/null @@ -1,228 +0,0 @@ -{'$ref'})) { - return new Reference($data->{'$ref'}, $context['document-origin']); - } - if (isset($data->{'$recursiveRef'})) { - return new Reference($data->{'$recursiveRef'}, $context['document-origin']); - } - $object = new \MonsieurBiz\SyliusSearchPlugin\generated\Model\Document(); - if (property_exists($data, 'type') && $data->{'type'} !== null) { - $object->setType($data->{'type'}); - } - elseif (property_exists($data, 'type') && $data->{'type'} === null) { - $object->setType(null); - } - if (property_exists($data, 'code') && $data->{'code'} !== null) { - $object->setCode($data->{'code'}); - } - elseif (property_exists($data, 'code') && $data->{'code'} === null) { - $object->setCode(null); - } - if (property_exists($data, 'id') && $data->{'id'} !== null) { - $object->setId($data->{'id'}); - } - elseif (property_exists($data, 'id') && $data->{'id'} === null) { - $object->setId(null); - } - if (property_exists($data, 'enabled') && $data->{'enabled'} !== null) { - $object->setEnabled($data->{'enabled'}); - } - elseif (property_exists($data, 'enabled') && $data->{'enabled'} === null) { - $object->setEnabled(null); - } - if (property_exists($data, 'slug') && $data->{'slug'} !== null) { - $object->setSlug($data->{'slug'}); - } - elseif (property_exists($data, 'slug') && $data->{'slug'} === null) { - $object->setSlug(null); - } - if (property_exists($data, 'image') && $data->{'image'} !== null) { - $object->setImage($data->{'image'}); - } - elseif (property_exists($data, 'image') && $data->{'image'} === null) { - $object->setImage(null); - } - if (property_exists($data, 'channel') && $data->{'channel'} !== null) { - $values = array(); - foreach ($data->{'channel'} as $value) { - $values[] = $value; - } - $object->setChannel($values); - } - elseif (property_exists($data, 'channel') && $data->{'channel'} === null) { - $object->setChannel(null); - } - if (property_exists($data, 'main_taxon') && $data->{'main_taxon'} !== null) { - $object->setMainTaxon($this->denormalizer->denormalize($data->{'main_taxon'}, 'MonsieurBiz\\SyliusSearchPlugin\\generated\\Model\\Taxon', 'json', $context)); - } - elseif (property_exists($data, 'main_taxon') && $data->{'main_taxon'} === null) { - $object->setMainTaxon(null); - } - if (property_exists($data, 'taxon') && $data->{'taxon'} !== null) { - $values_1 = array(); - foreach ($data->{'taxon'} as $value_1) { - $values_1[] = $this->denormalizer->denormalize($value_1, 'MonsieurBiz\\SyliusSearchPlugin\\generated\\Model\\Taxon', 'json', $context); - } - $object->setTaxon($values_1); - } - elseif (property_exists($data, 'taxon') && $data->{'taxon'} === null) { - $object->setTaxon(null); - } - if (property_exists($data, 'attributes') && $data->{'attributes'} !== null) { - $values_2 = array(); - foreach ($data->{'attributes'} as $value_2) { - $values_2[] = $this->denormalizer->denormalize($value_2, 'MonsieurBiz\\SyliusSearchPlugin\\generated\\Model\\Attributes', 'json', $context); - } - $object->setAttributes($values_2); - } - elseif (property_exists($data, 'attributes') && $data->{'attributes'} === null) { - $object->setAttributes(null); - } - if (property_exists($data, 'price') && $data->{'price'} !== null) { - $values_3 = array(); - foreach ($data->{'price'} as $value_3) { - $values_3[] = $this->denormalizer->denormalize($value_3, 'MonsieurBiz\\SyliusSearchPlugin\\generated\\Model\\Price', 'json', $context); - } - $object->setPrice($values_3); - } - elseif (property_exists($data, 'price') && $data->{'price'} === null) { - $object->setPrice(null); - } - if (property_exists($data, 'original_price') && $data->{'original_price'} !== null) { - $values_4 = array(); - foreach ($data->{'original_price'} as $value_4) { - $values_4[] = $this->denormalizer->denormalize($value_4, 'MonsieurBiz\\SyliusSearchPlugin\\generated\\Model\\Price', 'json', $context); - } - $object->setOriginalPrice($values_4); - } - elseif (property_exists($data, 'original_price') && $data->{'original_price'} === null) { - $object->setOriginalPrice(null); - } - return $object; - } - public function normalize($object, $format = null, array $context = array()) - { - $data = new \stdClass(); - if (null !== $object->getType()) { - $data->{'type'} = $object->getType(); - } - else { - $data->{'type'} = null; - } - if (null !== $object->getCode()) { - $data->{'code'} = $object->getCode(); - } - else { - $data->{'code'} = null; - } - if (null !== $object->getId()) { - $data->{'id'} = $object->getId(); - } - else { - $data->{'id'} = null; - } - if (null !== $object->getEnabled()) { - $data->{'enabled'} = $object->getEnabled(); - } - else { - $data->{'enabled'} = null; - } - if (null !== $object->getSlug()) { - $data->{'slug'} = $object->getSlug(); - } - else { - $data->{'slug'} = null; - } - if (null !== $object->getImage()) { - $data->{'image'} = $object->getImage(); - } - else { - $data->{'image'} = null; - } - if (null !== $object->getChannel()) { - $values = array(); - foreach ($object->getChannel() as $value) { - $values[] = $value; - } - $data->{'channel'} = $values; - } - else { - $data->{'channel'} = null; - } - if (null !== $object->getMainTaxon()) { - $data->{'main_taxon'} = $this->normalizer->normalize($object->getMainTaxon(), 'json', $context); - } - else { - $data->{'main_taxon'} = null; - } - if (null !== $object->getTaxon()) { - $values_1 = array(); - foreach ($object->getTaxon() as $value_1) { - $values_1[] = $this->normalizer->normalize($value_1, 'json', $context); - } - $data->{'taxon'} = $values_1; - } - else { - $data->{'taxon'} = null; - } - if (null !== $object->getAttributes()) { - $values_2 = array(); - foreach ($object->getAttributes() as $value_2) { - $values_2[] = $this->normalizer->normalize($value_2, 'json', $context); - } - $data->{'attributes'} = $values_2; - } - else { - $data->{'attributes'} = null; - } - if (null !== $object->getPrice()) { - $values_3 = array(); - foreach ($object->getPrice() as $value_3) { - $values_3[] = $this->normalizer->normalize($value_3, 'json', $context); - } - $data->{'price'} = $values_3; - } - else { - $data->{'price'} = null; - } - if (null !== $object->getOriginalPrice()) { - $values_4 = array(); - foreach ($object->getOriginalPrice() as $value_4) { - $values_4[] = $this->normalizer->normalize($value_4, 'json', $context); - } - $data->{'original_price'} = $values_4; - } - else { - $data->{'original_price'} = null; - } - return $data; - } -} \ No newline at end of file diff --git a/src/generated/Normalizer/JaneObjectNormalizer.php b/src/generated/Normalizer/JaneObjectNormalizer.php deleted file mode 100644 index 0f6c5f1d..00000000 --- a/src/generated/Normalizer/JaneObjectNormalizer.php +++ /dev/null @@ -1,48 +0,0 @@ - 'MonsieurBiz\\SyliusSearchPlugin\\generated\\Normalizer\\DocumentNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\generated\\Model\\Price' => 'MonsieurBiz\\SyliusSearchPlugin\\generated\\Normalizer\\PriceNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\generated\\Model\\Attributes' => 'MonsieurBiz\\SyliusSearchPlugin\\generated\\Normalizer\\AttributesNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\generated\\Model\\Taxon' => 'MonsieurBiz\\SyliusSearchPlugin\\generated\\Normalizer\\TaxonNormalizer', '\\Jane\\JsonSchemaRuntime\\Reference' => '\\Jane\\JsonSchemaRuntime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); - public function supportsDenormalization($data, $type, $format = null) - { - return array_key_exists($type, $this->normalizers); - } - public function supportsNormalization($data, $format = null) - { - return is_object($data) && array_key_exists(get_class($data), $this->normalizers); - } - public function normalize($object, $format = null, array $context = array()) - { - $normalizerClass = $this->normalizers[get_class($object)]; - $normalizer = $this->getNormalizer($normalizerClass); - return $normalizer->normalize($object, $format, $context); - } - public function denormalize($data, $class, $format = null, array $context = array()) - { - $denormalizerClass = $this->normalizers[$class]; - $denormalizer = $this->getNormalizer($denormalizerClass); - return $denormalizer->denormalize($data, $class, $format, $context); - } - private function getNormalizer(string $normalizerClass) - { - return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); - } - private function initNormalizer(string $normalizerClass) - { - $normalizer = new $normalizerClass(); - $normalizer->setNormalizer($this->normalizer); - $normalizer->setDenormalizer($this->denormalizer); - $this->normalizersCache[$normalizerClass] = $normalizer; - return $normalizer; - } -} \ No newline at end of file diff --git a/src/generated/Normalizer/NormalizerFactory.php b/src/generated/Normalizer/NormalizerFactory.php deleted file mode 100644 index 3373c88b..00000000 --- a/src/generated/Normalizer/NormalizerFactory.php +++ /dev/null @@ -1,18 +0,0 @@ -{'$ref'})) { - return new Reference($data->{'$ref'}, $context['document-origin']); - } - if (isset($data->{'$recursiveRef'})) { - return new Reference($data->{'$recursiveRef'}, $context['document-origin']); - } - $object = new \MonsieurBiz\SyliusSearchPlugin\generated\Model\Price(); - if (property_exists($data, 'channel') && $data->{'channel'} !== null) { - $object->setChannel($data->{'channel'}); - } - elseif (property_exists($data, 'channel') && $data->{'channel'} === null) { - $object->setChannel(null); - } - if (property_exists($data, 'currency') && $data->{'currency'} !== null) { - $object->setCurrency($data->{'currency'}); - } - elseif (property_exists($data, 'currency') && $data->{'currency'} === null) { - $object->setCurrency(null); - } - if (property_exists($data, 'value') && $data->{'value'} !== null) { - $object->setValue($data->{'value'}); - } - elseif (property_exists($data, 'value') && $data->{'value'} === null) { - $object->setValue(null); - } - return $object; - } - public function normalize($object, $format = null, array $context = array()) - { - $data = new \stdClass(); - if (null !== $object->getChannel()) { - $data->{'channel'} = $object->getChannel(); - } - else { - $data->{'channel'} = null; - } - if (null !== $object->getCurrency()) { - $data->{'currency'} = $object->getCurrency(); - } - else { - $data->{'currency'} = null; - } - if (null !== $object->getValue()) { - $data->{'value'} = $object->getValue(); - } - else { - $data->{'value'} = null; - } - return $data; - } -} \ No newline at end of file diff --git a/src/generated/Normalizer/TaxonNormalizer.php b/src/generated/Normalizer/TaxonNormalizer.php deleted file mode 100644 index 5d7bf413..00000000 --- a/src/generated/Normalizer/TaxonNormalizer.php +++ /dev/null @@ -1,104 +0,0 @@ -{'$ref'})) { - return new Reference($data->{'$ref'}, $context['document-origin']); - } - if (isset($data->{'$recursiveRef'})) { - return new Reference($data->{'$recursiveRef'}, $context['document-origin']); - } - $object = new \MonsieurBiz\SyliusSearchPlugin\generated\Model\Taxon(); - if (property_exists($data, 'name') && $data->{'name'} !== null) { - $object->setName($data->{'name'}); - } - elseif (property_exists($data, 'name') && $data->{'name'} === null) { - $object->setName(null); - } - if (property_exists($data, 'code') && $data->{'code'} !== null) { - $object->setCode($data->{'code'}); - } - elseif (property_exists($data, 'code') && $data->{'code'} === null) { - $object->setCode(null); - } - if (property_exists($data, 'position') && $data->{'position'} !== null) { - $object->setPosition($data->{'position'}); - } - elseif (property_exists($data, 'position') && $data->{'position'} === null) { - $object->setPosition(null); - } - if (property_exists($data, 'level') && $data->{'level'} !== null) { - $object->setLevel($data->{'level'}); - } - elseif (property_exists($data, 'level') && $data->{'level'} === null) { - $object->setLevel(null); - } - if (property_exists($data, 'product_position') && $data->{'product_position'} !== null) { - $object->setProductPosition($data->{'product_position'}); - } - elseif (property_exists($data, 'product_position') && $data->{'product_position'} === null) { - $object->setProductPosition(null); - } - return $object; - } - public function normalize($object, $format = null, array $context = array()) - { - $data = new \stdClass(); - if (null !== $object->getName()) { - $data->{'name'} = $object->getName(); - } - else { - $data->{'name'} = null; - } - if (null !== $object->getCode()) { - $data->{'code'} = $object->getCode(); - } - else { - $data->{'code'} = null; - } - if (null !== $object->getPosition()) { - $data->{'position'} = $object->getPosition(); - } - else { - $data->{'position'} = null; - } - if (null !== $object->getLevel()) { - $data->{'level'} = $object->getLevel(); - } - else { - $data->{'level'} = null; - } - if (null !== $object->getProductPosition()) { - $data->{'product_position'} = $object->getProductPosition(); - } - else { - $data->{'product_position'} = null; - } - return $data; - } -} \ No newline at end of file diff --git a/submit_filters.png b/submit_filters.png deleted file mode 100644 index 080e16fe1b8116e182136e00b22fbbc76745ace6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42313 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 . '/.env'); } -$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev'; +$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: '1.0'; $_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV']; $_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0'; diff --git a/tests/Application/config/bundles.php b/tests/Application/config/bundles.php index edc93d5f..7b2d8e05 100644 --- a/tests/Application/config/bundles.php +++ b/tests/Application/config/bundles.php @@ -62,7 +62,7 @@ FOS\OAuthServerBundle\FOSOAuthServerBundle::class => ['all' => true], Sylius\Bundle\AdminApiBundle\SyliusAdminApiBundle::class => ['all' => true], MonsieurBiz\SyliusSearchPlugin\MonsieurBizSyliusSearchPlugin::class => ['all' => true], - Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true], - Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true], + Symfony\Bundle\DebugBundle\DebugBundle::class => ['1.0' => true, 'test' => true, 'test_cached' => true], + Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['1.0' => true, 'test' => true, 'test_cached' => true], FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true, 'test_cached' => true], ]; diff --git a/tests/Application/config/packages/dev/monsieurbiz_sylius_search_plugin.yaml b/tests/Application/config/packages/dev/monsieurbiz_sylius_search_plugin.yaml deleted file mode 100644 index fc978376..00000000 --- a/tests/Application/config/packages/dev/monsieurbiz_sylius_search_plugin.yaml +++ /dev/null @@ -1,10 +0,0 @@ -monsieur_biz_sylius_search: - files: - search: '%kernel.project_dir%/../../src/Resources/config/elasticsearch/queries/search.yaml' - instant: '%kernel.project_dir%/../../src/Resources/config/elasticsearch/queries/instant.yaml' - taxon: '%kernel.project_dir%/../../src/Resources/config/elasticsearch/queries/taxon.yaml' - documentable_classes : - - 'Tests\MonsieurBiz\SyliusSearchPlugin\App\Entity\Product\Product' - grid: - filters: - apply_manually: false # Will refresh the filters depending on applied filters after you apply it manually diff --git a/tests/Application/config/packages/monsieurbiz_sylius_search_plugin.yaml b/tests/Application/config/packages/monsieurbiz_sylius_search_plugin.yaml deleted file mode 120000 index 5bc94855..00000000 --- a/tests/Application/config/packages/monsieurbiz_sylius_search_plugin.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../recipe/dev/config/packages/monsieurbiz_sylius_search_plugin.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/test/monsieurbiz_sylius_search_plugin.yaml b/tests/Application/config/packages/test/monsieurbiz_sylius_search_plugin.yaml deleted file mode 100644 index fc978376..00000000 --- a/tests/Application/config/packages/test/monsieurbiz_sylius_search_plugin.yaml +++ /dev/null @@ -1,10 +0,0 @@ -monsieur_biz_sylius_search: - files: - search: '%kernel.project_dir%/../../src/Resources/config/elasticsearch/queries/search.yaml' - instant: '%kernel.project_dir%/../../src/Resources/config/elasticsearch/queries/instant.yaml' - taxon: '%kernel.project_dir%/../../src/Resources/config/elasticsearch/queries/taxon.yaml' - documentable_classes : - - 'Tests\MonsieurBiz\SyliusSearchPlugin\App\Entity\Product\Product' - grid: - filters: - apply_manually: false # Will refresh the filters depending on applied filters after you apply it manually diff --git a/tests/Application/src/Entity/Product/Product.php b/tests/Application/src/Entity/Product/Product.php deleted file mode 100644 index 888c4a19..00000000 --- a/tests/Application/src/Entity/Product/Product.php +++ /dev/null @@ -1,35 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Tests\MonsieurBiz\SyliusSearchPlugin\App\Entity\Product; - -use Doctrine\ORM\Mapping as ORM; -use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; -use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableProductTrait; -use Sylius\Component\Core\Model\Product as BaseProduct; -use Sylius\Component\Core\Model\ProductTranslation; -use Sylius\Component\Product\Model\ProductTranslationInterface; - -/** - * @ORM\MappedSuperclass - * @ORM\Table(name="sylius_product") - */ -class Product extends BaseProduct implements DocumentableInterface -{ - use DocumentableProductTrait; - - protected function createTranslation(): ProductTranslationInterface - { - return new ProductTranslation(); - } -} diff --git a/tests/Application/src/Entity/Product/ProductAttribute.php b/tests/Application/src/Entity/Product/ProductAttribute.php deleted file mode 100644 index ce6b2391..00000000 --- a/tests/Application/src/Entity/Product/ProductAttribute.php +++ /dev/null @@ -1,35 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Tests\MonsieurBiz\SyliusSearchPlugin\App\Entity\Product; - -use Doctrine\ORM\Mapping as ORM; -use MonsieurBiz\SyliusSearchPlugin\Entity\Product\FilterableInterface; -use MonsieurBiz\SyliusSearchPlugin\Model\Product\FilterableTrait; -use Sylius\Component\Attribute\Model\AttributeTranslationInterface; -use Sylius\Component\Product\Model\ProductAttribute as BaseProductAttribute; -use Sylius\Component\Product\Model\ProductAttributeTranslation; - -/** - * @ORM\Entity - * @ORM\Table(name="sylius_product_attribute") - */ -class ProductAttribute extends BaseProductAttribute implements FilterableInterface -{ - use FilterableTrait; - - protected function createTranslation(): AttributeTranslationInterface - { - return new ProductAttributeTranslation(); - } -} diff --git a/tests/Application/src/Entity/Product/ProductOption.php b/tests/Application/src/Entity/Product/ProductOption.php deleted file mode 100644 index 3f468f74..00000000 --- a/tests/Application/src/Entity/Product/ProductOption.php +++ /dev/null @@ -1,35 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Tests\MonsieurBiz\SyliusSearchPlugin\App\Entity\Product; - -use Doctrine\ORM\Mapping as ORM; -use MonsieurBiz\SyliusSearchPlugin\Entity\Product\FilterableInterface; -use MonsieurBiz\SyliusSearchPlugin\Model\Product\FilterableTrait; -use Sylius\Component\Product\Model\ProductOption as BaseProductOption; -use Sylius\Component\Product\Model\ProductOptionTranslation; -use Sylius\Component\Product\Model\ProductOptionTranslationInterface; - -/** - * @ORM\Entity - * @ORM\Table(name="sylius_product_option") - */ -class ProductOption extends BaseProductOption implements FilterableInterface -{ - use FilterableTrait; - - protected function createTranslation(): ProductOptionTranslationInterface - { - return new ProductOptionTranslation(); - } -} diff --git a/tests/Application/src/Migrations/Version20201016105958.php b/tests/Application/src/Migrations/Version20201016105958.php deleted file mode 100644 index bf4fa206..00000000 --- a/tests/Application/src/Migrations/Version20201016105958.php +++ /dev/null @@ -1,37 +0,0 @@ -abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); - - $this->addSql('ALTER TABLE sylius_product_option ADD filterable TINYINT(1) DEFAULT \'1\' NOT NULL'); - $this->addSql('ALTER TABLE sylius_product_attribute ADD filterable TINYINT(1) DEFAULT \'1\' NOT NULL'); - } - - public function down(Schema $schema) : void - { - // this down() migration is auto-generated, please modify it to your needs - $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); - - $this->addSql('ALTER TABLE sylius_product_attribute DROP filterable'); - $this->addSql('ALTER TABLE sylius_product_option DROP filterable'); - } -} diff --git a/tests/Application/templates/bundles/SyliusAdminBundle b/tests/Application/templates/bundles/SyliusAdminBundle deleted file mode 120000 index 98a3226d..00000000 --- a/tests/Application/templates/bundles/SyliusAdminBundle +++ /dev/null @@ -1 +0,0 @@ -../../../../src/Resources/SyliusAdminBundle/views \ No newline at end of file From d9c19a05a54501b34ab91b67e1c4a871ba5a649a Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Thu, 30 Sep 2021 16:56:57 +0200 Subject: [PATCH 002/142] feat: Update dependencies & test application --- .gitignore | 1 - .idea/.gitignore | 8 - .idea/SyliusSearchPlugin.iml | 12 -- .idea/inspectionProfiles/Project_Default.xml | 6 - .idea/modules.xml | 8 - .idea/php.xml | 4 - .idea/vcs.xml | 6 - .php_cs.dist => .php-cs-fixer.dist.php | 22 ++- .php-version.dist | 1 + Makefile | 157 ++++++++++++++++++ composer.json | 10 +- phpmd.xml | 70 ++++++++ phpstan.neon | 14 +- .../monsieurbiz_sylius_search_plugin.yaml | 0 .../monsieurbiz_sylius_search_plugin.yaml | 0 {recipe => recipes}/1.0/manifest.json | 0 {recipe => recipes}/2.0-dev | 0 tests/Application/.babelrc | 16 +- tests/Application/.env | 16 +- tests/Application/.env.test | 4 +- tests/Application/.eslintrc.js | 21 +-- tests/Application/.gitignore | 41 ++++- tests/Application/.php-version | 2 +- tests/Application/bin/console | 4 +- tests/Application/composer.json | 4 +- tests/Application/config/bundles.php | 22 ++- tests/Application/config/jwt | 1 + .../Application/config/packages/_sylius.yaml | 31 +--- .../config/packages/dev/_sylius.yaml | 2 + .../config/packages/dev/jms_serializer.yaml | 7 +- .../config/packages/dev/nelmio_alice.yaml | 9 + .../Application/config/packages/doctrine.yaml | 13 +- .../config/packages/doctrine_migrations.yaml | 6 +- .../Application/config/packages/fos_rest.yaml | 12 +- .../config/packages/framework.yaml | 8 +- .../config/packages/jms_serializer.yaml | 5 +- .../packages/lexik_jwt_authentication.yaml | 1 + .../config/packages/liip_imagine.yaml | 7 +- .../config/packages/prod/doctrine.yaml | 31 ---- .../config/packages/prod/jms_serializer.yaml | 6 - .../config/packages/prod/monolog.yaml | 10 -- .../Application/config/packages/routing.yaml | 4 +- .../Application/config/packages/security.yaml | 104 +----------- .../config/packages/security_checker.yaml | 9 - .../config/packages/staging/monolog.yaml | 10 -- .../config/packages/staging/swiftmailer.yaml | 2 - .../packages/stof_doctrine_extensions.yaml | 5 +- .../config/packages/swiftmailer.yaml | 3 +- tests/Application/config/packages/test | 1 + .../config/packages/test/framework.yaml | 4 - .../config/packages/test/monolog.yaml | 6 - .../config/packages/test/swiftmailer.yaml | 6 - .../config/packages/test/sylius_theme.yaml | 3 - .../config/packages/test/web_profiler.yaml | 6 - tests/Application/config/packages/test_cached | 1 + .../config/packages/test_cached/doctrine.yaml | 16 -- .../config/packages/test_cached/fos_rest.yaml | 3 - .../packages/test_cached/framework.yaml | 4 - .../config/packages/test_cached/monolog.yaml | 6 - .../packages/test_cached/swiftmailer.yaml | 6 - .../packages/test_cached/sylius_channel.yaml | 2 - .../packages/test_cached/sylius_theme.yaml | 3 - .../config/packages/test_cached/twig.yaml | 2 - .../config/packages/translation.yaml | 9 +- tests/Application/config/packages/twig.yaml | 13 +- .../config/packages/validator.yaml | 4 +- tests/Application/config/routes.yaml | 1 + tests/Application/config/routes/dev | 1 + tests/Application/config/routes/dev/twig.yaml | 3 - .../config/routes/dev/web_profiler.yaml | 7 - .../config/routes/liip_imagine.yaml | 3 +- .../config/routes/sylius_admin.yaml | 4 +- .../config/routes/sylius_admin_api.yaml | 3 - .../Application/config/routes/sylius_api.yaml | 1 + .../config/routes/sylius_shop.yaml | 15 +- tests/Application/config/routes/test | 1 + tests/Application/config/routes/test_cached | 1 + tests/Application/config/secrets | 1 + tests/Application/config/services.yaml | 37 ----- tests/Application/config/services_test.yaml | 4 +- .../config/services_test_cached.yaml | 1 + tests/Application/docker-compose.yaml | 51 ++++++ .../docker/elasticsearch/Dockerfile | 5 + tests/Application/gulpfile.babel.js | 3 + tests/Application/public/.htaccess | 26 +-- tests/Application/public/favicon.ico | Bin 32038 -> 48 bytes tests/Application/public/index.php | 4 +- tests/Application/{ => src}/Kernel.php | 34 +--- tests/Application/webpack.config.js | 1 + 89 files changed, 433 insertions(+), 604 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/SyliusSearchPlugin.iml delete mode 100644 .idea/inspectionProfiles/Project_Default.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/php.xml delete mode 100644 .idea/vcs.xml rename .php_cs.dist => .php-cs-fixer.dist.php (93%) create mode 100644 .php-version.dist create mode 100644 Makefile create mode 100644 phpmd.xml rename {recipe => recipes}/1.0/config/packages/monsieurbiz_sylius_search_plugin.yaml (100%) rename {recipe => recipes}/1.0/config/routes/monsieurbiz_sylius_search_plugin.yaml (100%) rename {recipe => recipes}/1.0/manifest.json (100%) rename {recipe => recipes}/2.0-dev (100%) mode change 100644 => 120000 tests/Application/.babelrc mode change 100644 => 120000 tests/Application/.env.test mode change 100644 => 120000 tests/Application/.eslintrc.js mode change 100644 => 120000 tests/Application/.php-version create mode 120000 tests/Application/config/jwt mode change 100644 => 120000 tests/Application/config/packages/_sylius.yaml create mode 100644 tests/Application/config/packages/dev/_sylius.yaml create mode 100644 tests/Application/config/packages/dev/nelmio_alice.yaml mode change 100644 => 120000 tests/Application/config/packages/doctrine_migrations.yaml mode change 100644 => 120000 tests/Application/config/packages/fos_rest.yaml mode change 100644 => 120000 tests/Application/config/packages/framework.yaml mode change 100644 => 120000 tests/Application/config/packages/jms_serializer.yaml create mode 120000 tests/Application/config/packages/lexik_jwt_authentication.yaml mode change 100644 => 120000 tests/Application/config/packages/liip_imagine.yaml delete mode 100644 tests/Application/config/packages/prod/doctrine.yaml delete mode 100644 tests/Application/config/packages/prod/jms_serializer.yaml delete mode 100644 tests/Application/config/packages/prod/monolog.yaml mode change 100644 => 120000 tests/Application/config/packages/routing.yaml mode change 100644 => 120000 tests/Application/config/packages/security.yaml delete mode 100644 tests/Application/config/packages/security_checker.yaml delete mode 100644 tests/Application/config/packages/staging/monolog.yaml delete mode 100644 tests/Application/config/packages/staging/swiftmailer.yaml mode change 100644 => 120000 tests/Application/config/packages/stof_doctrine_extensions.yaml mode change 100644 => 120000 tests/Application/config/packages/swiftmailer.yaml create mode 120000 tests/Application/config/packages/test delete mode 100644 tests/Application/config/packages/test/framework.yaml delete mode 100644 tests/Application/config/packages/test/monolog.yaml delete mode 100644 tests/Application/config/packages/test/swiftmailer.yaml delete mode 100644 tests/Application/config/packages/test/sylius_theme.yaml delete mode 100644 tests/Application/config/packages/test/web_profiler.yaml create mode 120000 tests/Application/config/packages/test_cached delete mode 100644 tests/Application/config/packages/test_cached/doctrine.yaml delete mode 100644 tests/Application/config/packages/test_cached/fos_rest.yaml delete mode 100644 tests/Application/config/packages/test_cached/framework.yaml delete mode 100644 tests/Application/config/packages/test_cached/monolog.yaml delete mode 100644 tests/Application/config/packages/test_cached/swiftmailer.yaml delete mode 100644 tests/Application/config/packages/test_cached/sylius_channel.yaml delete mode 100644 tests/Application/config/packages/test_cached/sylius_theme.yaml delete mode 100644 tests/Application/config/packages/test_cached/twig.yaml mode change 100644 => 120000 tests/Application/config/packages/translation.yaml mode change 100644 => 120000 tests/Application/config/packages/twig.yaml mode change 100644 => 120000 tests/Application/config/packages/validator.yaml mode change 100644 => 120000 tests/Application/config/routes.yaml create mode 120000 tests/Application/config/routes/dev delete mode 100644 tests/Application/config/routes/dev/twig.yaml delete mode 100644 tests/Application/config/routes/dev/web_profiler.yaml mode change 100644 => 120000 tests/Application/config/routes/liip_imagine.yaml mode change 100644 => 120000 tests/Application/config/routes/sylius_admin.yaml delete mode 100644 tests/Application/config/routes/sylius_admin_api.yaml create mode 120000 tests/Application/config/routes/sylius_api.yaml mode change 100644 => 120000 tests/Application/config/routes/sylius_shop.yaml create mode 120000 tests/Application/config/routes/test create mode 120000 tests/Application/config/routes/test_cached create mode 120000 tests/Application/config/secrets mode change 100644 => 120000 tests/Application/config/services_test.yaml create mode 120000 tests/Application/config/services_test_cached.yaml create mode 100644 tests/Application/docker-compose.yaml create mode 100644 tests/Application/docker/elasticsearch/Dockerfile mode change 100644 => 120000 tests/Application/public/.htaccess mode change 100644 => 120000 tests/Application/public/favicon.ico rename tests/Application/{ => src}/Kernel.php (65%) create mode 120000 tests/Application/webpack.config.js diff --git a/.gitignore b/.gitignore index 1760897e..97137b9b 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,3 @@ /.php-version /.phpunit.result.cache /node_modules - diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b81..00000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/SyliusSearchPlugin.iml b/.idea/SyliusSearchPlugin.iml deleted file mode 100644 index c2fc8eb8..00000000 --- a/.idea/SyliusSearchPlugin.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index 03d9549e..00000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 2d734ccb..00000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml deleted file mode 100644 index 73416885..00000000 --- a/.idea/php.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7f..00000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.php_cs.dist b/.php-cs-fixer.dist.php similarity index 93% rename from .php_cs.dist rename to .php-cs-fixer.dist.php index b46f7ed1..d120d935 100644 --- a/.php_cs.dist +++ b/.php-cs-fixer.dist.php @@ -13,14 +13,17 @@ $finder = PhpCsFixer\Finder::create() ->in(__DIR__) - ->exclude('tests/Application/var') - ->exclude('tests/Application/src/Migrations') - ->exclude('src/generated') - ->append([ - 'tests/Application/bin/console', - ]); + ->exclude( + [ + 'tests/Application/var', + 'tests/Application/bin', + 'tests/Application/config', + ] + ) +; -return PhpCsFixer\Config::create() +$config = new PhpCsFixer\Config(); +$config ->setRiskyAllowed(true) ->setRules([ '@DoctrineAnnotation' => true, @@ -119,4 +122,7 @@ ], 'void_return' => true, ]) - ->setFinder($finder); + ->setFinder($finder) +; + +return $config; diff --git a/.php-version.dist b/.php-version.dist new file mode 100644 index 00000000..cc40bca6 --- /dev/null +++ b/.php-version.dist @@ -0,0 +1 @@ +8.0 diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..133c9f33 --- /dev/null +++ b/Makefile @@ -0,0 +1,157 @@ +.DEFAULT_GOAL := help +SHELL=/bin/bash +APP_DIR=tests/Application +SYMFONY=cd ${APP_DIR} && symfony +COMPOSER=symfony composer +CONSOLE=${SYMFONY} console +export COMPOSE_PROJECT_NAME=search +COMPOSE=docker-compose +YARN=cd ${APP_DIR} && yarn +PHPSTAN=symfony php vendor/bin/phpstan +PHPUNIT=symfony php vendor/bin/phpunit +PHPSPEC=symfony php vendor/bin/phpspec + +### +### DEVELOPMENT +### ¯¯¯¯¯¯¯¯¯¯¯ + +install: platform sylius ## Install the plugin +.PHONY: install + +up: docker.up server.start ## Up the project (start docker, start symfony server) +stop: server.stop docker.stop ## Stop the project (stop docker, stop symfony server) +down: server.stop docker.down ## Down the project (removes docker containers, stop symfony server) + +reset: docker.down ## Stop docker and remove dependencies + rm -rf ${APP_DIR}/{node_modules} ${APP_DIR}/yarn.lock + rm -rf vendor composer.lock +.PHONY: reset + +dependencies: vendor node_modules ## Setup the dependencies +.PHONY: dependencies + +.php-version: .php-version.dist + cp .php-version.dist .php-version + (cd ${APP_DIR} && ln -sf ../../.php-version) + +vendor: composer.lock ## Install the PHP dependencies using composer +ifdef GITHUB_ACTIONS + ${COMPOSER} install --prefer-dist +else + ${COMPOSER} install --prefer-source +endif + +composer.lock: composer.json +ifdef GITHUB_ACTIONS + ${COMPOSER} update --prefer-dist +else + ${COMPOSER} update --prefer-source +endif + +yarn.install: ${APP_DIR}/yarn.lock + +${APP_DIR}/yarn.lock: + ln -sf ${APP_DIR}/node_modules node_modules + ${YARN} install + ${YARN} build + +node_modules: ${APP_DIR}/node_modules ## Install the Node dependencies using yarn + +${APP_DIR}/node_modules: yarn.install + +### +### TESTS +### ¯¯¯¯¯ + +test.all: test.composer test.phpstan test.phpunit test.phpspec test.phpcs test.yaml test.schema test.twig ## Run all tests in once + +test.composer: ## Validate composer.json + ${COMPOSER} validate --strict + +test.phpstan: ## Run PHPStan + ${PHPSTAN} analyse -c phpstan.neon src/ + +test.phpunit: ## Run PHPUnit + ${PHPUNIT} + +test.phpspec: ## Run PHPSpec + ${PHPSPEC} run + +test.phpcs: ## Run PHP CS Fixer in dry-run + ${COMPOSER} run -- phpcs --dry-run -v + +test.phpcs.fix: ## Run PHP CS Fixer and fix issues if possible + ${COMPOSER} run -- phpcs -v + +test.container: ## Lint the symfony container + ${CONSOLE} lint:container + +test.yaml: ## Lint the symfony Yaml files + ${CONSOLE} lint:yaml ../../recipes ../../src/Resources/config + +test.schema: ## Validate MySQL Schema + ${CONSOLE} doctrine:schema:validate + +test.twig: ## Validate Twig templates + ${CONSOLE} lint:twig --no-debug templates/ ../../src/Resources/views/ + +### +### SYLIUS +### ¯¯¯¯¯¯ + +sylius: dependencies sylius.database sylius.fixtures sylius.assets ## Install Sylius +.PHONY: sylius + +sylius.database: ## Setup the database + ${CONSOLE} doctrine:database:drop --if-exists --force + ${CONSOLE} doctrine:database:create --if-not-exists + ${CONSOLE} doctrine:migration:migrate -n + +sylius.fixtures: ## Run the fixtures + ${CONSOLE} sylius:fixtures:load -n default + +sylius.assets: ## Install all assets with symlinks + ${CONSOLE} assets:install --symlink + ${CONSOLE} sylius:install:assets + ${CONSOLE} sylius:theme:assets:install --symlink + +### +### PLATFORM +### ¯¯¯¯¯¯¯¯ + +platform: .php-version up ## Setup the platform tools +.PHONY: platform + +docker.pull: ## Pull the docker images + cd ${APP_DIR} && ${COMPOSE} pull + +docker.up: ## Start the docker containers + cd ${APP_DIR} && ${COMPOSE} up -d +.PHONY: docker.up + +docker.stop: ## Stop the docker containers + cd ${APP_DIR} && ${COMPOSE} stop +.PHONY: docker.stop + +docker.down: ## Stop and remove the docker containers + cd ${APP_DIR} && ${COMPOSE} down +.PHONY: docker.down + +server.start: ## Run the local webserver using Symfony + ${SYMFONY} local:proxy:domain:attach ${COMPOSE_PROJECT_NAME} + ${SYMFONY} local:server:start -d + +server.stop: ## Stop the local webserver + ${SYMFONY} local:server:stop + +### +### HELP +### ¯¯¯¯ + +help: SHELL=/bin/bash +help: ## Dislay this help + @IFS=$$'\n'; for line in `grep -h -E '^[a-zA-Z_#-]+:?.*?##.*$$' $(MAKEFILE_LIST)`; do if [ "$${line:0:2}" = "##" ]; then \ + echo $$line | awk 'BEGIN {FS = "## "}; {printf "\033[33m %s\033[0m\n", $$2}'; else \ + echo $$line | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m%s\n", $$1, $$2}'; fi; \ + done; unset IFS; +.PHONY: help diff --git a/composer.json b/composer.json index bede7f0f..c97767b7 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,7 @@ "behat/mink-selenium2-driver": "^1.4", "dmore/behat-chrome-extension": "^1.3", "dmore/chrome-mink-driver": "^2.7", + "friendsofphp/php-cs-fixer": "^3.1", "friends-of-behat/mink": "^1.8", "friends-of-behat/mink-browserkit-driver": "^1.4", "friends-of-behat/mink-debug-extension": "^2.0.0", @@ -23,6 +24,8 @@ "friends-of-behat/symfony-extension": "^2.1", "friends-of-behat/variadic-extension": "^1.3", "friendsofsymfony/oauth-server-bundle": ">2.0.0-alpha.0 ^2.0@dev", + "lchrusciel/api-test-case": "^3.0 || ^5.0", + "phpmd/phpmd": "@stable", "phpspec/phpspec": "^7.0", "phpstan/extension-installer": "^1.0", "phpstan/phpstan": "0.12.85", @@ -47,10 +50,13 @@ } }, "autoload-dev": { - "classmap": ["tests/Application/Kernel.php"] + "classmap": ["tests/Application/src/Kernel.php"], + "psr-4": { + "App\\": "tests/Application/src/" + } }, "scripts": { - "phpcs": "php-cs-fixer fix --using-cache=false" + "phpcs": "php-cs-fixer fix --using-cache=no" }, "config": { "sort-packages": true diff --git a/phpmd.xml b/phpmd.xml new file mode 100644 index 00000000..39c72fac --- /dev/null +++ b/phpmd.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/phpstan.neon b/phpstan.neon index ea8e49e3..86babbad 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,17 +1,15 @@ -includes: - - vendor/phpstan/phpstan-doctrine/extension.neon - - vendor/phpstan/phpstan-webmozart-assert/extension.neon - parameters: - reportUnmatchedIgnoredErrors: false + level: max + paths: + - %rootDir%/src/ + + checkMissingIterableValueType: false excludes_analyse: # Makes PHPStan crash - 'src/DependencyInjection/Configuration.php' + - 'src/DependencyInjection/MonsieurBizSyliusSearchExtension.php' # Test dependencies - 'tests/Application/app/**.php' - 'tests/Application/src/**.php' - - ignoreErrors: - - '/Parameter #1 $configuration of method Symfony\Component\DependencyInjection\Extension\Extension::processConfiguration() expects Symfony\Component\Config\Definition\ConfigurationInterface, Symfony\Component\Config\Definition\ConfigurationInterface|null given./' diff --git a/recipe/1.0/config/packages/monsieurbiz_sylius_search_plugin.yaml b/recipes/1.0/config/packages/monsieurbiz_sylius_search_plugin.yaml similarity index 100% rename from recipe/1.0/config/packages/monsieurbiz_sylius_search_plugin.yaml rename to recipes/1.0/config/packages/monsieurbiz_sylius_search_plugin.yaml diff --git a/recipe/1.0/config/routes/monsieurbiz_sylius_search_plugin.yaml b/recipes/1.0/config/routes/monsieurbiz_sylius_search_plugin.yaml similarity index 100% rename from recipe/1.0/config/routes/monsieurbiz_sylius_search_plugin.yaml rename to recipes/1.0/config/routes/monsieurbiz_sylius_search_plugin.yaml diff --git a/recipe/1.0/manifest.json b/recipes/1.0/manifest.json similarity index 100% rename from recipe/1.0/manifest.json rename to recipes/1.0/manifest.json diff --git a/recipe/2.0-dev b/recipes/2.0-dev similarity index 100% rename from recipe/2.0-dev rename to recipes/2.0-dev diff --git a/tests/Application/.babelrc b/tests/Application/.babelrc deleted file mode 100644 index e563a62e..00000000 --- a/tests/Application/.babelrc +++ /dev/null @@ -1,15 +0,0 @@ -{ - "presets": [ - ["env", { - "targets": { - "node": "6" - }, - "useBuiltIns": true - }] - ], - "plugins": [ - ["transform-object-rest-spread", { - "useBuiltIns": true - }] - ] -} diff --git a/tests/Application/.babelrc b/tests/Application/.babelrc new file mode 120000 index 00000000..b63c4432 --- /dev/null +++ b/tests/Application/.babelrc @@ -0,0 +1 @@ +../../vendor/sylius/sylius/.babelrc \ No newline at end of file diff --git a/tests/Application/.env b/tests/Application/.env index c6f7517a..0e8d7966 100644 --- a/tests/Application/.env +++ b/tests/Application/.env @@ -2,6 +2,8 @@ # Set variables here that may be different on each deployment target of the app, e.g. development, staging, production. # https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration +COMPOSE_PROJECT_NAME=search + ###> symfony/framework-bundle ### APP_ENV=dev APP_DEBUG=1 @@ -12,17 +14,19 @@ APP_SECRET=EDITME # Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url # For a sqlite database, use: "sqlite:///%kernel.project_dir%/var/data.db" # Set "serverVersion" to your server version to avoid edge-case exceptions and extra database calls -DATABASE_URL=mysql://root@127.0.0.1/sylius_%kernel.environment%?serverVersion=5.5 +# If you use Symfony binary this URL is overridden by it +DATABASE_URL=mysql://root@127.0.0.1/sylius ###< doctrine/doctrine-bundle ### ###> symfony/swiftmailer-bundle ### # For Gmail as a transport, use: "gmail://username:password@localhost" # For a generic SMTP server, use: "smtp://localhost:25?encryption=&auth_mode=" # Delivery is disabled by default via "null://localhost" -MAILER_URL=smtp://localhost +MAILER_URL=smtp://localhost:1025 ###< symfony/swiftmailer-bundle ### -###> monsieurbiz/sylius-search-plugin ### -MONSIEURBIZ_SEARCHPLUGIN_ES_HOST=localhost -MONSIEURBIZ_SEARCHPLUGIN_ES_PORT=9200 -###< monsieurbiz/sylius-search-plugin ### +###> lexik/jwt-authentication-bundle ### +JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private-test.pem +JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public-test.pem +JWT_PASSPHRASE=ALL_THAT_IS_GOLD_DOES_NOT_GLITTER_NOT_ALL_THOSE_WHO_WANDER_ARE_LOST +###< lexik/jwt-authentication-bundle ### diff --git a/tests/Application/.env.test b/tests/Application/.env.test deleted file mode 100644 index eb87f36f..00000000 --- a/tests/Application/.env.test +++ /dev/null @@ -1,3 +0,0 @@ -APP_SECRET='ch4mb3r0f5ecr3ts' - -KERNEL_CLASS='Tests\MonsieurBiz\SyliusSearchPlugin\Application\Kernel' diff --git a/tests/Application/.env.test b/tests/Application/.env.test new file mode 120000 index 00000000..c2049e90 --- /dev/null +++ b/tests/Application/.env.test @@ -0,0 +1 @@ +../../vendor/sylius/sylius/.env.test \ No newline at end of file diff --git a/tests/Application/.eslintrc.js b/tests/Application/.eslintrc.js deleted file mode 100644 index 92c4cee3..00000000 --- a/tests/Application/.eslintrc.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - extends: 'airbnb-base', - env: { - node: true, - }, - rules: { - 'object-shorthand': ['error', 'always', { - avoidQuotes: true, - avoidExplicitReturnArrows: true, - }], - 'function-paren-newline': ['error', 'consistent'], - 'max-len': ['warn', 120, 2, { - ignoreUrls: true, - ignoreComments: false, - ignoreRegExpLiterals: true, - ignoreStrings: true, - ignoreTemplateLiterals: true, - }], - }, -}; diff --git a/tests/Application/.eslintrc.js b/tests/Application/.eslintrc.js new file mode 120000 index 00000000..1fc7cb69 --- /dev/null +++ b/tests/Application/.eslintrc.js @@ -0,0 +1 @@ +../../vendor/sylius/sylius/.eslintrc.js \ No newline at end of file diff --git a/tests/Application/.gitignore b/tests/Application/.gitignore index 8ad1225e..ba3c6967 100644 --- a/tests/Application/.gitignore +++ b/tests/Application/.gitignore @@ -1,13 +1,29 @@ /public/assets /public/css /public/js -/public/media/* -!/public/media/image/ -/public/media/image/* -!/public/media/image/.gitignore +/public/media +/public/build +!/public/media/image/.gitkeep +/app/config/parameters.yml + +/bin/symfony_requirements + +/docs/.doctrees + +/composer.lock /node_modules +/etc/build/* +!/etc/build/.gitignore + +/behat.yml +/phpspec.yml + +# Symfony CLI https://symfony.com/doc/current/setup/symfony_server.html#different-php-settings-per-project +/.php-version +/php.ini + ###> symfony/framework-bundle ### /.env.*.local /.env.local @@ -17,6 +33,17 @@ /vendor/ ###< symfony/framework-bundle ### -###> symfony/web-server-bundle ### -/.web-server-pid -###< symfony/web-server-bundle ### +###> friendsofphp/php-cs-fixer ### +/.php_cs +/.php_cs.cache +###< friendsofphp/php-cs-fixer ### + +###> phpunit/phpunit ### +/phpunit.xml +/.phpunit.result.cache +###< phpunit/phpunit ### + +###> lexik/jwt-authentication-bundle ### +/config/jwt/*.pem +!/config/jwt/*-test.pem +###< lexik/jwt-authentication-bundle ### diff --git a/tests/Application/.php-version b/tests/Application/.php-version deleted file mode 100644 index 37722ebb..00000000 --- a/tests/Application/.php-version +++ /dev/null @@ -1 +0,0 @@ -7.4 diff --git a/tests/Application/.php-version b/tests/Application/.php-version new file mode 120000 index 00000000..fffb853b --- /dev/null +++ b/tests/Application/.php-version @@ -0,0 +1 @@ +../../.php-version \ No newline at end of file diff --git a/tests/Application/bin/console b/tests/Application/bin/console index e4474639..b2c34817 100755 --- a/tests/Application/bin/console +++ b/tests/Application/bin/console @@ -1,14 +1,14 @@ #!/usr/bin/env php ['all' => true], Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], - Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle::class => ['all' => true], Sylius\Bundle\OrderBundle\SyliusOrderBundle::class => ['all' => true], Sylius\Bundle\MoneyBundle\SyliusMoneyBundle::class => ['all' => true], Sylius\Bundle\CurrencyBundle\SyliusCurrencyBundle::class => ['all' => true], @@ -51,18 +50,25 @@ Liip\ImagineBundle\LiipImagineBundle::class => ['all' => true], Payum\Bundle\PayumBundle\PayumBundle::class => ['all' => true], Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle::class => ['all' => true], - WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle::class => ['all' => true], + BabDev\PagerfantaBundle\BabDevPagerfantaBundle::class => ['all' => true], Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], Sylius\Bundle\FixturesBundle\SyliusFixturesBundle::class => ['all' => true], Sylius\Bundle\PayumBundle\SyliusPayumBundle::class => ['all' => true], Sylius\Bundle\ThemeBundle\SyliusThemeBundle::class => ['all' => true], - Symfony\Bundle\WebServerBundle\WebServerBundle::class => ['all' => true], Sylius\Bundle\AdminBundle\SyliusAdminBundle::class => ['all' => true], Sylius\Bundle\ShopBundle\SyliusShopBundle::class => ['all' => true], - FOS\OAuthServerBundle\FOSOAuthServerBundle::class => ['all' => true], - Sylius\Bundle\AdminApiBundle\SyliusAdminApiBundle::class => ['all' => true], - MonsieurBiz\SyliusSearchPlugin\MonsieurBizSyliusSearchPlugin::class => ['all' => true], - Symfony\Bundle\DebugBundle\DebugBundle::class => ['1.0' => true, 'test' => true, 'test_cached' => true], - Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['1.0' => true, 'test' => true, 'test_cached' => true], + ApiPlatform\Core\Bridge\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true], + Sylius\Bundle\ApiBundle\SyliusApiBundle::class => ['all' => true], + Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true], + Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true], + Fidry\AliceDataFixtures\Bridge\Symfony\FidryAliceDataFixturesBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true], + Nelmio\Alice\Bridge\Symfony\NelmioAliceBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true], FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true, 'test_cached' => true], + Sylius\Behat\Application\SyliusTestPlugin\SyliusTestPlugin::class => ['test' => true, 'test_cached' => true], + Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true], + SyliusLabs\DoctrineMigrationsExtraBundle\SyliusLabsDoctrineMigrationsExtraBundle::class => ['all' => true], + Sonata\Doctrine\Bridge\Symfony\SonataDoctrineSymfonyBundle::class => ['all' => true], + Sonata\Twig\Bridge\Symfony\SonataTwigSymfonyBundle::class => ['all' => true], + SyliusLabs\Polyfill\Symfony\Security\Bundle\SyliusLabsPolyfillSymfonySecurityBundle::class => ['all' => true], + MonsieurBiz\SyliusSearchPlugin\MonsieurBizSyliusSearchPlugin::class => ['all' => true], ]; diff --git a/tests/Application/config/jwt b/tests/Application/config/jwt new file mode 120000 index 00000000..efa47433 --- /dev/null +++ b/tests/Application/config/jwt @@ -0,0 +1 @@ +../../../vendor/sylius/sylius/config/jwt \ No newline at end of file diff --git a/tests/Application/config/packages/_sylius.yaml b/tests/Application/config/packages/_sylius.yaml deleted file mode 100644 index dfd932f1..00000000 --- a/tests/Application/config/packages/_sylius.yaml +++ /dev/null @@ -1,30 +0,0 @@ -imports: - - { resource: "@SyliusCoreBundle/Resources/config/app/config.yml" } - - - { resource: "@SyliusAdminBundle/Resources/config/app/config.yml" } - - { resource: "@SyliusAdminApiBundle/Resources/config/app/config.yml" } - - - { resource: "@SyliusShopBundle/Resources/config/app/config.yml" } - -parameters: - sylius_core.public_dir: '%kernel.project_dir%/public' - -sylius_shop: - product_grid: - include_all_descendants: true - -sylius_product: - resources: - product: - classes: - model: Tests\MonsieurBiz\SyliusSearchPlugin\App\Entity\Product\Product - product_option: - classes: - model: Tests\MonsieurBiz\SyliusSearchPlugin\App\Entity\Product\ProductOption - -sylius_attribute: - resources: - product: - attribute: - classes: - model: Tests\MonsieurBiz\SyliusSearchPlugin\App\Entity\Product\ProductAttribute diff --git a/tests/Application/config/packages/_sylius.yaml b/tests/Application/config/packages/_sylius.yaml new file mode 120000 index 00000000..6f7e9286 --- /dev/null +++ b/tests/Application/config/packages/_sylius.yaml @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/packages/_sylius.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/dev/_sylius.yaml b/tests/Application/config/packages/dev/_sylius.yaml new file mode 100644 index 00000000..cd01aaf7 --- /dev/null +++ b/tests/Application/config/packages/dev/_sylius.yaml @@ -0,0 +1,2 @@ +sylius_api: + enabled: true diff --git a/tests/Application/config/packages/dev/jms_serializer.yaml b/tests/Application/config/packages/dev/jms_serializer.yaml index 353e4602..2f32a9b1 100644 --- a/tests/Application/config/packages/dev/jms_serializer.yaml +++ b/tests/Application/config/packages/dev/jms_serializer.yaml @@ -1,6 +1,11 @@ jms_serializer: visitors: - json: + json_serialization: + options: + - JSON_PRETTY_PRINT + - JSON_UNESCAPED_SLASHES + - JSON_PRESERVE_ZERO_FRACTION + json_deserialization: options: - JSON_PRETTY_PRINT - JSON_UNESCAPED_SLASHES diff --git a/tests/Application/config/packages/dev/nelmio_alice.yaml b/tests/Application/config/packages/dev/nelmio_alice.yaml new file mode 100644 index 00000000..4cb9065c --- /dev/null +++ b/tests/Application/config/packages/dev/nelmio_alice.yaml @@ -0,0 +1,9 @@ +nelmio_alice: + functions_blacklist: + - 'current' + - 'shuffle' + - 'date' + - 'time' + - 'file' + - 'md5' + - 'sha1' diff --git a/tests/Application/config/packages/doctrine.yaml b/tests/Application/config/packages/doctrine.yaml index d854a0c2..1892ee89 100644 --- a/tests/Application/config/packages/doctrine.yaml +++ b/tests/Application/config/packages/doctrine.yaml @@ -8,17 +8,6 @@ parameters: doctrine: dbal: driver: 'pdo_mysql' - server_version: '5.7' - charset: UTF8 + charset: utf8mb4 url: '%env(resolve:DATABASE_URL)%' - orm: - auto_generate_proxy_classes: '%kernel.debug%' - auto_mapping: true - mappings: - Tests\MonsieurBiz\SyliusSearchPlugin\App: - is_bundle: false - type: annotation - dir: '%kernel.project_dir%/src/Entity' - prefix: 'Tests\MonsieurBiz\SyliusSearchPlugin\App\Entity' - alias: Tests\MonsieurBiz\SyliusSearchPlugin\App diff --git a/tests/Application/config/packages/doctrine_migrations.yaml b/tests/Application/config/packages/doctrine_migrations.yaml deleted file mode 100644 index c0a12026..00000000 --- a/tests/Application/config/packages/doctrine_migrations.yaml +++ /dev/null @@ -1,5 +0,0 @@ -doctrine_migrations: - dir_name: "%kernel.project_dir%/src/Migrations" - - # Namespace is arbitrary but should be different from App\Migrations as migrations classes should NOT be autoloaded - namespace: DoctrineMigrations diff --git a/tests/Application/config/packages/doctrine_migrations.yaml b/tests/Application/config/packages/doctrine_migrations.yaml new file mode 120000 index 00000000..1af39ca0 --- /dev/null +++ b/tests/Application/config/packages/doctrine_migrations.yaml @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/packages/doctrine_migrations.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/fos_rest.yaml b/tests/Application/config/packages/fos_rest.yaml deleted file mode 100644 index a72eef7c..00000000 --- a/tests/Application/config/packages/fos_rest.yaml +++ /dev/null @@ -1,11 +0,0 @@ -fos_rest: - exception: true - view: - formats: - json: true - xml: true - empty_content: 204 - format_listener: - rules: - - { path: '^/api/.*', priorities: ['json', 'xml'], fallback_format: json, prefer_extension: true } - - { path: '^/', stop: true } diff --git a/tests/Application/config/packages/fos_rest.yaml b/tests/Application/config/packages/fos_rest.yaml new file mode 120000 index 00000000..4ec0e792 --- /dev/null +++ b/tests/Application/config/packages/fos_rest.yaml @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/packages/fos_rest.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/framework.yaml b/tests/Application/config/packages/framework.yaml deleted file mode 100644 index e74ed811..00000000 --- a/tests/Application/config/packages/framework.yaml +++ /dev/null @@ -1,7 +0,0 @@ -framework: - secret: '%env(APP_SECRET)%' - form: true - csrf_protection: true - templating: { engines: ["twig"] } - session: - handler_id: ~ diff --git a/tests/Application/config/packages/framework.yaml b/tests/Application/config/packages/framework.yaml new file mode 120000 index 00000000..a74ddd00 --- /dev/null +++ b/tests/Application/config/packages/framework.yaml @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/packages/framework.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/jms_serializer.yaml b/tests/Application/config/packages/jms_serializer.yaml deleted file mode 100644 index 64dd8d10..00000000 --- a/tests/Application/config/packages/jms_serializer.yaml +++ /dev/null @@ -1,4 +0,0 @@ -jms_serializer: - visitors: - xml: - format_output: '%kernel.debug%' diff --git a/tests/Application/config/packages/jms_serializer.yaml b/tests/Application/config/packages/jms_serializer.yaml new file mode 120000 index 00000000..6d2169f2 --- /dev/null +++ b/tests/Application/config/packages/jms_serializer.yaml @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/packages/jms_serializer.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/lexik_jwt_authentication.yaml b/tests/Application/config/packages/lexik_jwt_authentication.yaml new file mode 120000 index 00000000..bc8765ac --- /dev/null +++ b/tests/Application/config/packages/lexik_jwt_authentication.yaml @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/packages/lexik_jwt_authentication.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/liip_imagine.yaml b/tests/Application/config/packages/liip_imagine.yaml deleted file mode 100644 index bb2e7ceb..00000000 --- a/tests/Application/config/packages/liip_imagine.yaml +++ /dev/null @@ -1,6 +0,0 @@ -liip_imagine: - resolvers: - default: - web_path: - web_root: "%kernel.project_dir%/public" - cache_prefix: "media/cache" diff --git a/tests/Application/config/packages/liip_imagine.yaml b/tests/Application/config/packages/liip_imagine.yaml new file mode 120000 index 00000000..dd814476 --- /dev/null +++ b/tests/Application/config/packages/liip_imagine.yaml @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/packages/liip_imagine.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/prod/doctrine.yaml b/tests/Application/config/packages/prod/doctrine.yaml deleted file mode 100644 index 2f16f0fd..00000000 --- a/tests/Application/config/packages/prod/doctrine.yaml +++ /dev/null @@ -1,31 +0,0 @@ -doctrine: - orm: - metadata_cache_driver: - type: service - id: doctrine.system_cache_provider - query_cache_driver: - type: service - id: doctrine.system_cache_provider - result_cache_driver: - type: service - id: doctrine.result_cache_provider - -services: - doctrine.result_cache_provider: - class: Symfony\Component\Cache\DoctrineProvider - public: false - arguments: - - '@doctrine.result_cache_pool' - doctrine.system_cache_provider: - class: Symfony\Component\Cache\DoctrineProvider - public: false - arguments: - - '@doctrine.system_cache_pool' - -framework: - cache: - pools: - doctrine.result_cache_pool: - adapter: cache.app - doctrine.system_cache_pool: - adapter: cache.system diff --git a/tests/Application/config/packages/prod/jms_serializer.yaml b/tests/Application/config/packages/prod/jms_serializer.yaml deleted file mode 100644 index bc97faf1..00000000 --- a/tests/Application/config/packages/prod/jms_serializer.yaml +++ /dev/null @@ -1,6 +0,0 @@ -jms_serializer: - visitors: - json: - options: - - JSON_UNESCAPED_SLASHES - - JSON_PRESERVE_ZERO_FRACTION diff --git a/tests/Application/config/packages/prod/monolog.yaml b/tests/Application/config/packages/prod/monolog.yaml deleted file mode 100644 index 64612114..00000000 --- a/tests/Application/config/packages/prod/monolog.yaml +++ /dev/null @@ -1,10 +0,0 @@ -monolog: - handlers: - main: - type: fingers_crossed - action_level: error - handler: nested - nested: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.log" - level: debug diff --git a/tests/Application/config/packages/routing.yaml b/tests/Application/config/packages/routing.yaml deleted file mode 100644 index 368bc7f4..00000000 --- a/tests/Application/config/packages/routing.yaml +++ /dev/null @@ -1,3 +0,0 @@ -framework: - router: - strict_requirements: ~ diff --git a/tests/Application/config/packages/routing.yaml b/tests/Application/config/packages/routing.yaml new file mode 120000 index 00000000..94d1a64b --- /dev/null +++ b/tests/Application/config/packages/routing.yaml @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/packages/routing.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/security.yaml b/tests/Application/config/packages/security.yaml deleted file mode 100644 index a6689955..00000000 --- a/tests/Application/config/packages/security.yaml +++ /dev/null @@ -1,103 +0,0 @@ -parameters: - sylius.security.admin_regex: "^/admin" - sylius.security.api_regex: "^/api" - sylius.security.shop_regex: "^/(?!admin|api/.*|api$|media/.*)[^/]++" - -security: - always_authenticate_before_granting: true - providers: - sylius_admin_user_provider: - id: sylius.admin_user_provider.email_or_name_based - sylius_shop_user_provider: - id: sylius.shop_user_provider.email_or_name_based - encoders: - Sylius\Component\User\Model\UserInterface: argon2i - firewalls: - admin: - switch_user: true - context: admin - pattern: "%sylius.security.admin_regex%" - provider: sylius_admin_user_provider - form_login: - provider: sylius_admin_user_provider - login_path: sylius_admin_login - check_path: sylius_admin_login_check - failure_path: sylius_admin_login - default_target_path: sylius_admin_dashboard - use_forward: false - use_referer: true - csrf_token_generator: security.csrf.token_manager - csrf_parameter: _csrf_admin_security_token - csrf_token_id: admin_authenticate - remember_me: - secret: "%env(APP_SECRET)%" - path: /admin - name: APP_ADMIN_REMEMBER_ME - lifetime: 31536000 - remember_me_parameter: _remember_me - logout: - path: sylius_admin_logout - target: sylius_admin_login - anonymous: true - - oauth_token: - pattern: "%sylius.security.api_regex%/oauth/v2/token" - security: false - - api: - pattern: "%sylius.security.api_regex%/.*" - provider: sylius_admin_user_provider - fos_oauth: true - stateless: true - anonymous: true - - shop: - switch_user: { role: ROLE_ALLOWED_TO_SWITCH } - context: shop - pattern: "%sylius.security.shop_regex%" - provider: sylius_shop_user_provider - form_login: - success_handler: sylius.authentication.success_handler - failure_handler: sylius.authentication.failure_handler - provider: sylius_shop_user_provider - login_path: sylius_shop_login - check_path: sylius_shop_login_check - failure_path: sylius_shop_login - default_target_path: sylius_shop_homepage - use_forward: false - use_referer: true - csrf_token_generator: security.csrf.token_manager - csrf_parameter: _csrf_shop_security_token - csrf_token_id: shop_authenticate - remember_me: - secret: "%env(APP_SECRET)%" - name: APP_SHOP_REMEMBER_ME - lifetime: 31536000 - remember_me_parameter: _remember_me - logout: - path: sylius_shop_logout - target: sylius_shop_login - invalidate_session: false - success_handler: sylius.handler.shop_user_logout - anonymous: true - - dev: - pattern: ^/(_(profiler|wdt)|css|images|js)/ - security: false - - access_control: - - { path: "%sylius.security.admin_regex%/_partial", role: IS_AUTHENTICATED_ANONYMOUSLY, ips: [127.0.0.1, ::1] } - - { path: "%sylius.security.admin_regex%/_partial", role: ROLE_NO_ACCESS } - - { path: "%sylius.security.shop_regex%/_partial", role: IS_AUTHENTICATED_ANONYMOUSLY, ips: [127.0.0.1, ::1] } - - { path: "%sylius.security.shop_regex%/_partial", role: ROLE_NO_ACCESS } - - - { path: "%sylius.security.admin_regex%/login", role: IS_AUTHENTICATED_ANONYMOUSLY } - - { path: "%sylius.security.api_regex%/login", role: IS_AUTHENTICATED_ANONYMOUSLY } - - { path: "%sylius.security.shop_regex%/login", role: IS_AUTHENTICATED_ANONYMOUSLY } - - - { path: "%sylius.security.shop_regex%/register", role: IS_AUTHENTICATED_ANONYMOUSLY } - - { path: "%sylius.security.shop_regex%/verify", role: IS_AUTHENTICATED_ANONYMOUSLY } - - - { path: "%sylius.security.admin_regex%", role: ROLE_ADMINISTRATION_ACCESS } - - { path: "%sylius.security.api_regex%/.*", role: ROLE_API_ACCESS } - - { path: "%sylius.security.shop_regex%/account", role: ROLE_USER } diff --git a/tests/Application/config/packages/security.yaml b/tests/Application/config/packages/security.yaml new file mode 120000 index 00000000..c9ef2491 --- /dev/null +++ b/tests/Application/config/packages/security.yaml @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/packages/security.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/security_checker.yaml b/tests/Application/config/packages/security_checker.yaml deleted file mode 100644 index 0f9cf00f..00000000 --- a/tests/Application/config/packages/security_checker.yaml +++ /dev/null @@ -1,9 +0,0 @@ -services: - SensioLabs\Security\SecurityChecker: - public: false - - SensioLabs\Security\Command\SecurityCheckerCommand: - arguments: ['@SensioLabs\Security\SecurityChecker'] - public: false - tags: - - { name: console.command, command: 'security:check' } diff --git a/tests/Application/config/packages/staging/monolog.yaml b/tests/Application/config/packages/staging/monolog.yaml deleted file mode 100644 index 64612114..00000000 --- a/tests/Application/config/packages/staging/monolog.yaml +++ /dev/null @@ -1,10 +0,0 @@ -monolog: - handlers: - main: - type: fingers_crossed - action_level: error - handler: nested - nested: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.log" - level: debug diff --git a/tests/Application/config/packages/staging/swiftmailer.yaml b/tests/Application/config/packages/staging/swiftmailer.yaml deleted file mode 100644 index f4380780..00000000 --- a/tests/Application/config/packages/staging/swiftmailer.yaml +++ /dev/null @@ -1,2 +0,0 @@ -swiftmailer: - disable_delivery: true diff --git a/tests/Application/config/packages/stof_doctrine_extensions.yaml b/tests/Application/config/packages/stof_doctrine_extensions.yaml deleted file mode 100644 index 7770f74e..00000000 --- a/tests/Application/config/packages/stof_doctrine_extensions.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Read the documentation: https://symfony.com/doc/current/bundles/StofDoctrineExtensionsBundle/index.html -# See the official DoctrineExtensions documentation for more details: https://github.com/Atlantic18/DoctrineExtensions/tree/master/doc/ -stof_doctrine_extensions: - default_locale: '%locale%' diff --git a/tests/Application/config/packages/stof_doctrine_extensions.yaml b/tests/Application/config/packages/stof_doctrine_extensions.yaml new file mode 120000 index 00000000..88a434a3 --- /dev/null +++ b/tests/Application/config/packages/stof_doctrine_extensions.yaml @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/packages/stof_doctrine_extensions.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/swiftmailer.yaml b/tests/Application/config/packages/swiftmailer.yaml deleted file mode 100644 index 3bab0d32..00000000 --- a/tests/Application/config/packages/swiftmailer.yaml +++ /dev/null @@ -1,2 +0,0 @@ -swiftmailer: - url: '%env(MAILER_URL)%' diff --git a/tests/Application/config/packages/swiftmailer.yaml b/tests/Application/config/packages/swiftmailer.yaml new file mode 120000 index 00000000..627c6afc --- /dev/null +++ b/tests/Application/config/packages/swiftmailer.yaml @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/packages/swiftmailer.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/test b/tests/Application/config/packages/test new file mode 120000 index 00000000..f7645146 --- /dev/null +++ b/tests/Application/config/packages/test @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/packages/test \ No newline at end of file diff --git a/tests/Application/config/packages/test/framework.yaml b/tests/Application/config/packages/test/framework.yaml deleted file mode 100644 index 76d7e5e1..00000000 --- a/tests/Application/config/packages/test/framework.yaml +++ /dev/null @@ -1,4 +0,0 @@ -framework: - test: ~ - session: - storage_id: session.storage.mock_file diff --git a/tests/Application/config/packages/test/monolog.yaml b/tests/Application/config/packages/test/monolog.yaml deleted file mode 100644 index 7e2b9e3a..00000000 --- a/tests/Application/config/packages/test/monolog.yaml +++ /dev/null @@ -1,6 +0,0 @@ -monolog: - handlers: - main: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.log" - level: error diff --git a/tests/Application/config/packages/test/swiftmailer.yaml b/tests/Application/config/packages/test/swiftmailer.yaml deleted file mode 100644 index c438f4b2..00000000 --- a/tests/Application/config/packages/test/swiftmailer.yaml +++ /dev/null @@ -1,6 +0,0 @@ -swiftmailer: - disable_delivery: true - logging: true - spool: - type: file - path: "%kernel.cache_dir%/spool" diff --git a/tests/Application/config/packages/test/sylius_theme.yaml b/tests/Application/config/packages/test/sylius_theme.yaml deleted file mode 100644 index 4d34199f..00000000 --- a/tests/Application/config/packages/test/sylius_theme.yaml +++ /dev/null @@ -1,3 +0,0 @@ -sylius_theme: - sources: - test: ~ diff --git a/tests/Application/config/packages/test/web_profiler.yaml b/tests/Application/config/packages/test/web_profiler.yaml deleted file mode 100644 index 03752de2..00000000 --- a/tests/Application/config/packages/test/web_profiler.yaml +++ /dev/null @@ -1,6 +0,0 @@ -web_profiler: - toolbar: false - intercept_redirects: false - -framework: - profiler: { collect: false } diff --git a/tests/Application/config/packages/test_cached b/tests/Application/config/packages/test_cached new file mode 120000 index 00000000..66ffd829 --- /dev/null +++ b/tests/Application/config/packages/test_cached @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/packages/test_cached \ No newline at end of file diff --git a/tests/Application/config/packages/test_cached/doctrine.yaml b/tests/Application/config/packages/test_cached/doctrine.yaml deleted file mode 100644 index 49528606..00000000 --- a/tests/Application/config/packages/test_cached/doctrine.yaml +++ /dev/null @@ -1,16 +0,0 @@ -doctrine: - orm: - entity_managers: - default: - result_cache_driver: - type: memcached - host: localhost - port: 11211 - query_cache_driver: - type: memcached - host: localhost - port: 11211 - metadata_cache_driver: - type: memcached - host: localhost - port: 11211 diff --git a/tests/Application/config/packages/test_cached/fos_rest.yaml b/tests/Application/config/packages/test_cached/fos_rest.yaml deleted file mode 100644 index 2b4189da..00000000 --- a/tests/Application/config/packages/test_cached/fos_rest.yaml +++ /dev/null @@ -1,3 +0,0 @@ -fos_rest: - exception: - debug: true diff --git a/tests/Application/config/packages/test_cached/framework.yaml b/tests/Application/config/packages/test_cached/framework.yaml deleted file mode 100644 index 76d7e5e1..00000000 --- a/tests/Application/config/packages/test_cached/framework.yaml +++ /dev/null @@ -1,4 +0,0 @@ -framework: - test: ~ - session: - storage_id: session.storage.mock_file diff --git a/tests/Application/config/packages/test_cached/monolog.yaml b/tests/Application/config/packages/test_cached/monolog.yaml deleted file mode 100644 index 7e2b9e3a..00000000 --- a/tests/Application/config/packages/test_cached/monolog.yaml +++ /dev/null @@ -1,6 +0,0 @@ -monolog: - handlers: - main: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.log" - level: error diff --git a/tests/Application/config/packages/test_cached/swiftmailer.yaml b/tests/Application/config/packages/test_cached/swiftmailer.yaml deleted file mode 100644 index c438f4b2..00000000 --- a/tests/Application/config/packages/test_cached/swiftmailer.yaml +++ /dev/null @@ -1,6 +0,0 @@ -swiftmailer: - disable_delivery: true - logging: true - spool: - type: file - path: "%kernel.cache_dir%/spool" diff --git a/tests/Application/config/packages/test_cached/sylius_channel.yaml b/tests/Application/config/packages/test_cached/sylius_channel.yaml deleted file mode 100644 index bab83ef2..00000000 --- a/tests/Application/config/packages/test_cached/sylius_channel.yaml +++ /dev/null @@ -1,2 +0,0 @@ -sylius_channel: - debug: true diff --git a/tests/Application/config/packages/test_cached/sylius_theme.yaml b/tests/Application/config/packages/test_cached/sylius_theme.yaml deleted file mode 100644 index 4d34199f..00000000 --- a/tests/Application/config/packages/test_cached/sylius_theme.yaml +++ /dev/null @@ -1,3 +0,0 @@ -sylius_theme: - sources: - test: ~ diff --git a/tests/Application/config/packages/test_cached/twig.yaml b/tests/Application/config/packages/test_cached/twig.yaml deleted file mode 100644 index 8c6e0b40..00000000 --- a/tests/Application/config/packages/test_cached/twig.yaml +++ /dev/null @@ -1,2 +0,0 @@ -twig: - strict_variables: true diff --git a/tests/Application/config/packages/translation.yaml b/tests/Application/config/packages/translation.yaml deleted file mode 100644 index 1f4f9664..00000000 --- a/tests/Application/config/packages/translation.yaml +++ /dev/null @@ -1,8 +0,0 @@ -framework: - default_locale: '%locale%' - translator: - paths: - - '%kernel.project_dir%/translations' - fallbacks: - - '%locale%' - - 'en' diff --git a/tests/Application/config/packages/translation.yaml b/tests/Application/config/packages/translation.yaml new file mode 120000 index 00000000..759bccbd --- /dev/null +++ b/tests/Application/config/packages/translation.yaml @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/packages/translation.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/twig.yaml b/tests/Application/config/packages/twig.yaml deleted file mode 100644 index 8545473d..00000000 --- a/tests/Application/config/packages/twig.yaml +++ /dev/null @@ -1,12 +0,0 @@ -twig: - paths: ['%kernel.project_dir%/templates'] - debug: '%kernel.debug%' - strict_variables: '%kernel.debug%' - -services: - _defaults: - public: false - autowire: true - autoconfigure: true - - Twig\Extra\Intl\IntlExtension: ~ diff --git a/tests/Application/config/packages/twig.yaml b/tests/Application/config/packages/twig.yaml new file mode 120000 index 00000000..305c69ef --- /dev/null +++ b/tests/Application/config/packages/twig.yaml @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/packages/twig.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/validator.yaml b/tests/Application/config/packages/validator.yaml deleted file mode 100644 index 61807db6..00000000 --- a/tests/Application/config/packages/validator.yaml +++ /dev/null @@ -1,3 +0,0 @@ -framework: - validation: - enable_annotations: true diff --git a/tests/Application/config/packages/validator.yaml b/tests/Application/config/packages/validator.yaml new file mode 120000 index 00000000..a381919b --- /dev/null +++ b/tests/Application/config/packages/validator.yaml @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/packages/validator.yaml \ No newline at end of file diff --git a/tests/Application/config/routes.yaml b/tests/Application/config/routes.yaml deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/Application/config/routes.yaml b/tests/Application/config/routes.yaml new file mode 120000 index 00000000..5ce0cf9f --- /dev/null +++ b/tests/Application/config/routes.yaml @@ -0,0 +1 @@ +../../../vendor/sylius/sylius/config/routes.yaml \ No newline at end of file diff --git a/tests/Application/config/routes/dev b/tests/Application/config/routes/dev new file mode 120000 index 00000000..260f2a1d --- /dev/null +++ b/tests/Application/config/routes/dev @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/routes/dev \ No newline at end of file diff --git a/tests/Application/config/routes/dev/twig.yaml b/tests/Application/config/routes/dev/twig.yaml deleted file mode 100644 index f4ee8396..00000000 --- a/tests/Application/config/routes/dev/twig.yaml +++ /dev/null @@ -1,3 +0,0 @@ -_errors: - resource: '@TwigBundle/Resources/config/routing/errors.xml' - prefix: /_error diff --git a/tests/Application/config/routes/dev/web_profiler.yaml b/tests/Application/config/routes/dev/web_profiler.yaml deleted file mode 100644 index 3e79dc21..00000000 --- a/tests/Application/config/routes/dev/web_profiler.yaml +++ /dev/null @@ -1,7 +0,0 @@ -_wdt: - resource: "@WebProfilerBundle/Resources/config/routing/wdt.xml" - prefix: /_wdt - -_profiler: - resource: "@WebProfilerBundle/Resources/config/routing/profiler.xml" - prefix: /_profiler diff --git a/tests/Application/config/routes/liip_imagine.yaml b/tests/Application/config/routes/liip_imagine.yaml deleted file mode 100644 index 201cbd5d..00000000 --- a/tests/Application/config/routes/liip_imagine.yaml +++ /dev/null @@ -1,2 +0,0 @@ -_liip_imagine: - resource: "@LiipImagineBundle/Resources/config/routing.yaml" diff --git a/tests/Application/config/routes/liip_imagine.yaml b/tests/Application/config/routes/liip_imagine.yaml new file mode 120000 index 00000000..a0cd29e7 --- /dev/null +++ b/tests/Application/config/routes/liip_imagine.yaml @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/routes/liip_imagine.yaml \ No newline at end of file diff --git a/tests/Application/config/routes/sylius_admin.yaml b/tests/Application/config/routes/sylius_admin.yaml deleted file mode 100644 index 1ba48d6c..00000000 --- a/tests/Application/config/routes/sylius_admin.yaml +++ /dev/null @@ -1,3 +0,0 @@ -sylius_admin: - resource: "@SyliusAdminBundle/Resources/config/routing.yml" - prefix: /admin diff --git a/tests/Application/config/routes/sylius_admin.yaml b/tests/Application/config/routes/sylius_admin.yaml new file mode 120000 index 00000000..fb7ebdfa --- /dev/null +++ b/tests/Application/config/routes/sylius_admin.yaml @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/routes/sylius_admin.yaml \ No newline at end of file diff --git a/tests/Application/config/routes/sylius_admin_api.yaml b/tests/Application/config/routes/sylius_admin_api.yaml deleted file mode 100644 index 80aed457..00000000 --- a/tests/Application/config/routes/sylius_admin_api.yaml +++ /dev/null @@ -1,3 +0,0 @@ -sylius_admin_api: - resource: "@SyliusAdminApiBundle/Resources/config/routing.yml" - prefix: /api diff --git a/tests/Application/config/routes/sylius_api.yaml b/tests/Application/config/routes/sylius_api.yaml new file mode 120000 index 00000000..8e6246c3 --- /dev/null +++ b/tests/Application/config/routes/sylius_api.yaml @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/routes/sylius_api.yaml \ No newline at end of file diff --git a/tests/Application/config/routes/sylius_shop.yaml b/tests/Application/config/routes/sylius_shop.yaml deleted file mode 100644 index 8818568b..00000000 --- a/tests/Application/config/routes/sylius_shop.yaml +++ /dev/null @@ -1,14 +0,0 @@ -sylius_shop: - resource: "@SyliusShopBundle/Resources/config/routing.yml" - prefix: /{_locale} - requirements: - _locale: ^[a-z]{2}(?:_[A-Z]{2})?$ - -sylius_shop_payum: - resource: "@SyliusShopBundle/Resources/config/routing/payum.yml" - -sylius_shop_default_locale: - path: / - methods: [GET] - defaults: - _controller: sylius.controller.shop.locale_switch:switchAction diff --git a/tests/Application/config/routes/sylius_shop.yaml b/tests/Application/config/routes/sylius_shop.yaml new file mode 120000 index 00000000..6ca08a47 --- /dev/null +++ b/tests/Application/config/routes/sylius_shop.yaml @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/routes/sylius_shop.yaml \ No newline at end of file diff --git a/tests/Application/config/routes/test b/tests/Application/config/routes/test new file mode 120000 index 00000000..b12612e7 --- /dev/null +++ b/tests/Application/config/routes/test @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/routes/test \ No newline at end of file diff --git a/tests/Application/config/routes/test_cached b/tests/Application/config/routes/test_cached new file mode 120000 index 00000000..e0707663 --- /dev/null +++ b/tests/Application/config/routes/test_cached @@ -0,0 +1 @@ +../../../../vendor/sylius/sylius/config/routes/test_cached \ No newline at end of file diff --git a/tests/Application/config/secrets b/tests/Application/config/secrets new file mode 120000 index 00000000..bd4b6402 --- /dev/null +++ b/tests/Application/config/secrets @@ -0,0 +1 @@ +../../../vendor/sylius/sylius/config/secrets \ No newline at end of file diff --git a/tests/Application/config/services.yaml b/tests/Application/config/services.yaml index d15eba83..615506eb 100644 --- a/tests/Application/config/services.yaml +++ b/tests/Application/config/services.yaml @@ -2,40 +2,3 @@ # https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration parameters: locale: en_US - -services: - # Client configuration. - JoliCode\Elastically\Client: - arguments: - $config: - host: '%env(MONSIEURBIZ_SEARCHPLUGIN_ES_HOST)%' - port: '%env(MONSIEURBIZ_SEARCHPLUGIN_ES_PORT)%' - elastically_mappings_directory: '%kernel.project_dir%/../../src/Resources/config/elasticsearch/mappings' - elastically_index_class_mapping: - documents-fr: \MonsieurBiz\SyliusSearchPlugin\Model\Document\Result - documents-en: \MonsieurBiz\SyliusSearchPlugin\Model\Document\Result - documents-en_us: \MonsieurBiz\SyliusSearchPlugin\Model\Document\Result - elastically_bulk_size: 100 - -sylius_fixtures: - suites: - default: - fixtures: - monsieurbiz_sylius_search_filterable: - options: - custom: - cap_collection: - attribute: 'cap_collection' - filterable: false - - dress_collection: - attribute: 'dress_collection' - filterable: false - - dress_height: - option: 'dress_height' - filterable: false - - dress_size: - option: 'dress_size' - filterable: true diff --git a/tests/Application/config/services_test.yaml b/tests/Application/config/services_test.yaml deleted file mode 100644 index b0c2d6a9..00000000 --- a/tests/Application/config/services_test.yaml +++ /dev/null @@ -1,3 +0,0 @@ -imports: -# - { resource: "../../Behat/Resources/services.xml" } -# - { resource: "../../../vendor/sylius/sylius/src/Sylius/Behat/Resources/config/services.xml" } diff --git a/tests/Application/config/services_test.yaml b/tests/Application/config/services_test.yaml new file mode 120000 index 00000000..177cc6cb --- /dev/null +++ b/tests/Application/config/services_test.yaml @@ -0,0 +1 @@ +../../../vendor/sylius/sylius/config/services_test.yaml \ No newline at end of file diff --git a/tests/Application/config/services_test_cached.yaml b/tests/Application/config/services_test_cached.yaml new file mode 120000 index 00000000..486f379f --- /dev/null +++ b/tests/Application/config/services_test_cached.yaml @@ -0,0 +1 @@ +../../../vendor/sylius/sylius/config/services_test_cached.yaml \ No newline at end of file diff --git a/tests/Application/docker-compose.yaml b/tests/Application/docker-compose.yaml new file mode 100644 index 00000000..b5302995 --- /dev/null +++ b/tests/Application/docker-compose.yaml @@ -0,0 +1,51 @@ +version: '3.8' +services: + database: + image: percona:5.7 + ports: + - 3306 + environment: + MYSQL_ALLOW_EMPTY_PASSWORD: 1 + MYSQL_DATABASE: sylius + volumes: + - database:/var/lib/mysql + mailer: + image: monsieurbiz/mailcatcher + ports: + - 1025 + - 1080 + elasticsearch: + build: + context: ./docker/elasticsearch/ + volumes: + - esdata:/usr/share/elasticsearch/data:rw + environment: + - cluster.name=docker-cluster + - bootstrap.memory_lock=true + - discovery.type=single-node + - "ES_JAVA_OPTS=-Xms512m -Xmx512m" + - "xpack.security.enabled=false" + ulimits: + memlock: + soft: -1 + hard: -1 + ports: + - "9200:9200" + - "9300:9300" + kibana: + image: docker.elastic.co/kibana/kibana:7.15.0 + environment: + - "ELASTICSEARCH_HOSTS=http://elasticsearch:9200" + - "XPACK_GRAPH_ENABLED=false" + - "XPACK_ML_ENABLED=false" + - "XPACK_REPORTING_ENABLED=false" + - "XPACK_SECURITY_ENABLED=false" + - "XPACK_WATCHER_ENABLED=false" + links: + - elasticsearch + ports: + - "5601:5601" + +volumes: + database: {} + esdata: {} diff --git a/tests/Application/docker/elasticsearch/Dockerfile b/tests/Application/docker/elasticsearch/Dockerfile new file mode 100644 index 00000000..c5764255 --- /dev/null +++ b/tests/Application/docker/elasticsearch/Dockerfile @@ -0,0 +1,5 @@ +FROM docker.elastic.co/elasticsearch/elasticsearch:7.15.0 + +# Install ES plugins +RUN bin/elasticsearch-plugin install analysis-phonetic +RUN bin/elasticsearch-plugin install analysis-icu diff --git a/tests/Application/gulpfile.babel.js b/tests/Application/gulpfile.babel.js index 5920316f..279cf3d1 100644 --- a/tests/Application/gulpfile.babel.js +++ b/tests/Application/gulpfile.babel.js @@ -52,6 +52,9 @@ watchShop.description = 'Watch shop asset sources and rebuild on changes.'; export const build = gulp.parallel(buildAdmin, buildShop); build.description = 'Build assets.'; +export const watch = gulp.parallel(watchAdmin, watchShop); +watch.description = 'Watch asset sources and rebuild on changes.'; + gulp.task('admin', buildAdmin); gulp.task('admin-watch', watchAdmin); gulp.task('shop', buildShop); diff --git a/tests/Application/public/.htaccess b/tests/Application/public/.htaccess deleted file mode 100644 index 99ed00df..00000000 --- a/tests/Application/public/.htaccess +++ /dev/null @@ -1,25 +0,0 @@ -DirectoryIndex app.php - - - RewriteEngine On - - RewriteCond %{HTTP:Authorization} ^(.*) - RewriteRule .* - [e=HTTP_AUTHORIZATION:%1] - - RewriteCond %{REQUEST_URI}::$1 ^(/.+)/(.*)::\2$ - RewriteRule ^(.*) - [E=BASE:%1] - - RewriteCond %{ENV:REDIRECT_STATUS} ^$ - RewriteRule ^index\.php(/(.*)|$) %{ENV:BASE}/$2 [R=301,L] - - RewriteCond %{REQUEST_FILENAME} -f - RewriteRule .? - [L] - - RewriteRule .? %{ENV:BASE}/index.php [L] - - - - - RedirectMatch 302 ^/$ /index.php/ - - diff --git a/tests/Application/public/.htaccess b/tests/Application/public/.htaccess new file mode 120000 index 00000000..59de487f --- /dev/null +++ b/tests/Application/public/.htaccess @@ -0,0 +1 @@ +../../../vendor/sylius/sylius/public/.htaccess \ No newline at end of file diff --git a/tests/Application/public/favicon.ico b/tests/Application/public/favicon.ico deleted file mode 100644 index 592f7a8e45d5ef6fe2a3c0426b5276e463480ba5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32038 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 diff --git a/tests/Application/public/favicon.ico b/tests/Application/public/favicon.ico new file mode 120000 index 00000000..85df8df0 --- /dev/null +++ b/tests/Application/public/favicon.ico @@ -0,0 +1 @@ +../../../vendor/sylius/sylius/public/favicon.ico \ No newline at end of file diff --git a/tests/Application/public/index.php b/tests/Application/public/index.php index 9b21af88..d73dcb2e 100644 --- a/tests/Application/public/index.php +++ b/tests/Application/public/index.php @@ -11,9 +11,9 @@ declare(strict_types=1); -use Symfony\Component\Debug\Debug; +use App\Kernel; +use Symfony\Component\ErrorHandler\Debug; use Symfony\Component\HttpFoundation\Request; -use Tests\MonsieurBiz\SyliusSearchPlugin\Application\Kernel; require dirname(__DIR__) . '/config/bootstrap.php'; diff --git a/tests/Application/Kernel.php b/tests/Application/src/Kernel.php similarity index 65% rename from tests/Application/Kernel.php rename to tests/Application/src/Kernel.php index 132b0ad4..038732ca 100644 --- a/tests/Application/Kernel.php +++ b/tests/Application/src/Kernel.php @@ -11,27 +11,15 @@ declare(strict_types=1); -namespace Tests\MonsieurBiz\SyliusSearchPlugin\Application; +namespace App; use PSS\SymfonyMockerContainer\DependencyInjection\MockerContainer; use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; -use Symfony\Component\Config\Loader\DelegatingLoader; use Symfony\Component\Config\Loader\LoaderInterface; -use Symfony\Component\Config\Loader\LoaderResolver; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\DependencyInjection\Loader\ClosureLoader; -use Symfony\Component\DependencyInjection\Loader\DirectoryLoader; -use Symfony\Component\DependencyInjection\Loader\GlobFileLoader; -use Symfony\Component\DependencyInjection\Loader\IniFileLoader; -use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; -use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; -use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; -use Symfony\Component\HttpKernel\Config\FileLocator; use Symfony\Component\HttpKernel\Kernel as BaseKernel; use Symfony\Component\Routing\RouteCollectionBuilder; -use Webmozart\Assert\Assert; final class Kernel extends BaseKernel { @@ -51,6 +39,7 @@ public function getLogDir(): string public function registerBundles(): iterable { + /** @psalm-suppress UnresolvableInclude */ $contents = require $this->getProjectDir() . '/config/bundles.php'; foreach ($contents as $class => $envs) { if (isset($envs['all']) || isset($envs[$this->environment])) { @@ -89,25 +78,6 @@ protected function getContainerBaseClass(): string return parent::getContainerBaseClass(); } - protected function getContainerLoader(ContainerInterface $container): LoaderInterface - { - /** @var ContainerBuilder $container */ - Assert::isInstanceOf($container, ContainerBuilder::class); - - $locator = new FileLocator($this, $this->getRootDir() . '/Resources'); - $resolver = new LoaderResolver([ - new XmlFileLoader($container, $locator), - new YamlFileLoader($container, $locator), - new IniFileLoader($container, $locator), - new PhpFileLoader($container, $locator), - new GlobFileLoader($container, $locator), - new DirectoryLoader($container, $locator), - new ClosureLoader($container), - ]); - - return new DelegatingLoader($resolver); - } - private function isTestEnvironment(): bool { return 0 === strpos($this->getEnvironment(), 'test'); diff --git a/tests/Application/webpack.config.js b/tests/Application/webpack.config.js new file mode 120000 index 00000000..5603856b --- /dev/null +++ b/tests/Application/webpack.config.js @@ -0,0 +1 @@ +../../vendor/sylius/sylius/webpack.config.js \ No newline at end of file From 1e266453fee153bcb96e005bcf7dd5b3d11f0a4f Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Fri, 1 Oct 2021 10:27:45 +0200 Subject: [PATCH 003/142] fix: DI services file --- .../MonsieurBizSyliusSearchExtension.php | 5 +++++ src/Resources/config/services.yaml | 9 --------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php b/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php index a7aec66d..93ad36fc 100644 --- a/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php +++ b/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php @@ -13,8 +13,10 @@ namespace MonsieurBiz\SyliusSearchPlugin\DependencyInjection; +use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\Extension; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; final class MonsieurBizSyliusSearchExtension extends Extension { @@ -26,5 +28,8 @@ public function load(array $configs, ContainerBuilder $container): void foreach ($config as $name => $value) { $container->setParameter(self::EXTENSION_CONFIG_NAME . '.' . $name, $value); } + + $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); + $loader->load('services.yaml'); } } diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index 0466d0a9..c6f36199 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -8,12 +8,3 @@ services: MonsieurBiz\SyliusSearchPlugin\: resource: '../../*' exclude: '../../{Entity,Migrations,Tests,Kernel.php}' - - MonsieurBiz\SyliusSearchPlugin\Controller\: - resource: '../../Controller' - tags: ['controller.service_arguments'] - - MonsieurBiz\SyliusSearchPlugin\Form\Extension\: - resource: '../../Form/Extension' - tags: - - { name: form.type_extension } From 7dcfc85d35dc9f2d3048b5714ea0760d327a2b7d Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Fri, 8 Oct 2021 17:05:52 +0200 Subject: [PATCH 004/142] wip - add indexable document --- composer.json | 9 +- generated/Model/Channel.php | 34 ++ generated/Model/Image.php | 34 ++ generated/Model/Product.php | 304 ++++++++++++++++++ generated/Model/ProductTaxon.php | 61 ++++ generated/Model/Taxon.php | 115 +++++++ generated/Normalizer/ChannelNormalizer.php | 52 +++ generated/Normalizer/ImageNormalizer.php | 67 ++++ generated/Normalizer/JaneObjectNormalizer.php | 50 +++ generated/Normalizer/ProductNormalizer.php | 166 ++++++++++ .../Normalizer/ProductTaxonNormalizer.php | 73 +++++ generated/Normalizer/TaxonNormalizer.php | 70 ++++ generated/Runtime/Normalizer/CheckArray.php | 13 + .../Normalizer/ReferenceNormalizer.php | 25 ++ src/AutoMapper/ProductMapperConfiguration.php | 47 +++ src/Command/PopulateCommand.php | 45 +++ .../DocumentableRegistryPass.php | 19 ++ .../MonsieurBizSyliusSearchExtension.php | 2 +- src/Event/ProductMapperConfigurationEvent.php | 22 ++ src/Index/Indexer.php | 109 +++++++ src/Mapping/YamlWithLocaleProvider.php | 68 ++++ .../Documentable/DocumentableInterface.php | 15 + .../DocumentableMappingProviderTrait.php | 20 ++ src/Model/TestClass.php | 34 ++ src/MonsieurBizSyliusSearchPlugin.php | 8 + .../config/elasticsearch/analyzers.yaml | 14 + .../config/elasticsearch/analyzers_en.yaml | 7 + .../config/elasticsearch/analyzers_fr.yaml | 7 + .../config/elasticsearch/product_mapping.yaml | 46 +++ .../elasticsearch/test_class_mapping.yaml | 5 + .../config/jane/jane-configuration.php | 8 + src/Resources/config/jane/json-schema.json | 108 +++++++ src/Resources/config/services.yaml | 59 ++++ 33 files changed, 1712 insertions(+), 4 deletions(-) create mode 100644 generated/Model/Channel.php create mode 100644 generated/Model/Image.php create mode 100644 generated/Model/Product.php create mode 100644 generated/Model/ProductTaxon.php create mode 100644 generated/Model/Taxon.php create mode 100644 generated/Normalizer/ChannelNormalizer.php create mode 100644 generated/Normalizer/ImageNormalizer.php create mode 100644 generated/Normalizer/JaneObjectNormalizer.php create mode 100644 generated/Normalizer/ProductNormalizer.php create mode 100644 generated/Normalizer/ProductTaxonNormalizer.php create mode 100644 generated/Normalizer/TaxonNormalizer.php create mode 100644 generated/Runtime/Normalizer/CheckArray.php create mode 100644 generated/Runtime/Normalizer/ReferenceNormalizer.php create mode 100644 src/AutoMapper/ProductMapperConfiguration.php create mode 100644 src/Command/PopulateCommand.php create mode 100644 src/DependencyInjection/DocumentableRegistryPass.php create mode 100644 src/Event/ProductMapperConfigurationEvent.php create mode 100644 src/Index/Indexer.php create mode 100644 src/Mapping/YamlWithLocaleProvider.php create mode 100644 src/Model/Documentable/DocumentableInterface.php create mode 100644 src/Model/Documentable/DocumentableMappingProviderTrait.php create mode 100644 src/Model/TestClass.php create mode 100644 src/Resources/config/elasticsearch/analyzers.yaml create mode 100644 src/Resources/config/elasticsearch/analyzers_en.yaml create mode 100644 src/Resources/config/elasticsearch/analyzers_fr.yaml create mode 100644 src/Resources/config/elasticsearch/product_mapping.yaml create mode 100644 src/Resources/config/elasticsearch/test_class_mapping.yaml create mode 100644 src/Resources/config/jane/jane-configuration.php create mode 100644 src/Resources/config/jane/json-schema.json diff --git a/composer.json b/composer.json index c97767b7..fb5029b4 100644 --- a/composer.json +++ b/composer.json @@ -6,8 +6,9 @@ "license": "MIT", "require": { "php": "^7.4 || ^8.0", - "sylius/sylius": "^1.9", - "jolicode/elastically": "^1.0.0" + "jane-php/automapper-bundle": "^7.1", + "jolicode/elastically": "dev-master", + "sylius/sylius": "^1.9" }, "require-dev": { "behat/behat": "^3.6.1", @@ -40,12 +41,14 @@ "symfony/dotenv": "^4.4 || ^5.2", "symfony/intl": "^4.4 || ^5.2", "symfony/web-profiler-bundle": "^4.4 || ^5.2", - "vimeo/psalm": "4.7.1" + "vimeo/psalm": "4.7.1", + "jane-php/json-schema": "^7.1" }, "prefer-stable": true, "autoload": { "psr-4": { "MonsieurBiz\\SyliusSearchPlugin\\": "src/", + "MonsieurBiz\\SyliusSearchPlugin\\Generated\\": "generated/", "Tests\\MonsieurBiz\\SyliusSearchPlugin\\": "tests/" } }, diff --git a/generated/Model/Channel.php b/generated/Model/Channel.php new file mode 100644 index 00000000..81a074e8 --- /dev/null +++ b/generated/Model/Channel.php @@ -0,0 +1,34 @@ +code; + } + /** + * + * + * @param string $code + * + * @return self + */ + public function setCode(string $code) : self + { + $this->code = $code; + return $this; + } +} \ No newline at end of file diff --git a/generated/Model/Image.php b/generated/Model/Image.php new file mode 100644 index 00000000..0ca33727 --- /dev/null +++ b/generated/Model/Image.php @@ -0,0 +1,34 @@ +path; + } + /** + * + * + * @param null|string $path + * + * @return self + */ + public function setPath(?string $path) : self + { + $this->path = $path; + return $this; + } +} \ No newline at end of file diff --git a/generated/Model/Product.php b/generated/Model/Product.php new file mode 100644 index 00000000..96a4e6d1 --- /dev/null +++ b/generated/Model/Product.php @@ -0,0 +1,304 @@ +id; + } + /** + * + * + * @param int $id + * + * @return self + */ + public function setId(int $id) : self + { + $this->id = $id; + return $this; + } + /** + * + * + * @return string + */ + public function getCode() : string + { + return $this->code; + } + /** + * + * + * @param string $code + * + * @return self + */ + public function setCode(string $code) : self + { + $this->code = $code; + return $this; + } + /** + * + * + * @return bool + */ + public function getEnabled() : bool + { + return $this->enabled; + } + /** + * + * + * @param bool $enabled + * + * @return self + */ + public function setEnabled(bool $enabled) : self + { + $this->enabled = $enabled; + return $this; + } + /** + * + * + * @return string + */ + public function getSlug() : string + { + return $this->slug; + } + /** + * + * + * @param string $slug + * + * @return self + */ + public function setSlug(string $slug) : self + { + $this->slug = $slug; + return $this; + } + /** + * + * + * @return string + */ + public function getName() : string + { + return $this->name; + } + /** + * + * + * @param string $name + * + * @return self + */ + public function setName(string $name) : self + { + $this->name = $name; + return $this; + } + /** + * + * + * @return Taxon + */ + public function getMainTaxon() : Taxon + { + return $this->mainTaxon; + } + /** + * + * + * @param Taxon $mainTaxon + * + * @return self + */ + public function setMainTaxon(Taxon $mainTaxon) : self + { + $this->mainTaxon = $mainTaxon; + return $this; + } + /** + * + * + * @return ProductTaxon[] + */ + public function getProductTaxons() : array + { + return $this->productTaxons; + } + /** + * + * + * @param ProductTaxon[] $productTaxons + * + * @return self + */ + public function setProductTaxons(array $productTaxons) : self + { + $this->productTaxons = $productTaxons; + return $this; + } + /** + * + * + * @return null|string + */ + public function getDescription() : ?string + { + return $this->description; + } + /** + * + * + * @param null|string $description + * + * @return self + */ + public function setDescription(?string $description) : self + { + $this->description = $description; + return $this; + } + /** + * + * + * @return null|Image[] + */ + public function getImages() : ?array + { + return $this->images; + } + /** + * + * + * @param null|Image[] $images + * + * @return self + */ + public function setImages(?array $images) : self + { + $this->images = $images; + return $this; + } + /** + * + * + * @return Channel[] + */ + public function getChannels() : array + { + return $this->channels; + } + /** + * + * + * @param Channel[] $channels + * + * @return self + */ + public function setChannels(array $channels) : self + { + $this->channels = $channels; + return $this; + } + /** + * + * + * @return int + */ + public function getPrice() : int + { + return $this->price; + } + /** + * + * + * @param int $price + * + * @return self + */ + public function setPrice(int $price) : self + { + $this->price = $price; + return $this; + } +} \ No newline at end of file diff --git a/generated/Model/ProductTaxon.php b/generated/Model/ProductTaxon.php new file mode 100644 index 00000000..686ac83c --- /dev/null +++ b/generated/Model/ProductTaxon.php @@ -0,0 +1,61 @@ +taxon; + } + /** + * + * + * @param Taxon $taxon + * + * @return self + */ + public function setTaxon(Taxon $taxon) : self + { + $this->taxon = $taxon; + return $this; + } + /** + * + * + * @return null|int + */ + public function getPosition() : ?int + { + return $this->position; + } + /** + * + * + * @param null|int $position + * + * @return self + */ + public function setPosition(?int $position) : self + { + $this->position = $position; + return $this; + } +} \ No newline at end of file diff --git a/generated/Model/Taxon.php b/generated/Model/Taxon.php new file mode 100644 index 00000000..11df4559 --- /dev/null +++ b/generated/Model/Taxon.php @@ -0,0 +1,115 @@ +name; + } + /** + * + * + * @param string $name + * + * @return self + */ + public function setName(string $name) : self + { + $this->name = $name; + return $this; + } + /** + * + * + * @return string + */ + public function getCode() : string + { + return $this->code; + } + /** + * + * + * @param string $code + * + * @return self + */ + public function setCode(string $code) : self + { + $this->code = $code; + return $this; + } + /** + * + * + * @return int + */ + public function getPosition() : int + { + return $this->position; + } + /** + * + * + * @param int $position + * + * @return self + */ + public function setPosition(int $position) : self + { + $this->position = $position; + return $this; + } + /** + * + * + * @return int + */ + public function getLevel() : int + { + return $this->level; + } + /** + * + * + * @param int $level + * + * @return self + */ + public function setLevel(int $level) : self + { + $this->level = $level; + return $this; + } +} \ No newline at end of file diff --git a/generated/Normalizer/ChannelNormalizer.php b/generated/Normalizer/ChannelNormalizer.php new file mode 100644 index 00000000..550774ac --- /dev/null +++ b/generated/Normalizer/ChannelNormalizer.php @@ -0,0 +1,52 @@ +setCode($data['code']); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getCode()) { + $data['code'] = $object->getCode(); + } + return $data; + } +} \ No newline at end of file diff --git a/generated/Normalizer/ImageNormalizer.php b/generated/Normalizer/ImageNormalizer.php new file mode 100644 index 00000000..233442ec --- /dev/null +++ b/generated/Normalizer/ImageNormalizer.php @@ -0,0 +1,67 @@ +setPath($value); + } + elseif (\array_key_exists('path', $data) && $data['path'] === null) { + $object->setPath(null); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getPath()) { + $value = $object->getPath(); + if (is_null($object->getPath())) { + $value = $object->getPath(); + } elseif (is_string($object->getPath())) { + $value = $object->getPath(); + } + $data['path'] = $value; + } + return $data; + } +} \ No newline at end of file diff --git a/generated/Normalizer/JaneObjectNormalizer.php b/generated/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 00000000..46ef699f --- /dev/null +++ b/generated/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Image' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ImageNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Channel' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ChannelNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductTaxon' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductTaxonNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Taxon' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\TaxonNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\MonsieurBiz\\SyliusSearchPlugin\\Generated\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/generated/Normalizer/ProductNormalizer.php b/generated/Normalizer/ProductNormalizer.php new file mode 100644 index 00000000..2f8d3503 --- /dev/null +++ b/generated/Normalizer/ProductNormalizer.php @@ -0,0 +1,166 @@ +setId($data['id']); + } + if (\array_key_exists('code', $data)) { + $object->setCode($data['code']); + } + if (\array_key_exists('enabled', $data)) { + $object->setEnabled($data['enabled']); + } + if (\array_key_exists('slug', $data)) { + $object->setSlug($data['slug']); + } + if (\array_key_exists('name', $data)) { + $object->setName($data['name']); + } + if (\array_key_exists('main_taxon', $data)) { + $object->setMainTaxon($this->denormalizer->denormalize($data['main_taxon'], 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Taxon', 'json', $context)); + } + if (\array_key_exists('product_taxons', $data)) { + $values = array(); + foreach ($data['product_taxons'] as $value) { + $values[] = $this->denormalizer->denormalize($value, 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductTaxon', 'json', $context); + } + $object->setProductTaxons($values); + } + if (\array_key_exists('description', $data) && $data['description'] !== null) { + $value_1 = $data['description']; + if (is_null($data['description'])) { + $value_1 = $data['description']; + } elseif (is_string($data['description'])) { + $value_1 = $data['description']; + } + $object->setDescription($value_1); + } + elseif (\array_key_exists('description', $data) && $data['description'] === null) { + $object->setDescription(null); + } + if (\array_key_exists('images', $data) && $data['images'] !== null) { + $value_2 = $data['images']; + if (is_null($data['images'])) { + $value_2 = $data['images']; + } elseif (is_array($data['images']) && $this->isOnlyNumericKeys($data['images'])) { + $values_1 = array(); + foreach ($data['images'] as $value_3) { + $values_1[] = $this->denormalizer->denormalize($value_3, 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Image', 'json', $context); + } + $value_2 = $values_1; + } + $object->setImages($value_2); + } + elseif (\array_key_exists('images', $data) && $data['images'] === null) { + $object->setImages(null); + } + if (\array_key_exists('channels', $data)) { + $values_2 = array(); + foreach ($data['channels'] as $value_4) { + $values_2[] = $this->denormalizer->denormalize($value_4, 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Channel', 'json', $context); + } + $object->setChannels($values_2); + } + if (\array_key_exists('price', $data)) { + $object->setPrice($data['price']); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getId()) { + $data['id'] = $object->getId(); + } + if (null !== $object->getCode()) { + $data['code'] = $object->getCode(); + } + if (null !== $object->getEnabled()) { + $data['enabled'] = $object->getEnabled(); + } + if (null !== $object->getSlug()) { + $data['slug'] = $object->getSlug(); + } + if (null !== $object->getName()) { + $data['name'] = $object->getName(); + } + if (null !== $object->getMainTaxon()) { + $data['main_taxon'] = $this->normalizer->normalize($object->getMainTaxon(), 'json', $context); + } + if (null !== $object->getProductTaxons()) { + $values = array(); + foreach ($object->getProductTaxons() as $value) { + $values[] = $this->normalizer->normalize($value, 'json', $context); + } + $data['product_taxons'] = $values; + } + if (null !== $object->getDescription()) { + $value_1 = $object->getDescription(); + if (is_null($object->getDescription())) { + $value_1 = $object->getDescription(); + } elseif (is_string($object->getDescription())) { + $value_1 = $object->getDescription(); + } + $data['description'] = $value_1; + } + if (null !== $object->getImages()) { + $value_2 = $object->getImages(); + if (is_null($object->getImages())) { + $value_2 = $object->getImages(); + } elseif (is_array($object->getImages())) { + $values_1 = array(); + foreach ($object->getImages() as $value_3) { + $values_1[] = $this->normalizer->normalize($value_3, 'json', $context); + } + $value_2 = $values_1; + } + $data['images'] = $value_2; + } + if (null !== $object->getChannels()) { + $values_2 = array(); + foreach ($object->getChannels() as $value_4) { + $values_2[] = $this->normalizer->normalize($value_4, 'json', $context); + } + $data['channels'] = $values_2; + } + if (null !== $object->getPrice()) { + $data['price'] = $object->getPrice(); + } + return $data; + } +} \ No newline at end of file diff --git a/generated/Normalizer/ProductTaxonNormalizer.php b/generated/Normalizer/ProductTaxonNormalizer.php new file mode 100644 index 00000000..e8dc17dd --- /dev/null +++ b/generated/Normalizer/ProductTaxonNormalizer.php @@ -0,0 +1,73 @@ +setTaxon($this->denormalizer->denormalize($data['taxon'], 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Taxon', 'json', $context)); + } + if (\array_key_exists('position', $data) && $data['position'] !== null) { + $value = $data['position']; + if (is_null($data['position'])) { + $value = $data['position']; + } elseif (is_int($data['position'])) { + $value = $data['position']; + } + $object->setPosition($value); + } + elseif (\array_key_exists('position', $data) && $data['position'] === null) { + $object->setPosition(null); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getTaxon()) { + $data['taxon'] = $this->normalizer->normalize($object->getTaxon(), 'json', $context); + } + if (null !== $object->getPosition()) { + $value = $object->getPosition(); + if (is_null($object->getPosition())) { + $value = $object->getPosition(); + } elseif (is_int($object->getPosition())) { + $value = $object->getPosition(); + } + $data['position'] = $value; + } + return $data; + } +} \ No newline at end of file diff --git a/generated/Normalizer/TaxonNormalizer.php b/generated/Normalizer/TaxonNormalizer.php new file mode 100644 index 00000000..03edf1ea --- /dev/null +++ b/generated/Normalizer/TaxonNormalizer.php @@ -0,0 +1,70 @@ +setName($data['name']); + } + if (\array_key_exists('code', $data)) { + $object->setCode($data['code']); + } + if (\array_key_exists('position', $data)) { + $object->setPosition($data['position']); + } + if (\array_key_exists('level', $data)) { + $object->setLevel($data['level']); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getName()) { + $data['name'] = $object->getName(); + } + if (null !== $object->getCode()) { + $data['code'] = $object->getCode(); + } + if (null !== $object->getPosition()) { + $data['position'] = $object->getPosition(); + } + if (null !== $object->getLevel()) { + $data['level'] = $object->getLevel(); + } + return $data; + } +} \ No newline at end of file diff --git a/generated/Runtime/Normalizer/CheckArray.php b/generated/Runtime/Normalizer/CheckArray.php new file mode 100644 index 00000000..4e48da3d --- /dev/null +++ b/generated/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/src/AutoMapper/ProductMapperConfiguration.php b/src/AutoMapper/ProductMapperConfiguration.php new file mode 100644 index 00000000..4966a371 --- /dev/null +++ b/src/AutoMapper/ProductMapperConfiguration.php @@ -0,0 +1,47 @@ +eventDispatcher = $eventDispatcher; + } + + public function process(MapperGeneratorMetadataInterface $metadata): void + { + if (!$metadata instanceof MapperMetadata) { + return; + } + + $metadata->forMember('price', function (Product $product) { + return '1000'; + }); + + $this->eventDispatcher->dispatch( + new ProductMapperConfigurationEvent($metadata), + ProductMapperConfigurationEvent::EVENT_NAME + ); + } + + public function getSource(): string + { + return Product::class; + } + + public function getTarget(): string + { + return ProductDTO::class; + } +} diff --git a/src/Command/PopulateCommand.php b/src/Command/PopulateCommand.php new file mode 100644 index 00000000..efd4b66e --- /dev/null +++ b/src/Command/PopulateCommand.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Command; + +use MonsieurBiz\SyliusSearchPlugin\Index\Indexer; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class PopulateCommand extends Command +{ + protected static $defaultName = 'monsieurbiz:search:populate'; + private Indexer $indexer; + + public function __construct(Indexer $indexer, $name = null) + { + parent::__construct($name); + $this->indexer = $indexer; + } + + protected function configure(): void + { + parent::configure(); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + + $this->indexer->indexAll(); + $output->writeln('ok'); + + return Command::SUCCESS; + } +} diff --git a/src/DependencyInjection/DocumentableRegistryPass.php b/src/DependencyInjection/DocumentableRegistryPass.php new file mode 100644 index 00000000..9057f4f3 --- /dev/null +++ b/src/DependencyInjection/DocumentableRegistryPass.php @@ -0,0 +1,19 @@ +getDefinition('monsieurbiz.search.registry.documentable'); + $documentableIds = array_keys($container->findTaggedServiceIds('monsieurbiz.search.documentable')); + foreach ($documentableIds as $documentableId) { + $registry->addMethodCall('register', [$documentableId, new Reference($documentableId)]); + } + } +} diff --git a/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php b/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php index 93ad36fc..c0ea3829 100644 --- a/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php +++ b/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php @@ -20,7 +20,7 @@ final class MonsieurBizSyliusSearchExtension extends Extension { - public const EXTENSION_CONFIG_NAME = 'monsieurbiz_sylius_search'; + public const EXTENSION_CONFIG_NAME = 'monsieur_biz_sylius_search'; public function load(array $configs, ContainerBuilder $container): void { diff --git a/src/Event/ProductMapperConfigurationEvent.php b/src/Event/ProductMapperConfigurationEvent.php new file mode 100644 index 00000000..c93d89ae --- /dev/null +++ b/src/Event/ProductMapperConfigurationEvent.php @@ -0,0 +1,22 @@ +mapperGeneratorMetadata = $mapperGeneratorMetadata; + } + + public function getMapperGeneratorMetadata(): MapperGeneratorMetadataInterface + { + return $this->mapperGeneratorMetadata; + } +} diff --git a/src/Index/Indexer.php b/src/Index/Indexer.php new file mode 100644 index 00000000..8f0f035b --- /dev/null +++ b/src/Index/Indexer.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Index; + +use Doctrine\ORM\EntityManagerInterface; +use Elastica\Document; +use Jane\Component\AutoMapper\AutoMapperInterface; +use JoliCode\Elastically\Factory; +use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Product; +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use Sylius\Component\Locale\Model\LocaleInterface; +use Sylius\Component\Registry\ServiceRegistryInterface; +use Sylius\Component\Resource\Model\TranslatableInterface; +use Sylius\Component\Resource\Repository\RepositoryInterface; + +final class Indexer +{ + private ServiceRegistryInterface $documentableRegistry; + private RepositoryInterface $localeRepository; + private array $locales = []; + private EntityManagerInterface $entityManager; + private AutoMapperInterface $autoMapper; + + public function __construct( + ServiceRegistryInterface $documentableRegistry, + RepositoryInterface $localeRepository, + EntityManagerInterface $entityManager, + AutoMapperInterface $autoMapper + ) { + $this->documentableRegistry = $documentableRegistry; + $this->localeRepository = $localeRepository; + $this->entityManager = $entityManager; + $this->autoMapper = $autoMapper; + } + + /** + * Retrieve all available locales. + */ + public function getLocales(): array + { + if (empty($this->locales)) { + $locales = $this->localeRepository->findAll(); + $this->locales = array_filter(array_map( + function(LocaleInterface $locale): string { + return $locale->getCode() ?? ''; + }, + $locales + )); + } + + return $this->locales; + } + + /** + * Index all documentable object + */ + public function indexAll(): void + { + foreach ($this->documentableRegistry->all() as $documentable) { + $this->indexDocumentable($documentable); + } + } + + private function indexDocumentable(DocumentableInterface $documentable, ?string $locale = null): void + { + if (null === $locale && $documentable instanceof TranslatableInterface) { + foreach ($this->getLocales() as $localeCode) { + $this->indexDocumentable($documentable, $localeCode); + } + + return; + } + $indexName = $this->getIndexName($documentable, $locale); + $factory = new Factory([ + Factory::CONFIG_MAPPINGS_PROVIDER => $documentable->getMappingProvider(), + ]); + $indexBuilder = $factory->buildIndexBuilder(); + $newIndex = $indexBuilder->createIndex($indexName, ['index_code' => $documentable->getIndexCode(), 'locale' => strtolower($locale)]); + + $indexer = $factory->buildIndexer(); + $test = $this->entityManager->getRepository(\get_class($documentable))->findAll(); + foreach ($test as $item) { + $item->setCurrentLocale($locale); // if TranslatableInterface + $indexer->scheduleIndex($newIndex, new Document((string) $item->getId(), $this->autoMapper->map($item, Product::class))); + //dump($this->autoMapper->map($item, Product::class));die; + } + $indexer->flush(); + + $indexBuilder->markAsLive($newIndex, $indexName); + $indexBuilder->speedUpRefresh($newIndex); + $indexBuilder->purgeOldIndices($indexName); + } + + private function getIndexName(DocumentableInterface $documentable, ?string $locale = null): string + { + return $documentable->getIndexCode() . strtolower(null !== $locale ? '_' . $locale : ''); + } +} diff --git a/src/Mapping/YamlWithLocaleProvider.php b/src/Mapping/YamlWithLocaleProvider.php new file mode 100644 index 00000000..cbd65381 --- /dev/null +++ b/src/Mapping/YamlWithLocaleProvider.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Mapping; + +use JoliCode\Elastically\Mapping\MappingProviderInterface; +use JoliCode\Elastically\Mapping\YamlProvider; +use Symfony\Component\Yaml\Exception\ParseException; +use Symfony\Component\Yaml\Parser; + +class YamlWithLocaleProvider implements MappingProviderInterface +{ + private YamlProvider $decorated; + private string $configurationDirectory; + private Parser $parser; + + public function __construct(YamlProvider $decorated, string $configurationDirectory, ?Parser $parser = null) + { + $this->decorated = $decorated; + $this->configurationDirectory = $configurationDirectory; + $this->parser = $parser ?? new Parser(); + } + + public function provideMapping(string $indexName, array $context = []): ?array + { + $mapping = $this->decorated->provideMapping($context['index_code'] ?? $indexName, $context); + + $locale = $context['locale'] ?? null; + if (null !== $mapping && null !== $locale) { + $mapping = $this->appendLocaleAnalyzers($mapping, $locale); + } + + return $mapping; + } + + private function appendLocaleAnalyzers(array $mapping, string $locale): array + { + foreach ($this->getLocaleCode($locale) as $localeCode) { + $analyzerFilePath = $this->configurationDirectory . \DIRECTORY_SEPARATOR . 'analyzers_' . $localeCode . '.yaml'; + try { + $analyzer = $this->parser->parseFile($analyzerFilePath) ?? []; + $mapping['settings']['analysis'] = array_merge_recursive($mapping['settings']['analysis'] ?? [], $analyzer); + } catch (ParseException $exception) { + // the yaml file does not exist or does not exist. + } + } + + return $mapping; + } + + private function getLocaleCode(string $locale): array + { + return [ + current(explode('_', $locale)), + $locale, + ]; + } +} diff --git a/src/Model/Documentable/DocumentableInterface.php b/src/Model/Documentable/DocumentableInterface.php new file mode 100644 index 00000000..4f4f7d64 --- /dev/null +++ b/src/Model/Documentable/DocumentableInterface.php @@ -0,0 +1,15 @@ +mappingProvider = $mapping; + } + + public function getMappingProvider(): MappingProviderInterface + { + return $this->mappingProvider; + } +} diff --git a/src/Model/TestClass.php b/src/Model/TestClass.php new file mode 100644 index 00000000..890acce4 --- /dev/null +++ b/src/Model/TestClass.php @@ -0,0 +1,34 @@ +name; + } + + /** + * @param string $name + */ + public function setName(string $name): void + { + $this->name = $name; + } +} diff --git a/src/MonsieurBizSyliusSearchPlugin.php b/src/MonsieurBizSyliusSearchPlugin.php index c7293b4f..34e51534 100644 --- a/src/MonsieurBizSyliusSearchPlugin.php +++ b/src/MonsieurBizSyliusSearchPlugin.php @@ -13,10 +13,18 @@ namespace MonsieurBiz\SyliusSearchPlugin; +use MonsieurBiz\SyliusSearchPlugin\DependencyInjection\DocumentableRegistryPass; use Sylius\Bundle\CoreBundle\Application\SyliusPluginTrait; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; final class MonsieurBizSyliusSearchPlugin extends Bundle { use SyliusPluginTrait; + + public function build(ContainerBuilder $container): void + { + parent::build($container); + $container->addCompilerPass(new DocumentableRegistryPass()); + } } diff --git a/src/Resources/config/elasticsearch/analyzers.yaml b/src/Resources/config/elasticsearch/analyzers.yaml new file mode 100644 index 00000000..e9bdbc5e --- /dev/null +++ b/src/Resources/config/elasticsearch/analyzers.yaml @@ -0,0 +1,14 @@ +filter: + search_autocomplete_filter: + type: 'edge_ngram' + min_gram: 1 + max_gram: 20 +analyzer: + search_autocomplete: + type: 'custom' + tokenizer: 'icu_tokenizer' + filter: [ 'lowercase', 'icu_folding', 'elision', 'search_autocomplete_filter' ] + search_standard: + type: 'custom' + tokenizer: 'icu_tokenizer' + filter: [ 'lowercase', 'icu_folding', 'elision' ] diff --git a/src/Resources/config/elasticsearch/analyzers_en.yaml b/src/Resources/config/elasticsearch/analyzers_en.yaml new file mode 100644 index 00000000..1c8efaf0 --- /dev/null +++ b/src/Resources/config/elasticsearch/analyzers_en.yaml @@ -0,0 +1,7 @@ +filter: + stemmer: + type: stemmer + language: english +analyzer: + search_standard: + filter: [ 'stemmer' ] diff --git a/src/Resources/config/elasticsearch/analyzers_fr.yaml b/src/Resources/config/elasticsearch/analyzers_fr.yaml new file mode 100644 index 00000000..4209d640 --- /dev/null +++ b/src/Resources/config/elasticsearch/analyzers_fr.yaml @@ -0,0 +1,7 @@ +filter: + stemmer: + type: stemmer + language: french +analyzer: + search_standard: + filter: [ 'stemmer' ] diff --git a/src/Resources/config/elasticsearch/product_mapping.yaml b/src/Resources/config/elasticsearch/product_mapping.yaml new file mode 100644 index 00000000..66c7e88f --- /dev/null +++ b/src/Resources/config/elasticsearch/product_mapping.yaml @@ -0,0 +1,46 @@ +mappings: + dynamic: false + properties: + code: + type: keyword + enabled: + type: boolean + channels: + type: nested + properties: + code: + type: keyword + images: + type: nested + properties: + path: + type: keyword + mainTaxon: + type: nested + properties: + code: + type: keyword + name: + type: keyword + position: + type: integer + level: + type: integer + product_taxon: + type: nested + properties: + taxon: + type: nested + properties: + code: + type: keyword + name: + type: keyword + position: + type: integer + level: + type: integer + position: + type: integer + price: + type: integer diff --git a/src/Resources/config/elasticsearch/test_class_mapping.yaml b/src/Resources/config/elasticsearch/test_class_mapping.yaml new file mode 100644 index 00000000..da3fb9be --- /dev/null +++ b/src/Resources/config/elasticsearch/test_class_mapping.yaml @@ -0,0 +1,5 @@ +mappings: + dynamic: false + properties: + name: + type: keyword diff --git a/src/Resources/config/jane/jane-configuration.php b/src/Resources/config/jane/jane-configuration.php new file mode 100644 index 00000000..a78ebb8f --- /dev/null +++ b/src/Resources/config/jane/jane-configuration.php @@ -0,0 +1,8 @@ + __DIR__ . '/json-schema.json', + 'root-class' => 'Model', + 'namespace' => 'MonsieurBiz\SyliusSearchPlugin\Generated', + 'directory' => __DIR__ . '/../../../../generated', +]; diff --git a/src/Resources/config/jane/json-schema.json b/src/Resources/config/jane/json-schema.json new file mode 100644 index 00000000..1693fae4 --- /dev/null +++ b/src/Resources/config/jane/json-schema.json @@ -0,0 +1,108 @@ +{ + "$schema": "http://json-schema.org/2019-09/schema#", + "definitions": { + "Product": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "code": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "slug": { + "type": "string" + }, + "name": { + "type": "string" + }, + "main_taxon": { + "$ref": "#/definitions/Taxon" + }, + "product_taxons": { + "type": "array", + "items": { + "$ref": "#/definitions/ProductTaxon" + } + }, + "description": { + "type": [ + "null", + "string" + ] + }, + "images": { + "type": [ + "null", + "array" + ], + "items": { + "$ref": "#/definitions/Image" + } + }, + "channels": { + "type": "array", + "items": { + "$ref": "#/definitions/Channel" + } + }, + "price": { + "type": "integer" + } + } + }, + "Image": { + "type": "object", + "properties": { + "path": { + "type": [ + "null", + "string" + ] + } + } + }, + "Channel": { + "type": "object", + "properties": { + "code": { + "type": "string" + } + } + }, + "ProductTaxon": { + "type": "object", + "properties": { + "taxon": { + "$ref": "#/definitions/Taxon" + }, + "position": { + "type": [ + "null", + "integer" + ] + } + } + }, + "Taxon": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "code": { + "type": "string" + }, + "position": { + "type": "integer" + }, + "level": { + "type": "integer" + } + } + } + } +} diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index c6f36199..c533b087 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -1,10 +1,69 @@ +parameters: + monsieurbiz.search.model.documentable.interface: MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface + services: _defaults: autowire: true autoconfigure: true public: false + bind: + $documentableRegistry: '@monsieurbiz.search.registry.documentable' + $configurationDirectory: '@=service("file_locator").locate("@MonsieurBizSyliusSearchPlugin/Resources/config/elasticsearch")' + + _instanceof: + MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface: + tags: [ 'monsieurbiz.search.documentable' ] + calls: + - [setMappingProvider, ['@monsieurbiz.search.mapper_provider']] MonsieurBiz\SyliusSearchPlugin\: resource: '../../*' exclude: '../../{Entity,Migrations,Tests,Kernel.php}' + + JoliCode\Elastically\IndexNameMapper: + arguments: + $prefix: null # or a string to prefix index name + $indexClassMapping: {} + + JoliCode\Elastically\Serializer\StaticContextBuilder: + arguments: + $mapping: {} + + JoliCode\Elastically\ResultSetBuilder: + arguments: + $indexNameMapper: '@JoliCode\Elastically\IndexNameMapper' + $contextBuilder: '@JoliCode\Elastically\Serializer\StaticContextBuilder' + $denormalizer: '@serializer' + + # ES Client configuration + monsieurbiz.search.elasticsearch.client: + class: JoliCode\Elastically\Client + arguments: + $config: + host: '%env(ELASTICSEARCH_HOST)%' + $logger: '@logger' + $resultSetBuilder: '@JoliCode\Elastically\ResultSetBuilder' + $indexNameMapper: '@JoliCode\Elastically\IndexNameMapper' + + JoliCode\Elastically\Mapping\YamlProvider: ~ + + # Define the annotations provider arguments + monsieurbiz.search.mapper_provider: + class: MonsieurBiz\SyliusSearchPlugin\Mapping\YamlWithLocaleProvider + decorates: JoliCode\Elastically\Mapping\YamlProvider + arguments: + $decorated: '@.inner' + + + # Replace the mapping provider by our annotations provider + JoliCode\Elastically\IndexBuilder: + arguments: + $client: '@monsieurbiz.search.elasticsearch.client' + $mappingProvider: '@monsieurbiz.search.mapper_provider' + + monsieurbiz.search.registry.documentable: + class: Sylius\Component\Registry\ServiceRegistry + arguments: + $className: '%monsieurbiz.search.model.documentable.interface%' + $context: 'documentable' From 939a4d64a72c89b6d9c3eed35c395979e89146ea Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Fri, 8 Oct 2021 17:06:34 +0200 Subject: [PATCH 005/142] wip - install plugin search --- tests/Application/config/bundles.php | 1 + .../Application/config/packages/doctrine.yaml | 12 ++++++ .../monsieurbiz_sylius_search_plugin.yaml | 2 + tests/Application/config/services.yaml | 7 ++++ .../src/Entity/Product/Product.php | 40 +++++++++++++++++++ 5 files changed, 62 insertions(+) create mode 100644 tests/Application/config/packages/monsieurbiz_sylius_search_plugin.yaml create mode 100644 tests/Application/src/Entity/Product/Product.php diff --git a/tests/Application/config/bundles.php b/tests/Application/config/bundles.php index 768e1aeb..2ea97401 100644 --- a/tests/Application/config/bundles.php +++ b/tests/Application/config/bundles.php @@ -71,4 +71,5 @@ Sonata\Twig\Bridge\Symfony\SonataTwigSymfonyBundle::class => ['all' => true], SyliusLabs\Polyfill\Symfony\Security\Bundle\SyliusLabsPolyfillSymfonySecurityBundle::class => ['all' => true], MonsieurBiz\SyliusSearchPlugin\MonsieurBizSyliusSearchPlugin::class => ['all' => true], + Jane\Bundle\AutoMapperBundle\JaneAutoMapperBundle::class => ['all' => true], ]; diff --git a/tests/Application/config/packages/doctrine.yaml b/tests/Application/config/packages/doctrine.yaml index 1892ee89..f05546a3 100644 --- a/tests/Application/config/packages/doctrine.yaml +++ b/tests/Application/config/packages/doctrine.yaml @@ -11,3 +11,15 @@ doctrine: charset: utf8mb4 url: '%env(resolve:DATABASE_URL)%' + orm: + auto_generate_proxy_classes: '%kernel.debug%' + entity_managers: + default: + auto_mapping: true + mappings: + App: + is_bundle: false + type: annotation + dir: '%kernel.project_dir%/src/Entity' + prefix: 'App\Entity' + alias: App diff --git a/tests/Application/config/packages/monsieurbiz_sylius_search_plugin.yaml b/tests/Application/config/packages/monsieurbiz_sylius_search_plugin.yaml new file mode 100644 index 00000000..d39a7f98 --- /dev/null +++ b/tests/Application/config/packages/monsieurbiz_sylius_search_plugin.yaml @@ -0,0 +1,2 @@ +imports: + - { resource: "@MonsieurBizSyliusSearchPlugin/Resources/config/config.yaml" } diff --git a/tests/Application/config/services.yaml b/tests/Application/config/services.yaml index 615506eb..963e956f 100644 --- a/tests/Application/config/services.yaml +++ b/tests/Application/config/services.yaml @@ -2,3 +2,10 @@ # https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration parameters: locale: en_US + + +services: + App\Entity\Product\Product: + tags: [ 'monsieurbiz.search.documentable' ] + calls: + - [setMappingProvider, ['@monsieurbiz.search.mapper_provider']] diff --git a/tests/Application/src/Entity/Product/Product.php b/tests/Application/src/Entity/Product/Product.php new file mode 100644 index 00000000..74e2575f --- /dev/null +++ b/tests/Application/src/Entity/Product/Product.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace App\Entity\Product; + +use Doctrine\ORM\Mapping as ORM; +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableMappingProviderTrait; +use Sylius\Component\Core\Model\Product as BaseProduct; +use Sylius\Component\Core\Model\ProductTranslation; +use Sylius\Component\Product\Model\ProductTranslationInterface; + +/** + * @ORM\Entity + * @ORM\Table(name="sylius_product") + */ +class Product extends BaseProduct implements DocumentableInterface +{ + use DocumentableMappingProviderTrait; + + protected function createTranslation(): ProductTranslationInterface + { + return new ProductTranslation(); + } + + public function getIndexCode(): string + { + return 'product'; + } +} From c75e823309df408a5c321580686e0929378314d1 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 12 Oct 2021 13:48:39 +0200 Subject: [PATCH 006/142] wip - add product attributes --- generated/Model/ProductAttribute.php | 66 ++++++++++++++ .../Normalizer/ProductAttributeNormalizer.php | 89 +++++++++++++++++++ src/AutoMapper/ProductMapperConfiguration.php | 34 ++++++- src/Entity/Product/SearchableInterface.php | 29 ++++++ src/Event/MappingProviderEvent.php | 40 +++++++++ src/Event/ProductMapperConfigurationEvent.php | 11 +++ .../ProductAttributeTypeExtension.php | 51 +++++++++++ src/Mapping/YamlWithLocaleProvider.php | 24 ++++- src/Model/Product/SearchableTrait.php | 64 +++++++++++++ src/Model/TestClass.php | 34 ------- src/Repository/ProductAttributeRepository.php | 37 ++++++++ .../ProductAttributeRepositoryInterface.php | 24 +++++ .../config/elasticsearch/product_mapping.yaml | 6 +- .../elasticsearch/test_class_mapping.yaml | 5 -- src/Resources/config/jane/json-schema.json | 17 ++++ src/Resources/config/services.yaml | 14 ++- .../ProductAttribute/_form.html.twig | 30 +++++++ .../ProductOption/_form.html.twig | 20 +++++ .../Application/config/packages/_sylius.yaml | 35 +++++++- .../config/packages/doctrine_migrations.yaml | 1 - .../src/Entity/Product/ProductAttribute.php | 35 ++++++++ .../src/Entity/Product/ProductOption.php | 28 ++++++ .../src/Migrations/Version20211012092353.php | 42 +++++++++ .../templates/bundles/SyliusAdminBundle | 1 + 24 files changed, 687 insertions(+), 50 deletions(-) create mode 100644 generated/Model/ProductAttribute.php create mode 100644 generated/Normalizer/ProductAttributeNormalizer.php create mode 100644 src/Entity/Product/SearchableInterface.php create mode 100644 src/Event/MappingProviderEvent.php create mode 100644 src/Form/Extension/ProductAttributeTypeExtension.php create mode 100644 src/Model/Product/SearchableTrait.php delete mode 100644 src/Model/TestClass.php create mode 100644 src/Repository/ProductAttributeRepository.php create mode 100644 src/Repository/ProductAttributeRepositoryInterface.php delete mode 100644 src/Resources/config/elasticsearch/test_class_mapping.yaml create mode 100644 src/Resources/templates/bundles/SyliusAdminBundle/ProductAttribute/_form.html.twig create mode 100644 src/Resources/templates/bundles/SyliusAdminBundle/ProductOption/_form.html.twig mode change 120000 => 100644 tests/Application/config/packages/_sylius.yaml mode change 120000 => 100644 tests/Application/config/packages/doctrine_migrations.yaml create mode 100644 tests/Application/src/Entity/Product/ProductAttribute.php create mode 100644 tests/Application/src/Entity/Product/ProductOption.php create mode 100644 tests/Application/src/Migrations/Version20211012092353.php create mode 120000 tests/Application/templates/bundles/SyliusAdminBundle diff --git a/generated/Model/ProductAttribute.php b/generated/Model/ProductAttribute.php new file mode 100644 index 00000000..7f3990c3 --- /dev/null +++ b/generated/Model/ProductAttribute.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Generated\Model; + +class ProductAttribute +{ + /** + * @var string + */ + protected $name; + /** + * @var string|null + */ + protected $value; + + /** + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * @param string $name + * + * @return self + */ + public function setName(string $name): self + { + $this->name = $name; + + return $this; + } + + /** + * @return string|null + */ + public function getValue(): ?string + { + return $this->value; + } + + /** + * @param string|null $value + * + * @return self + */ + public function setValue(?string $value): self + { + $this->value = $value; + + return $this; + } +} diff --git a/generated/Normalizer/ProductAttributeNormalizer.php b/generated/Normalizer/ProductAttributeNormalizer.php new file mode 100644 index 00000000..36219e4a --- /dev/null +++ b/generated/Normalizer/ProductAttributeNormalizer.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Generated\Normalizer; + +use Jane\Component\JsonSchemaRuntime\Reference; +use MonsieurBiz\SyliusSearchPlugin\Generated\Runtime\Normalizer\CheckArray; +use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; +use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait; +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + +class ProductAttributeNormalizer implements DenormalizerInterface, NormalizerInterface, DenormalizerAwareInterface, NormalizerAwareInterface +{ + use CheckArray; + use DenormalizerAwareTrait; + use NormalizerAwareTrait; + + public function supportsDenormalization($data, $type, $format = null) + { + return 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductAttribute' === $type; + } + + public function supportsNormalization($data, $format = null) + { + return $data instanceof \MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductAttribute; + } + + public function denormalize($data, $class, $format = null, array $context = []) + { + if (isset($data['$ref'])) { + return new Reference($data['$ref'], $context['document-origin']); + } + if (isset($data['$recursiveRef'])) { + return new Reference($data['$recursiveRef'], $context['document-origin']); + } + $object = new \MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductAttribute(); + if (null === $data || false === \is_array($data)) { + return $object; + } + if (\array_key_exists('name', $data)) { + $object->setName($data['name']); + } + if (\array_key_exists('value', $data) && null !== $data['value']) { + $value = $data['value']; + if (null === $data['value']) { + $value = $data['value']; + } elseif (\is_string($data['value'])) { + $value = $data['value']; + } + $object->setValue($value); + } elseif (\array_key_exists('value', $data) && null === $data['value']) { + $object->setValue(null); + } + + return $object; + } + + public function normalize($object, $format = null, array $context = []) + { + $data = []; + if (null !== $object->getName()) { + $data['name'] = $object->getName(); + } + if (null !== $object->getValue()) { + $value = $object->getValue(); + if (null === $object->getValue()) { + $value = $object->getValue(); + } elseif (\is_string($object->getValue())) { + $value = $object->getValue(); + } + $data['value'] = $value; + } + + return $data; + } +} diff --git a/src/AutoMapper/ProductMapperConfiguration.php b/src/AutoMapper/ProductMapperConfiguration.php index 4966a371..08df7745 100644 --- a/src/AutoMapper/ProductMapperConfiguration.php +++ b/src/AutoMapper/ProductMapperConfiguration.php @@ -1,13 +1,27 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\AutoMapper; use App\Entity\Product\Product; use Jane\Bundle\AutoMapperBundle\Configuration\MapperConfigurationInterface; use Jane\Component\AutoMapper\MapperGeneratorMetadataInterface; use Jane\Component\AutoMapper\MapperMetadata; +use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; use MonsieurBiz\SyliusSearchPlugin\Event\ProductMapperConfigurationEvent; use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Product as ProductDTO; +use MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductAttribute; +use Sylius\Component\Core\Model\ProductInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; class ProductMapperConfiguration implements MapperConfigurationInterface @@ -25,8 +39,24 @@ public function process(MapperGeneratorMetadataInterface $metadata): void return; } - $metadata->forMember('price', function (Product $product) { - return '1000'; + $metadata->forMember('attributes', function(ProductInterface $product) { + $attributes = []; + /** @var \Sylius\Component\Product\Model\ProductAttributeValue $attributeValue */ + foreach ($product->getAttributes() as $attributeValue) { + if (null === $attributeValue->getName() || null === $attributeValue->getValue()) { + continue; + } + $attribute = $attributeValue->getAttribute(); + if ($attribute instanceof SearchableInterface && !$attribute->isSearchable() && !$attribute->isFilterable()) { + continue; + } + $attributeDTO = new ProductAttribute(); + $attributeDTO->setName($attributeValue->getName()); + $attributeDTO->setValue($attributeValue->getValue()); + $attributes[$attributeValue->getCode()] = $attributeDTO; + } + + return $attributes; }); $this->eventDispatcher->dispatch( diff --git a/src/Entity/Product/SearchableInterface.php b/src/Entity/Product/SearchableInterface.php new file mode 100644 index 00000000..06b49f44 --- /dev/null +++ b/src/Entity/Product/SearchableInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Entity\Product; + +interface SearchableInterface +{ + public function isSearchable(): bool; + + public function setSearchable(bool $searchable): void; + + public function isFilterable(): bool; + + public function setFilterable(bool $filterable): void; + + public function getSearchWeight(): int; + + public function setSearchWeight(int $searchWeight): void; +} diff --git a/src/Event/MappingProviderEvent.php b/src/Event/MappingProviderEvent.php new file mode 100644 index 00000000..8e73fd26 --- /dev/null +++ b/src/Event/MappingProviderEvent.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Event; + +use Symfony\Contracts\EventDispatcher\Event; + +class MappingProviderEvent extends Event +{ + public const EVENT_NAME = 'monsieurbiz.search.mapping.provider'; + + private string $indexCode; + private ?\ArrayObject $mapping; + + public function __construct(string $indexCode, ?\ArrayObject $mapping) + { + $this->indexCode = $indexCode; + $this->mapping = $mapping; + } + + public function getIndexCode(): string + { + return $this->indexCode; + } + + public function getMapping(): ?\ArrayObject + { + return $this->mapping; + } +} diff --git a/src/Event/ProductMapperConfigurationEvent.php b/src/Event/ProductMapperConfigurationEvent.php index c93d89ae..bb935e57 100644 --- a/src/Event/ProductMapperConfigurationEvent.php +++ b/src/Event/ProductMapperConfigurationEvent.php @@ -1,5 +1,16 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Event; use Jane\Component\AutoMapper\MapperGeneratorMetadataInterface; diff --git a/src/Form/Extension/ProductAttributeTypeExtension.php b/src/Form/Extension/ProductAttributeTypeExtension.php new file mode 100644 index 00000000..c8de8abd --- /dev/null +++ b/src/Form/Extension/ProductAttributeTypeExtension.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Form\Extension; + +use Sylius\Bundle\ProductBundle\Form\Type\ProductAttributeType; +use Symfony\Component\Form\AbstractTypeExtension; +use Symfony\Component\Form\Extension\Core\Type\CheckboxType; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; +use Symfony\Component\Form\FormBuilderInterface; + +final class ProductAttributeTypeExtension extends AbstractTypeExtension +{ + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $searchWeightValues = range(1, 10); + + $builder + ->add('searchable', CheckboxType::class, [ + 'label' => 'monsieurbiz_searchplugin.admin.product_attribute.form.searchable', + 'required' => true, + ]) + ->add('filterable', CheckboxType::class, [ + 'label' => 'monsieurbiz_searchplugin.admin.product_attribute.form.filterable', + 'required' => true, + ]) + ->add('search_weight', ChoiceType::class, [ + 'label' => 'monsieurbiz_searchplugin.admin.product_attribute.form.search_weight', + 'required' => true, + 'choices' => array_combine($searchWeightValues, $searchWeightValues), + ]) + ; + } + + public static function getExtendedTypes(): array + { + return [ + ProductAttributeType::class, + ]; + } +} diff --git a/src/Mapping/YamlWithLocaleProvider.php b/src/Mapping/YamlWithLocaleProvider.php index cbd65381..97cf2148 100644 --- a/src/Mapping/YamlWithLocaleProvider.php +++ b/src/Mapping/YamlWithLocaleProvider.php @@ -15,6 +15,9 @@ use JoliCode\Elastically\Mapping\MappingProviderInterface; use JoliCode\Elastically\Mapping\YamlProvider; +use MonsieurBiz\SyliusSearchPlugin\Event\MappingProviderEvent; +use MonsieurBiz\SyliusSearchPlugin\Repository\ProductAttributeRepositoryInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Yaml\Exception\ParseException; use Symfony\Component\Yaml\Parser; @@ -23,12 +26,21 @@ class YamlWithLocaleProvider implements MappingProviderInterface private YamlProvider $decorated; private string $configurationDirectory; private Parser $parser; + private ProductAttributeRepositoryInterface $attributeRepository; + private EventDispatcherInterface $eventDispatcher; - public function __construct(YamlProvider $decorated, string $configurationDirectory, ?Parser $parser = null) - { + public function __construct( + YamlProvider $decorated, + string $configurationDirectory, + EventDispatcherInterface $eventDispatcher, + ProductAttributeRepositoryInterface $attributeRepository, + ?Parser $parser = null + ) { $this->decorated = $decorated; $this->configurationDirectory = $configurationDirectory; $this->parser = $parser ?? new Parser(); + $this->attributeRepository = $attributeRepository; + $this->eventDispatcher = $eventDispatcher; } public function provideMapping(string $indexName, array $context = []): ?array @@ -40,7 +52,13 @@ public function provideMapping(string $indexName, array $context = []): ?array $mapping = $this->appendLocaleAnalyzers($mapping, $locale); } - return $mapping; + $mappingProviderEvent = new MappingProviderEvent($context['index_code'] ?? $indexName, new \ArrayObject($mapping)); + $this->eventDispatcher->dispatch( + $mappingProviderEvent, + MappingProviderEvent::EVENT_NAME + ); + + return (array) $mappingProviderEvent->getMapping(); } private function appendLocaleAnalyzers(array $mapping, string $locale): array diff --git a/src/Model/Product/SearchableTrait.php b/src/Model/Product/SearchableTrait.php new file mode 100644 index 00000000..cb0dd326 --- /dev/null +++ b/src/Model/Product/SearchableTrait.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Model\Product; + +use Doctrine\ORM\Mapping as ORM; + +trait SearchableTrait +{ + /** + * @ORM\Column(name="searchable", type="boolean", nullable=false, options={"default"=true}) + */ + protected bool $searchable = true; + + /** + * @ORM\Column(name="filterable", type="boolean", nullable=false, options={"default"=false}) + */ + protected bool $filterable = false; + + /** + * @ORM\Column(name="search_weight", type="smallint", nullable=false, options={"default"=1, "unsigned"=true}) + */ + protected int $searchWeight = 1; + + public function isSearchable(): bool + { + return $this->searchable; + } + + public function setSearchable(bool $searchable): void + { + $this->searchable = $searchable; + } + + public function isFilterable(): bool + { + return $this->filterable; + } + + public function setFilterable(bool $filterable): void + { + $this->filterable = $filterable; + } + + public function getSearchWeight(): int + { + return $this->searchWeight; + } + + public function setSearchWeight(int $searchWeight): void + { + $this->searchWeight = $searchWeight; + } +} diff --git a/src/Model/TestClass.php b/src/Model/TestClass.php deleted file mode 100644 index 890acce4..00000000 --- a/src/Model/TestClass.php +++ /dev/null @@ -1,34 +0,0 @@ -name; - } - - /** - * @param string $name - */ - public function setName(string $name): void - { - $this->name = $name; - } -} diff --git a/src/Repository/ProductAttributeRepository.php b/src/Repository/ProductAttributeRepository.php new file mode 100644 index 00000000..966eddec --- /dev/null +++ b/src/Repository/ProductAttributeRepository.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Repository; + +use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository; + +class ProductAttributeRepository implements ProductAttributeRepositoryInterface +{ + private EntityRepository $attributeRepository; + + public function __construct(EntityRepository $attributeRepository) + { + $this->attributeRepository = $attributeRepository; + } + + public function findIsSearchableOrFilterable(): array + { + return $this->attributeRepository->createQueryBuilder('o') + ->innerJoin('o.translations', 'translation') + ->andWhere('o.searchable = true') + ->orWhere('o.filterable = true') + ->getQuery() + ->getResult() + ; + } +} diff --git a/src/Repository/ProductAttributeRepositoryInterface.php b/src/Repository/ProductAttributeRepositoryInterface.php new file mode 100644 index 00000000..cc3a8252 --- /dev/null +++ b/src/Repository/ProductAttributeRepositoryInterface.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Repository; + +use Sylius\Component\Product\Model\ProductAttributeInterface; + +interface ProductAttributeRepositoryInterface +{ + /** + * @return ProductAttributeInterface[] + */ + public function findIsSearchableOrFilterable(): array; +} diff --git a/src/Resources/config/elasticsearch/product_mapping.yaml b/src/Resources/config/elasticsearch/product_mapping.yaml index 66c7e88f..89dd809b 100644 --- a/src/Resources/config/elasticsearch/product_mapping.yaml +++ b/src/Resources/config/elasticsearch/product_mapping.yaml @@ -10,6 +10,10 @@ mappings: properties: code: type: keyword + name: + type: text + description: + type: text images: type: nested properties: @@ -43,4 +47,4 @@ mappings: position: type: integer price: - type: integer + type: integer # todo - WIP diff --git a/src/Resources/config/elasticsearch/test_class_mapping.yaml b/src/Resources/config/elasticsearch/test_class_mapping.yaml deleted file mode 100644 index da3fb9be..00000000 --- a/src/Resources/config/elasticsearch/test_class_mapping.yaml +++ /dev/null @@ -1,5 +0,0 @@ -mappings: - dynamic: false - properties: - name: - type: keyword diff --git a/src/Resources/config/jane/json-schema.json b/src/Resources/config/jane/json-schema.json index 1693fae4..a0206e21 100644 --- a/src/Resources/config/jane/json-schema.json +++ b/src/Resources/config/jane/json-schema.json @@ -51,6 +51,12 @@ }, "price": { "type": "integer" + }, + "attributes": { + "type": "array", + "items": { + "$ref": "#/definitions/ProductAttribute" + } } } }, @@ -103,6 +109,17 @@ "type": "integer" } } + }, + "ProductAttribute": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": ["null", "string"] + } + } } } } diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index c533b087..11359e7d 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -21,6 +21,11 @@ services: resource: '../../*' exclude: '../../{Entity,Migrations,Tests,Kernel.php}' + MonsieurBiz\SyliusSearchPlugin\Form\Extension\: + resource: '../../Form/Extension' + tags: + - { name: form.type_extension } + JoliCode\Elastically\IndexNameMapper: arguments: $prefix: null # or a string to prefix index name @@ -48,15 +53,18 @@ services: JoliCode\Elastically\Mapping\YamlProvider: ~ - # Define the annotations provider arguments + MonsieurBiz\SyliusSearchPlugin\Repository\ProductAttributeRepository: + arguments: + $attributeRepository: '@sylius.repository.product_attribute' + + # Define our mapping provider monsieurbiz.search.mapper_provider: class: MonsieurBiz\SyliusSearchPlugin\Mapping\YamlWithLocaleProvider decorates: JoliCode\Elastically\Mapping\YamlProvider arguments: $decorated: '@.inner' - - # Replace the mapping provider by our annotations provider + # Replace the mapping provider by our mapping provider JoliCode\Elastically\IndexBuilder: arguments: $client: '@monsieurbiz.search.elasticsearch.client' diff --git a/src/Resources/templates/bundles/SyliusAdminBundle/ProductAttribute/_form.html.twig b/src/Resources/templates/bundles/SyliusAdminBundle/ProductAttribute/_form.html.twig new file mode 100644 index 00000000..3130f269 --- /dev/null +++ b/src/Resources/templates/bundles/SyliusAdminBundle/ProductAttribute/_form.html.twig @@ -0,0 +1,30 @@ +{% from '@SyliusAdmin/Macro/translationForm.html.twig' import translationForm %} + +{{ form_errors(form) }} + +
+
+ {{ form_row(form.code) }} + {{ form_row(form.position) }} + {{ form_row(form.type) }} +
+ {{ form_row(form.translatable) }} +
+
+

{{ 'monsieurbiz_searchplugin.admin.product_attribute.form.title'|trans }}

+
+ {{ form_row(form.searchable) }} + {{ form_row(form.filterable) }} + {{ form_row(form.search_weight) }} +
+
+{% if form.configuration is defined %} +
+

{{ 'sylius.ui.configuration'|trans }}

+ {% for field in form.configuration %} + {{ form_row(field) }} + {% endfor %} +
+{% endif %} +{{ translationForm(form.translations) }} + diff --git a/src/Resources/templates/bundles/SyliusAdminBundle/ProductOption/_form.html.twig b/src/Resources/templates/bundles/SyliusAdminBundle/ProductOption/_form.html.twig new file mode 100644 index 00000000..661788ae --- /dev/null +++ b/src/Resources/templates/bundles/SyliusAdminBundle/ProductOption/_form.html.twig @@ -0,0 +1,20 @@ +{% from '@SyliusAdmin/Macro/translationForm.html.twig' import translationForm %} + +
+ {{ form_errors(form) }} +
+ {{ form_row(form.code) }} + {{ form_row(form.position) }} +
+ {{ translationForm(form.translations) }} +
+
+

{{ 'monsieurbiz_searchplugin.admin.product_option.form.title'|trans }}

+
+ {{ form_row(form.searchable) }} + {{ form_row(form.filterable) }} + {{ form_row(form.search_weight) }} +
+
+

{{ 'sylius.ui.values'|trans }}

+{{ form_row(form.values) }} diff --git a/tests/Application/config/packages/_sylius.yaml b/tests/Application/config/packages/_sylius.yaml deleted file mode 120000 index 6f7e9286..00000000 --- a/tests/Application/config/packages/_sylius.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/packages/_sylius.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/_sylius.yaml b/tests/Application/config/packages/_sylius.yaml new file mode 100644 index 00000000..00221693 --- /dev/null +++ b/tests/Application/config/packages/_sylius.yaml @@ -0,0 +1,34 @@ +imports: + - { resource: "@SyliusCoreBundle/Resources/config/app/config.yml" } + + - { resource: "@SyliusAdminBundle/Resources/config/app/config.yml" } + + - { resource: "@SyliusShopBundle/Resources/config/app/config.yml" } + + - { resource: "@SyliusApiBundle/Resources/config/app/config.yaml" } + +parameters: + sylius_core.public_dir: '%kernel.project_dir%/public' + +sylius_api: + enabled: false + +sylius_shop: + product_grid: + include_all_descendants: true + +sylius_attribute: + resources: + product: + attribute: + classes: + model: App\Entity\Product\ProductAttribute + +sylius_product: + resources: + product: + classes: + model: App\Entity\Product\Product + product_option: + classes: + model: App\Entity\Product\ProductOption diff --git a/tests/Application/config/packages/doctrine_migrations.yaml b/tests/Application/config/packages/doctrine_migrations.yaml deleted file mode 120000 index 1af39ca0..00000000 --- a/tests/Application/config/packages/doctrine_migrations.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/packages/doctrine_migrations.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/doctrine_migrations.yaml b/tests/Application/config/packages/doctrine_migrations.yaml new file mode 100644 index 00000000..e69de29b diff --git a/tests/Application/src/Entity/Product/ProductAttribute.php b/tests/Application/src/Entity/Product/ProductAttribute.php new file mode 100644 index 00000000..c01070cb --- /dev/null +++ b/tests/Application/src/Entity/Product/ProductAttribute.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace App\Entity\Product; + +use Doctrine\ORM\Mapping as ORM; +use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; +use MonsieurBiz\SyliusSearchPlugin\Model\Product\SearchableTrait; +use Sylius\Component\Attribute\Model\AttributeTranslationInterface; +use Sylius\Component\Product\Model\ProductAttribute as BaseProductAttribute; +use Sylius\Component\Product\Model\ProductAttributeTranslation; + +/** + * @ORM\Entity + * @ORM\Table(name="sylius_product_attribute") + */ +class ProductAttribute extends BaseProductAttribute implements SearchableInterface +{ + use SearchableTrait; + + protected function createTranslation(): AttributeTranslationInterface + { + return new ProductAttributeTranslation(); + } +} diff --git a/tests/Application/src/Entity/Product/ProductOption.php b/tests/Application/src/Entity/Product/ProductOption.php new file mode 100644 index 00000000..1c586a91 --- /dev/null +++ b/tests/Application/src/Entity/Product/ProductOption.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace App\Entity\Product; + +use Doctrine\ORM\Mapping as ORM; +use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; +use MonsieurBiz\SyliusSearchPlugin\Model\Product\SearchableTrait; +use Sylius\Component\Product\Model\ProductOption as BaseProductOption; + +/** + * @ORM\Entity + * @ORM\Table(name="sylius_product_option") + */ +class ProductOption extends BaseProductOption implements SearchableInterface +{ + use SearchableTrait; +} diff --git a/tests/Application/src/Migrations/Version20211012092353.php b/tests/Application/src/Migrations/Version20211012092353.php new file mode 100644 index 00000000..efbfcfa4 --- /dev/null +++ b/tests/Application/src/Migrations/Version20211012092353.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace App\Migrations; + +use Doctrine\DBAL\Schema\Schema; +use Doctrine\Migrations\AbstractMigration; + +/** + * Auto-generated Migration: Please modify to your needs! + */ +final class Version20211012092353 extends AbstractMigration +{ + public function getDescription(): string + { + return 'Add search columns on product attribute and product option'; + } + + public function up(Schema $schema): void + { + // this up() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE sylius_product_attribute ADD searchable TINYINT(1) DEFAULT \'1\' NOT NULL, ADD filterable TINYINT(1) DEFAULT \'0\' NOT NULL, ADD search_weight SMALLINT UNSIGNED DEFAULT 1 NOT NULL'); + $this->addSql('ALTER TABLE sylius_product_option ADD searchable TINYINT(1) DEFAULT \'1\' NOT NULL, ADD filterable TINYINT(1) DEFAULT \'0\' NOT NULL, ADD search_weight SMALLINT UNSIGNED DEFAULT 1 NOT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE sylius_product_attribute DROP searchable, DROP filterable, DROP search_weight'); + $this->addSql('ALTER TABLE sylius_product_option DROP searchable, DROP filterable, DROP search_weight'); + } +} diff --git a/tests/Application/templates/bundles/SyliusAdminBundle b/tests/Application/templates/bundles/SyliusAdminBundle new file mode 120000 index 00000000..d0ee031e --- /dev/null +++ b/tests/Application/templates/bundles/SyliusAdminBundle @@ -0,0 +1 @@ +../../../../src/Resources/templates/bundles/SyliusAdminBundle/ \ No newline at end of file From adc556428d1d13d24d4e7d2e5fd55fa27d871fa6 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 12 Oct 2021 13:50:26 +0200 Subject: [PATCH 007/142] fix: phpcs errors --- .php-cs-fixer.dist.php | 1 + src/Command/PopulateCommand.php | 1 - .../DocumentableRegistryPass.php | 13 ++++++++++++- src/Index/Indexer.php | 9 +++++---- src/Model/Documentable/DocumentableInterface.php | 15 +++++++++++++++ .../DocumentableMappingProviderTrait.php | 11 +++++++++++ src/Resources/config/jane/jane-configuration.php | 11 +++++++++++ .../config/packages/doctrine_migrations.yaml | 6 ++++++ 8 files changed, 61 insertions(+), 6 deletions(-) diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index d120d935..b0267ce0 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -18,6 +18,7 @@ 'tests/Application/var', 'tests/Application/bin', 'tests/Application/config', + 'generated', ] ) ; diff --git a/src/Command/PopulateCommand.php b/src/Command/PopulateCommand.php index efd4b66e..34c9ea82 100644 --- a/src/Command/PopulateCommand.php +++ b/src/Command/PopulateCommand.php @@ -36,7 +36,6 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output) { - $this->indexer->indexAll(); $output->writeln('ok'); diff --git a/src/DependencyInjection/DocumentableRegistryPass.php b/src/DependencyInjection/DocumentableRegistryPass.php index 9057f4f3..073722c8 100644 --- a/src/DependencyInjection/DocumentableRegistryPass.php +++ b/src/DependencyInjection/DocumentableRegistryPass.php @@ -1,5 +1,16 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\DependencyInjection; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; @@ -8,7 +19,7 @@ class DocumentableRegistryPass implements CompilerPassInterface { - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { $registry = $container->getDefinition('monsieurbiz.search.registry.documentable'); $documentableIds = array_keys($container->findTaggedServiceIds('monsieurbiz.search.documentable')); diff --git a/src/Index/Indexer.php b/src/Index/Indexer.php index 8f0f035b..05f0a235 100644 --- a/src/Index/Indexer.php +++ b/src/Index/Indexer.php @@ -63,7 +63,7 @@ function(LocaleInterface $locale): string { } /** - * Index all documentable object + * Index all documentable object. */ public function indexAll(): void { @@ -89,11 +89,12 @@ private function indexDocumentable(DocumentableInterface $documentable, ?string $newIndex = $indexBuilder->createIndex($indexName, ['index_code' => $documentable->getIndexCode(), 'locale' => strtolower($locale)]); $indexer = $factory->buildIndexer(); - $test = $this->entityManager->getRepository(\get_class($documentable))->findAll(); + $test = $this->entityManager->getRepository(\get_class($documentable))->findAll(); // TODO pagniation foreach ($test as $item) { $item->setCurrentLocale($locale); // if TranslatableInterface - $indexer->scheduleIndex($newIndex, new Document((string) $item->getId(), $this->autoMapper->map($item, Product::class))); - //dump($this->autoMapper->map($item, Product::class));die; + $document = new Document((string) $item->getId(), $this->autoMapper->map($item, Product::class)); + $indexer->scheduleIndex($newIndex, $document); +// dump($this->autoMapper->map($item, Product::class));die; } $indexer->flush(); diff --git a/src/Model/Documentable/DocumentableInterface.php b/src/Model/Documentable/DocumentableInterface.php index 4f4f7d64..8cd81204 100644 --- a/src/Model/Documentable/DocumentableInterface.php +++ b/src/Model/Documentable/DocumentableInterface.php @@ -1,5 +1,16 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Model\Documentable; use JoliCode\Elastically\Mapping\MappingProviderInterface; @@ -12,4 +23,8 @@ public function getIndexCode(): string; public function setMappingProvider(MappingProviderInterface $mapping): void; public function getMappingProvider(): MappingProviderInterface; + +// dataSource +// +// className => Product } diff --git a/src/Model/Documentable/DocumentableMappingProviderTrait.php b/src/Model/Documentable/DocumentableMappingProviderTrait.php index c7a4628b..a8d79e2b 100644 --- a/src/Model/Documentable/DocumentableMappingProviderTrait.php +++ b/src/Model/Documentable/DocumentableMappingProviderTrait.php @@ -1,5 +1,16 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Model\Documentable; use JoliCode\Elastically\Mapping\MappingProviderInterface; diff --git a/src/Resources/config/jane/jane-configuration.php b/src/Resources/config/jane/jane-configuration.php index a78ebb8f..66b92f59 100644 --- a/src/Resources/config/jane/jane-configuration.php +++ b/src/Resources/config/jane/jane-configuration.php @@ -1,5 +1,16 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + return [ 'json-schema-file' => __DIR__ . '/json-schema.json', 'root-class' => 'Model', diff --git a/tests/Application/config/packages/doctrine_migrations.yaml b/tests/Application/config/packages/doctrine_migrations.yaml index e69de29b..765b5c5e 100644 --- a/tests/Application/config/packages/doctrine_migrations.yaml +++ b/tests/Application/config/packages/doctrine_migrations.yaml @@ -0,0 +1,6 @@ +doctrine_migrations: + storage: + table_storage: + table_name: sylius_migrations + migrations_paths: + 'App\Migrations': "%kernel.project_dir%/src/Migrations" From a92132acc9c8b998cc0ba13ff9c0e0b4fdd065b7 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 12 Oct 2021 15:46:16 +0200 Subject: [PATCH 008/142] refactor: Use a documentable object instead of the entity --- src/DependencyInjection/Configuration.php | 18 +++++++ .../DocumentableRegistryPass.php | 26 +++++++-- .../MonsieurBizSyliusSearchExtension.php | 10 +++- src/Index/Indexer.php | 15 ++++-- src/Model/Datasource/DatasourceInterface.php | 19 +++++++ src/Model/Datasource/RepositoryDatasource.php | 37 +++++++++++++ src/Model/Documentable/Documentable.php | 54 +++++++++++++++++++ .../DocumentableDatasourceTrait.php | 31 +++++++++++ .../Documentable/DocumentableInterface.php | 13 +++-- src/MonsieurBizSyliusSearchPlugin.php | 16 ++++++ src/Resources/config/config.yaml | 1 + src/Resources/config/monsieurbiz_search.yaml | 7 +++ src/Resources/config/services.yaml | 11 +--- tests/Application/config/services.yaml | 4 -- .../src/Entity/Product/Product.php | 11 +--- 15 files changed, 238 insertions(+), 35 deletions(-) create mode 100644 src/Model/Datasource/DatasourceInterface.php create mode 100644 src/Model/Datasource/RepositoryDatasource.php create mode 100644 src/Model/Documentable/Documentable.php create mode 100644 src/Model/Documentable/DocumentableDatasourceTrait.php create mode 100644 src/Resources/config/monsieurbiz_search.yaml diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 361af12d..5d7aa102 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -13,6 +13,8 @@ namespace MonsieurBiz\SyliusSearchPlugin\DependencyInjection; +use MonsieurBiz\SyliusSearchPlugin\Mapping\YamlWithLocaleProvider; +use MonsieurBiz\SyliusSearchPlugin\Model\Datasource\RepositoryDatasource; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; @@ -25,6 +27,22 @@ public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder('monsieur_biz_sylius_search'); $rootNode = $treeBuilder->getRootNode(); + $rootNode + ->children() + ->arrayNode('documents') + ->useAttributeAsKey('code', false) + ->defaultValue([]) + ->arrayPrototype() + ->children() + ->scalarNode('source')->isRequired()->cannotBeEmpty()->end() + ->scalarNode('target')->isRequired()->cannotBeEmpty()->end() + ->scalarNode('mapping_provider')->defaultValue(YamlWithLocaleProvider::class)->end() + ->scalarNode('datasource')->defaultValue(RepositoryDatasource::class)->end() + ->end() + ->end() + ->end() + ->end() + ; return $treeBuilder; } diff --git a/src/DependencyInjection/DocumentableRegistryPass.php b/src/DependencyInjection/DocumentableRegistryPass.php index 073722c8..1d25dc28 100644 --- a/src/DependencyInjection/DocumentableRegistryPass.php +++ b/src/DependencyInjection/DocumentableRegistryPass.php @@ -13,8 +13,10 @@ namespace MonsieurBiz\SyliusSearchPlugin\DependencyInjection; +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\Documentable; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; class DocumentableRegistryPass implements CompilerPassInterface @@ -22,9 +24,27 @@ class DocumentableRegistryPass implements CompilerPassInterface public function process(ContainerBuilder $container): void { $registry = $container->getDefinition('monsieurbiz.search.registry.documentable'); - $documentableIds = array_keys($container->findTaggedServiceIds('monsieurbiz.search.documentable')); - foreach ($documentableIds as $documentableId) { - $registry->addMethodCall('register', [$documentableId, new Reference($documentableId)]); + $documentables = $container->getParameter('monsieurbiz.search.config.documents'); + if (!\is_array($documentables)) { + return; + } + foreach ($documentables as $indexCode => $documentableConfiguration) { + $documentableServiceId = 'search.documentable.' . $indexCode; + $documentableDefinition = (new Definition(Documentable::class)) // TODO - move into config + ->setAutowired(true) + ->setArguments([ + '$indexCode' => $indexCode, + '$sourceClass' => $documentableConfiguration['source'], + '$targetClass' => $documentableConfiguration['target'], + ]) + ; + $documentableDefinition = $container->setDefinition($documentableServiceId, $documentableDefinition); + $documentableDefinition->addTag('monsieurbiz.search.documentable'); + $documentableDefinition->addMethodCall('setMappingProvider', [new Reference($documentableConfiguration['mapping_provider'])]); + $documentableDefinition->addMethodCall('setDatasource', [new Reference($documentableConfiguration['datasource'])]); + + // Add documentable into registry + $registry->addMethodCall('register', [$documentableServiceId, new Reference($documentableServiceId)]); } } } diff --git a/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php b/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php index c0ea3829..4e05f1e6 100644 --- a/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php +++ b/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php @@ -20,7 +20,7 @@ final class MonsieurBizSyliusSearchExtension extends Extension { - public const EXTENSION_CONFIG_NAME = 'monsieur_biz_sylius_search'; + public const EXTENSION_CONFIG_NAME = 'monsieurbiz.search.config'; public function load(array $configs, ContainerBuilder $container): void { @@ -32,4 +32,12 @@ public function load(array $configs, ContainerBuilder $container): void $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); $loader->load('services.yaml'); } + + /** + * {@inheritdoc} + */ + public function getAlias() + { + return str_replace(['monsieur_biz'], ['monsieurbiz'], parent::getAlias()); + } } diff --git a/src/Index/Indexer.php b/src/Index/Indexer.php index 05f0a235..a1b31bc4 100644 --- a/src/Index/Indexer.php +++ b/src/Index/Indexer.php @@ -67,6 +67,7 @@ function(LocaleInterface $locale): string { */ public function indexAll(): void { + /** @var DocumentableInterface $documentable */ foreach ($this->documentableRegistry->all() as $documentable) { $this->indexDocumentable($documentable); } @@ -74,7 +75,7 @@ public function indexAll(): void private function indexDocumentable(DocumentableInterface $documentable, ?string $locale = null): void { - if (null === $locale && $documentable instanceof TranslatableInterface) { + if (null === $locale && $documentable->isTranslatable()) { foreach ($this->getLocales() as $localeCode) { $this->indexDocumentable($documentable, $localeCode); } @@ -86,15 +87,19 @@ private function indexDocumentable(DocumentableInterface $documentable, ?string Factory::CONFIG_MAPPINGS_PROVIDER => $documentable->getMappingProvider(), ]); $indexBuilder = $factory->buildIndexBuilder(); - $newIndex = $indexBuilder->createIndex($indexName, ['index_code' => $documentable->getIndexCode(), 'locale' => strtolower($locale)]); + $newIndex = $indexBuilder->createIndex($indexName, [ + 'index_code' => $documentable->getIndexCode(), + 'locale' => $locale ? strtolower($locale) : null, + ]); $indexer = $factory->buildIndexer(); - $test = $this->entityManager->getRepository(\get_class($documentable))->findAll(); // TODO pagniation + $test = $documentable->getDatasource()->getItems($documentable->getSourceClass()); foreach ($test as $item) { - $item->setCurrentLocale($locale); // if TranslatableInterface + if (null !== $locale && $item instanceof TranslatableInterface) { + $item->setCurrentLocale($locale); + } $document = new Document((string) $item->getId(), $this->autoMapper->map($item, Product::class)); $indexer->scheduleIndex($newIndex, $document); -// dump($this->autoMapper->map($item, Product::class));die; } $indexer->flush(); diff --git a/src/Model/Datasource/DatasourceInterface.php b/src/Model/Datasource/DatasourceInterface.php new file mode 100644 index 00000000..16374107 --- /dev/null +++ b/src/Model/Datasource/DatasourceInterface.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Model\Datasource; + +interface DatasourceInterface +{ + public function getItems(string $sourceClass): iterable; +} diff --git a/src/Model/Datasource/RepositoryDatasource.php b/src/Model/Datasource/RepositoryDatasource.php new file mode 100644 index 00000000..b97ba72c --- /dev/null +++ b/src/Model/Datasource/RepositoryDatasource.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Model\Datasource; + +use Doctrine\ORM\EntityManagerInterface; +use Sylius\Component\Resource\Repository\RepositoryInterface; + +class RepositoryDatasource implements DatasourceInterface +{ + private EntityManagerInterface $entityManager; + + public function __construct(EntityManagerInterface $entityManager) + { + $this->entityManager = $entityManager; + } + + public function getItems(string $sourceClass): iterable + { + $repository = $this->entityManager->getRepository($sourceClass); + if ($repository instanceof RepositoryInterface) { + return $repository->createPaginator(); + } + + return $repository->createQueryBuilder('o')->getQuery()->toIterable(); + } +} diff --git a/src/Model/Documentable/Documentable.php b/src/Model/Documentable/Documentable.php new file mode 100644 index 00000000..b683879c --- /dev/null +++ b/src/Model/Documentable/Documentable.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Model\Documentable; + +use Sylius\Component\Resource\Model\TranslatableInterface; + +class Documentable implements DocumentableInterface +{ + use DocumentableDatasourceTrait; + use DocumentableMappingProviderTrait; + private string $indexCode; + private string $sourceClass; + private string $targetClass; + + public function __construct(string $indexCode, string $sourceClass, string $targetClass) + { + $this->indexCode = $indexCode; + $this->sourceClass = $sourceClass; + $this->targetClass = $targetClass; + } + + public function getIndexCode(): string + { + return $this->indexCode; + } + + public function getSourceClass(): string + { + return $this->sourceClass; + } + + public function getTargetClass(): string + { + return $this->targetClass; + } + + public function isTranslatable(): bool + { + $interface = (array) (class_implements($this->getSourceClass()) ?? []); + + return \in_array(TranslatableInterface::class, $interface, true); + } +} diff --git a/src/Model/Documentable/DocumentableDatasourceTrait.php b/src/Model/Documentable/DocumentableDatasourceTrait.php new file mode 100644 index 00000000..f6216231 --- /dev/null +++ b/src/Model/Documentable/DocumentableDatasourceTrait.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Model\Documentable; + +use MonsieurBiz\SyliusSearchPlugin\Model\Datasource\DatasourceInterface; + +trait DocumentableDatasourceTrait +{ + protected DatasourceInterface $datasource; + + public function setDatasource(DatasourceInterface $datasource): void + { + $this->datasource = $datasource; + } + + public function getDatasource(): DatasourceInterface + { + return $this->datasource; + } +} diff --git a/src/Model/Documentable/DocumentableInterface.php b/src/Model/Documentable/DocumentableInterface.php index 8cd81204..5c646e8d 100644 --- a/src/Model/Documentable/DocumentableInterface.php +++ b/src/Model/Documentable/DocumentableInterface.php @@ -14,6 +14,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Model\Documentable; use JoliCode\Elastically\Mapping\MappingProviderInterface; +use MonsieurBiz\SyliusSearchPlugin\Model\Datasource\DatasourceInterface; interface DocumentableInterface { @@ -24,7 +25,13 @@ public function setMappingProvider(MappingProviderInterface $mapping): void; public function getMappingProvider(): MappingProviderInterface; -// dataSource -// -// className => Product + public function getSourceClass(): string; + + public function getTargetClass(): string; + + public function setDatasource(DatasourceInterface $datasource): void; + + public function getDatasource(): DatasourceInterface; + + public function isTranslatable(): bool; } diff --git a/src/MonsieurBizSyliusSearchPlugin.php b/src/MonsieurBizSyliusSearchPlugin.php index 34e51534..22d574d3 100644 --- a/src/MonsieurBizSyliusSearchPlugin.php +++ b/src/MonsieurBizSyliusSearchPlugin.php @@ -16,12 +16,28 @@ use MonsieurBiz\SyliusSearchPlugin\DependencyInjection\DocumentableRegistryPass; use Sylius\Bundle\CoreBundle\Application\SyliusPluginTrait; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; use Symfony\Component\HttpKernel\Bundle\Bundle; final class MonsieurBizSyliusSearchPlugin extends Bundle { use SyliusPluginTrait; + public function getContainerExtension(): ?ExtensionInterface + { + if (null === $this->containerExtension) { + $this->containerExtension = false; + $extension = $this->createContainerExtension(); + if (null !== $extension) { + $this->containerExtension = $extension; + } + } + + return $this->containerExtension instanceof ExtensionInterface + ? $this->containerExtension + : null; + } + public function build(ContainerBuilder $container): void { parent::build($container); diff --git a/src/Resources/config/config.yaml b/src/Resources/config/config.yaml index acdf743b..b512429a 100644 --- a/src/Resources/config/config.yaml +++ b/src/Resources/config/config.yaml @@ -1,2 +1,3 @@ imports: - { resource: "services.yaml" } + - { resource: "monsieurbiz_search.yaml" } diff --git a/src/Resources/config/monsieurbiz_search.yaml b/src/Resources/config/monsieurbiz_search.yaml new file mode 100644 index 00000000..dc7371d9 --- /dev/null +++ b/src/Resources/config/monsieurbiz_search.yaml @@ -0,0 +1,7 @@ +monsieurbiz_sylius_search: + documents: + product: + source: 'Sylius\Component\Core\Model\ProductInterface' + target: 'MonsieurBiz\SyliusSearchPlugin\Generated\Model\Product' + #mapping_provider: '...' # by default monsieurbiz.search.mapper_provider + #dataprovider: '...' # by default MonsieurBiz\SyliusSearchPlugin\Model\Datasource\RepositoryDatasource diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index 11359e7d..fccf1952 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -11,12 +11,6 @@ services: $documentableRegistry: '@monsieurbiz.search.registry.documentable' $configurationDirectory: '@=service("file_locator").locate("@MonsieurBizSyliusSearchPlugin/Resources/config/elasticsearch")' - _instanceof: - MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface: - tags: [ 'monsieurbiz.search.documentable' ] - calls: - - [setMappingProvider, ['@monsieurbiz.search.mapper_provider']] - MonsieurBiz\SyliusSearchPlugin\: resource: '../../*' exclude: '../../{Entity,Migrations,Tests,Kernel.php}' @@ -58,8 +52,7 @@ services: $attributeRepository: '@sylius.repository.product_attribute' # Define our mapping provider - monsieurbiz.search.mapper_provider: - class: MonsieurBiz\SyliusSearchPlugin\Mapping\YamlWithLocaleProvider + MonsieurBiz\SyliusSearchPlugin\Mapping\YamlWithLocaleProvider: decorates: JoliCode\Elastically\Mapping\YamlProvider arguments: $decorated: '@.inner' @@ -68,7 +61,7 @@ services: JoliCode\Elastically\IndexBuilder: arguments: $client: '@monsieurbiz.search.elasticsearch.client' - $mappingProvider: '@monsieurbiz.search.mapper_provider' + $mappingProvider: 'MonsieurBiz\SyliusSearchPlugin\Mapping\YamlWithLocaleProvide' monsieurbiz.search.registry.documentable: class: Sylius\Component\Registry\ServiceRegistry diff --git a/tests/Application/config/services.yaml b/tests/Application/config/services.yaml index 963e956f..2d615dc9 100644 --- a/tests/Application/config/services.yaml +++ b/tests/Application/config/services.yaml @@ -5,7 +5,3 @@ parameters: services: - App\Entity\Product\Product: - tags: [ 'monsieurbiz.search.documentable' ] - calls: - - [setMappingProvider, ['@monsieurbiz.search.mapper_provider']] diff --git a/tests/Application/src/Entity/Product/Product.php b/tests/Application/src/Entity/Product/Product.php index 74e2575f..82f07fa3 100644 --- a/tests/Application/src/Entity/Product/Product.php +++ b/tests/Application/src/Entity/Product/Product.php @@ -14,8 +14,6 @@ namespace App\Entity\Product; use Doctrine\ORM\Mapping as ORM; -use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; -use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableMappingProviderTrait; use Sylius\Component\Core\Model\Product as BaseProduct; use Sylius\Component\Core\Model\ProductTranslation; use Sylius\Component\Product\Model\ProductTranslationInterface; @@ -24,17 +22,10 @@ * @ORM\Entity * @ORM\Table(name="sylius_product") */ -class Product extends BaseProduct implements DocumentableInterface +class Product extends BaseProduct { - use DocumentableMappingProviderTrait; - protected function createTranslation(): ProductTranslationInterface { return new ProductTranslation(); } - - public function getIndexCode(): string - { - return 'product'; - } } From a3374afd52acaf6921e7865a188d71b9c1b773b7 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 12 Oct 2021 16:39:35 +0200 Subject: [PATCH 009/142] fix: product attribute mapping --- generated/Model/Product.php | 16 +++---- generated/Model/ProductAttribute.php | 45 +++++++++---------- generated/Normalizer/JaneObjectNormalizer.php | 2 +- .../Normalizer/ProductAttributeNormalizer.php | 44 ++++++------------ generated/Normalizer/ProductNormalizer.php | 16 +++++-- src/Resources/config/jane/json-schema.json | 5 +-- 6 files changed, 56 insertions(+), 72 deletions(-) diff --git a/generated/Model/Product.php b/generated/Model/Product.php index 96a4e6d1..9cb224bb 100644 --- a/generated/Model/Product.php +++ b/generated/Model/Product.php @@ -67,9 +67,9 @@ class Product /** * * - * @var int + * @var ProductAttribute[] */ - protected $price; + protected $attributes; /** * * @@ -283,22 +283,22 @@ public function setChannels(array $channels) : self /** * * - * @return int + * @return ProductAttribute[] */ - public function getPrice() : int + public function getAttributes() : array { - return $this->price; + return $this->attributes; } /** * * - * @param int $price + * @param ProductAttribute[] $attributes * * @return self */ - public function setPrice(int $price) : self + public function setAttributes(array $attributes) : self { - $this->price = $price; + $this->attributes = $attributes; return $this; } } \ No newline at end of file diff --git a/generated/Model/ProductAttribute.php b/generated/Model/ProductAttribute.php index 7f3990c3..62abda9a 100644 --- a/generated/Model/ProductAttribute.php +++ b/generated/Model/ProductAttribute.php @@ -1,66 +1,61 @@ - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - namespace MonsieurBiz\SyliusSearchPlugin\Generated\Model; class ProductAttribute { /** + * + * * @var string */ protected $name; /** - * @var string|null + * + * + * @var null|mixed */ protected $value; - /** + * + * * @return string */ - public function getName(): string + public function getName() : string { return $this->name; } - /** + * + * * @param string $name * * @return self */ - public function setName(string $name): self + public function setName(string $name) : self { $this->name = $name; - return $this; } - /** - * @return string|null + * + * + * @return null|mixed */ - public function getValue(): ?string + public function getValue() { return $this->value; } - /** - * @param string|null $value + * + * + * @param null|mixed $value * * @return self */ - public function setValue(?string $value): self + public function setValue($value) : self { $this->value = $value; - return $this; } -} +} \ No newline at end of file diff --git a/generated/Normalizer/JaneObjectNormalizer.php b/generated/Normalizer/JaneObjectNormalizer.php index 46ef699f..f72e4535 100644 --- a/generated/Normalizer/JaneObjectNormalizer.php +++ b/generated/Normalizer/JaneObjectNormalizer.php @@ -14,7 +14,7 @@ class JaneObjectNormalizer implements DenormalizerInterface, NormalizerInterface use DenormalizerAwareTrait; use NormalizerAwareTrait; use CheckArray; - protected $normalizers = array('MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Product' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Image' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ImageNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Channel' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ChannelNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductTaxon' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductTaxonNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Taxon' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\TaxonNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\MonsieurBiz\\SyliusSearchPlugin\\Generated\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + protected $normalizers = array('MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Product' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Image' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ImageNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Channel' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ChannelNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductTaxon' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductTaxonNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Taxon' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\TaxonNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductAttribute' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductAttributeNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\MonsieurBiz\\SyliusSearchPlugin\\Generated\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); public function supportsDenormalization($data, $type, $format = null) { return array_key_exists($type, $this->normalizers); diff --git a/generated/Normalizer/ProductAttributeNormalizer.php b/generated/Normalizer/ProductAttributeNormalizer.php index 36219e4a..5c1e640c 100644 --- a/generated/Normalizer/ProductAttributeNormalizer.php +++ b/generated/Normalizer/ProductAttributeNormalizer.php @@ -1,44 +1,30 @@ - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - namespace MonsieurBiz\SyliusSearchPlugin\Generated\Normalizer; use Jane\Component\JsonSchemaRuntime\Reference; use MonsieurBiz\SyliusSearchPlugin\Generated\Runtime\Normalizer\CheckArray; +use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; - class ProductAttributeNormalizer implements DenormalizerInterface, NormalizerInterface, DenormalizerAwareInterface, NormalizerAwareInterface { - use CheckArray; use DenormalizerAwareTrait; use NormalizerAwareTrait; - + use CheckArray; public function supportsDenormalization($data, $type, $format = null) { - return 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductAttribute' === $type; + return $type === 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductAttribute'; } - public function supportsNormalization($data, $format = null) { return $data instanceof \MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductAttribute; } - - public function denormalize($data, $class, $format = null, array $context = []) + public function denormalize($data, $class, $format = null, array $context = array()) { if (isset($data['$ref'])) { return new Reference($data['$ref'], $context['document-origin']); @@ -53,37 +39,35 @@ public function denormalize($data, $class, $format = null, array $context = []) if (\array_key_exists('name', $data)) { $object->setName($data['name']); } - if (\array_key_exists('value', $data) && null !== $data['value']) { + if (\array_key_exists('value', $data) && $data['value'] !== null) { $value = $data['value']; - if (null === $data['value']) { + if (is_null($data['value'])) { $value = $data['value']; - } elseif (\is_string($data['value'])) { + } elseif (isset($data['value'])) { $value = $data['value']; } $object->setValue($value); - } elseif (\array_key_exists('value', $data) && null === $data['value']) { + } + elseif (\array_key_exists('value', $data) && $data['value'] === null) { $object->setValue(null); } - return $object; } - - public function normalize($object, $format = null, array $context = []) + public function normalize($object, $format = null, array $context = array()) { - $data = []; + $data = array(); if (null !== $object->getName()) { $data['name'] = $object->getName(); } if (null !== $object->getValue()) { $value = $object->getValue(); - if (null === $object->getValue()) { + if (is_null($object->getValue())) { $value = $object->getValue(); - } elseif (\is_string($object->getValue())) { + } elseif (!is_null($object->getValue())) { $value = $object->getValue(); } $data['value'] = $value; } - return $data; } -} +} \ No newline at end of file diff --git a/generated/Normalizer/ProductNormalizer.php b/generated/Normalizer/ProductNormalizer.php index 2f8d3503..1a235185 100644 --- a/generated/Normalizer/ProductNormalizer.php +++ b/generated/Normalizer/ProductNormalizer.php @@ -96,8 +96,12 @@ public function denormalize($data, $class, $format = null, array $context = arra } $object->setChannels($values_2); } - if (\array_key_exists('price', $data)) { - $object->setPrice($data['price']); + if (\array_key_exists('attributes', $data)) { + $values_3 = array(); + foreach ($data['attributes'] as $value_5) { + $values_3[] = $this->denormalizer->denormalize($value_5, 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductAttribute', 'json', $context); + } + $object->setAttributes($values_3); } return $object; } @@ -158,8 +162,12 @@ public function normalize($object, $format = null, array $context = array()) } $data['channels'] = $values_2; } - if (null !== $object->getPrice()) { - $data['price'] = $object->getPrice(); + if (null !== $object->getAttributes()) { + $values_3 = array(); + foreach ($object->getAttributes() as $value_5) { + $values_3[] = $this->normalizer->normalize($value_5, 'json', $context); + } + $data['attributes'] = $values_3; } return $data; } diff --git a/src/Resources/config/jane/json-schema.json b/src/Resources/config/jane/json-schema.json index a0206e21..269fab90 100644 --- a/src/Resources/config/jane/json-schema.json +++ b/src/Resources/config/jane/json-schema.json @@ -49,9 +49,6 @@ "$ref": "#/definitions/Channel" } }, - "price": { - "type": "integer" - }, "attributes": { "type": "array", "items": { @@ -117,7 +114,7 @@ "type": "string" }, "value": { - "type": ["null", "string"] + "type": ["null", "mixed"] } } } From 0cae500e1b04ae4185b4256f1efbd02904404031 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Wed, 13 Oct 2021 15:47:57 +0200 Subject: [PATCH 010/142] refactor: replace ProductDTO by a eater model with mapper --- composer.json | 1 + generated/Model/Product.php | 304 ------------------ generated/Normalizer/JaneObjectNormalizer.php | 2 +- generated/Normalizer/ProductNormalizer.php | 174 ---------- src/AutoMapper/ProductMapperConfiguration.php | 79 ++++- src/Event/ProductMapperConfigurationEvent.php | 33 -- src/Model/Product/ProductDTO.php | 20 ++ .../Product/ProductDTONormalizer.php | 93 ++++++ ....yaml => monsieurbiz_product_mapping.yaml} | 0 src/Resources/config/jane/json-schema.json | 56 ---- src/Resources/config/monsieurbiz_search.yaml | 4 +- src/Resources/config/services.yaml | 3 + 12 files changed, 184 insertions(+), 585 deletions(-) delete mode 100644 generated/Model/Product.php delete mode 100644 generated/Normalizer/ProductNormalizer.php delete mode 100644 src/Event/ProductMapperConfigurationEvent.php create mode 100644 src/Model/Product/ProductDTO.php create mode 100644 src/Normalizer/Product/ProductDTONormalizer.php rename src/Resources/config/elasticsearch/{product_mapping.yaml => monsieurbiz_product_mapping.yaml} (100%) diff --git a/composer.json b/composer.json index fb5029b4..728ea916 100644 --- a/composer.json +++ b/composer.json @@ -6,6 +6,7 @@ "license": "MIT", "require": { "php": "^7.4 || ^8.0", + "jacquesbh/eater": "^2.0", "jane-php/automapper-bundle": "^7.1", "jolicode/elastically": "dev-master", "sylius/sylius": "^1.9" diff --git a/generated/Model/Product.php b/generated/Model/Product.php deleted file mode 100644 index 9cb224bb..00000000 --- a/generated/Model/Product.php +++ /dev/null @@ -1,304 +0,0 @@ -id; - } - /** - * - * - * @param int $id - * - * @return self - */ - public function setId(int $id) : self - { - $this->id = $id; - return $this; - } - /** - * - * - * @return string - */ - public function getCode() : string - { - return $this->code; - } - /** - * - * - * @param string $code - * - * @return self - */ - public function setCode(string $code) : self - { - $this->code = $code; - return $this; - } - /** - * - * - * @return bool - */ - public function getEnabled() : bool - { - return $this->enabled; - } - /** - * - * - * @param bool $enabled - * - * @return self - */ - public function setEnabled(bool $enabled) : self - { - $this->enabled = $enabled; - return $this; - } - /** - * - * - * @return string - */ - public function getSlug() : string - { - return $this->slug; - } - /** - * - * - * @param string $slug - * - * @return self - */ - public function setSlug(string $slug) : self - { - $this->slug = $slug; - return $this; - } - /** - * - * - * @return string - */ - public function getName() : string - { - return $this->name; - } - /** - * - * - * @param string $name - * - * @return self - */ - public function setName(string $name) : self - { - $this->name = $name; - return $this; - } - /** - * - * - * @return Taxon - */ - public function getMainTaxon() : Taxon - { - return $this->mainTaxon; - } - /** - * - * - * @param Taxon $mainTaxon - * - * @return self - */ - public function setMainTaxon(Taxon $mainTaxon) : self - { - $this->mainTaxon = $mainTaxon; - return $this; - } - /** - * - * - * @return ProductTaxon[] - */ - public function getProductTaxons() : array - { - return $this->productTaxons; - } - /** - * - * - * @param ProductTaxon[] $productTaxons - * - * @return self - */ - public function setProductTaxons(array $productTaxons) : self - { - $this->productTaxons = $productTaxons; - return $this; - } - /** - * - * - * @return null|string - */ - public function getDescription() : ?string - { - return $this->description; - } - /** - * - * - * @param null|string $description - * - * @return self - */ - public function setDescription(?string $description) : self - { - $this->description = $description; - return $this; - } - /** - * - * - * @return null|Image[] - */ - public function getImages() : ?array - { - return $this->images; - } - /** - * - * - * @param null|Image[] $images - * - * @return self - */ - public function setImages(?array $images) : self - { - $this->images = $images; - return $this; - } - /** - * - * - * @return Channel[] - */ - public function getChannels() : array - { - return $this->channels; - } - /** - * - * - * @param Channel[] $channels - * - * @return self - */ - public function setChannels(array $channels) : self - { - $this->channels = $channels; - return $this; - } - /** - * - * - * @return ProductAttribute[] - */ - public function getAttributes() : array - { - return $this->attributes; - } - /** - * - * - * @param ProductAttribute[] $attributes - * - * @return self - */ - public function setAttributes(array $attributes) : self - { - $this->attributes = $attributes; - return $this; - } -} \ No newline at end of file diff --git a/generated/Normalizer/JaneObjectNormalizer.php b/generated/Normalizer/JaneObjectNormalizer.php index f72e4535..45c4d01a 100644 --- a/generated/Normalizer/JaneObjectNormalizer.php +++ b/generated/Normalizer/JaneObjectNormalizer.php @@ -14,7 +14,7 @@ class JaneObjectNormalizer implements DenormalizerInterface, NormalizerInterface use DenormalizerAwareTrait; use NormalizerAwareTrait; use CheckArray; - protected $normalizers = array('MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Product' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Image' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ImageNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Channel' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ChannelNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductTaxon' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductTaxonNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Taxon' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\TaxonNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductAttribute' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductAttributeNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\MonsieurBiz\\SyliusSearchPlugin\\Generated\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + protected $normalizers = array('MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Image' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ImageNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Channel' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ChannelNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductTaxon' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductTaxonNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Taxon' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\TaxonNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductAttribute' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductAttributeNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\MonsieurBiz\\SyliusSearchPlugin\\Generated\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); public function supportsDenormalization($data, $type, $format = null) { return array_key_exists($type, $this->normalizers); diff --git a/generated/Normalizer/ProductNormalizer.php b/generated/Normalizer/ProductNormalizer.php deleted file mode 100644 index 1a235185..00000000 --- a/generated/Normalizer/ProductNormalizer.php +++ /dev/null @@ -1,174 +0,0 @@ -setId($data['id']); - } - if (\array_key_exists('code', $data)) { - $object->setCode($data['code']); - } - if (\array_key_exists('enabled', $data)) { - $object->setEnabled($data['enabled']); - } - if (\array_key_exists('slug', $data)) { - $object->setSlug($data['slug']); - } - if (\array_key_exists('name', $data)) { - $object->setName($data['name']); - } - if (\array_key_exists('main_taxon', $data)) { - $object->setMainTaxon($this->denormalizer->denormalize($data['main_taxon'], 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Taxon', 'json', $context)); - } - if (\array_key_exists('product_taxons', $data)) { - $values = array(); - foreach ($data['product_taxons'] as $value) { - $values[] = $this->denormalizer->denormalize($value, 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductTaxon', 'json', $context); - } - $object->setProductTaxons($values); - } - if (\array_key_exists('description', $data) && $data['description'] !== null) { - $value_1 = $data['description']; - if (is_null($data['description'])) { - $value_1 = $data['description']; - } elseif (is_string($data['description'])) { - $value_1 = $data['description']; - } - $object->setDescription($value_1); - } - elseif (\array_key_exists('description', $data) && $data['description'] === null) { - $object->setDescription(null); - } - if (\array_key_exists('images', $data) && $data['images'] !== null) { - $value_2 = $data['images']; - if (is_null($data['images'])) { - $value_2 = $data['images']; - } elseif (is_array($data['images']) && $this->isOnlyNumericKeys($data['images'])) { - $values_1 = array(); - foreach ($data['images'] as $value_3) { - $values_1[] = $this->denormalizer->denormalize($value_3, 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Image', 'json', $context); - } - $value_2 = $values_1; - } - $object->setImages($value_2); - } - elseif (\array_key_exists('images', $data) && $data['images'] === null) { - $object->setImages(null); - } - if (\array_key_exists('channels', $data)) { - $values_2 = array(); - foreach ($data['channels'] as $value_4) { - $values_2[] = $this->denormalizer->denormalize($value_4, 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Channel', 'json', $context); - } - $object->setChannels($values_2); - } - if (\array_key_exists('attributes', $data)) { - $values_3 = array(); - foreach ($data['attributes'] as $value_5) { - $values_3[] = $this->denormalizer->denormalize($value_5, 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductAttribute', 'json', $context); - } - $object->setAttributes($values_3); - } - return $object; - } - public function normalize($object, $format = null, array $context = array()) - { - $data = array(); - if (null !== $object->getId()) { - $data['id'] = $object->getId(); - } - if (null !== $object->getCode()) { - $data['code'] = $object->getCode(); - } - if (null !== $object->getEnabled()) { - $data['enabled'] = $object->getEnabled(); - } - if (null !== $object->getSlug()) { - $data['slug'] = $object->getSlug(); - } - if (null !== $object->getName()) { - $data['name'] = $object->getName(); - } - if (null !== $object->getMainTaxon()) { - $data['main_taxon'] = $this->normalizer->normalize($object->getMainTaxon(), 'json', $context); - } - if (null !== $object->getProductTaxons()) { - $values = array(); - foreach ($object->getProductTaxons() as $value) { - $values[] = $this->normalizer->normalize($value, 'json', $context); - } - $data['product_taxons'] = $values; - } - if (null !== $object->getDescription()) { - $value_1 = $object->getDescription(); - if (is_null($object->getDescription())) { - $value_1 = $object->getDescription(); - } elseif (is_string($object->getDescription())) { - $value_1 = $object->getDescription(); - } - $data['description'] = $value_1; - } - if (null !== $object->getImages()) { - $value_2 = $object->getImages(); - if (is_null($object->getImages())) { - $value_2 = $object->getImages(); - } elseif (is_array($object->getImages())) { - $values_1 = array(); - foreach ($object->getImages() as $value_3) { - $values_1[] = $this->normalizer->normalize($value_3, 'json', $context); - } - $value_2 = $values_1; - } - $data['images'] = $value_2; - } - if (null !== $object->getChannels()) { - $values_2 = array(); - foreach ($object->getChannels() as $value_4) { - $values_2[] = $this->normalizer->normalize($value_4, 'json', $context); - } - $data['channels'] = $values_2; - } - if (null !== $object->getAttributes()) { - $values_3 = array(); - foreach ($object->getAttributes() as $value_5) { - $values_3[] = $this->normalizer->normalize($value_5, 'json', $context); - } - $data['attributes'] = $values_3; - } - return $data; - } -} \ No newline at end of file diff --git a/src/AutoMapper/ProductMapperConfiguration.php b/src/AutoMapper/ProductMapperConfiguration.php index 08df7745..b4021e05 100644 --- a/src/AutoMapper/ProductMapperConfiguration.php +++ b/src/AutoMapper/ProductMapperConfiguration.php @@ -15,22 +15,27 @@ use App\Entity\Product\Product; use Jane\Bundle\AutoMapperBundle\Configuration\MapperConfigurationInterface; +use Jane\Component\AutoMapper\AutoMapperInterface; use Jane\Component\AutoMapper\MapperGeneratorMetadataInterface; use Jane\Component\AutoMapper\MapperMetadata; use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; -use MonsieurBiz\SyliusSearchPlugin\Event\ProductMapperConfigurationEvent; -use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Product as ProductDTO; +use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Channel; +use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Image; use MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductAttribute; +use MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductTaxon; +use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Taxon; +use MonsieurBiz\SyliusSearchPlugin\Model\Product\ProductDTO; +use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\ProductInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Sylius\Component\Core\Model\ProductTaxonInterface; -class ProductMapperConfiguration implements MapperConfigurationInterface +final class ProductMapperConfiguration implements MapperConfigurationInterface { - private EventDispatcherInterface $eventDispatcher; + private AutoMapperInterface $autoMapper; - public function __construct(EventDispatcherInterface $eventDispatcher) + public function __construct(AutoMapperInterface $autoMapper) { - $this->eventDispatcher = $eventDispatcher; + $this->autoMapper = $autoMapper; } public function process(MapperGeneratorMetadataInterface $metadata): void @@ -39,7 +44,56 @@ public function process(MapperGeneratorMetadataInterface $metadata): void return; } - $metadata->forMember('attributes', function(ProductInterface $product) { + $metadata->forMember('id', function(ProductInterface $product): int { + return $product->getId(); + }); + + $metadata->forMember('code', function(ProductInterface $product): ?string { + return $product->getCode(); + }); + + $metadata->forMember('enabled', function(ProductInterface $product): bool { + return $product->isEnabled(); + }); + + $metadata->forMember('slug', function(ProductInterface $product): ?string { + return $product->getSlug(); + }); + + $metadata->forMember('name', function(ProductInterface $product): ?string { + return $product->getName(); + }); + + $metadata->forMember('description', function(ProductInterface $product): ?string { + return $product->getDescription(); + }); + + $metadata->forMember('images', function(ProductInterface $product): array { + $images = []; + foreach ($product->getImages() as $image) { + $images[] = $this->autoMapper->map($image, Image::class); + } + + return $images; + }); + + $metadata->forMember('mainTaxon', function(ProductInterface $product): Taxon { + return $this->autoMapper->map($product->getMainTaxon(), Taxon::class); + }); + + $metadata->forMember('product_taxons', function(ProductInterface $product): array { + return array_map(function(ProductTaxonInterface $productTaxon) { + return $this->autoMapper->map($productTaxon, ProductTaxon::class); + }, $product->getProductTaxons()->toArray()); + }); + + $metadata->forMember('channels', function(ProductInterface $product): array { + return array_map(function(ChannelInterface $channel) { + return $this->autoMapper->map($channel, Channel::class); + }, $product->getChannels()->toArray()); + }); + + $metadata->forMember('attributes', function(ProductInterface $product): array { $attributes = []; /** @var \Sylius\Component\Product\Model\ProductAttributeValue $attributeValue */ foreach ($product->getAttributes() as $attributeValue) { @@ -47,22 +101,17 @@ public function process(MapperGeneratorMetadataInterface $metadata): void continue; } $attribute = $attributeValue->getAttribute(); - if ($attribute instanceof SearchableInterface && !$attribute->isSearchable() && !$attribute->isFilterable()) { + if (!$attribute instanceof SearchableInterface || (!$attribute->isSearchable() && !$attribute->isFilterable())) { continue; } $attributeDTO = new ProductAttribute(); $attributeDTO->setName($attributeValue->getName()); $attributeDTO->setValue($attributeValue->getValue()); - $attributes[$attributeValue->getCode()] = $attributeDTO; + $attributes[$attributeValue->getCode()] = $attributeDTO; // we can't use the automapper because value has a mixed type } return $attributes; }); - - $this->eventDispatcher->dispatch( - new ProductMapperConfigurationEvent($metadata), - ProductMapperConfigurationEvent::EVENT_NAME - ); } public function getSource(): string diff --git a/src/Event/ProductMapperConfigurationEvent.php b/src/Event/ProductMapperConfigurationEvent.php deleted file mode 100644 index bb935e57..00000000 --- a/src/Event/ProductMapperConfigurationEvent.php +++ /dev/null @@ -1,33 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Event; - -use Jane\Component\AutoMapper\MapperGeneratorMetadataInterface; -use Symfony\Contracts\EventDispatcher\Event; - -class ProductMapperConfigurationEvent extends Event -{ - public const EVENT_NAME = 'monsieurbiz.search.product.mapper.configuration'; - private MapperGeneratorMetadataInterface $mapperGeneratorMetadata; - - public function __construct(MapperGeneratorMetadataInterface $mapperGeneratorMetadata) - { - $this->mapperGeneratorMetadata = $mapperGeneratorMetadata; - } - - public function getMapperGeneratorMetadata(): MapperGeneratorMetadataInterface - { - return $this->mapperGeneratorMetadata; - } -} diff --git a/src/Model/Product/ProductDTO.php b/src/Model/Product/ProductDTO.php new file mode 100644 index 00000000..e67bc428 --- /dev/null +++ b/src/Model/Product/ProductDTO.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Model\Product; + +use Jacquesbh\Eater\Eater; + +class ProductDTO extends Eater +{ +} diff --git a/src/Normalizer/Product/ProductDTONormalizer.php b/src/Normalizer/Product/ProductDTONormalizer.php new file mode 100644 index 00000000..cab8e59c --- /dev/null +++ b/src/Normalizer/Product/ProductDTONormalizer.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Normalizer\Product; + +use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Channel; +use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Image; +use MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductAttribute; +use MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductTaxon; +use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Taxon; +use MonsieurBiz\SyliusSearchPlugin\Model\Product\ProductDTO; +use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; +use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait; +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; + +final class ProductDTONormalizer extends ObjectNormalizer implements DenormalizerInterface, NormalizerInterface, DenormalizerAwareInterface, NormalizerAwareInterface +{ + use DenormalizerAwareTrait; + use NormalizerAwareTrait; + + public function denormalize($data, string $type, string $format = null, array $context = []) + { + /** @var ProductDTO $object */ + $object = parent::denormalize($data, $type, $format, $context); + + if (\array_key_exists('main_taxon', $data)) { + $object->setMainTaxon($this->denormalizer->denormalize($data['main_taxon'], Taxon::class, 'json', $context)); + unset($data['main_taxon']); + } + + if (\array_key_exists('product_taxons', $data)) { + $values = []; + foreach ($data['product_taxons'] as $value) { + $values[] = $this->denormalizer->denormalize($value, ProductTaxon::class, 'json', $context); + } + $object->setProductTaxons($values); + unset($data['product_taxons']); + } + + if (\array_key_exists('images', $data) && null !== $data['images']) { + $values = []; + foreach ($data['images'] as $value) { + $values[] = $this->denormalizer->denormalize($value, Image::class, 'json', $context); + } + $object->setImages($values); + unset($data['product_taxons']); + } + + if (\array_key_exists('channels', $data)) { + $values = []; + foreach ($data['channels'] as $value) { + $values[] = $this->denormalizer->denormalize($value, Channel::class, 'json', $context); + } + $object->setChannels($values); + unset($data['channels']); + } + + if (\array_key_exists('attributes', $data)) { + $values = []; + foreach ($data['attributes'] as $key => $value) { + $values[$key] = $this->denormalizer->denormalize($value, ProductAttribute::class, 'json', $context); + } + $object->setAttributes($values); + unset($data['channels']); + } + + return $object; + } + + public function supportsDenormalization($data, string $type, string $format = null) + { + return ProductDTO::class === $type; + } + + public function supportsNormalization($data, string $format = null) + { + return false; + } +} diff --git a/src/Resources/config/elasticsearch/product_mapping.yaml b/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml similarity index 100% rename from src/Resources/config/elasticsearch/product_mapping.yaml rename to src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml diff --git a/src/Resources/config/jane/json-schema.json b/src/Resources/config/jane/json-schema.json index 269fab90..77ae08b3 100644 --- a/src/Resources/config/jane/json-schema.json +++ b/src/Resources/config/jane/json-schema.json @@ -1,62 +1,6 @@ { "$schema": "http://json-schema.org/2019-09/schema#", "definitions": { - "Product": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "code": { - "type": "string" - }, - "enabled": { - "type": "boolean" - }, - "slug": { - "type": "string" - }, - "name": { - "type": "string" - }, - "main_taxon": { - "$ref": "#/definitions/Taxon" - }, - "product_taxons": { - "type": "array", - "items": { - "$ref": "#/definitions/ProductTaxon" - } - }, - "description": { - "type": [ - "null", - "string" - ] - }, - "images": { - "type": [ - "null", - "array" - ], - "items": { - "$ref": "#/definitions/Image" - } - }, - "channels": { - "type": "array", - "items": { - "$ref": "#/definitions/Channel" - } - }, - "attributes": { - "type": "array", - "items": { - "$ref": "#/definitions/ProductAttribute" - } - } - } - }, "Image": { "type": "object", "properties": { diff --git a/src/Resources/config/monsieurbiz_search.yaml b/src/Resources/config/monsieurbiz_search.yaml index dc7371d9..2cb64573 100644 --- a/src/Resources/config/monsieurbiz_search.yaml +++ b/src/Resources/config/monsieurbiz_search.yaml @@ -1,7 +1,7 @@ monsieurbiz_sylius_search: documents: - product: + monsieurbiz_product: source: 'Sylius\Component\Core\Model\ProductInterface' - target: 'MonsieurBiz\SyliusSearchPlugin\Generated\Model\Product' + target: 'MonsieurBiz\SyliusSearchPlugin\Model\Product\ProductDTO' #mapping_provider: '...' # by default monsieurbiz.search.mapper_provider #dataprovider: '...' # by default MonsieurBiz\SyliusSearchPlugin\Model\Datasource\RepositoryDatasource diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index fccf1952..015fb559 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -20,6 +20,9 @@ services: tags: - { name: form.type_extension } + MonsieurBiz\SyliusSearchPlugin\Generated\: + resource: '../../../generated' + JoliCode\Elastically\IndexNameMapper: arguments: $prefix: null # or a string to prefix index name From 080f5f8da1b63f2e47e63d5470d66e8ac2e0ba30 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Wed, 13 Oct 2021 15:49:29 +0200 Subject: [PATCH 011/142] fix: use target class in automapper after refactor documentable --- src/Index/Indexer.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Index/Indexer.php b/src/Index/Indexer.php index a1b31bc4..7d460050 100644 --- a/src/Index/Indexer.php +++ b/src/Index/Indexer.php @@ -17,12 +17,12 @@ use Elastica\Document; use Jane\Component\AutoMapper\AutoMapperInterface; use JoliCode\Elastically\Factory; -use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Product; use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; use Sylius\Component\Locale\Model\LocaleInterface; use Sylius\Component\Registry\ServiceRegistryInterface; use Sylius\Component\Resource\Model\TranslatableInterface; use Sylius\Component\Resource\Repository\RepositoryInterface; +use Symfony\Component\Serializer\SerializerInterface; final class Indexer { @@ -31,17 +31,20 @@ final class Indexer private array $locales = []; private EntityManagerInterface $entityManager; private AutoMapperInterface $autoMapper; + private SerializerInterface $serializer; public function __construct( ServiceRegistryInterface $documentableRegistry, RepositoryInterface $localeRepository, EntityManagerInterface $entityManager, - AutoMapperInterface $autoMapper + AutoMapperInterface $autoMapper, + SerializerInterface $serializer ) { $this->documentableRegistry = $documentableRegistry; $this->localeRepository = $localeRepository; $this->entityManager = $entityManager; $this->autoMapper = $autoMapper; + $this->serializer = $serializer; } /** @@ -85,6 +88,7 @@ private function indexDocumentable(DocumentableInterface $documentable, ?string $indexName = $this->getIndexName($documentable, $locale); $factory = new Factory([ Factory::CONFIG_MAPPINGS_PROVIDER => $documentable->getMappingProvider(), + Factory::CONFIG_SERIALIZER => $this->serializer, ]); $indexBuilder = $factory->buildIndexBuilder(); $newIndex = $indexBuilder->createIndex($indexName, [ @@ -98,8 +102,8 @@ private function indexDocumentable(DocumentableInterface $documentable, ?string if (null !== $locale && $item instanceof TranslatableInterface) { $item->setCurrentLocale($locale); } - $document = new Document((string) $item->getId(), $this->autoMapper->map($item, Product::class)); - $indexer->scheduleIndex($newIndex, $document); + $dto = $this->autoMapper->map($item, $documentable->getTargetClass()); + $indexer->scheduleIndex($newIndex, new Document((string) $item->getId(), $dto)); } $indexer->flush(); From 60b2ba7a1049e66bb104963efdda86d950ddc8d5 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Wed, 13 Oct 2021 17:54:49 +0200 Subject: [PATCH 012/142] refacor: use a pagnitor in the repostiroy datasource --- src/Index/Indexer.php | 3 +-- src/Model/Datasource/RepositoryDatasource.php | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/Index/Indexer.php b/src/Index/Indexer.php index 7d460050..d2507259 100644 --- a/src/Index/Indexer.php +++ b/src/Index/Indexer.php @@ -97,8 +97,7 @@ private function indexDocumentable(DocumentableInterface $documentable, ?string ]); $indexer = $factory->buildIndexer(); - $test = $documentable->getDatasource()->getItems($documentable->getSourceClass()); - foreach ($test as $item) { + foreach ($documentable->getDatasource()->getItems($documentable->getSourceClass()) as $item) { if (null !== $locale && $item instanceof TranslatableInterface) { $item->setCurrentLocale($locale); } diff --git a/src/Model/Datasource/RepositoryDatasource.php b/src/Model/Datasource/RepositoryDatasource.php index b97ba72c..7f547434 100644 --- a/src/Model/Datasource/RepositoryDatasource.php +++ b/src/Model/Datasource/RepositoryDatasource.php @@ -14,6 +14,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Model\Datasource; use Doctrine\ORM\EntityManagerInterface; +use Pagerfanta\Pagerfanta; use Sylius\Component\Resource\Repository\RepositoryInterface; class RepositoryDatasource implements DatasourceInterface @@ -29,7 +30,20 @@ public function getItems(string $sourceClass): iterable { $repository = $this->entityManager->getRepository($sourceClass); if ($repository instanceof RepositoryInterface) { - return $repository->createPaginator(); + /** @var Pagerfanta $paginator */ + $paginator = $repository->createPaginator(); + $page = 1; + while ($paginator->hasNextPage()) { + $paginator->setCurrentPage($page); + foreach ($paginator as $item) { + yield $item; + } + if ($paginator->hasNextPage()) { + $page = $paginator->getNextPage(); + } + } + + return null; } return $repository->createQueryBuilder('o')->getQuery()->toIterable(); From 1bc6bd54d845dcc6a7437467228d0a999a772a50 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Wed, 13 Oct 2021 17:55:40 +0200 Subject: [PATCH 013/142] feat: add basic search command --- src/Command/SearchCommand.php | 84 +++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/Command/SearchCommand.php diff --git a/src/Command/SearchCommand.php b/src/Command/SearchCommand.php new file mode 100644 index 00000000..e3465e6b --- /dev/null +++ b/src/Command/SearchCommand.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Command; + +use Elastica\Query; +use Elastica\Query\MultiMatch; +use JoliCode\Elastically\Factory; +use MonsieurBiz\SyliusSearchPlugin\Index\Indexer; +use MonsieurBiz\SyliusSearchPlugin\Model\Product\ProductDTO; +use Pagerfanta\Elastica\ElasticaAdapter; +use Pagerfanta\Pagerfanta; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Serializer\SerializerInterface; + +class SearchCommand extends Command +{ + protected static $defaultName = 'monsieurbiz:search:search'; + private Indexer $indexer; + private SerializerInterface $serializer; + + public function __construct(Indexer $indexer, SerializerInterface $serializer, $name = null) + { + parent::__construct($name); + $this->indexer = $indexer; + $this->serializer = $serializer; + } + + protected function configure(): void + { + parent::configure(); + $this->addArgument('query', InputArgument::REQUIRED, 'Search query'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + $factory = new Factory([ + Factory::CONFIG_INDEX_CLASS_MAPPING => [ + 'monsieurbiz_product_fr_fr' => ProductDTO::class, + ], + Factory::CONFIG_SERIALIZER => $this->serializer, + ]); + + $client = $factory->buildClient(); + + $query = $input->getArgument('query'); + + $searchQuery = new MultiMatch(); + $searchQuery->setFields([ + 'name^5', + 'description', + ]); + $searchQuery->setQuery($query); + $searchQuery->setType(MultiMatch::TYPE_MOST_FIELDS); + + $foundPosts = new Pagerfanta(new ElasticaAdapter($client->getIndex('monsieurbiz_product_fr_fr'), Query::create($searchQuery))); + $io->title('Search result for: ' . $query); + $io->section('Nb results: ' . $foundPosts->getNbResults()); + $documents = []; + foreach ($foundPosts as $result) { + /** @var ProductDTO $productDTO */ + $productDTO = $result->getModel(); + $documents[] = [$result->getScore(), $productDTO->getId()]; + } + $io->table(['Score', 'Document ID'], $documents); + + return Command::SUCCESS; + } +} From 4d4c556190bcaf16671e45dfddf4919c13f1876f Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Fri, 15 Oct 2021 10:26:43 +0200 Subject: [PATCH 014/142] fix: index the right attribute value and add attribute code in document --- generated/Model/ProductAttribute.php | 27 +++++++++++++++++++ .../Normalizer/ProductAttributeNormalizer.php | 6 +++++ src/AutoMapper/ProductMapperConfiguration.php | 14 +++++----- src/Resources/config/jane/json-schema.json | 3 +++ 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/generated/Model/ProductAttribute.php b/generated/Model/ProductAttribute.php index 62abda9a..80b2a4f7 100644 --- a/generated/Model/ProductAttribute.php +++ b/generated/Model/ProductAttribute.php @@ -4,6 +4,12 @@ class ProductAttribute { + /** + * + * + * @var string + */ + protected $code; /** * * @@ -16,6 +22,27 @@ class ProductAttribute * @var null|mixed */ protected $value; + /** + * + * + * @return string + */ + public function getCode() : string + { + return $this->code; + } + /** + * + * + * @param string $code + * + * @return self + */ + public function setCode(string $code) : self + { + $this->code = $code; + return $this; + } /** * * diff --git a/generated/Normalizer/ProductAttributeNormalizer.php b/generated/Normalizer/ProductAttributeNormalizer.php index 5c1e640c..2108ca67 100644 --- a/generated/Normalizer/ProductAttributeNormalizer.php +++ b/generated/Normalizer/ProductAttributeNormalizer.php @@ -36,6 +36,9 @@ public function denormalize($data, $class, $format = null, array $context = arra if (null === $data || false === \is_array($data)) { return $object; } + if (\array_key_exists('code', $data)) { + $object->setCode($data['code']); + } if (\array_key_exists('name', $data)) { $object->setName($data['name']); } @@ -56,6 +59,9 @@ public function denormalize($data, $class, $format = null, array $context = arra public function normalize($object, $format = null, array $context = array()) { $data = array(); + if (null !== $object->getCode()) { + $data['code'] = $object->getCode(); + } if (null !== $object->getName()) { $data['name'] = $object->getName(); } diff --git a/src/AutoMapper/ProductMapperConfiguration.php b/src/AutoMapper/ProductMapperConfiguration.php index b4021e05..50a91d28 100644 --- a/src/AutoMapper/ProductMapperConfiguration.php +++ b/src/AutoMapper/ProductMapperConfiguration.php @@ -28,6 +28,7 @@ use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\ProductInterface; use Sylius\Component\Core\Model\ProductTaxonInterface; +use Sylius\Component\Product\Model\ProductAttributeValue; final class ProductMapperConfiguration implements MapperConfigurationInterface { @@ -94,9 +95,9 @@ public function process(MapperGeneratorMetadataInterface $metadata): void }); $metadata->forMember('attributes', function(ProductInterface $product): array { - $attributes = []; - /** @var \Sylius\Component\Product\Model\ProductAttributeValue $attributeValue */ - foreach ($product->getAttributes() as $attributeValue) { + $currentLocale = $product->getTranslation()->getLocale(); + /** @var ProductAttributeValue $attributeValue */ + foreach ($product->getAttributesByLocale($currentLocale, $currentLocale) as $attributeValue) { if (null === $attributeValue->getName() || null === $attributeValue->getValue()) { continue; } @@ -104,10 +105,9 @@ public function process(MapperGeneratorMetadataInterface $metadata): void if (!$attribute instanceof SearchableInterface || (!$attribute->isSearchable() && !$attribute->isFilterable())) { continue; } - $attributeDTO = new ProductAttribute(); - $attributeDTO->setName($attributeValue->getName()); - $attributeDTO->setValue($attributeValue->getValue()); - $attributes[$attributeValue->getCode()] = $attributeDTO; // we can't use the automapper because value has a mixed type + $attributeDTO = $this->autoMapper->map($attributeValue, ProductAttribute::class); + $attributeDTO->setValue($attributeValue->getValue()); // we can't use the automapper for the value because it has a mixed type + $attributes[$attributeValue->getCode()] = $attributeDTO; } return $attributes; diff --git a/src/Resources/config/jane/json-schema.json b/src/Resources/config/jane/json-schema.json index 77ae08b3..0f557c9a 100644 --- a/src/Resources/config/jane/json-schema.json +++ b/src/Resources/config/jane/json-schema.json @@ -54,6 +54,9 @@ "ProductAttribute": { "type": "object", "properties": { + "code": { + "type": "string" + }, "name": { "type": "string" }, From ad72842a686fa7dc702f9c546d9c51e092173115 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Fri, 15 Oct 2021 10:30:27 +0200 Subject: [PATCH 015/142] feat: add search classes, append attributes in mapping --- composer.json | 1 + .../DocumentableRegistryPass.php | 4 + .../MonsieurBizSyliusSearchExtension.php | 5 + .../RegisterSearchRequestPass.php | 35 +++++ ...ppendProductAttributeMappingSubscriber.php | 76 ++++++++++ src/MonsieurBizSyliusSearchPlugin.php | 2 + .../ProductAttributeRepositoryInterface.php | 3 +- .../monsieurbiz_product_mapping.yaml | 3 +- src/Resources/config/services.yaml | 8 + src/Search/Request/Search/Product.php | 139 ++++++++++++++++++ src/Search/RequestFactory.php | 38 +++++ src/Search/RequestInterface.php | 34 +++++ src/Search/Response.php | 47 ++++++ src/Search/Response/FilterInterface.php | 18 +++ src/Search/ResponseInterface.php | 24 +++ src/Search/Search.php | 47 ++++++ src/Search/SearchInterface.php | 19 +++ 17 files changed, 501 insertions(+), 2 deletions(-) create mode 100644 src/DependencyInjection/RegisterSearchRequestPass.php create mode 100644 src/EventSubscriber/AppendProductAttributeMappingSubscriber.php create mode 100644 src/Search/Request/Search/Product.php create mode 100644 src/Search/RequestFactory.php create mode 100644 src/Search/RequestInterface.php create mode 100644 src/Search/Response.php create mode 100644 src/Search/Response/FilterInterface.php create mode 100644 src/Search/ResponseInterface.php create mode 100644 src/Search/Search.php create mode 100644 src/Search/SearchInterface.php diff --git a/composer.json b/composer.json index 728ea916..755ada0e 100644 --- a/composer.json +++ b/composer.json @@ -6,6 +6,7 @@ "license": "MIT", "require": { "php": "^7.4 || ^8.0", + "babdev/pagerfanta-bundle": "^2.5", "jacquesbh/eater": "^2.0", "jane-php/automapper-bundle": "^7.1", "jolicode/elastically": "dev-master", diff --git a/src/DependencyInjection/DocumentableRegistryPass.php b/src/DependencyInjection/DocumentableRegistryPass.php index 1d25dc28..352f36a0 100644 --- a/src/DependencyInjection/DocumentableRegistryPass.php +++ b/src/DependencyInjection/DocumentableRegistryPass.php @@ -23,6 +23,10 @@ class DocumentableRegistryPass implements CompilerPassInterface { public function process(ContainerBuilder $container): void { + if (!$container->hasDefinition('monsieurbiz.search.registry.documentable')) { + return; + } + $registry = $container->getDefinition('monsieurbiz.search.registry.documentable'); $documentables = $container->getParameter('monsieurbiz.search.config.documents'); if (!\is_array($documentables)) { diff --git a/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php b/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php index 4e05f1e6..100162aa 100644 --- a/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php +++ b/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php @@ -13,6 +13,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\DependencyInjection; +use MonsieurBiz\SyliusSearchPlugin\Search\RequestInterface; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\Extension; @@ -31,6 +32,10 @@ public function load(array $configs, ContainerBuilder $container): void $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); $loader->load('services.yaml'); + + $container->registerForAutoconfiguration(RequestInterface::class) + ->addTag('monsieurbiz.search.request') + ; } /** diff --git a/src/DependencyInjection/RegisterSearchRequestPass.php b/src/DependencyInjection/RegisterSearchRequestPass.php new file mode 100644 index 00000000..c76aab82 --- /dev/null +++ b/src/DependencyInjection/RegisterSearchRequestPass.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\DependencyInjection; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +class RegisterSearchRequestPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container): void + { + if (!$container->hasDefinition('monsieurbiz.search.registry.search_request')) { + return; + } + + $registry = $container->getDefinition('monsieurbiz.search.registry.search_request'); + foreach ($container->findTaggedServiceIds('monsieurbiz.search.request') as $serviceId => $tags) { + foreach ($tags as $tag) { + $registry->addMethodCall('register', [$tag['id'] ?? $serviceId, new Reference($serviceId)]); + } + } + } +} diff --git a/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php b/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php new file mode 100644 index 00000000..5b1d94d9 --- /dev/null +++ b/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\EventSubscriber; + +use MonsieurBiz\SyliusSearchPlugin\Event\MappingProviderEvent; +use MonsieurBiz\SyliusSearchPlugin\Repository\ProductAttributeRepositoryInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class AppendProductAttributeMappingSubscriber implements EventSubscriberInterface +{ + private ProductAttributeRepositoryInterface $productAttributeRepository; + + public function __construct(ProductAttributeRepositoryInterface $productAttributeRepository) + { + $this->productAttributeRepository = $productAttributeRepository; + } + + public static function getSubscribedEvents() + { + return [ + MappingProviderEvent::EVENT_NAME => 'omMappingProvider', + ]; + } + + public function omMappingProvider(MappingProviderEvent $event): void + { + if ('monsieurbiz_product' !== $event->getIndexCode()) { + return; + } + $mapping = $event->getMapping(); + if (null === $mapping || !$mapping->offsetExists('mappings')) { + return; + } + $mappings = $mapping->offsetGet('mappings'); + foreach ($this->productAttributeRepository->findIsSearchableOrFilterable() as $productAttribute) { + if (!\array_key_exists('attributes', $mappings['properties'])) { + $mappings['properties']['attributes'] = [ + 'type' => 'nested', + 'properties' => [], + ]; + } + $mappings['properties']['attributes']['properties'][$productAttribute->getCode()] = [ + 'type' => 'nested', + 'properties' => [ + 'code' => ['type' => 'keyword'], + 'name' => ['type' => 'keyword'], + 'value' => ['type' => 'text'], + ], + ]; + + if ($productAttribute->isFilterable()) { + $mappings['properties']['attributes']['properties'][$productAttribute->getCode()]['properties']['value']['fields'] = [ + 'keyword' => ['type' => 'keyword'], + ]; + } + + if ($productAttribute->isSearchable()) { + // TODO replace to a configurable value + $mappings['properties']['attributes']['properties'][$productAttribute->getCode()]['properties']['value']['analyzer'] = 'search_standard'; + } + } + + $mapping->offsetSet('mappings', $mappings); + } +} diff --git a/src/MonsieurBizSyliusSearchPlugin.php b/src/MonsieurBizSyliusSearchPlugin.php index 22d574d3..4c46b17f 100644 --- a/src/MonsieurBizSyliusSearchPlugin.php +++ b/src/MonsieurBizSyliusSearchPlugin.php @@ -14,6 +14,7 @@ namespace MonsieurBiz\SyliusSearchPlugin; use MonsieurBiz\SyliusSearchPlugin\DependencyInjection\DocumentableRegistryPass; +use MonsieurBiz\SyliusSearchPlugin\DependencyInjection\RegisterSearchRequestPass; use Sylius\Bundle\CoreBundle\Application\SyliusPluginTrait; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; @@ -42,5 +43,6 @@ public function build(ContainerBuilder $container): void { parent::build($container); $container->addCompilerPass(new DocumentableRegistryPass()); + $container->addCompilerPass(new RegisterSearchRequestPass()); } } diff --git a/src/Repository/ProductAttributeRepositoryInterface.php b/src/Repository/ProductAttributeRepositoryInterface.php index cc3a8252..5935c035 100644 --- a/src/Repository/ProductAttributeRepositoryInterface.php +++ b/src/Repository/ProductAttributeRepositoryInterface.php @@ -13,12 +13,13 @@ namespace MonsieurBiz\SyliusSearchPlugin\Repository; +use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; use Sylius\Component\Product\Model\ProductAttributeInterface; interface ProductAttributeRepositoryInterface { /** - * @return ProductAttributeInterface[] + * @return ProductAttributeInterface&SearchableInterface[] */ public function findIsSearchableOrFilterable(): array; } diff --git a/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml b/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml index 89dd809b..cb88fed2 100644 --- a/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml +++ b/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml @@ -19,7 +19,7 @@ mappings: properties: path: type: keyword - mainTaxon: + main_taxon: type: nested properties: code: @@ -48,3 +48,4 @@ mappings: type: integer price: type: integer # todo - WIP + # attributes mapping is managed dynamically diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index 015fb559..b2d2990e 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -1,5 +1,6 @@ parameters: monsieurbiz.search.model.documentable.interface: MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface + monsieurbiz.search.request.interface: MonsieurBiz\SyliusSearchPlugin\Search\RequestInterface services: @@ -9,6 +10,7 @@ services: public: false bind: $documentableRegistry: '@monsieurbiz.search.registry.documentable' + $searchRequestsRegistry: '@monsieurbiz.search.registry.search_request' $configurationDirectory: '@=service("file_locator").locate("@MonsieurBizSyliusSearchPlugin/Resources/config/elasticsearch")' MonsieurBiz\SyliusSearchPlugin\: @@ -71,3 +73,9 @@ services: arguments: $className: '%monsieurbiz.search.model.documentable.interface%' $context: 'documentable' + + monsieurbiz.search.registry.search_request: + class: Sylius\Component\Registry\ServiceRegistry + arguments: + $className: '%monsieurbiz.search.request.interface%' + $context: 'documentable' diff --git a/src/Search/Request/Search/Product.php b/src/Search/Request/Search/Product.php new file mode 100644 index 00000000..8d82faf8 --- /dev/null +++ b/src/Search/Request/Search/Product.php @@ -0,0 +1,139 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Search; + +use Elastica\Aggregation\Nested; +use Elastica\Aggregation\Terms; +use Elastica\Query; +use Elastica\Query\MultiMatch; +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use MonsieurBiz\SyliusSearchPlugin\Repository\ProductAttributeRepositoryInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\RequestInterface; +use Sylius\Component\Registry\ServiceRegistryInterface; + +class Product implements RequestInterface +{ + private DocumentableInterface $documentable; + + private array $queryParameters = []; + private ProductAttributeRepositoryInterface $productAttributeRepository; + + public function __construct( + ServiceRegistryInterface $documentableRegistry, + ProductAttributeRepositoryInterface $productAttributeRepository + ) { + //TODO check if exist, return a dummy documentable if not + $this->documentable = $documentableRegistry->get('search.documentable.monsieurbiz_product'); + $this->productAttributeRepository = $productAttributeRepository; + } + + public function getType(): string + { + return RequestInterface::SEARCH_TYPE; + } + + public function getDocumentable(): DocumentableInterface + { + return $this->documentable; + } + + public function setQueryParameters(array $parameters): void + { + $this->queryParameters = $parameters; + } + + public function getQuery(): Query + { + if (!\array_key_exists('query_text', $this->queryParameters)) { + throw new \Exception('missing query text'); //todo + } + + $enableFilter = new Query\Terms('enabled', [true]); + // todo add channel filter + + $searchCode = new Query\Terms('code', [$this->queryParameters['query_text']]); + + $nameAndDescriptionQuery = new MultiMatch(); + $nameAndDescriptionQuery->setFields([ + 'name^5', // todo configuration + 'description', // move to should ? score impact but not include in result + ]); + $nameAndDescriptionQuery->setQuery($this->queryParameters['query_text']); + $nameAndDescriptionQuery->setType(MultiMatch::TYPE_MOST_FIELDS); + + $searchQuery = new Query\BoolQuery(); + $searchQuery + ->addShould($searchCode) + ->addShould($nameAndDescriptionQuery) + ; + + $this->addAttributesQueries($searchQuery); + + $bool = new Query\BoolQuery(); + $bool->addFilter($enableFilter); + $bool->addMust($searchQuery); + + $esQuery = Query::create($bool); + $this->addAggregations($esQuery); + + return $esQuery; + } + + public function supports(string $type, string $documentableCode): bool + { + return $type == $this->getType() && $this->getDocumentable()->getIndexCode() == $documentableCode; + } + + private function addAttributesQueries(Query\BoolQuery $searchQuery): void + { + foreach ($this->productAttributeRepository->findIsSearchableOrFilterable() as $productAttribute) { + if (!$productAttribute->isSearchable()) { + continue; + } + + $attributeValueQuery = new MultiMatch(); + $attributeValueQuery->setFields([ + sprintf('attributes.%s.value^%d', $productAttribute->getCode(), $productAttribute->getSearchWeight()), + ]); + $attributeValueQuery->setQuery($this->queryParameters['query_text']); + + $attributeQuery = new Query\Nested(); + $attributeQuery->setPath(sprintf('attributes.%s', $productAttribute->getCode()))->setQuery($attributeValueQuery); + + $attributesQuery = new Query\Nested(); + $attributesQuery->setPath('attributes')->setQuery($attributeQuery); + + $searchQuery->addShould($attributeQuery); + } + } + + private function addAggregations(Query $query): void + { + foreach ($this->productAttributeRepository->findIsSearchableOrFilterable() as $productAttribute) { + if (!$productAttribute->isFilterable()) { + continue; + } + $attributeValuesAgg = new Terms('values'); + $attributeValuesAgg->setField(sprintf('attributes.%s.value.keyword', $productAttribute->getCode())); + + $attributeAgg = new Nested($productAttribute->getCode(), sprintf('attributes.%s', $productAttribute->getCode())); + $attributeAgg->addAggregation($attributeValuesAgg); + + $attributesAgg = new Nested('attributes', 'attributes'); + $attributesAgg->addAggregation($attributeAgg); + + $query->addAggregation($attributesAgg); + } + } +} diff --git a/src/Search/RequestFactory.php b/src/Search/RequestFactory.php new file mode 100644 index 00000000..ca731e1f --- /dev/null +++ b/src/Search/RequestFactory.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search; + +use Sylius\Component\Registry\ServiceRegistryInterface; + +class RequestFactory +{ + private ServiceRegistryInterface $searchRequestsRegistry; + + public function __construct(ServiceRegistryInterface $searchRequestsRegistry) + { + $this->searchRequestsRegistry = $searchRequestsRegistry; + } + + public function create(string $type, string $documentType): RequestInterface + { + /** @var RequestInterface $request */ + foreach ($this->searchRequestsRegistry->all() as $request) { + if ($request->supports($type, $documentType)) { + return $request; + } + } + + throw new \Exception('Unknow request type'); // TODO + } +} diff --git a/src/Search/RequestInterface.php b/src/Search/RequestInterface.php new file mode 100644 index 00000000..99eee77f --- /dev/null +++ b/src/Search/RequestInterface.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search; + +use Elastica\Query; +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; + +interface RequestInterface +{ + public const SEARCH_TYPE = 'search'; + public const TAXON_TYPE = 'taxon'; + public const INSTANT_TYPE = 'instant'; + + public function getType(): string; + + public function getDocumentable(): DocumentableInterface; + + public function getQuery(): Query; + + public function supports(string $type, string $documentableCode): bool; + + public function setQueryParameters(array $parameters): void; +} diff --git a/src/Search/Response.php b/src/Search/Response.php new file mode 100644 index 00000000..ea2115ee --- /dev/null +++ b/src/Search/Response.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search; + +use Pagerfanta\Adapter\AdapterInterface; +use Pagerfanta\Pagerfanta; + +class Response implements ResponseInterface +{ + private AdapterInterface $adapter; + + public function __construct(AdapterInterface $adapter) + { + $this->adapter = $adapter; + } + + public function getIterator() + { + return $this->createPaginator(); + } + + public function count() + { + return $this->createPaginator()->getNbResults(); + } + + public function getFilters(): array + { + return []; + } + + private function createPaginator(): Pagerfanta + { + return new Pagerfanta($this->adapter); + } +} diff --git a/src/Search/Response/FilterInterface.php b/src/Search/Response/FilterInterface.php new file mode 100644 index 00000000..528a9d5f --- /dev/null +++ b/src/Search/Response/FilterInterface.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Response; + +interface FilterInterface +{ +} diff --git a/src/Search/ResponseInterface.php b/src/Search/ResponseInterface.php new file mode 100644 index 00000000..4e7b3f7f --- /dev/null +++ b/src/Search/ResponseInterface.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search; + +use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterInterface; + +interface ResponseInterface extends \IteratorAggregate, \Countable +{ + /** + * @return FilterInterface[] + */ + public function getFilters(): array; +} diff --git a/src/Search/Search.php b/src/Search/Search.php new file mode 100644 index 00000000..87d6f35d --- /dev/null +++ b/src/Search/Search.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search; + +use JoliCode\Elastically\Factory; +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use Pagerfanta\Elastica\ElasticaAdapter; +use Symfony\Component\Serializer\SerializerInterface; + +class Search implements SearchInterface +{ + private SerializerInterface $serializer; + + public function __construct(SerializerInterface $serializer) + { + $this->serializer = $serializer; + } + + public function query(RequestInterface $request): ResponseInterface + { + $factory = new Factory([ + Factory::CONFIG_INDEX_CLASS_MAPPING => [ + $this->getIndexName($request->getDocumentable(), 'fr_FR') => $request->getDocumentable()->getTargetClass(), + ], + Factory::CONFIG_SERIALIZER => $this->serializer, + ]); + $client = $factory->buildClient(); + + return new Response(new ElasticaAdapter($client->getIndex('monsieurbiz_product_fr_fr'), $request->getQuery())); + } + + private function getIndexName(DocumentableInterface $documentable, ?string $locale = null): string + { + return $documentable->getIndexCode() . strtolower(null !== $locale ? '_' . $locale : ''); + } +} diff --git a/src/Search/SearchInterface.php b/src/Search/SearchInterface.php new file mode 100644 index 00000000..df0dd0c6 --- /dev/null +++ b/src/Search/SearchInterface.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search; + +interface SearchInterface +{ + public function query(RequestInterface $request): ResponseInterface; +} From f7d1edd7067720dae7ef58a0ca35d884004b72c8 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Fri, 15 Oct 2021 10:31:34 +0200 Subject: [PATCH 016/142] refactor: use new search in command --- src/Command/SearchCommand.php | 43 +++++++++++++---------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/src/Command/SearchCommand.php b/src/Command/SearchCommand.php index e3465e6b..dd7eb01c 100644 --- a/src/Command/SearchCommand.php +++ b/src/Command/SearchCommand.php @@ -13,13 +13,11 @@ namespace MonsieurBiz\SyliusSearchPlugin\Command; -use Elastica\Query; -use Elastica\Query\MultiMatch; -use JoliCode\Elastically\Factory; use MonsieurBiz\SyliusSearchPlugin\Index\Indexer; use MonsieurBiz\SyliusSearchPlugin\Model\Product\ProductDTO; -use Pagerfanta\Elastica\ElasticaAdapter; -use Pagerfanta\Pagerfanta; +use MonsieurBiz\SyliusSearchPlugin\Search\RequestFactory; +use MonsieurBiz\SyliusSearchPlugin\Search\RequestInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Search; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -32,12 +30,17 @@ class SearchCommand extends Command protected static $defaultName = 'monsieurbiz:search:search'; private Indexer $indexer; private SerializerInterface $serializer; + private RequestFactory $requestFactory; + private Search $search; - public function __construct(Indexer $indexer, SerializerInterface $serializer, $name = null) + public function __construct( + Indexer $indexer, SerializerInterface $serializer, RequestFactory $requestFactory, Search $search, $name = null) { parent::__construct($name); $this->indexer = $indexer; $this->serializer = $serializer; + $this->requestFactory = $requestFactory; + $this->search = $search; } protected function configure(): void @@ -49,33 +52,19 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output) { $io = new SymfonyStyle($input, $output); - $factory = new Factory([ - Factory::CONFIG_INDEX_CLASS_MAPPING => [ - 'monsieurbiz_product_fr_fr' => ProductDTO::class, - ], - Factory::CONFIG_SERIALIZER => $this->serializer, - ]); - - $client = $factory->buildClient(); $query = $input->getArgument('query'); + $request = $this->requestFactory->create(RequestInterface::SEARCH_TYPE, 'monsieurbiz_product'); + $request->setQueryParameters(['query_text' => $query]); - $searchQuery = new MultiMatch(); - $searchQuery->setFields([ - 'name^5', - 'description', - ]); - $searchQuery->setQuery($query); - $searchQuery->setType(MultiMatch::TYPE_MOST_FIELDS); - - $foundPosts = new Pagerfanta(new ElasticaAdapter($client->getIndex('monsieurbiz_product_fr_fr'), Query::create($searchQuery))); + $result = $this->search->query($request); $io->title('Search result for: ' . $query); - $io->section('Nb results: ' . $foundPosts->getNbResults()); + $io->section('Nb results: ' . $result->count()); $documents = []; - foreach ($foundPosts as $result) { + foreach ($result->getIterator() as $resultItem) { /** @var ProductDTO $productDTO */ - $productDTO = $result->getModel(); - $documents[] = [$result->getScore(), $productDTO->getId()]; + $productDTO = $resultItem->getModel(); + $documents[] = [$resultItem->getScore(), $productDTO->getId()]; } $io->table(['Score', 'Document ID'], $documents); From 5ca4332ee0efe3080ca75f1d77a757b08d84a940 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 18 Oct 2021 09:59:29 +0200 Subject: [PATCH 017/142] feat: add search controller with filters --- src/AutoMapper/ProductMapperConfiguration.php | 5 +- src/Command/SearchCommand.php | 10 +- src/Controller/SearchController.php | 38 ++++++++ src/DependencyInjection/Configuration.php | 6 ++ .../DocumentableRegistryPass.php | 1 + src/Helper/SlugHelper.php | 27 ++++++ src/Model/Documentable/Documentable.php | 12 ++- .../Documentable/DocumentableInterface.php | 2 + src/Resources/config/monsieurbiz_search.yaml | 2 + src/Resources/config/routing.yaml | 5 + src/Resources/config/routing/shop.yaml | 7 ++ src/Resources/views/Search/_filter.html.twig | 28 ++++++ src/Resources/views/Search/_filters.html.twig | 41 ++++++++ src/Resources/views/Search/_header.html.twig | 5 + src/Resources/views/Search/_sidebar.html.twig | 1 + .../views/Search/product/_box.html.twig | 28 ++++++ src/Resources/views/Search/result.html.twig | 44 +++++++++ src/Search/Filter/Filter.php | 94 +++++++++++++++++++ src/Search/Filter/FilterValue.php | 71 ++++++++++++++ src/Search/Request/Search/Product.php | 6 +- src/Search/Response.php | 50 +++++++++- src/Search/Search.php | 12 ++- 22 files changed, 474 insertions(+), 21 deletions(-) create mode 100644 src/Controller/SearchController.php create mode 100644 src/Helper/SlugHelper.php create mode 100644 src/Resources/config/routing.yaml create mode 100644 src/Resources/config/routing/shop.yaml create mode 100644 src/Resources/views/Search/_filter.html.twig create mode 100644 src/Resources/views/Search/_filters.html.twig create mode 100644 src/Resources/views/Search/_header.html.twig create mode 100644 src/Resources/views/Search/_sidebar.html.twig create mode 100644 src/Resources/views/Search/product/_box.html.twig create mode 100644 src/Resources/views/Search/result.html.twig create mode 100644 src/Search/Filter/Filter.php create mode 100644 src/Search/Filter/FilterValue.php diff --git a/src/AutoMapper/ProductMapperConfiguration.php b/src/AutoMapper/ProductMapperConfiguration.php index 50a91d28..807c70ee 100644 --- a/src/AutoMapper/ProductMapperConfiguration.php +++ b/src/AutoMapper/ProductMapperConfiguration.php @@ -95,8 +95,8 @@ public function process(MapperGeneratorMetadataInterface $metadata): void }); $metadata->forMember('attributes', function(ProductInterface $product): array { - $currentLocale = $product->getTranslation()->getLocale(); - /** @var ProductAttributeValue $attributeValue */ + $attributes = []; + $currentLocale = $product->getTranslation()->getLocale(); // TODO default locale if it's null? foreach ($product->getAttributesByLocale($currentLocale, $currentLocale) as $attributeValue) { if (null === $attributeValue->getName() || null === $attributeValue->getValue()) { continue; @@ -105,6 +105,7 @@ public function process(MapperGeneratorMetadataInterface $metadata): void if (!$attribute instanceof SearchableInterface || (!$attribute->isSearchable() && !$attribute->isFilterable())) { continue; } + /** @var ProductAttribute $attributeDTO */ $attributeDTO = $this->autoMapper->map($attributeValue, ProductAttribute::class); $attributeDTO->setValue($attributeValue->getValue()); // we can't use the automapper for the value because it has a mixed type $attributes[$attributeValue->getCode()] = $attributeDTO; diff --git a/src/Command/SearchCommand.php b/src/Command/SearchCommand.php index dd7eb01c..0673dbd4 100644 --- a/src/Command/SearchCommand.php +++ b/src/Command/SearchCommand.php @@ -13,7 +13,6 @@ namespace MonsieurBiz\SyliusSearchPlugin\Command; -use MonsieurBiz\SyliusSearchPlugin\Index\Indexer; use MonsieurBiz\SyliusSearchPlugin\Model\Product\ProductDTO; use MonsieurBiz\SyliusSearchPlugin\Search\RequestFactory; use MonsieurBiz\SyliusSearchPlugin\Search\RequestInterface; @@ -23,22 +22,17 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -use Symfony\Component\Serializer\SerializerInterface; class SearchCommand extends Command { protected static $defaultName = 'monsieurbiz:search:search'; - private Indexer $indexer; - private SerializerInterface $serializer; private RequestFactory $requestFactory; private Search $search; public function __construct( - Indexer $indexer, SerializerInterface $serializer, RequestFactory $requestFactory, Search $search, $name = null) - { + RequestFactory $requestFactory, Search $search, $name = null + ) { parent::__construct($name); - $this->indexer = $indexer; - $this->serializer = $serializer; $this->requestFactory = $requestFactory; $this->search = $search; } diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php new file mode 100644 index 00000000..34624e1b --- /dev/null +++ b/src/Controller/SearchController.php @@ -0,0 +1,38 @@ +requestFactory = $requestFactory; + $this->search = $search; + } + + // TODO add an optional parameter $documentType (nullable => get the default document type) + public function searchAction(Request $request, string $query): Response + { + // TODO create a requestConfiguration (like \Sylius\Bundle\ResourceBundle\Controller\RequestConfiguration without metadata) + $elasticsearchRequest = $this->requestFactory->create(RequestInterface::SEARCH_TYPE, 'monsieurbiz_product'); + $elasticsearchRequest->setQueryParameters(['query_text' => $query]); + + $result = $this->search->query($elasticsearchRequest); + + return $this->render('@MonsieurBizSyliusSearchPlugin/Search/result.html.twig', [ + 'documentable' => $elasticsearchRequest->getDocumentable(), + 'query' => $query, + 'result' => $result, + ]); + } +} diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 5d7aa102..6d5bc15c 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -38,6 +38,12 @@ public function getConfigTreeBuilder(): TreeBuilder ->scalarNode('target')->isRequired()->cannotBeEmpty()->end() ->scalarNode('mapping_provider')->defaultValue(YamlWithLocaleProvider::class)->end() ->scalarNode('datasource')->defaultValue(RepositoryDatasource::class)->end() + ->arrayNode('templates') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('item')->isRequired()->cannotBeEmpty()->end() + ->end() + ->end() ->end() ->end() ->end() diff --git a/src/DependencyInjection/DocumentableRegistryPass.php b/src/DependencyInjection/DocumentableRegistryPass.php index 352f36a0..045a5378 100644 --- a/src/DependencyInjection/DocumentableRegistryPass.php +++ b/src/DependencyInjection/DocumentableRegistryPass.php @@ -40,6 +40,7 @@ public function process(ContainerBuilder $container): void '$indexCode' => $indexCode, '$sourceClass' => $documentableConfiguration['source'], '$targetClass' => $documentableConfiguration['target'], + '$templates' => $documentableConfiguration['templates'], ]) ; $documentableDefinition = $container->setDefinition($documentableServiceId, $documentableDefinition); diff --git a/src/Helper/SlugHelper.php b/src/Helper/SlugHelper.php new file mode 100644 index 00000000..09881656 --- /dev/null +++ b/src/Helper/SlugHelper.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Helper; + +class SlugHelper +{ + public static function toSlug(string $label): string + { + return urlencode($label); + } + + public static function toLabel(string $slug): string + { + return urldecode($slug); + } +} diff --git a/src/Model/Documentable/Documentable.php b/src/Model/Documentable/Documentable.php index b683879c..6a50f859 100644 --- a/src/Model/Documentable/Documentable.php +++ b/src/Model/Documentable/Documentable.php @@ -22,12 +22,17 @@ class Documentable implements DocumentableInterface private string $indexCode; private string $sourceClass; private string $targetClass; + /** + * @var array + */ + private array $templates; - public function __construct(string $indexCode, string $sourceClass, string $targetClass) + public function __construct(string $indexCode, string $sourceClass, string $targetClass, array $templates) { $this->indexCode = $indexCode; $this->sourceClass = $sourceClass; $this->targetClass = $targetClass; + $this->templates = $templates; } public function getIndexCode(): string @@ -51,4 +56,9 @@ public function isTranslatable(): bool return \in_array(TranslatableInterface::class, $interface, true); } + + public function getTemplate(string $type): ?string + { + return $this->templates[$type] ?? null; + } } diff --git a/src/Model/Documentable/DocumentableInterface.php b/src/Model/Documentable/DocumentableInterface.php index 5c646e8d..22fa8b6e 100644 --- a/src/Model/Documentable/DocumentableInterface.php +++ b/src/Model/Documentable/DocumentableInterface.php @@ -34,4 +34,6 @@ public function setDatasource(DatasourceInterface $datasource): void; public function getDatasource(): DatasourceInterface; public function isTranslatable(): bool; + + public function getTemplate(string $type): ?string; } diff --git a/src/Resources/config/monsieurbiz_search.yaml b/src/Resources/config/monsieurbiz_search.yaml index 2cb64573..ecce7867 100644 --- a/src/Resources/config/monsieurbiz_search.yaml +++ b/src/Resources/config/monsieurbiz_search.yaml @@ -3,5 +3,7 @@ monsieurbiz_sylius_search: monsieurbiz_product: source: 'Sylius\Component\Core\Model\ProductInterface' target: 'MonsieurBiz\SyliusSearchPlugin\Model\Product\ProductDTO' + templates: + item: '@MonsieurBizSyliusSearchPlugin/Search/product/_box.html.twig' #mapping_provider: '...' # by default monsieurbiz.search.mapper_provider #dataprovider: '...' # by default MonsieurBiz\SyliusSearchPlugin\Model\Datasource\RepositoryDatasource diff --git a/src/Resources/config/routing.yaml b/src/Resources/config/routing.yaml new file mode 100644 index 00000000..03d57fd0 --- /dev/null +++ b/src/Resources/config/routing.yaml @@ -0,0 +1,5 @@ +monsieurbiz_search_shop: + resource: "@MonsieurBizSyliusSearchPlugin/Resources/config/routing/shop.yaml" + prefix: /{_locale} + requirements: + _locale: "^[A-Za-z]{2,4}(_([A-Za-z]{4}|[0-9]{3}))?(_([A-Za-z]{2}|[0-9]{3}))?$" diff --git a/src/Resources/config/routing/shop.yaml b/src/Resources/config/routing/shop.yaml new file mode 100644 index 00000000..be353e8a --- /dev/null +++ b/src/Resources/config/routing/shop.yaml @@ -0,0 +1,7 @@ +monsieurbiz_search_search: + path: /search/{query} + methods: [GET] + defaults: + _controller: MonsieurBiz\SyliusSearchPlugin\Controller\SearchController:searchAction + requirements: + query: .+ diff --git a/src/Resources/views/Search/_filter.html.twig b/src/Resources/views/Search/_filter.html.twig new file mode 100644 index 00000000..c0954734 --- /dev/null +++ b/src/Resources/views/Search/_filter.html.twig @@ -0,0 +1,28 @@ +{% set appliedAttributes = app.request.query.get('attribute') %} +{% set currentValues = (appliedAttributes[filter.code] is defined) ? appliedAttributes[filter.code] : [] %} +{% if filter.values|length or currentValues is not empty %} +
+
+
{{ filter.label | trans }}
+
+ {% for value in filter.values %} + {% set valueIsApplied = (value.slug in currentValues) %} +
+
+ +
+
+ {% endfor %} +
+
+
+{% endif %} diff --git a/src/Resources/views/Search/_filters.html.twig b/src/Resources/views/Search/_filters.html.twig new file mode 100644 index 00000000..4f44e6c7 --- /dev/null +++ b/src/Resources/views/Search/_filters.html.twig @@ -0,0 +1,41 @@ + diff --git a/src/Resources/views/Search/_header.html.twig b/src/Resources/views/Search/_header.html.twig new file mode 100644 index 00000000..23771156 --- /dev/null +++ b/src/Resources/views/Search/_header.html.twig @@ -0,0 +1,5 @@ +{{ sylius_template_event('sylius.shop.search.header.before', _context) }} +

+ {{ 'monsieurbiz_searchplugin.search.result.search_result'|trans({'%query%': query, '%count%': result.count}) }} +

+{{ sylius_template_event('sylius.shop.search.header.after', _context) }} diff --git a/src/Resources/views/Search/_sidebar.html.twig b/src/Resources/views/Search/_sidebar.html.twig new file mode 100644 index 00000000..9c13626a --- /dev/null +++ b/src/Resources/views/Search/_sidebar.html.twig @@ -0,0 +1 @@ +{% include '@MonsieurBizSyliusSearchPlugin/Search/_filters.html.twig' %} diff --git a/src/Resources/views/Search/product/_box.html.twig b/src/Resources/views/Search/product/_box.html.twig new file mode 100644 index 00000000..feb69bdd --- /dev/null +++ b/src/Resources/views/Search/product/_box.html.twig @@ -0,0 +1,28 @@ +{% import "@SyliusShop/Common/Macro/money.html.twig" as money %} + +
+ {# TODO find the locale... #} + +
+
+
+
{{ 'sylius.ui.view_more'|trans }}
+
+
+
+ {% if item.images|first %} + {% set path = item.images|first.path|imagine_filter(filter|default('sylius_shop_product_thumbnail')) %} + {% else %} + {% set path = '//placehold.it/200x200' %} + {% endif %} + + {{ item.name }} +
+
+ {{ item.name }} + + {# {% if item.price is not empty %}#} + {#
{{ money.convertAndFormat(result.priceByChannelAndCurrency(channel.code, currencyCode).value) }}
#} + {# {% endif %}#} +
+
diff --git a/src/Resources/views/Search/result.html.twig b/src/Resources/views/Search/result.html.twig new file mode 100644 index 00000000..54a71ca1 --- /dev/null +++ b/src/Resources/views/Search/result.html.twig @@ -0,0 +1,44 @@ +{% extends '@SyliusShop/layout.html.twig' %} +{% import '@SyliusUi/Macro/pagination.html.twig' as pagination %} + +{% block content %} + {% include '@MonsieurBizSyliusSearchPlugin/Search/_header.html.twig' %} + +
+
+
+

{{ 'monsieurbiz_searchplugin.filters.loading' | trans }}

+
+
+
+ {% include '@MonsieurBizSyliusSearchPlugin/Search/_sidebar.html.twig' %} +
+
+ {% if result.count == 0 %} +
+
+
+

+ {{ 'monsieurbiz_searchplugin.search.result.no_result'|trans }} +

+
+
+
+ {% else %} + {# include '@MonsieurBizSyliusSearchPlugin/Search/_pagination.html.twig' #} + {# include '@MonsieurBizSyliusSearchPlugin/Search/_sorting.html.twig' #} + + +
+ {% for item in result %} + {% include documentable.template('item') with {'item': item.model} %} + {% endfor %} +
+ + + + {# {{ pagination.simple(resultSet.pager) }} #} + {% endif %} +
+
+{% endblock %} diff --git a/src/Search/Filter/Filter.php b/src/Search/Filter/Filter.php new file mode 100644 index 00000000..0e6f259d --- /dev/null +++ b/src/Search/Filter/Filter.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Filter; + +use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterInterface; + +class Filter implements FilterInterface +{ + /** + * @var string + */ + private $code; + + /** + * @var string + */ + private $label; + + /** + * @var FilterValue[] + */ + private $values = []; + + /** + * @var int + */ + private $count; + + /** + * Filter constructor. + * + * @param string $code + * @param string $label + * @param int $count + */ + public function __construct(string $code, string $label, int $count) + { + $this->code = $code; + $this->label = $label; + $this->count = $count; + } + + /** + * @return string + */ + public function getCode(): string + { + return $this->code; + } + + /** + * @return string + */ + public function getLabel(): string + { + return $this->label; + } + + /** + * @return FilterValue[] + */ + public function getValues(): array + { + return $this->values; + } + + /** + * @param $value + * @param $count + */ + public function addValue($value, $count): void + { + $this->values[] = new FilterValue($value, $count); + } + + /** + * @return int + */ + public function getCount(): int + { + return $this->count; + } +} diff --git a/src/Search/Filter/FilterValue.php b/src/Search/Filter/FilterValue.php new file mode 100644 index 00000000..5e0c5eba --- /dev/null +++ b/src/Search/Filter/FilterValue.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Filter; + +use MonsieurBiz\SyliusSearchPlugin\Helper\SlugHelper; + +class FilterValue +{ + /** + * @var string + */ + private $slug; + + /** + * @var string + */ + private $label; + + /** + * @var int + */ + private $count; + + /** + * Filter constructor. + * + * @param string $label + * @param int $count + */ + public function __construct(string $label, int $count) + { + $this->slug = SlugHelper::toSlug($label); + $this->label = $label; + $this->count = $count; + } + + /** + * @return string + */ + public function getSlug(): string + { + return $this->slug; + } + + /** + * @return string + */ + public function getLabel(): string + { + return $this->label; + } + + /** + * @return int + */ + public function getCount(): int + { + return $this->count; + } +} diff --git a/src/Search/Request/Search/Product.php b/src/Search/Request/Search/Product.php index 8d82faf8..0b36f2a6 100644 --- a/src/Search/Request/Search/Product.php +++ b/src/Search/Request/Search/Product.php @@ -127,8 +127,12 @@ private function addAggregations(Query $query): void $attributeValuesAgg = new Terms('values'); $attributeValuesAgg->setField(sprintf('attributes.%s.value.keyword', $productAttribute->getCode())); + $attributeCodesAgg = new Terms('names'); + $attributeCodesAgg->setField(sprintf('attributes.%s.name', $productAttribute->getCode())); + $attributeCodesAgg->addAggregation($attributeValuesAgg); + $attributeAgg = new Nested($productAttribute->getCode(), sprintf('attributes.%s', $productAttribute->getCode())); - $attributeAgg->addAggregation($attributeValuesAgg); + $attributeAgg->addAggregation($attributeCodesAgg); $attributesAgg = new Nested('attributes', 'attributes'); $attributesAgg->addAggregation($attributeAgg); diff --git a/src/Search/Response.php b/src/Search/Response.php index ea2115ee..a2812670 100644 --- a/src/Search/Response.php +++ b/src/Search/Response.php @@ -13,35 +13,75 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search; +use Elastica\ResultSet; +use MonsieurBiz\SyliusSearchPlugin\Search\Filter\Filter; use Pagerfanta\Adapter\AdapterInterface; use Pagerfanta\Pagerfanta; class Response implements ResponseInterface { private AdapterInterface $adapter; + private ?Pagerfanta $paginator = null; + private array $filters = []; public function __construct(AdapterInterface $adapter) { $this->adapter = $adapter; + $this->buildFilters(); } public function getIterator() { - return $this->createPaginator(); + return $this->getPaginator(); } public function count() { - return $this->createPaginator()->getNbResults(); + return $this->getPaginator()->getNbResults(); } public function getFilters(): array { - return []; + return $this->filters; } - private function createPaginator(): Pagerfanta + private function getPaginator(): Pagerfanta { - return new Pagerfanta($this->adapter); + if (null === $this->paginator) { + $this->paginator = new Pagerfanta($this->adapter); + } + + return $this->paginator; + } + + private function buildFilters(): void + { + /** @var ResultSet $results */ + $results = $this->getPaginator()->getCurrentPageResults(); + $aggregations = $results->getAggregations(); + // No aggregation so don't perform filters + if (0 === \count($aggregations)) { + return; + } + + // Retrieve filters in aggregations + $attributeAggregations = $aggregations['attributes'] ?? []; + unset($attributeAggregations['doc_count']); + foreach ($attributeAggregations as $attributeCode => $attributeAggregation) { + $attributeNameBuckets = $attributeAggregation['names']['buckets'] ?? []; + foreach ($attributeNameBuckets as $attributeNameBucket) { + $attributeValueBuckets = $attributeNameBucket['values']['buckets'] ?? []; + $filter = new Filter($attributeCode, $attributeNameBucket['key'], $attributeNameBucket['doc_count']); + foreach ($attributeValueBuckets as $attributeValueBucket) { + if (0 === $attributeValueBucket['doc_count']) { + continue; + } + if (isset($attributeValueBucket['key']) && isset($attributeValueBucket['doc_count'])) { + $filter->addValue($attributeValueBucket['key'], $attributeValueBucket['doc_count']); + } + } + $this->filters[] = $filter; + } + } } } diff --git a/src/Search/Search.php b/src/Search/Search.php index 87d6f35d..87893c6d 100644 --- a/src/Search/Search.php +++ b/src/Search/Search.php @@ -16,32 +16,36 @@ use JoliCode\Elastically\Factory; use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; use Pagerfanta\Elastica\ElasticaAdapter; +use Sylius\Component\Locale\Context\LocaleContextInterface; use Symfony\Component\Serializer\SerializerInterface; class Search implements SearchInterface { private SerializerInterface $serializer; + private LocaleContextInterface $localeContext; - public function __construct(SerializerInterface $serializer) + public function __construct(SerializerInterface $serializer, LocaleContextInterface $localeContext) { $this->serializer = $serializer; + $this->localeContext = $localeContext; } public function query(RequestInterface $request): ResponseInterface { + $indexName = $this->getIndexName($request->getDocumentable(), $this->localeContext->getLocaleCode()); $factory = new Factory([ Factory::CONFIG_INDEX_CLASS_MAPPING => [ - $this->getIndexName($request->getDocumentable(), 'fr_FR') => $request->getDocumentable()->getTargetClass(), + $indexName => $request->getDocumentable()->getTargetClass(), ], Factory::CONFIG_SERIALIZER => $this->serializer, ]); $client = $factory->buildClient(); - return new Response(new ElasticaAdapter($client->getIndex('monsieurbiz_product_fr_fr'), $request->getQuery())); + return new Response(new ElasticaAdapter($client->getIndex($indexName), $request->getQuery())); } private function getIndexName(DocumentableInterface $documentable, ?string $locale = null): string { - return $documentable->getIndexCode() . strtolower(null !== $locale ? '_' . $locale : ''); + return $documentable->getIndexCode() . strtolower((null !== $locale && $documentable->isTranslatable()) ? '_' . $locale : ''); } } From 06ddae1ff83ad30217719004e671e384d449f037 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 18 Oct 2021 10:01:37 +0200 Subject: [PATCH 018/142] feat: init receipe 2.0 and "install" plugin --- recipes/2.0/config/routes/monsieurbiz_sylius_search_plugin.yaml | 2 ++ .../config/routes/monsieurbiz_sylius_search_plugin.yaml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 recipes/2.0/config/routes/monsieurbiz_sylius_search_plugin.yaml diff --git a/recipes/2.0/config/routes/monsieurbiz_sylius_search_plugin.yaml b/recipes/2.0/config/routes/monsieurbiz_sylius_search_plugin.yaml new file mode 100644 index 00000000..ea7b6f92 --- /dev/null +++ b/recipes/2.0/config/routes/monsieurbiz_sylius_search_plugin.yaml @@ -0,0 +1,2 @@ +monsieurbiz_search_plugin: + resource: "@MonsieurBizSyliusSearchPlugin/Resources/config/routing.yaml" diff --git a/tests/Application/config/routes/monsieurbiz_sylius_search_plugin.yaml b/tests/Application/config/routes/monsieurbiz_sylius_search_plugin.yaml index 14ac27dc..5f50104d 120000 --- a/tests/Application/config/routes/monsieurbiz_sylius_search_plugin.yaml +++ b/tests/Application/config/routes/monsieurbiz_sylius_search_plugin.yaml @@ -1 +1 @@ -../../../../recipe/dev/config/routes/monsieurbiz_sylius_search_plugin.yaml \ No newline at end of file +../../../../recipes/2.0/config/routes/monsieurbiz_sylius_search_plugin.yaml \ No newline at end of file From 69ccb0ae9bd7260960cba2d26d758033e4b4c0c3 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 18 Oct 2021 11:44:47 +0200 Subject: [PATCH 019/142] refacto: add a request configuration object --- src/Controller/SearchController.php | 4 +++- src/Search/Request/RequestConfiguration.php | 25 +++++++++++++++++++++ src/Search/RequestInterface.php | 3 ++- 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 src/Search/Request/RequestConfiguration.php diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php index 34624e1b..d34a7eea 100644 --- a/src/Controller/SearchController.php +++ b/src/Controller/SearchController.php @@ -2,6 +2,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Controller; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use MonsieurBiz\SyliusSearchPlugin\Search\RequestFactory; use MonsieurBiz\SyliusSearchPlugin\Search\RequestInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Search; @@ -23,9 +24,10 @@ public function __construct(RequestFactory $requestFactory, Search $search) // TODO add an optional parameter $documentType (nullable => get the default document type) public function searchAction(Request $request, string $query): Response { + $requestConfiguration = new RequestConfiguration($request); // TODO create a requestConfiguration (like \Sylius\Bundle\ResourceBundle\Controller\RequestConfiguration without metadata) $elasticsearchRequest = $this->requestFactory->create(RequestInterface::SEARCH_TYPE, 'monsieurbiz_product'); - $elasticsearchRequest->setQueryParameters(['query_text' => $query]); + $elasticsearchRequest->setConfiguration($requestConfiguration); $result = $this->search->query($elasticsearchRequest); diff --git a/src/Search/Request/RequestConfiguration.php b/src/Search/Request/RequestConfiguration.php new file mode 100644 index 00000000..9def5b12 --- /dev/null +++ b/src/Search/Request/RequestConfiguration.php @@ -0,0 +1,25 @@ +request = $request; + } + + public function getQueryText(): string + { + return $this->request->get('query', ''); + } + + public function getAppliedFilters(): array + { + return $this->request->get('attribute') ?? []; + } +} diff --git a/src/Search/RequestInterface.php b/src/Search/RequestInterface.php index 99eee77f..a5286474 100644 --- a/src/Search/RequestInterface.php +++ b/src/Search/RequestInterface.php @@ -15,6 +15,7 @@ use Elastica\Query; use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; interface RequestInterface { @@ -30,5 +31,5 @@ public function getQuery(): Query; public function supports(string $type, string $documentableCode): bool; - public function setQueryParameters(array $parameters): void; + public function setConfiguration(RequestConfiguration $configuration): void; } From 810adc4398fe387af8b4de236dc8b0099484f654 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 18 Oct 2021 11:45:29 +0200 Subject: [PATCH 020/142] feat: add attributes filters in search query --- src/Search/Request/Search/Product.php | 40 ++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/src/Search/Request/Search/Product.php b/src/Search/Request/Search/Product.php index 0b36f2a6..71357ded 100644 --- a/src/Search/Request/Search/Product.php +++ b/src/Search/Request/Search/Product.php @@ -17,8 +17,10 @@ use Elastica\Aggregation\Terms; use Elastica\Query; use Elastica\Query\MultiMatch; +use MonsieurBiz\SyliusSearchPlugin\Helper\SlugHelper; use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; use MonsieurBiz\SyliusSearchPlugin\Repository\ProductAttributeRepositoryInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use MonsieurBiz\SyliusSearchPlugin\Search\RequestInterface; use Sylius\Component\Registry\ServiceRegistryInterface; @@ -26,7 +28,7 @@ class Product implements RequestInterface { private DocumentableInterface $documentable; - private array $queryParameters = []; + private RequestConfiguration $configuration; private ProductAttributeRepositoryInterface $productAttributeRepository; public function __construct( @@ -48,28 +50,28 @@ public function getDocumentable(): DocumentableInterface return $this->documentable; } - public function setQueryParameters(array $parameters): void + public function setConfiguration(RequestConfiguration $configuration): void { - $this->queryParameters = $parameters; + $this->configuration = $configuration; } public function getQuery(): Query { - if (!\array_key_exists('query_text', $this->queryParameters)) { + if ('' === $this->configuration->getQueryText()) { throw new \Exception('missing query text'); //todo } $enableFilter = new Query\Terms('enabled', [true]); // todo add channel filter - $searchCode = new Query\Terms('code', [$this->queryParameters['query_text']]); + $searchCode = new Query\Terms('code', [$this->configuration->getQueryText()]); $nameAndDescriptionQuery = new MultiMatch(); $nameAndDescriptionQuery->setFields([ 'name^5', // todo configuration 'description', // move to should ? score impact but not include in result ]); - $nameAndDescriptionQuery->setQuery($this->queryParameters['query_text']); + $nameAndDescriptionQuery->setQuery($this->configuration->getQueryText()); $nameAndDescriptionQuery->setType(MultiMatch::TYPE_MOST_FIELDS); $searchQuery = new Query\BoolQuery(); @@ -85,6 +87,7 @@ public function getQuery(): Query $bool->addMust($searchQuery); $esQuery = Query::create($bool); + $this->addFilters($esQuery); $this->addAggregations($esQuery); return $esQuery; @@ -106,7 +109,7 @@ private function addAttributesQueries(Query\BoolQuery $searchQuery): void $attributeValueQuery->setFields([ sprintf('attributes.%s.value^%d', $productAttribute->getCode(), $productAttribute->getSearchWeight()), ]); - $attributeValueQuery->setQuery($this->queryParameters['query_text']); + $attributeValueQuery->setQuery($this->configuration->getQueryText()); $attributeQuery = new Query\Nested(); $attributeQuery->setPath(sprintf('attributes.%s', $productAttribute->getCode()))->setQuery($attributeValueQuery); @@ -140,4 +143,27 @@ private function addAggregations(Query $query): void $query->addAggregation($attributesAgg); } } + + private function addFilters(Query $query): void + { + $bool = new Query\BoolQuery(); + foreach ($this->configuration->getAppliedFilters() as $field => $values) { + $attributeValueQuery = new Query\BoolQuery(); + + foreach ($values as $value) { + $termQuery = new Query\Terms(sprintf('attributes.%s.value.keyword', $field), [SlugHelper::toLabel($value)]); + $attributeValueQuery->addShould($termQuery); // todo configure the "and" or "or" + } + + $attributeQuery = new Query\Nested(); + $attributeQuery->setPath(sprintf('attributes.%s', $field))->setQuery($attributeValueQuery); + + $attributesQuery = new Query\Nested(); + $attributesQuery->setPath('attributes')->setQuery($attributeQuery); + + $bool->addFilter($attributesQuery); + } + + $query->setPostFilter($bool); + } } From 3820cfa64b6950de78f09df0cf07e8e3558dc269 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 2 Nov 2021 12:20:00 +0100 Subject: [PATCH 021/142] fix: append all attributes aggs into product search query --- src/Search/Request/Search/Product.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Search/Request/Search/Product.php b/src/Search/Request/Search/Product.php index 71357ded..e6aeb139 100644 --- a/src/Search/Request/Search/Product.php +++ b/src/Search/Request/Search/Product.php @@ -89,6 +89,7 @@ public function getQuery(): Query $esQuery = Query::create($bool); $this->addFilters($esQuery); $this->addAggregations($esQuery); + dump($esQuery->toArray()); return $esQuery; } @@ -123,6 +124,7 @@ private function addAttributesQueries(Query\BoolQuery $searchQuery): void private function addAggregations(Query $query): void { + $attributesAgg = new Nested('attributes', 'attributes'); foreach ($this->productAttributeRepository->findIsSearchableOrFilterable() as $productAttribute) { if (!$productAttribute->isFilterable()) { continue; @@ -137,9 +139,9 @@ private function addAggregations(Query $query): void $attributeAgg = new Nested($productAttribute->getCode(), sprintf('attributes.%s', $productAttribute->getCode())); $attributeAgg->addAggregation($attributeCodesAgg); - $attributesAgg = new Nested('attributes', 'attributes'); $attributesAgg->addAggregation($attributeAgg); - + } + if (0 < \count($attributesAgg->getAggs())) { $query->addAggregation($attributesAgg); } } From 3424b1b251d9cd4841b9eca54a42473dc38d4648 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 2 Nov 2021 14:49:01 +0100 Subject: [PATCH 022/142] feat: extends the product option form & add custom repository --- .../Extension/ProductOptionTypeExtension.php | 51 +++++++++++++++++++ src/Repository/ProductOptionRepository.php | 26 ++++++++++ .../ProductOptionRepositoryInterface.php | 14 +++++ 3 files changed, 91 insertions(+) create mode 100644 src/Form/Extension/ProductOptionTypeExtension.php create mode 100644 src/Repository/ProductOptionRepository.php create mode 100644 src/Repository/ProductOptionRepositoryInterface.php diff --git a/src/Form/Extension/ProductOptionTypeExtension.php b/src/Form/Extension/ProductOptionTypeExtension.php new file mode 100644 index 00000000..7a3bda11 --- /dev/null +++ b/src/Form/Extension/ProductOptionTypeExtension.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Form\Extension; + +use Sylius\Bundle\ProductBundle\Form\Type\ProductOptionType; +use Symfony\Component\Form\AbstractTypeExtension; +use Symfony\Component\Form\Extension\Core\Type\CheckboxType; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; +use Symfony\Component\Form\FormBuilderInterface; + +final class ProductOptionTypeExtension extends AbstractTypeExtension +{ + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $searchWeightValues = range(1, 10); + + $builder + ->add('searchable', CheckboxType::class, [ + 'label' => 'monsieurbiz_searchplugin.admin.product_attribute.form.searchable', + 'required' => true, + ]) + ->add('filterable', CheckboxType::class, [ + 'label' => 'monsieurbiz_searchplugin.admin.product_attribute.form.filterable', + 'required' => true, + ]) + ->add('search_weight', ChoiceType::class, [ + 'label' => 'monsieurbiz_searchplugin.admin.product_attribute.form.search_weight', + 'required' => true, + 'choices' => array_combine($searchWeightValues, $searchWeightValues), + ]) + ; + } + + public static function getExtendedTypes(): array + { + return [ + ProductOptionType::class, + ]; + } +} diff --git a/src/Repository/ProductOptionRepository.php b/src/Repository/ProductOptionRepository.php new file mode 100644 index 00000000..eb8e0214 --- /dev/null +++ b/src/Repository/ProductOptionRepository.php @@ -0,0 +1,26 @@ +productOptionRepository = $productOptionRepository; + } + + public function findIsSearchableOrFilterable(): array + { + return $this->productOptionRepository->createQueryBuilder('o') + ->innerJoin('o.translations', 'translation') + ->andWhere('o.searchable = true') + ->orWhere('o.filterable = true') + ->getQuery() + ->getResult() + ; + } +} diff --git a/src/Repository/ProductOptionRepositoryInterface.php b/src/Repository/ProductOptionRepositoryInterface.php new file mode 100644 index 00000000..263d6d89 --- /dev/null +++ b/src/Repository/ProductOptionRepositoryInterface.php @@ -0,0 +1,14 @@ + Date: Tue, 2 Nov 2021 14:49:58 +0100 Subject: [PATCH 023/142] fix: fix the return type of ProductAttributeRepository --- src/Repository/ProductAttributeRepositoryInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Repository/ProductAttributeRepositoryInterface.php b/src/Repository/ProductAttributeRepositoryInterface.php index 5935c035..7ce34b8b 100644 --- a/src/Repository/ProductAttributeRepositoryInterface.php +++ b/src/Repository/ProductAttributeRepositoryInterface.php @@ -19,7 +19,7 @@ interface ProductAttributeRepositoryInterface { /** - * @return ProductAttributeInterface&SearchableInterface[] + * @return ProductAttributeInterface[]&SearchableInterface[] */ public function findIsSearchableOrFilterable(): array; } From 965fdee19139bfbf3e7f14c8c548785b087111c9 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Wed, 3 Nov 2021 14:16:33 +0100 Subject: [PATCH 024/142] feat: add variants and variants option in ES document --- src/AutoMapper/ProductMapperConfiguration.php | 35 +++++++++++++++++- ...ppendProductAttributeMappingSubscriber.php | 37 ++++++++++++++++++- .../monsieurbiz_product_mapping.yaml | 10 ++++- 3 files changed, 77 insertions(+), 5 deletions(-) diff --git a/src/AutoMapper/ProductMapperConfiguration.php b/src/AutoMapper/ProductMapperConfiguration.php index 807c70ee..04fbe391 100644 --- a/src/AutoMapper/ProductMapperConfiguration.php +++ b/src/AutoMapper/ProductMapperConfiguration.php @@ -25,18 +25,22 @@ use MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductTaxon; use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Taxon; use MonsieurBiz\SyliusSearchPlugin\Model\Product\ProductDTO; +use MonsieurBiz\SyliusSearchPlugin\Model\Product\VariantDTO; use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\ProductInterface; use Sylius\Component\Core\Model\ProductTaxonInterface; -use Sylius\Component\Product\Model\ProductAttributeValue; +use Sylius\Component\Inventory\Checker\AvailabilityCheckerInterface; +use Sylius\Component\Inventory\Model\StockableInterface; final class ProductMapperConfiguration implements MapperConfigurationInterface { private AutoMapperInterface $autoMapper; + private AvailabilityCheckerInterface $availabilityChecker; - public function __construct(AutoMapperInterface $autoMapper) + public function __construct(AutoMapperInterface $autoMapper, AvailabilityCheckerInterface $availabilityChecker) { $this->autoMapper = $autoMapper; + $this->availabilityChecker = $availabilityChecker; } public function process(MapperGeneratorMetadataInterface $metadata): void @@ -113,6 +117,33 @@ public function process(MapperGeneratorMetadataInterface $metadata): void return $attributes; }); + + $metadata->forMember('variants', function(ProductInterface $product): array { + $variants = []; + $currentLocale = $product->getTranslation()->getLocale(); // TODO default locale if it's null? + + foreach ($product->getVariants() as $variant) { + $isInStock = true; + if ($variant instanceof StockableInterface) { + $isInStock = $this->availabilityChecker->isStockAvailable($variant); + } +// TODO use auto mapper but we want the final class ... $this->autoMapper->map($variant, VariantDTO::class)); + $variantDTO = [ + 'enabled' => $variant->isEnabled(), + 'is_in_stock' => $isInStock, + ]; + foreach ($variant->getOptionValues() as $optionValue) { + $variantDTO['options'][$optionValue->getOptionCode()] = [ + 'name' => $optionValue->getName(), + 'code' => $optionValue->getCode(), + 'value' => $optionValue->getTranslation($currentLocale)->getValue(), + ]; + } + $variants[] = $variantDTO; + } + + return $variants; + }); } public function getSource(): string diff --git a/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php b/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php index 5b1d94d9..d6ec3055 100644 --- a/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php +++ b/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php @@ -15,15 +15,20 @@ use MonsieurBiz\SyliusSearchPlugin\Event\MappingProviderEvent; use MonsieurBiz\SyliusSearchPlugin\Repository\ProductAttributeRepositoryInterface; +use MonsieurBiz\SyliusSearchPlugin\Repository\ProductOptionRepositoryInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class AppendProductAttributeMappingSubscriber implements EventSubscriberInterface { private ProductAttributeRepositoryInterface $productAttributeRepository; + private ProductOptionRepositoryInterface $productOptionRepository; - public function __construct(ProductAttributeRepositoryInterface $productAttributeRepository) - { + public function __construct( + ProductAttributeRepositoryInterface $productAttributeRepository, + ProductOptionRepositoryInterface $productOptionRepository + ) { $this->productAttributeRepository = $productAttributeRepository; + $this->productOptionRepository = $productOptionRepository; } public static function getSubscribedEvents() @@ -71,6 +76,34 @@ public function omMappingProvider(MappingProviderEvent $event): void } } + foreach ($this->productOptionRepository->findIsSearchableOrFilterable() as $productOption) { + if (!\array_key_exists('options', $mappings['properties']['variants']['properties'])) { + $mappings['properties']['variants']['properties']['options'] = [ + 'type' => 'nested', + 'properties' => [], + ]; + } + $mappings['properties']['variants']['properties']['options']['properties'][$productOption->getCode()] = [ + 'type' => 'nested', + 'properties' => [ + 'code' => ['type' => 'keyword'], + 'name' => ['type' => 'keyword'], + 'value' => ['type' => 'text'], + ], + ]; + + if ($productOption->isFilterable()) { + $mappings['properties']['variants']['properties']['options']['properties'][$productOption->getCode()]['properties']['value']['fields'] = [ + 'keyword' => ['type' => 'keyword'], + ]; + } + + if ($productOption->isSearchable()) { + // TODO replace to a configurable value + $mappings['properties']['variants']['properties']['options']['properties'][$productOption->getCode()]['properties']['value']['analyzer'] = 'search_standard'; + } + } + $mapping->offsetSet('mappings', $mappings); } } diff --git a/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml b/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml index cb88fed2..5a169756 100644 --- a/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml +++ b/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml @@ -1,6 +1,7 @@ mappings: dynamic: false properties: + # attributes mapping is managed dynamically code: type: keyword enabled: @@ -48,4 +49,11 @@ mappings: type: integer price: type: integer # todo - WIP - # attributes mapping is managed dynamically + variants: + type: nested + properties: + enabled: + type: boolean + is_in_stock: + type: boolean + # variant options mapping is managed dynamically From 5f15c7ceba6382832ddd60ed1f273c214ef508a3 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Wed, 3 Nov 2021 15:11:56 +0100 Subject: [PATCH 025/142] fix: fix aggregations - append a filter with current filters --- src/Search/Request/Search/Product.php | 23 +++++++++++++++-------- src/Search/Response.php | 3 +++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/Search/Request/Search/Product.php b/src/Search/Request/Search/Product.php index e6aeb139..8c080877 100644 --- a/src/Search/Request/Search/Product.php +++ b/src/Search/Request/Search/Product.php @@ -13,6 +13,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Search; +use Elastica\Aggregation; use Elastica\Aggregation\Nested; use Elastica\Aggregation\Terms; use Elastica\Query; @@ -87,7 +88,8 @@ public function getQuery(): Query $bool->addMust($searchQuery); $esQuery = Query::create($bool); - $this->addFilters($esQuery); + $boolFilter = $this->getFilters(); + $esQuery->setPostFilter($boolFilter); $this->addAggregations($esQuery); dump($esQuery->toArray()); @@ -139,17 +141,25 @@ private function addAggregations(Query $query): void $attributeAgg = new Nested($productAttribute->getCode(), sprintf('attributes.%s', $productAttribute->getCode())); $attributeAgg->addAggregation($attributeCodesAgg); - $attributesAgg->addAggregation($attributeAgg); + $boolFilter = $this->getFilters($productAttribute->getCode()); + $filter = new Aggregation\Filter($productAttribute->getCode()); + $filter->setFilter($boolFilter); + $filter->addAggregation($attributeAgg); + + $attributesAgg->addAggregation($filter); } if (0 < \count($attributesAgg->getAggs())) { $query->addAggregation($attributesAgg); } } - private function addFilters(Query $query): void + private function getFilters($currentAttribute = null): Query\BoolQuery { $bool = new Query\BoolQuery(); foreach ($this->configuration->getAppliedFilters() as $field => $values) { + if ($currentAttribute == $field) { + continue; + } $attributeValueQuery = new Query\BoolQuery(); foreach ($values as $value) { @@ -160,12 +170,9 @@ private function addFilters(Query $query): void $attributeQuery = new Query\Nested(); $attributeQuery->setPath(sprintf('attributes.%s', $field))->setQuery($attributeValueQuery); - $attributesQuery = new Query\Nested(); - $attributesQuery->setPath('attributes')->setQuery($attributeQuery); - - $bool->addFilter($attributesQuery); + $bool->addMust($attributeQuery); } - $query->setPostFilter($bool); + return $bool; } } diff --git a/src/Search/Response.php b/src/Search/Response.php index a2812670..031120ac 100644 --- a/src/Search/Response.php +++ b/src/Search/Response.php @@ -68,6 +68,9 @@ private function buildFilters(): void $attributeAggregations = $aggregations['attributes'] ?? []; unset($attributeAggregations['doc_count']); foreach ($attributeAggregations as $attributeCode => $attributeAggregation) { + if (isset($attributeAggregation[$attributeCode])) { + $attributeAggregation = $attributeAggregation[$attributeCode]; + } $attributeNameBuckets = $attributeAggregation['names']['buckets'] ?? []; foreach ($attributeNameBuckets as $attributeNameBucket) { $attributeValueBuckets = $attributeNameBucket['values']['buckets'] ?? []; From 2afe39b5a90a15f87aa2fafbd131051875e058d8 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Wed, 3 Nov 2021 17:29:53 +0100 Subject: [PATCH 026/142] feat: add options facets and filters --- src/Resources/views/Search/_filter.html.twig | 4 +- src/Search/Filter/Filter.php | 21 ++++- src/Search/Request/RequestConfiguration.php | 9 +- src/Search/Request/Search/Product.php | 88 +++++++++++++++++++- src/Search/Response.php | 38 +++++---- 5 files changed, 137 insertions(+), 23 deletions(-) diff --git a/src/Resources/views/Search/_filter.html.twig b/src/Resources/views/Search/_filter.html.twig index c0954734..33b4b2cf 100644 --- a/src/Resources/views/Search/_filter.html.twig +++ b/src/Resources/views/Search/_filter.html.twig @@ -1,4 +1,4 @@ -{% set appliedAttributes = app.request.query.get('attribute') %} +{% set appliedAttributes = app.request.query.get(filter.type) %} {% set currentValues = (appliedAttributes[filter.code] is defined) ? appliedAttributes[filter.code] : [] %} {% if filter.values|length or currentValues is not empty %}
@@ -12,7 +12,7 @@ code = $code; $this->label = $label; $this->count = $count; + $this->type = $type; } /** @@ -91,4 +94,20 @@ public function getCount(): int { return $this->count; } + + /** + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * @param string $type + */ + public function setType(string $type): void + { + $this->type = $type; + } } diff --git a/src/Search/Request/RequestConfiguration.php b/src/Search/Request/RequestConfiguration.php index 9def5b12..d1ce90a0 100644 --- a/src/Search/Request/RequestConfiguration.php +++ b/src/Search/Request/RequestConfiguration.php @@ -18,8 +18,13 @@ public function getQueryText(): string return $this->request->get('query', ''); } - public function getAppliedFilters(): array + public function getAppliedFilters($type = null): array { - return $this->request->get('attribute') ?? []; + $appliedFilters = [ + 'attributes' => $this->request->get('attributes', []), + 'options' => $this->request->get('options', []), + ]; + + return null !== $type ? ($appliedFilters[$type] ?? []) : $appliedFilters; } } diff --git a/src/Search/Request/Search/Product.php b/src/Search/Request/Search/Product.php index 8c080877..306a7b2e 100644 --- a/src/Search/Request/Search/Product.php +++ b/src/Search/Request/Search/Product.php @@ -21,6 +21,7 @@ use MonsieurBiz\SyliusSearchPlugin\Helper\SlugHelper; use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; use MonsieurBiz\SyliusSearchPlugin\Repository\ProductAttributeRepositoryInterface; +use MonsieurBiz\SyliusSearchPlugin\Repository\ProductOptionRepositoryInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use MonsieurBiz\SyliusSearchPlugin\Search\RequestInterface; use Sylius\Component\Registry\ServiceRegistryInterface; @@ -31,14 +32,17 @@ class Product implements RequestInterface private RequestConfiguration $configuration; private ProductAttributeRepositoryInterface $productAttributeRepository; + private ProductOptionRepositoryInterface $productOptionRepository; public function __construct( ServiceRegistryInterface $documentableRegistry, - ProductAttributeRepositoryInterface $productAttributeRepository + ProductAttributeRepositoryInterface $productAttributeRepository, + ProductOptionRepositoryInterface $productOptionRepository ) { //TODO check if exist, return a dummy documentable if not $this->documentable = $documentableRegistry->get('search.documentable.monsieurbiz_product'); $this->productAttributeRepository = $productAttributeRepository; + $this->productOptionRepository = $productOptionRepository; } public function getType(): string @@ -148,15 +152,61 @@ private function addAggregations(Query $query): void $attributesAgg->addAggregation($filter); } + + $enabledFilter = new Query\Term(); + $enabledFilter->setTerm('variants.enabled', true); + + $isInStockFilter = new Query\Term(); + $isInStockFilter->setTerm('variants.is_in_stock', true); + + $filterVariants = new Query\BoolQuery(); + $filterVariants->addFilter($enabledFilter); + $filterVariants->addFilter($isInStockFilter); + + $enabledVariants = new Aggregation\Filter('enabled_variants'); + $enabledVariants->setFilter($filterVariants); + + $variants = new Nested('variants', 'variants'); + $variants->addAggregation($enabledVariants); + + $optionsAgg = new Nested('options', 'variants.options'); + foreach ($this->productOptionRepository->findIsSearchableOrFilterable() as $productOption) { + if (!$productOption->isFilterable()) { + continue; + } + $attributeValuesAgg = new Terms('values'); + $attributeValuesAgg->setField(sprintf('variants.options.%s.value.keyword', $productOption->getCode())); + + $attributeCodesAgg = new Terms('names'); + $attributeCodesAgg->setField(sprintf('variants.options.%s.name', $productOption->getCode())); + $attributeCodesAgg->addAggregation($attributeValuesAgg); + + $attributeAgg = new Nested($productOption->getCode(), sprintf('variants.options.%s', $productOption->getCode())); + $attributeAgg->addAggregation($attributeCodesAgg); + + + $boolFilter = $this->getFilters($productOption->getCode()); + $filter = new Aggregation\Filter($productOption->getCode()); + $filter->setFilter($boolFilter); + $filter->addAggregation($attributeAgg); + + $optionsAgg->addAggregation($filter); + } + if (0 < \count($attributesAgg->getAggs())) { $query->addAggregation($attributesAgg); } + + if (0 < \count($optionsAgg->getAggs())) { + $enabledVariants->addAggregation($optionsAgg); + $query->addAggregation($variants); + } } private function getFilters($currentAttribute = null): Query\BoolQuery { $bool = new Query\BoolQuery(); - foreach ($this->configuration->getAppliedFilters() as $field => $values) { + foreach ($this->configuration->getAppliedFilters('attributes') as $field => $values) { if ($currentAttribute == $field) { continue; } @@ -173,6 +223,40 @@ private function getFilters($currentAttribute = null): Query\BoolQuery $bool->addMust($attributeQuery); } + + + foreach ($this->configuration->getAppliedFilters('options') as $field => $values) { + if ($currentAttribute == $field) { + continue; + } + $enabledVariantsFilter = new Query\Term(); + $enabledVariantsFilter->setTerm('variants.enabled', true); + + $isInStockVariantsFilter = new Query\Term(); + $isInStockVariantsFilter->setTerm('variants.is_in_stock', true); + + $variantsQuery = new Query\BoolQuery(); + $variantsQuery->addMust($enabledVariantsFilter); + $variantsQuery->addMust($isInStockVariantsFilter); + + $attributeValueQuery = new Query\BoolQuery(); + + foreach ($values as $value) { + $termQuery = new Query\Terms(sprintf('variants.options.%s.value.keyword', $field), [SlugHelper::toLabel($value)]); + $attributeValueQuery->addShould($termQuery); // todo configure the "and" or "or" + } + + $attributeQuery = new Query\Nested(); + $attributeQuery->setPath(sprintf('variants.options.%s', $field))->setQuery($attributeValueQuery); + + $variantsQuery->addMust($attributeQuery); + + $variantsFilter = new Query\Nested(); + $variantsFilter->setPath('variants') + ->setQuery($variantsQuery); + $bool->addMust($variantsFilter); + } + return $bool; } } diff --git a/src/Search/Response.php b/src/Search/Response.php index 031120ac..62ee543d 100644 --- a/src/Search/Response.php +++ b/src/Search/Response.php @@ -65,25 +65,31 @@ private function buildFilters(): void } // Retrieve filters in aggregations - $attributeAggregations = $aggregations['attributes'] ?? []; - unset($attributeAggregations['doc_count']); - foreach ($attributeAggregations as $attributeCode => $attributeAggregation) { - if (isset($attributeAggregation[$attributeCode])) { - $attributeAggregation = $attributeAggregation[$attributeCode]; + foreach (['attributes', 'options'] as $aggregationType) { + $currentAggregation = $aggregations; + if ($aggregationType === 'options') { + $currentAggregation = $aggregations['variants']['enabled_variants'] ?? []; } - $attributeNameBuckets = $attributeAggregation['names']['buckets'] ?? []; - foreach ($attributeNameBuckets as $attributeNameBucket) { - $attributeValueBuckets = $attributeNameBucket['values']['buckets'] ?? []; - $filter = new Filter($attributeCode, $attributeNameBucket['key'], $attributeNameBucket['doc_count']); - foreach ($attributeValueBuckets as $attributeValueBucket) { - if (0 === $attributeValueBucket['doc_count']) { - continue; - } - if (isset($attributeValueBucket['key']) && isset($attributeValueBucket['doc_count'])) { - $filter->addValue($attributeValueBucket['key'], $attributeValueBucket['doc_count']); + $attributeAggregations = $currentAggregation[$aggregationType] ?? []; + unset($attributeAggregations['doc_count']); + foreach ($attributeAggregations as $attributeCode => $attributeAggregation) { + if (isset($attributeAggregation[$attributeCode])) { + $attributeAggregation = $attributeAggregation[$attributeCode]; + } + $attributeNameBuckets = $attributeAggregation['names']['buckets'] ?? []; + foreach ($attributeNameBuckets as $attributeNameBucket) { + $attributeValueBuckets = $attributeNameBucket['values']['buckets'] ?? []; + $filter = new Filter($attributeCode, $attributeNameBucket['key'], $attributeNameBucket['doc_count'], $aggregationType); + foreach ($attributeValueBuckets as $attributeValueBucket) { + if (0 === $attributeValueBucket['doc_count']) { + continue; + } + if (isset($attributeValueBucket['key']) && isset($attributeValueBucket['doc_count'])) { + $filter->addValue($attributeValueBucket['key'], $attributeValueBucket['doc_count']); + } } + $this->filters[] = $filter; } - $this->filters[] = $filter; } } } From 95c7207451f36a41f52c522071f7b88e993a9f40 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Fri, 5 Nov 2021 07:43:33 +0100 Subject: [PATCH 027/142] refactor: variants option simplification --- src/AutoMapper/ProductMapperConfiguration.php | 5 ++- src/Search/Request/Search/Product.php | 45 ++----------------- src/Search/Response.php | 6 +-- 3 files changed, 9 insertions(+), 47 deletions(-) diff --git a/src/AutoMapper/ProductMapperConfiguration.php b/src/AutoMapper/ProductMapperConfiguration.php index 04fbe391..defb80b9 100644 --- a/src/AutoMapper/ProductMapperConfiguration.php +++ b/src/AutoMapper/ProductMapperConfiguration.php @@ -122,11 +122,14 @@ public function process(MapperGeneratorMetadataInterface $metadata): void $variants = []; $currentLocale = $product->getTranslation()->getLocale(); // TODO default locale if it's null? - foreach ($product->getVariants() as $variant) { + foreach ($product->getEnabledVariants() as $variant) { $isInStock = true; if ($variant instanceof StockableInterface) { $isInStock = $this->availabilityChecker->isStockAvailable($variant); } + if (!$isInStock) { + continue; + } // TODO use auto mapper but we want the final class ... $this->autoMapper->map($variant, VariantDTO::class)); $variantDTO = [ 'enabled' => $variant->isEnabled(), diff --git a/src/Search/Request/Search/Product.php b/src/Search/Request/Search/Product.php index 306a7b2e..21348e86 100644 --- a/src/Search/Request/Search/Product.php +++ b/src/Search/Request/Search/Product.php @@ -153,22 +153,6 @@ private function addAggregations(Query $query): void $attributesAgg->addAggregation($filter); } - $enabledFilter = new Query\Term(); - $enabledFilter->setTerm('variants.enabled', true); - - $isInStockFilter = new Query\Term(); - $isInStockFilter->setTerm('variants.is_in_stock', true); - - $filterVariants = new Query\BoolQuery(); - $filterVariants->addFilter($enabledFilter); - $filterVariants->addFilter($isInStockFilter); - - $enabledVariants = new Aggregation\Filter('enabled_variants'); - $enabledVariants->setFilter($filterVariants); - - $variants = new Nested('variants', 'variants'); - $variants->addAggregation($enabledVariants); - $optionsAgg = new Nested('options', 'variants.options'); foreach ($this->productOptionRepository->findIsSearchableOrFilterable() as $productOption) { if (!$productOption->isFilterable()) { @@ -184,13 +168,7 @@ private function addAggregations(Query $query): void $attributeAgg = new Nested($productOption->getCode(), sprintf('variants.options.%s', $productOption->getCode())); $attributeAgg->addAggregation($attributeCodesAgg); - - $boolFilter = $this->getFilters($productOption->getCode()); - $filter = new Aggregation\Filter($productOption->getCode()); - $filter->setFilter($boolFilter); - $filter->addAggregation($attributeAgg); - - $optionsAgg->addAggregation($filter); + $optionsAgg->addAggregation($attributeAgg); } if (0 < \count($attributesAgg->getAggs())) { @@ -198,8 +176,7 @@ private function addAggregations(Query $query): void } if (0 < \count($optionsAgg->getAggs())) { - $enabledVariants->addAggregation($optionsAgg); - $query->addAggregation($variants); + $query->addAggregation($optionsAgg); } } @@ -214,7 +191,7 @@ private function getFilters($currentAttribute = null): Query\BoolQuery foreach ($values as $value) { $termQuery = new Query\Terms(sprintf('attributes.%s.value.keyword', $field), [SlugHelper::toLabel($value)]); - $attributeValueQuery->addShould($termQuery); // todo configure the "and" or "or" + $attributeValueQuery->addShould($termQuery); // src/Search/Request/Search/Product.php configure the "and" or "or" } $attributeQuery = new Query\Nested(); @@ -229,15 +206,6 @@ private function getFilters($currentAttribute = null): Query\BoolQuery if ($currentAttribute == $field) { continue; } - $enabledVariantsFilter = new Query\Term(); - $enabledVariantsFilter->setTerm('variants.enabled', true); - - $isInStockVariantsFilter = new Query\Term(); - $isInStockVariantsFilter->setTerm('variants.is_in_stock', true); - - $variantsQuery = new Query\BoolQuery(); - $variantsQuery->addMust($enabledVariantsFilter); - $variantsQuery->addMust($isInStockVariantsFilter); $attributeValueQuery = new Query\BoolQuery(); @@ -249,12 +217,7 @@ private function getFilters($currentAttribute = null): Query\BoolQuery $attributeQuery = new Query\Nested(); $attributeQuery->setPath(sprintf('variants.options.%s', $field))->setQuery($attributeValueQuery); - $variantsQuery->addMust($attributeQuery); - - $variantsFilter = new Query\Nested(); - $variantsFilter->setPath('variants') - ->setQuery($variantsQuery); - $bool->addMust($variantsFilter); + $bool->addMust($attributeQuery); } return $bool; diff --git a/src/Search/Response.php b/src/Search/Response.php index 62ee543d..df1210b0 100644 --- a/src/Search/Response.php +++ b/src/Search/Response.php @@ -66,11 +66,7 @@ private function buildFilters(): void // Retrieve filters in aggregations foreach (['attributes', 'options'] as $aggregationType) { - $currentAggregation = $aggregations; - if ($aggregationType === 'options') { - $currentAggregation = $aggregations['variants']['enabled_variants'] ?? []; - } - $attributeAggregations = $currentAggregation[$aggregationType] ?? []; + $attributeAggregations = $aggregations[$aggregationType] ?? []; unset($attributeAggregations['doc_count']); foreach ($attributeAggregations as $attributeCode => $attributeAggregation) { if (isset($attributeAggregation[$attributeCode])) { From c984109a5887bf7395e8cc840f705bb3a545da63 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 23 Nov 2021 16:28:17 +0100 Subject: [PATCH 028/142] fix: add file header comment --- src/Controller/SearchController.php | 11 +++++++++++ src/Repository/ProductOptionRepository.php | 11 +++++++++++ src/Repository/ProductOptionRepositoryInterface.php | 11 +++++++++++ src/Search/Request/RequestConfiguration.php | 11 +++++++++++ 4 files changed, 44 insertions(+) diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php index d34a7eea..79b1c099 100644 --- a/src/Controller/SearchController.php +++ b/src/Controller/SearchController.php @@ -1,5 +1,16 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Controller; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; diff --git a/src/Repository/ProductOptionRepository.php b/src/Repository/ProductOptionRepository.php index eb8e0214..af3b8a27 100644 --- a/src/Repository/ProductOptionRepository.php +++ b/src/Repository/ProductOptionRepository.php @@ -1,5 +1,16 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Repository; use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository; diff --git a/src/Repository/ProductOptionRepositoryInterface.php b/src/Repository/ProductOptionRepositoryInterface.php index 263d6d89..0d85ef07 100644 --- a/src/Repository/ProductOptionRepositoryInterface.php +++ b/src/Repository/ProductOptionRepositoryInterface.php @@ -1,5 +1,16 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Repository; use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; diff --git a/src/Search/Request/RequestConfiguration.php b/src/Search/Request/RequestConfiguration.php index d1ce90a0..f7abc2e4 100644 --- a/src/Search/Request/RequestConfiguration.php +++ b/src/Search/Request/RequestConfiguration.php @@ -1,5 +1,16 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Search\Request; use Symfony\Component\HttpFoundation\Request; From 87f3ec9156155cd9ca5cc4fc3d7a7a04ca9ab863 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 23 Nov 2021 16:29:09 +0100 Subject: [PATCH 029/142] feat: add automapper configuration class for the classes source --- src/AutoMapper/Configuration.php | 33 ++++++++++++++++++++ src/DependencyInjection/Configuration.php | 5 +++ src/MonsieurBizSyliusSearchPlugin.php | 2 ++ src/Resources/config/monsieurbiz_search.yaml | 3 ++ 4 files changed, 43 insertions(+) create mode 100644 src/AutoMapper/Configuration.php diff --git a/src/AutoMapper/Configuration.php b/src/AutoMapper/Configuration.php new file mode 100644 index 00000000..04f866de --- /dev/null +++ b/src/AutoMapper/Configuration.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\AutoMapper; + +final class Configuration +{ + private array $sourceClasses = []; + + public function addSourceClass(string $identifier, string $className): void + { + $this->sourceClasses[$identifier] = $className; + } + + public function getSourceClass($identifier): string + { + if (!\array_key_exists($identifier, $this->sourceClasses)) { + throw new \Exception('Unknown source class for: ' . $identifier); + } + + return $this->sourceClasses[$identifier]; + } +} diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 6d5bc15c..02fbc05a 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -47,6 +47,11 @@ public function getConfigTreeBuilder(): TreeBuilder ->end() ->end() ->end() + ->arrayNode('automapper_classes') + ->useAttributeAsKey('code', false) + ->defaultValue([]) + ->prototype('scalar')->end() + ->end() ->end() ; diff --git a/src/MonsieurBizSyliusSearchPlugin.php b/src/MonsieurBizSyliusSearchPlugin.php index 4c46b17f..e5282258 100644 --- a/src/MonsieurBizSyliusSearchPlugin.php +++ b/src/MonsieurBizSyliusSearchPlugin.php @@ -13,6 +13,7 @@ namespace MonsieurBiz\SyliusSearchPlugin; +use MonsieurBiz\SyliusSearchPlugin\DependencyInjection\AutomapperConfigurationRegistryPass; use MonsieurBiz\SyliusSearchPlugin\DependencyInjection\DocumentableRegistryPass; use MonsieurBiz\SyliusSearchPlugin\DependencyInjection\RegisterSearchRequestPass; use Sylius\Bundle\CoreBundle\Application\SyliusPluginTrait; @@ -44,5 +45,6 @@ public function build(ContainerBuilder $container): void parent::build($container); $container->addCompilerPass(new DocumentableRegistryPass()); $container->addCompilerPass(new RegisterSearchRequestPass()); + $container->addCompilerPass(new AutomapperConfigurationRegistryPass()); } } diff --git a/src/Resources/config/monsieurbiz_search.yaml b/src/Resources/config/monsieurbiz_search.yaml index ecce7867..4ba9a845 100644 --- a/src/Resources/config/monsieurbiz_search.yaml +++ b/src/Resources/config/monsieurbiz_search.yaml @@ -7,3 +7,6 @@ monsieurbiz_sylius_search: item: '@MonsieurBizSyliusSearchPlugin/Search/product/_box.html.twig' #mapping_provider: '...' # by default monsieurbiz.search.mapper_provider #dataprovider: '...' # by default MonsieurBiz\SyliusSearchPlugin\Model\Datasource\RepositoryDatasource + automapper_classes: + product: '%sylius.model.product.class%' + product_variant: '%sylius.model.product_variant.class%' From 8a1bf0ce99e903cb4f3e792e983b224821389a29 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 23 Nov 2021 16:29:57 +0100 Subject: [PATCH 030/142] feat: use the automapper configration to get the source class and add VariantDTO --- src/AutoMapper/ProductMapperConfiguration.php | 37 +++-------- src/AutoMapper/VariantMapperConfiguration.php | 63 +++++++++++++++++++ .../AutomapperConfigurationRegistryPass.php | 29 +++++++++ src/Model/Product/VariantDTO.php | 51 +++++++++++++++ 4 files changed, 150 insertions(+), 30 deletions(-) create mode 100644 src/AutoMapper/VariantMapperConfiguration.php create mode 100644 src/DependencyInjection/AutomapperConfigurationRegistryPass.php create mode 100644 src/Model/Product/VariantDTO.php diff --git a/src/AutoMapper/ProductMapperConfiguration.php b/src/AutoMapper/ProductMapperConfiguration.php index defb80b9..4d900b66 100644 --- a/src/AutoMapper/ProductMapperConfiguration.php +++ b/src/AutoMapper/ProductMapperConfiguration.php @@ -13,7 +13,6 @@ namespace MonsieurBiz\SyliusSearchPlugin\AutoMapper; -use App\Entity\Product\Product; use Jane\Bundle\AutoMapperBundle\Configuration\MapperConfigurationInterface; use Jane\Component\AutoMapper\AutoMapperInterface; use Jane\Component\AutoMapper\MapperGeneratorMetadataInterface; @@ -29,18 +28,16 @@ use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\ProductInterface; use Sylius\Component\Core\Model\ProductTaxonInterface; -use Sylius\Component\Inventory\Checker\AvailabilityCheckerInterface; -use Sylius\Component\Inventory\Model\StockableInterface; final class ProductMapperConfiguration implements MapperConfigurationInterface { + private Configuration $configuration; private AutoMapperInterface $autoMapper; - private AvailabilityCheckerInterface $availabilityChecker; - public function __construct(AutoMapperInterface $autoMapper, AvailabilityCheckerInterface $availabilityChecker) + public function __construct(Configuration $configuration, AutoMapperInterface $autoMapper) { + $this->configuration = $configuration; $this->autoMapper = $autoMapper; - $this->availabilityChecker = $availabilityChecker; } public function process(MapperGeneratorMetadataInterface $metadata): void @@ -76,7 +73,7 @@ public function process(MapperGeneratorMetadataInterface $metadata): void $metadata->forMember('images', function(ProductInterface $product): array { $images = []; foreach ($product->getImages() as $image) { - $images[] = $this->autoMapper->map($image, Image::class); + $images[] = $this->autoMapper->map($image, Image::class); // rename the target class to DTO } return $images; @@ -111,6 +108,7 @@ public function process(MapperGeneratorMetadataInterface $metadata): void } /** @var ProductAttribute $attributeDTO */ $attributeDTO = $this->autoMapper->map($attributeValue, ProductAttribute::class); + // todo the getValue depends on the type ... $attributeDTO->setValue($attributeValue->getValue()); // we can't use the automapper for the value because it has a mixed type $attributes[$attributeValue->getCode()] = $attributeDTO; } @@ -120,29 +118,8 @@ public function process(MapperGeneratorMetadataInterface $metadata): void $metadata->forMember('variants', function(ProductInterface $product): array { $variants = []; - $currentLocale = $product->getTranslation()->getLocale(); // TODO default locale if it's null? - foreach ($product->getEnabledVariants() as $variant) { - $isInStock = true; - if ($variant instanceof StockableInterface) { - $isInStock = $this->availabilityChecker->isStockAvailable($variant); - } - if (!$isInStock) { - continue; - } -// TODO use auto mapper but we want the final class ... $this->autoMapper->map($variant, VariantDTO::class)); - $variantDTO = [ - 'enabled' => $variant->isEnabled(), - 'is_in_stock' => $isInStock, - ]; - foreach ($variant->getOptionValues() as $optionValue) { - $variantDTO['options'][$optionValue->getOptionCode()] = [ - 'name' => $optionValue->getName(), - 'code' => $optionValue->getCode(), - 'value' => $optionValue->getTranslation($currentLocale)->getValue(), - ]; - } - $variants[] = $variantDTO; + $variants[] = $this->autoMapper->map($variant, VariantDTO::class); } return $variants; @@ -151,7 +128,7 @@ public function process(MapperGeneratorMetadataInterface $metadata): void public function getSource(): string { - return Product::class; + return $this->configuration->getSourceClass('product'); } public function getTarget(): string diff --git a/src/AutoMapper/VariantMapperConfiguration.php b/src/AutoMapper/VariantMapperConfiguration.php new file mode 100644 index 00000000..a5039c98 --- /dev/null +++ b/src/AutoMapper/VariantMapperConfiguration.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\AutoMapper; + +use Jane\Bundle\AutoMapperBundle\Configuration\MapperConfigurationInterface; +use Jane\Component\AutoMapper\MapperGeneratorMetadataInterface; +use MonsieurBiz\SyliusSearchPlugin\Model\Product\VariantDTO; +use Sylius\Component\Core\Model\ProductVariantInterface; +use Sylius\Component\Inventory\Checker\AvailabilityCheckerInterface; + +final class VariantMapperConfiguration implements MapperConfigurationInterface +{ + private Configuration $configuration; + private AvailabilityCheckerInterface $availabilityChecker; + + public function __construct(Configuration $configuration, AvailabilityCheckerInterface $availabilityChecker) + { + $this->configuration = $configuration; + $this->availabilityChecker = $availabilityChecker; + } + + public function process(MapperGeneratorMetadataInterface $metadata): void + { + $metadata->forMember('is_in_stock', function(ProductVariantInterface $productVariant): bool { + return $this->availabilityChecker->isStockAvailable($productVariant); + }); + + $metadata->forMember('options', function(ProductVariantInterface $productVariant): array { + $currentLocale = $productVariant->getTranslation()->getLocale(); + $options = []; + foreach ($productVariant->getOptionValues() as $optionValue) { + $options[$optionValue->getOptionCode()] = [ + 'name' => $optionValue->getName(), + 'code' => $optionValue->getCode(), + 'value' => $optionValue->getTranslation($currentLocale)->getValue(), + ]; + } + + return $options; + }); + } + + public function getSource(): string + { + return $this->configuration->getSourceClass('product_variant'); + } + + public function getTarget(): string + { + return VariantDTO::class; + } +} diff --git a/src/DependencyInjection/AutomapperConfigurationRegistryPass.php b/src/DependencyInjection/AutomapperConfigurationRegistryPass.php new file mode 100644 index 00000000..e9a86dff --- /dev/null +++ b/src/DependencyInjection/AutomapperConfigurationRegistryPass.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\DependencyInjection; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +final class AutomapperConfigurationRegistryPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container): void + { + $automapperConfig = $container->getDefinition(\MonsieurBiz\SyliusSearchPlugin\AutoMapper\Configuration::class); + $automapperClasses = $container->getParameter('monsieurbiz.search.config.automapper_classes'); + foreach ($automapperClasses as $identifier => $sourceClass) { + $automapperConfig->addMethodCall('addSourceClass', [$identifier, $sourceClass]); + } + } +} diff --git a/src/Model/Product/VariantDTO.php b/src/Model/Product/VariantDTO.php new file mode 100644 index 00000000..6bf3976c --- /dev/null +++ b/src/Model/Product/VariantDTO.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Model\Product; + +use Jacquesbh\Eater\Eater; + +final class VariantDTO extends Eater +{ + /** + * @return bool + */ + public function isEnabled(): bool + { + return (bool) $this->getData('enabled'); + } + + /** + * @param bool $enabled + */ + public function setEnabled(bool $enabled): void + { + $this->setData('enabled', $enabled); + } + + /** + * @return bool + */ + public function isInStock(): bool + { + return (bool) $this->getData('is_in_stock'); + } + + /** + * @param bool $isInStock + */ + public function setIsInStock(bool $isInStock): void + { + $this->setData('is_in_stock', $isInStock); + } +} From f2e66891e3d1d48f92dac9635c0e5a84abcdb756 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Wed, 24 Nov 2021 11:06:44 +0100 Subject: [PATCH 031/142] feat: add attribute value reader for attribute with select type --- .../ProductAttributeValueConfiguration.php | 63 +++++++++++++++++++ .../DefaultReader.php | 24 +++++++ .../ReaderInterface.php | 24 +++++++ .../SelectReader.php | 52 +++++++++++++++ src/AutoMapper/ProductMapperConfiguration.php | 6 +- src/Resources/config/monsieurbiz_search.yaml | 1 + src/Resources/config/services.yaml | 11 +++- 7 files changed, 175 insertions(+), 6 deletions(-) create mode 100644 src/AutoMapper/ProductAttributeValueConfiguration.php create mode 100644 src/AutoMapper/ProductAttributeValueReader/DefaultReader.php create mode 100644 src/AutoMapper/ProductAttributeValueReader/ReaderInterface.php create mode 100644 src/AutoMapper/ProductAttributeValueReader/SelectReader.php diff --git a/src/AutoMapper/ProductAttributeValueConfiguration.php b/src/AutoMapper/ProductAttributeValueConfiguration.php new file mode 100644 index 00000000..a2fdae85 --- /dev/null +++ b/src/AutoMapper/ProductAttributeValueConfiguration.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\AutoMapper; + +use Jane\Bundle\AutoMapperBundle\Configuration\MapperConfigurationInterface; +use Jane\Component\AutoMapper\MapperGeneratorMetadataInterface; +use MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader\ReaderInterface; +use MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductAttribute; +use RuntimeException; +use Sylius\Component\Product\Model\ProductAttributeValueInterface; + +final class ProductAttributeValueConfiguration implements MapperConfigurationInterface +{ + private Configuration $configuration; + /** + * @var ReaderInterface[] + */ + private array $productAttributeValueReaders; + + public function __construct(Configuration $configuration, array $productAttributeValueReaders = []) + { + $this->configuration = $configuration; + $this->productAttributeValueReaders = $productAttributeValueReaders; + } + + public function process(MapperGeneratorMetadataInterface $metadata): void + { + if (0 === \count($this->productAttributeValueReaders) || !\array_key_exists('default', $this->productAttributeValueReaders)) { + throw new RuntimeException('Please define the "default" product attribute value reader'); + } + + $metadata->forMember('value', function(ProductAttributeValueInterface $productAttributeValue) { + /** @var ReaderInterface $reader */ + $reader = $this->productAttributeValueReaders['default']; + if (\array_key_exists($productAttributeValue->getType(), $this->productAttributeValueReaders)) { + $reader = $this->productAttributeValueReaders[$productAttributeValue->getType()]; + } + + return $reader->getValue($productAttributeValue); + }); + } + + public function getSource(): string + { + return $this->configuration->getSourceClass('product_attribute_value'); + } + + public function getTarget(): string + { + return ProductAttribute::class; + } +} diff --git a/src/AutoMapper/ProductAttributeValueReader/DefaultReader.php b/src/AutoMapper/ProductAttributeValueReader/DefaultReader.php new file mode 100644 index 00000000..4364e605 --- /dev/null +++ b/src/AutoMapper/ProductAttributeValueReader/DefaultReader.php @@ -0,0 +1,24 @@ +getValue(); + } +} diff --git a/src/AutoMapper/ProductAttributeValueReader/ReaderInterface.php b/src/AutoMapper/ProductAttributeValueReader/ReaderInterface.php new file mode 100644 index 00000000..bf1ad168 --- /dev/null +++ b/src/AutoMapper/ProductAttributeValueReader/ReaderInterface.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader; + +use Sylius\Component\Product\Model\ProductAttributeValueInterface; +use Sylius\Component\Resource\Translation\Provider\TranslationLocaleProviderInterface; + +class SelectReader implements ReaderInterface +{ + private string $defaultLocaleCode; + + public function __construct(TranslationLocaleProviderInterface $localeProvider) + { + $this->defaultLocaleCode = $localeProvider->getDefaultLocaleCode(); + } + + public function getValue(ProductAttributeValueInterface $productAttribute) + { + if (null === $productAttribute->getAttribute()) { + return ''; + } + + $currentLocale = $productAttribute->getLocaleCode(); + $choices = $productAttribute->getAttribute()->getConfiguration()['choices'] ?? []; + $productAttributeValue = $productAttribute->getValue(); + if (!is_iterable($productAttributeValue)) { + $productAttributeValue = [$productAttributeValue]; + } + + $result = []; + foreach ($productAttributeValue as $value) { + $locale = $currentLocale; + if (!isset($choices[$value][$locale])) { + $locale = $this->defaultLocaleCode; + } + $result[] = $choices[$value][$locale]; + } + + return $result; + } +} diff --git a/src/AutoMapper/ProductMapperConfiguration.php b/src/AutoMapper/ProductMapperConfiguration.php index 4d900b66..ece6e9b5 100644 --- a/src/AutoMapper/ProductMapperConfiguration.php +++ b/src/AutoMapper/ProductMapperConfiguration.php @@ -106,11 +106,7 @@ public function process(MapperGeneratorMetadataInterface $metadata): void if (!$attribute instanceof SearchableInterface || (!$attribute->isSearchable() && !$attribute->isFilterable())) { continue; } - /** @var ProductAttribute $attributeDTO */ - $attributeDTO = $this->autoMapper->map($attributeValue, ProductAttribute::class); - // todo the getValue depends on the type ... - $attributeDTO->setValue($attributeValue->getValue()); // we can't use the automapper for the value because it has a mixed type - $attributes[$attributeValue->getCode()] = $attributeDTO; + $attributes[$attributeValue->getCode()] = $this->autoMapper->map($attributeValue, ProductAttribute::class); } return $attributes; diff --git a/src/Resources/config/monsieurbiz_search.yaml b/src/Resources/config/monsieurbiz_search.yaml index 4ba9a845..55d8ead5 100644 --- a/src/Resources/config/monsieurbiz_search.yaml +++ b/src/Resources/config/monsieurbiz_search.yaml @@ -10,3 +10,4 @@ monsieurbiz_sylius_search: automapper_classes: product: '%sylius.model.product.class%' product_variant: '%sylius.model.product_variant.class%' + product_attribute_value: '%sylius.model.product_attribute_value.class%' diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index b2d2990e..39d5aac3 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -9,6 +9,7 @@ services: autoconfigure: true public: false bind: + $localeProvider: '@sylius.translation_locale_provider' $documentableRegistry: '@monsieurbiz.search.registry.documentable' $searchRequestsRegistry: '@monsieurbiz.search.registry.search_request' $configurationDirectory: '@=service("file_locator").locate("@MonsieurBizSyliusSearchPlugin/Resources/config/elasticsearch")' @@ -66,7 +67,7 @@ services: JoliCode\Elastically\IndexBuilder: arguments: $client: '@monsieurbiz.search.elasticsearch.client' - $mappingProvider: 'MonsieurBiz\SyliusSearchPlugin\Mapping\YamlWithLocaleProvide' + $mappingProvider: '@MonsieurBiz\SyliusSearchPlugin\Mapping\YamlWithLocaleProvider' monsieurbiz.search.registry.documentable: class: Sylius\Component\Registry\ServiceRegistry @@ -79,3 +80,11 @@ services: arguments: $className: '%monsieurbiz.search.request.interface%' $context: 'documentable' + + # Define product attribute value readers + MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueConfiguration: + arguments: + $productAttributeValueReaders: { + default: '@MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader\DefaultReader', + select: '@MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader\SelectReader' + } From a9846450e80958c01f8779cc3178ae1669a47c4d Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Wed, 24 Nov 2021 11:35:50 +0100 Subject: [PATCH 032/142] feat: add datetime & date attribute value reader --- .../DateReader.php | 19 ++++++++++ .../DateTimeReader.php | 35 +++++++++++++++++++ src/Resources/config/services.yaml | 3 +- 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 src/AutoMapper/ProductAttributeValueReader/DateReader.php create mode 100644 src/AutoMapper/ProductAttributeValueReader/DateTimeReader.php diff --git a/src/AutoMapper/ProductAttributeValueReader/DateReader.php b/src/AutoMapper/ProductAttributeValueReader/DateReader.php new file mode 100644 index 00000000..180e09df --- /dev/null +++ b/src/AutoMapper/ProductAttributeValueReader/DateReader.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader; + +class DateReader extends DateTimeReader implements ReaderInterface +{ + protected string $defaultFormat = 'Y-m-d'; +} diff --git a/src/AutoMapper/ProductAttributeValueReader/DateTimeReader.php b/src/AutoMapper/ProductAttributeValueReader/DateTimeReader.php new file mode 100644 index 00000000..7e2c9656 --- /dev/null +++ b/src/AutoMapper/ProductAttributeValueReader/DateTimeReader.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader; + +use Sylius\Component\Product\Model\ProductAttributeValueInterface; + +class DateTimeReader implements ReaderInterface +{ + protected string $defaultFormat = 'Y-m-d H:i:s'; + + public function getValue(ProductAttributeValueInterface $productAttribute) + { + if (null === $productAttribute->getAttribute()) { + return ''; + } + + $productAttributeValue = $productAttribute->getValue(); + if ($productAttributeValue instanceof \DateTime) { + $productAttributeValue = $productAttributeValue->format($this->defaultFormat); + } + + return $productAttributeValue; + } +} diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index 39d5aac3..c001305d 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -86,5 +86,6 @@ services: arguments: $productAttributeValueReaders: { default: '@MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader\DefaultReader', - select: '@MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader\SelectReader' + select: '@MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader\SelectReader', + date: '@MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader\DateReader' } From 010b560b88350e0c4ff2ab86d7a25a6f51f7552d Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Wed, 24 Nov 2021 14:32:18 +0100 Subject: [PATCH 033/142] fix: allow attribute & option facets --- src/Search/Request/Search/Product.php | 77 ++++++++++++++++----------- src/Search/Response.php | 1 + 2 files changed, 46 insertions(+), 32 deletions(-) diff --git a/src/Search/Request/Search/Product.php b/src/Search/Request/Search/Product.php index 21348e86..615c34bb 100644 --- a/src/Search/Request/Search/Product.php +++ b/src/Search/Request/Search/Product.php @@ -131,6 +131,9 @@ private function addAttributesQueries(Query\BoolQuery $searchQuery): void private function addAggregations(Query $query): void { $attributesAgg = new Nested('attributes', 'attributes'); + $filtredAttributesAgg = new Aggregation\Filter('attributes'); + $filtredAttributesAgg->setFilter($this->getFilters(null, ['options'])); + $filtredAttributesAgg->addAggregation($attributesAgg); foreach ($this->productAttributeRepository->findIsSearchableOrFilterable() as $productAttribute) { if (!$productAttribute->isFilterable()) { continue; @@ -145,7 +148,7 @@ private function addAggregations(Query $query): void $attributeAgg = new Nested($productAttribute->getCode(), sprintf('attributes.%s', $productAttribute->getCode())); $attributeAgg->addAggregation($attributeCodesAgg); - $boolFilter = $this->getFilters($productAttribute->getCode()); + $boolFilter = $this->getFilters($productAttribute->getCode(), ['attributes']); $filter = new Aggregation\Filter($productAttribute->getCode()); $filter->setFilter($boolFilter); $filter->addAggregation($attributeAgg); @@ -154,6 +157,9 @@ private function addAggregations(Query $query): void } $optionsAgg = new Nested('options', 'variants.options'); + $filtredOptionsAgg = new Aggregation\Filter('options'); + $filtredOptionsAgg->setFilter($this->getFilters(null, ['attributes'])); + $filtredOptionsAgg->addAggregation($optionsAgg); foreach ($this->productOptionRepository->findIsSearchableOrFilterable() as $productOption) { if (!$productOption->isFilterable()) { continue; @@ -168,56 +174,63 @@ private function addAggregations(Query $query): void $attributeAgg = new Nested($productOption->getCode(), sprintf('variants.options.%s', $productOption->getCode())); $attributeAgg->addAggregation($attributeCodesAgg); - $optionsAgg->addAggregation($attributeAgg); + $boolFilter = $this->getFilters($productOption->getCode(), ['options']); + $filter = new Aggregation\Filter($productOption->getCode()); + $filter->setFilter($boolFilter); + $filter->addAggregation($attributeAgg); + + $optionsAgg->addAggregation($filter); } if (0 < \count($attributesAgg->getAggs())) { - $query->addAggregation($attributesAgg); + $query->addAggregation($filtredAttributesAgg); } if (0 < \count($optionsAgg->getAggs())) { - $query->addAggregation($optionsAgg); + $query->addAggregation($filtredOptionsAgg); } } - private function getFilters($currentAttribute = null): Query\BoolQuery + private function getFilters($currentAttribute = null, array $filtreTypes = ['attributes', 'options']): Query\BoolQuery { $bool = new Query\BoolQuery(); - foreach ($this->configuration->getAppliedFilters('attributes') as $field => $values) { - if ($currentAttribute == $field) { - continue; + if (\in_array('attributes', $filtreTypes, true)) { + foreach ($this->configuration->getAppliedFilters('attributes') as $field => $values) { + if ($currentAttribute == $field) { + continue; + } + $attributeValueQuery = new Query\BoolQuery(); + + foreach ($values as $value) { + $termQuery = new Query\Terms(sprintf('attributes.%s.value.keyword', $field), [SlugHelper::toLabel($value)]); + $attributeValueQuery->addShould($termQuery); // todo configure the "and" or "or" + } + + $attributeQuery = new Query\Nested(); + $attributeQuery->setPath(sprintf('attributes.%s', $field))->setQuery($attributeValueQuery); + + $bool->addMust($attributeQuery); } - $attributeValueQuery = new Query\BoolQuery(); - - foreach ($values as $value) { - $termQuery = new Query\Terms(sprintf('attributes.%s.value.keyword', $field), [SlugHelper::toLabel($value)]); - $attributeValueQuery->addShould($termQuery); // src/Search/Request/Search/Product.php configure the "and" or "or" - } - - $attributeQuery = new Query\Nested(); - $attributeQuery->setPath(sprintf('attributes.%s', $field))->setQuery($attributeValueQuery); - - $bool->addMust($attributeQuery); } + if (\in_array('options', $filtreTypes, true)) { + foreach ($this->configuration->getAppliedFilters('options') as $field => $values) { + if ($currentAttribute == $field) { + continue; + } + $attributeValueQuery = new Query\BoolQuery(); - foreach ($this->configuration->getAppliedFilters('options') as $field => $values) { - if ($currentAttribute == $field) { - continue; - } + foreach ($values as $value) { + $termQuery = new Query\Terms(sprintf('variants.options.%s.value.keyword', $field), [SlugHelper::toLabel($value)]); + $attributeValueQuery->addShould($termQuery); // todo configure the "and" or "or" + } - $attributeValueQuery = new Query\BoolQuery(); + $attributeQuery = new Query\Nested(); + $attributeQuery->setPath(sprintf('variants.options.%s', $field))->setQuery($attributeValueQuery); - foreach ($values as $value) { - $termQuery = new Query\Terms(sprintf('variants.options.%s.value.keyword', $field), [SlugHelper::toLabel($value)]); - $attributeValueQuery->addShould($termQuery); // todo configure the "and" or "or" + $bool->addMust($attributeQuery); } - - $attributeQuery = new Query\Nested(); - $attributeQuery->setPath(sprintf('variants.options.%s', $field))->setQuery($attributeValueQuery); - - $bool->addMust($attributeQuery); } return $bool; diff --git a/src/Search/Response.php b/src/Search/Response.php index df1210b0..5c5ab60f 100644 --- a/src/Search/Response.php +++ b/src/Search/Response.php @@ -67,6 +67,7 @@ private function buildFilters(): void // Retrieve filters in aggregations foreach (['attributes', 'options'] as $aggregationType) { $attributeAggregations = $aggregations[$aggregationType] ?? []; + $attributeAggregations = $attributeAggregations[$aggregationType] ?? $attributeAggregations; unset($attributeAggregations['doc_count']); foreach ($attributeAggregations as $attributeCode => $attributeAggregation) { if (isset($attributeAggregation[$attributeCode])) { From e13bc82027761cefb8b60fa185d8cd6e2f917f27 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Wed, 24 Nov 2021 15:21:24 +0100 Subject: [PATCH 034/142] feat: add main taxon filter --- src/Search/Request/RequestConfiguration.php | 1 + src/Search/Request/Search/Product.php | 56 +++++++++++++++++++-- src/Search/Response.php | 33 ++++++++++++ 3 files changed, 87 insertions(+), 3 deletions(-) diff --git a/src/Search/Request/RequestConfiguration.php b/src/Search/Request/RequestConfiguration.php index f7abc2e4..5f84e7ec 100644 --- a/src/Search/Request/RequestConfiguration.php +++ b/src/Search/Request/RequestConfiguration.php @@ -32,6 +32,7 @@ public function getQueryText(): string public function getAppliedFilters($type = null): array { $appliedFilters = [ + 'taxon' => $this->request->get('taxon', []), 'attributes' => $this->request->get('attributes', []), 'options' => $this->request->get('options', []), ]; diff --git a/src/Search/Request/Search/Product.php b/src/Search/Request/Search/Product.php index 615c34bb..1abaf0b3 100644 --- a/src/Search/Request/Search/Product.php +++ b/src/Search/Request/Search/Product.php @@ -95,6 +95,7 @@ public function getQuery(): Query $boolFilter = $this->getFilters(); $esQuery->setPostFilter($boolFilter); $this->addAggregations($esQuery); + $esQuery->addAggregation($this->getMainTaxonAggregation()); dump($esQuery->toArray()); return $esQuery; @@ -132,7 +133,7 @@ private function addAggregations(Query $query): void { $attributesAgg = new Nested('attributes', 'attributes'); $filtredAttributesAgg = new Aggregation\Filter('attributes'); - $filtredAttributesAgg->setFilter($this->getFilters(null, ['options'])); + $filtredAttributesAgg->setFilter($this->getFilters(null, ['options', 'taxon'])); $filtredAttributesAgg->addAggregation($attributesAgg); foreach ($this->productAttributeRepository->findIsSearchableOrFilterable() as $productAttribute) { if (!$productAttribute->isFilterable()) { @@ -158,7 +159,7 @@ private function addAggregations(Query $query): void $optionsAgg = new Nested('options', 'variants.options'); $filtredOptionsAgg = new Aggregation\Filter('options'); - $filtredOptionsAgg->setFilter($this->getFilters(null, ['attributes'])); + $filtredOptionsAgg->setFilter($this->getFilters(null, ['attributes', 'taxon'])); $filtredOptionsAgg->addAggregation($optionsAgg); foreach ($this->productOptionRepository->findIsSearchableOrFilterable() as $productOption) { if (!$productOption->isFilterable()) { @@ -191,9 +192,58 @@ private function addAggregations(Query $query): void } } - private function getFilters($currentAttribute = null, array $filtreTypes = ['attributes', 'options']): Query\BoolQuery + private function getMainTaxonAggregation(): Aggregation\AbstractAggregation + { + $qb = new \Elastica\QueryBuilder(); + + return $qb->aggregation() + ->nested('main_taxon', 'main_taxon') + ->addAggregation( + $qb->aggregation() + ->terms('codes') + ->setField('main_taxon.code') + ->addAggregation( + $qb->aggregation() + ->terms('levels') + ->setField('main_taxon.level') + ->addAggregation( + $qb->aggregation() + ->terms('names') + ->setField('main_taxon.name') + ) + ) + ) + ; + } + + private function getFilters($currentAttribute = null, array $filtreTypes = ['attributes', 'options', 'taxon']): Query\BoolQuery { $bool = new Query\BoolQuery(); + + //todo + if (\in_array('taxon', $filtreTypes, true)) { + $qb = new \Elastica\QueryBuilder(); + foreach ($this->configuration->getAppliedFilters('taxon') as $field => $values) { + $mainTaxonQuery = $qb->query() + ->bool(); + foreach ($values as $value) { + $mainTaxonQuery->addShould( + $qb->query() + ->term() + ->setTerm(sprintf('%s.name', $field), SlugHelper::toLabel($value)) + ); + } + $bool->addMust( + $qb->query() + ->nested() + ->setPath($field) + ->setQuery( + $mainTaxonQuery + ) + ); + } + } + if (\in_array('attributes', $filtreTypes, true)) { foreach ($this->configuration->getAppliedFilters('attributes') as $field => $values) { if ($currentAttribute == $field) { diff --git a/src/Search/Response.php b/src/Search/Response.php index 5c5ab60f..c1d9e1a5 100644 --- a/src/Search/Response.php +++ b/src/Search/Response.php @@ -64,6 +64,39 @@ private function buildFilters(): void return; } + // todo main taxon + $taxonAggregation = $aggregations['main_taxon'] ?? null; + if ($taxonAggregation && $taxonAggregation['doc_count'] > 0) { + $filter = new Filter('main_taxon', 'monsieurbiz_searchplugin.filters.taxon_filter', $taxonAggregation['doc_count'], 'taxon'); + + // Get main taxon code in aggregation + $taxonCodeBuckets = $taxonAggregation['codes']['buckets'] ?? []; + foreach ($taxonCodeBuckets as $taxonCodeBucket) { + if (0 === $taxonCodeBucket['doc_count']) { + continue; + } + $taxonCode = $taxonCodeBucket['key']; + $taxonName = null; + + // Get main taxon level in aggregation + $taxonLevelBuckets = $taxonCodeBucket['levels']['buckets'] ?? []; + foreach ($taxonLevelBuckets as $taxonLevelBucket) { + // Get main taxon name in aggregation + $taxonNameBuckets = $taxonLevelBucket['names']['buckets'] ?? []; + foreach ($taxonNameBuckets as $taxonNameBucket) { + $taxonName = $taxonNameBucket['key']; + $filter->addValue($taxonName ?? $taxonCode, $taxonCodeBucket['doc_count']); + break 2; + } + } + } + + // Put taxon filter in first if contains value + if (0 !== \count($filter->getValues())) { + $this->filters[] = $filter; + } + } + // Retrieve filters in aggregations foreach (['attributes', 'options'] as $aggregationType) { $attributeAggregations = $aggregations[$aggregationType] ?? []; From 4d54b4e5f92ad51cf26d108d545a5d3ec27a5fe9 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Wed, 24 Nov 2021 17:42:29 +0100 Subject: [PATCH 035/142] feat: index price information in product document --- generated/Model/PricingDTO.php | 115 ++++++++++++++++++ generated/Normalizer/JaneObjectNormalizer.php | 2 +- generated/Normalizer/PricingDTONormalizer.php | 108 ++++++++++++++++ src/AutoMapper/ProductMapperConfiguration.php | 24 +++- .../monsieurbiz_product_mapping.yaml | 13 +- src/Resources/config/jane/json-schema.json | 17 +++ src/Resources/config/services.yaml | 1 + 7 files changed, 275 insertions(+), 5 deletions(-) create mode 100644 generated/Model/PricingDTO.php create mode 100644 generated/Normalizer/PricingDTONormalizer.php diff --git a/generated/Model/PricingDTO.php b/generated/Model/PricingDTO.php new file mode 100644 index 00000000..63d9c9b1 --- /dev/null +++ b/generated/Model/PricingDTO.php @@ -0,0 +1,115 @@ +channelCode; + } + /** + * + * + * @param string $channelCode + * + * @return self + */ + public function setChannelCode(string $channelCode) : self + { + $this->channelCode = $channelCode; + return $this; + } + /** + * + * + * @return null|int + */ + public function getPrice() : ?int + { + return $this->price; + } + /** + * + * + * @param null|int $price + * + * @return self + */ + public function setPrice(?int $price) : self + { + $this->price = $price; + return $this; + } + /** + * + * + * @return null|int + */ + public function getOriginalPrice() : ?int + { + return $this->originalPrice; + } + /** + * + * + * @param null|int $originalPrice + * + * @return self + */ + public function setOriginalPrice(?int $originalPrice) : self + { + $this->originalPrice = $originalPrice; + return $this; + } + /** + * + * + * @return bool + */ + public function getPriceReduced() : bool + { + return $this->priceReduced; + } + /** + * + * + * @param bool $priceReduced + * + * @return self + */ + public function setPriceReduced(bool $priceReduced) : self + { + $this->priceReduced = $priceReduced; + return $this; + } +} \ No newline at end of file diff --git a/generated/Normalizer/JaneObjectNormalizer.php b/generated/Normalizer/JaneObjectNormalizer.php index 45c4d01a..9d3bde0c 100644 --- a/generated/Normalizer/JaneObjectNormalizer.php +++ b/generated/Normalizer/JaneObjectNormalizer.php @@ -14,7 +14,7 @@ class JaneObjectNormalizer implements DenormalizerInterface, NormalizerInterface use DenormalizerAwareTrait; use NormalizerAwareTrait; use CheckArray; - protected $normalizers = array('MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Image' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ImageNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Channel' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ChannelNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductTaxon' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductTaxonNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Taxon' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\TaxonNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductAttribute' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductAttributeNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\MonsieurBiz\\SyliusSearchPlugin\\Generated\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + protected $normalizers = array('MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Image' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ImageNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Channel' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ChannelNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductTaxon' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductTaxonNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Taxon' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\TaxonNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductAttribute' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductAttributeNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\PricingDTO' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\PricingDTONormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\MonsieurBiz\\SyliusSearchPlugin\\Generated\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); public function supportsDenormalization($data, $type, $format = null) { return array_key_exists($type, $this->normalizers); diff --git a/generated/Normalizer/PricingDTONormalizer.php b/generated/Normalizer/PricingDTONormalizer.php new file mode 100644 index 00000000..e0e1d23b --- /dev/null +++ b/generated/Normalizer/PricingDTONormalizer.php @@ -0,0 +1,108 @@ +setChannelCode($data['channel_code']); + } + if (\array_key_exists('price', $data) && $data['price'] !== null) { + $value = $data['price']; + if (is_null($data['price'])) { + $value = $data['price']; + } elseif (is_int($data['price'])) { + $value = $data['price']; + } + $object->setPrice($value); + } + elseif (\array_key_exists('price', $data) && $data['price'] === null) { + $object->setPrice(null); + } + if (\array_key_exists('original_price', $data) && $data['original_price'] !== null) { + $value_1 = $data['original_price']; + if (is_null($data['original_price'])) { + $value_1 = $data['original_price']; + } elseif (is_int($data['original_price'])) { + $value_1 = $data['original_price']; + } + $object->setOriginalPrice($value_1); + } + elseif (\array_key_exists('original_price', $data) && $data['original_price'] === null) { + $object->setOriginalPrice(null); + } + if (\array_key_exists('price_reduced', $data)) { + $value_2 = $data['price_reduced']; + if (is_bool($data['price_reduced'])) { + $value_2 = $data['price_reduced']; + } + $object->setPriceReduced($value_2); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getChannelCode()) { + $data['channel_code'] = $object->getChannelCode(); + } + if (null !== $object->getPrice()) { + $value = $object->getPrice(); + if (is_null($object->getPrice())) { + $value = $object->getPrice(); + } elseif (is_int($object->getPrice())) { + $value = $object->getPrice(); + } + $data['price'] = $value; + } + if (null !== $object->getOriginalPrice()) { + $value_1 = $object->getOriginalPrice(); + if (is_null($object->getOriginalPrice())) { + $value_1 = $object->getOriginalPrice(); + } elseif (is_int($object->getOriginalPrice())) { + $value_1 = $object->getOriginalPrice(); + } + $data['original_price'] = $value_1; + } + if (null !== $object->getPriceReduced()) { + $value_2 = $object->getPriceReduced(); + if (is_bool($object->getPriceReduced())) { + $value_2 = $object->getPriceReduced(); + } + $data['price_reduced'] = $value_2; + } + return $data; + } +} \ No newline at end of file diff --git a/src/AutoMapper/ProductMapperConfiguration.php b/src/AutoMapper/ProductMapperConfiguration.php index ece6e9b5..6ff036fc 100644 --- a/src/AutoMapper/ProductMapperConfiguration.php +++ b/src/AutoMapper/ProductMapperConfiguration.php @@ -20,6 +20,7 @@ use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Channel; use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Image; +use MonsieurBiz\SyliusSearchPlugin\Generated\Model\PricingDTO; use MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductAttribute; use MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductTaxon; use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Taxon; @@ -28,16 +29,24 @@ use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\ProductInterface; use Sylius\Component\Core\Model\ProductTaxonInterface; +use Sylius\Component\Core\Model\ProductVariantInterface; +use Sylius\Component\Product\Resolver\ProductVariantResolverInterface; final class ProductMapperConfiguration implements MapperConfigurationInterface { private Configuration $configuration; private AutoMapperInterface $autoMapper; + private ProductVariantResolverInterface $productVariantResolver; - public function __construct(Configuration $configuration, AutoMapperInterface $autoMapper) - { + public function __construct( + Configuration $configuration, + AutoMapperInterface $autoMapper, + ProductVariantResolverInterface $productVariantResolver + ) { $this->configuration = $configuration; $this->autoMapper = $autoMapper; + // todo change the resolver from the configuration + $this->productVariantResolver = $productVariantResolver; } public function process(MapperGeneratorMetadataInterface $metadata): void @@ -120,6 +129,17 @@ public function process(MapperGeneratorMetadataInterface $metadata): void return $variants; }); + + $metadata->forMember('prices', function(ProductInterface $product): array { + $prices = []; + /** @var ProductVariantInterface $variant */ + $variant = $this->productVariantResolver->getVariant($product); + foreach ($variant->getChannelPricings() as $channelPricing) { + $prices[] = $this->autoMapper->map($channelPricing, PricingDTO::class); + } + + return $prices; + }); } public function getSource(): string diff --git a/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml b/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml index 5a169756..84602fc6 100644 --- a/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml +++ b/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml @@ -47,8 +47,17 @@ mappings: type: integer position: type: integer - price: - type: integer # todo - WIP + prices: + type: nested + properties: + channel_code: + type: keyword + price: + type: integer + original_price: + type: integer + is_price_reduced: + type: boolean variants: type: nested properties: diff --git a/src/Resources/config/jane/json-schema.json b/src/Resources/config/jane/json-schema.json index 0f557c9a..6de22381 100644 --- a/src/Resources/config/jane/json-schema.json +++ b/src/Resources/config/jane/json-schema.json @@ -64,6 +64,23 @@ "type": ["null", "mixed"] } } + }, + "PricingDTO": { + "type": "object", + "properties": { + "channel_code": { + "type": "string" + }, + "price": { + "type": ["null", "integer"] + }, + "original_price": { + "type": ["null", "integer"] + }, + "price_reduced": { + "type": ["boolean"] + } + } } } } diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index c001305d..04843dc3 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -10,6 +10,7 @@ services: public: false bind: $localeProvider: '@sylius.translation_locale_provider' + $productVariantResolver: '@sylius.product_variant_resolver.default' $documentableRegistry: '@monsieurbiz.search.registry.documentable' $searchRequestsRegistry: '@monsieurbiz.search.registry.search_request' $configurationDirectory: '@=service("file_locator").locate("@MonsieurBizSyliusSearchPlugin/Resources/config/elasticsearch")' From d5e30fbb8fb67c50e86c13ce0615a767b6cc57e0 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Wed, 24 Nov 2021 17:57:10 +0100 Subject: [PATCH 036/142] feat: display price in product box --- src/Resources/views/Search/product/_box.html.twig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Resources/views/Search/product/_box.html.twig b/src/Resources/views/Search/product/_box.html.twig index feb69bdd..b019f888 100644 --- a/src/Resources/views/Search/product/_box.html.twig +++ b/src/Resources/views/Search/product/_box.html.twig @@ -21,8 +21,9 @@
{{ item.name }} - {# {% if item.price is not empty %}#} - {#
{{ money.convertAndFormat(result.priceByChannelAndCurrency(channel.code, currencyCode).value) }}
#} - {# {% endif %}#} + {% if item.prices is not empty %} + {% set pricing = item.prices|filter(price => price.channel_code == sylius.channel.code)|first %} +
{{ money.convertAndFormat(pricing.price) }}
+ {% endif %}
From a50112b2d4b3536c1333294f56f9ce0972dbc868 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Thu, 25 Nov 2021 11:38:34 +0100 Subject: [PATCH 037/142] feat: manage sort --- src/AutoMapper/ProductMapperConfiguration.php | 4 ++ .../monsieurbiz_product_mapping.yaml | 5 ++ src/Resources/views/Search/_sorting.html.twig | 51 +++++++++++++++ src/Resources/views/Search/result.html.twig | 2 +- src/Search/Request/RequestConfiguration.php | 5 ++ src/Search/Request/Search/Product.php | 65 ++++++++++++++++++- 6 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 src/Resources/views/Search/_sorting.html.twig diff --git a/src/AutoMapper/ProductMapperConfiguration.php b/src/AutoMapper/ProductMapperConfiguration.php index 6ff036fc..c0e87bc6 100644 --- a/src/AutoMapper/ProductMapperConfiguration.php +++ b/src/AutoMapper/ProductMapperConfiguration.php @@ -79,6 +79,10 @@ public function process(MapperGeneratorMetadataInterface $metadata): void return $product->getDescription(); }); + $metadata->forMember('created_at', function(ProductInterface $product) { + return $product->getCreatedAt(); + }); + $metadata->forMember('images', function(ProductInterface $product): array { $images = []; foreach ($product->getImages() as $image) { diff --git a/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml b/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml index 84602fc6..1a75597c 100644 --- a/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml +++ b/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml @@ -13,6 +13,11 @@ mappings: type: keyword name: type: text + fields: + keyword: + type: keyword + created_at: + type: date description: type: text images: diff --git a/src/Resources/views/Search/_sorting.html.twig b/src/Resources/views/Search/_sorting.html.twig new file mode 100644 index 00000000..7103c174 --- /dev/null +++ b/src/Resources/views/Search/_sorting.html.twig @@ -0,0 +1,51 @@ +{% if result.count > 0 %} + {% set route = app.request.attributes.get('_route') %} + {% set route_parameters = app.request.attributes.get('_route_params')|merge(app.request.query.all) %} + + {% set criteria = app.request.query.get('criteria', {}) %} + + {% set default_path = path(route, route_parameters|merge({'sorting': null, 'criteria': criteria})) %} + {% set from_a_to_z_path = path(route, route_parameters|merge({'sorting': {'name': 'asc'}, 'criteria': criteria})) %} + {% set from_z_to_a_path = path(route, route_parameters|merge({'sorting': {'name': 'desc'}, 'criteria': criteria})) %} + {% set oldest_first_path = path(route, route_parameters|merge({'sorting': {'created_at': 'asc'}, 'criteria': criteria})) %} + {% set newest_first_path = path(route, route_parameters|merge({'sorting': {'created_at': 'desc'}, 'criteria': criteria})) %} + {% set cheapest_first_path = path(route, route_parameters|merge({'sorting': {'price': 'asc'}, 'criteria': criteria})) %} + {% set most_expensive_first_path = path(route, route_parameters|merge({'sorting': {'price': 'desc'}, 'criteria': criteria})) %} + + {% if app.request.query.get('sorting') is empty %} + {% set current_sorting_label = 'sylius.ui.by_position'|trans|lower %} + {% elseif app.request.query.get('sorting').name is defined and app.request.query.get('sorting').name == 'asc'%} + {% set current_sorting_label = 'sylius.ui.from_a_to_z'|trans|lower %} + {% elseif app.request.query.get('sorting').name is defined and app.request.query.get('sorting').name == 'desc'%} + {% set current_sorting_label = 'sylius.ui.from_z_to_a'|trans|lower %} + {% elseif app.request.query.get('sorting').created_at is defined and app.request.query.get('sorting').created_at == 'desc'%} + {% set current_sorting_label = 'sylius.ui.newest_first'|trans|lower %} + {% elseif app.request.query.get('sorting').created_at is defined and app.request.query.get('sorting').created_at == 'asc'%} + {% set current_sorting_label = 'sylius.ui.oldest_first'|trans|lower %} + {% elseif app.request.query.get('sorting').price is defined and app.request.query.get('sorting').price == 'asc'%} + {% set current_sorting_label = 'sylius.ui.cheapest_first'|trans|lower %} + {% elseif app.request.query.get('sorting').price is defined and app.request.query.get('sorting').price == 'desc' %} + {% set current_sorting_label = 'sylius.ui.most_expensive_first'|trans|lower %} + {% else %} + {% set current_sorting_label = 'sylius.ui.by_position'|trans|lower %} + {% endif %} + + +{% endif %} diff --git a/src/Resources/views/Search/result.html.twig b/src/Resources/views/Search/result.html.twig index 54a71ca1..f15a5bf5 100644 --- a/src/Resources/views/Search/result.html.twig +++ b/src/Resources/views/Search/result.html.twig @@ -26,7 +26,7 @@ {% else %} {# include '@MonsieurBizSyliusSearchPlugin/Search/_pagination.html.twig' #} - {# include '@MonsieurBizSyliusSearchPlugin/Search/_sorting.html.twig' #} + {% include '@MonsieurBizSyliusSearchPlugin/Search/_sorting.html.twig' %}
diff --git a/src/Search/Request/RequestConfiguration.php b/src/Search/Request/RequestConfiguration.php index 5f84e7ec..85363545 100644 --- a/src/Search/Request/RequestConfiguration.php +++ b/src/Search/Request/RequestConfiguration.php @@ -39,4 +39,9 @@ public function getAppliedFilters($type = null): array return null !== $type ? ($appliedFilters[$type] ?? []) : $appliedFilters; } + + public function getSorting(): array + { + return $this->request->get('sorting', []); + } } diff --git a/src/Search/Request/Search/Product.php b/src/Search/Request/Search/Product.php index 1abaf0b3..9617e66d 100644 --- a/src/Search/Request/Search/Product.php +++ b/src/Search/Request/Search/Product.php @@ -24,6 +24,7 @@ use MonsieurBiz\SyliusSearchPlugin\Repository\ProductOptionRepositoryInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use MonsieurBiz\SyliusSearchPlugin\Search\RequestInterface; +use Sylius\Component\Channel\Context\ChannelContextInterface; use Sylius\Component\Registry\ServiceRegistryInterface; class Product implements RequestInterface @@ -33,16 +34,19 @@ class Product implements RequestInterface private RequestConfiguration $configuration; private ProductAttributeRepositoryInterface $productAttributeRepository; private ProductOptionRepositoryInterface $productOptionRepository; + private ChannelContextInterface $channelContext; public function __construct( ServiceRegistryInterface $documentableRegistry, ProductAttributeRepositoryInterface $productAttributeRepository, - ProductOptionRepositoryInterface $productOptionRepository + ProductOptionRepositoryInterface $productOptionRepository, + ChannelContextInterface $channelContext ) { //TODO check if exist, return a dummy documentable if not $this->documentable = $documentableRegistry->get('search.documentable.monsieurbiz_product'); $this->productAttributeRepository = $productAttributeRepository; $this->productOptionRepository = $productOptionRepository; + $this->channelContext = $channelContext; } public function getType(): string @@ -96,6 +100,16 @@ public function getQuery(): Query $esQuery->setPostFilter($boolFilter); $this->addAggregations($esQuery); $esQuery->addAggregation($this->getMainTaxonAggregation()); + + // Manage sorting +// $channelCode = $this->channelContext->getChannel()->getCode(); + foreach ($this->configuration->getSorting() as $field => $order) { + $sort = $this->getSort($field, $order); + if (0 !== \count($sort)) { + $esQuery->addSort($this->getSort($field, $order)); + } + } + dump($esQuery->toArray()); return $esQuery; @@ -225,7 +239,8 @@ private function getFilters($currentAttribute = null, array $filtreTypes = ['att $qb = new \Elastica\QueryBuilder(); foreach ($this->configuration->getAppliedFilters('taxon') as $field => $values) { $mainTaxonQuery = $qb->query() - ->bool(); + ->bool() + ; foreach ($values as $value) { $mainTaxonQuery->addShould( $qb->query() @@ -285,4 +300,50 @@ private function getFilters($currentAttribute = null, array $filtreTypes = ['att return $bool; } + + // todo find solution to get more extendable + private function getSort(string $field, string $order) + { + $fieldName = $field; + if ('name' == $field) { + $fieldName = $field . '.keyword'; + } + + switch ($field) { + case 'name': + case 'created_at': + return $this->buildSort($fieldName, $order); + case 'price': + return self::buildSort( + 'prices.price', + $order, + 'prices', + 'prices.channel_code', $this->channelContext->getChannel()->getCode() + ); + case 'position': + default: + // Dummy value to have null sorting in ES and keep ES results sorting + return $this->buildSort('_score', 'desc'); + } + } + + private function buildSort( + string $field, + string $order, + ?string $nestedPath = null, + ?string $sortFilterField = null, + ?string $sortFilterValue = null + ): array { + $sort = [$field => ['order' => $order]]; + if (null !== $nestedPath) { + $sort[$field]['nested_path'] = $nestedPath; + $sort[$field]['nested_filter'] = [ + 'term' => [ + $sortFilterField => $sortFilterValue, + ], + ]; + } + + return $sort; + } } From e037c1da4e766eead97506dd0741329d80b4795c Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Thu, 25 Nov 2021 11:38:57 +0100 Subject: [PATCH 038/142] feat: add channel filter in elasticsearch query --- src/Search/Request/Search/Product.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Search/Request/Search/Product.php b/src/Search/Request/Search/Product.php index 9617e66d..c411519b 100644 --- a/src/Search/Request/Search/Product.php +++ b/src/Search/Request/Search/Product.php @@ -71,7 +71,11 @@ public function getQuery(): Query } $enableFilter = new Query\Terms('enabled', [true]); - // todo add channel filter + $currentChannelFilter = new Query\Term(); + $currentChannelFilter->setTerm('channels.code', $this->channelContext->getChannel()->getCode()); + $channelFilter = new Query\Nested(); + $channelFilter->setPath('channels'); + $channelFilter->setQuery($currentChannelFilter); $searchCode = new Query\Terms('code', [$this->configuration->getQueryText()]); @@ -93,6 +97,7 @@ public function getQuery(): Query $bool = new Query\BoolQuery(); $bool->addFilter($enableFilter); + $bool->addFilter($channelFilter); $bool->addMust($searchQuery); $esQuery = Query::create($bool); From 6a30c7a3b32d33b7434c8cab1399f1ba7b0fe682 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Thu, 25 Nov 2021 15:10:21 +0100 Subject: [PATCH 039/142] feat: add paginator --- src/Controller/SearchController.php | 2 +- src/Resources/views/Search/result.html.twig | 4 ++-- src/Search/Request/RequestConfiguration.php | 5 +++++ src/Search/Response.php | 9 +++++++-- src/Search/ResponseInterface.php | 3 +++ src/Search/Search.php | 5 +++-- src/Search/SearchInterface.php | 4 +++- 7 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php index 79b1c099..0f4f92e2 100644 --- a/src/Controller/SearchController.php +++ b/src/Controller/SearchController.php @@ -40,7 +40,7 @@ public function searchAction(Request $request, string $query): Response $elasticsearchRequest = $this->requestFactory->create(RequestInterface::SEARCH_TYPE, 'monsieurbiz_product'); $elasticsearchRequest->setConfiguration($requestConfiguration); - $result = $this->search->query($elasticsearchRequest); + $result = $this->search->query($requestConfiguration, $elasticsearchRequest); return $this->render('@MonsieurBizSyliusSearchPlugin/Search/result.html.twig', [ 'documentable' => $elasticsearchRequest->getDocumentable(), diff --git a/src/Resources/views/Search/result.html.twig b/src/Resources/views/Search/result.html.twig index f15a5bf5..afb2a9d3 100644 --- a/src/Resources/views/Search/result.html.twig +++ b/src/Resources/views/Search/result.html.twig @@ -25,7 +25,7 @@
{% else %} - {# include '@MonsieurBizSyliusSearchPlugin/Search/_pagination.html.twig' #} +{# {% include '@MonsieurBizSyliusSearchPlugin/Search/_pagination.html.twig' %}#} {% include '@MonsieurBizSyliusSearchPlugin/Search/_sorting.html.twig' %} @@ -37,7 +37,7 @@ - {# {{ pagination.simple(resultSet.pager) }} #} + {{ pagination.simple(result.paginator) }} {% endif %} diff --git a/src/Search/Request/RequestConfiguration.php b/src/Search/Request/RequestConfiguration.php index 85363545..c1be13a4 100644 --- a/src/Search/Request/RequestConfiguration.php +++ b/src/Search/Request/RequestConfiguration.php @@ -44,4 +44,9 @@ public function getSorting(): array { return $this->request->get('sorting', []); } + + public function getPage(): int + { + return (int) $this->request->get('page', 1); + } } diff --git a/src/Search/Response.php b/src/Search/Response.php index c1d9e1a5..25ba07b2 100644 --- a/src/Search/Response.php +++ b/src/Search/Response.php @@ -15,17 +15,20 @@ use Elastica\ResultSet; use MonsieurBiz\SyliusSearchPlugin\Search\Filter\Filter; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use Pagerfanta\Adapter\AdapterInterface; use Pagerfanta\Pagerfanta; class Response implements ResponseInterface { + private RequestConfiguration $requestConfiguration; private AdapterInterface $adapter; private ?Pagerfanta $paginator = null; private array $filters = []; - public function __construct(AdapterInterface $adapter) + public function __construct(RequestConfiguration $requestConfiguration, AdapterInterface $adapter) { + $this->requestConfiguration = $requestConfiguration; $this->adapter = $adapter; $this->buildFilters(); } @@ -45,10 +48,12 @@ public function getFilters(): array return $this->filters; } - private function getPaginator(): Pagerfanta + public function getPaginator(): Pagerfanta { if (null === $this->paginator) { $this->paginator = new Pagerfanta($this->adapter); + $this->paginator->setCurrentPage($this->requestConfiguration->getPage()); + $this->paginator->setMaxPerPage(10); // @todo } return $this->paginator; diff --git a/src/Search/ResponseInterface.php b/src/Search/ResponseInterface.php index 4e7b3f7f..28e28052 100644 --- a/src/Search/ResponseInterface.php +++ b/src/Search/ResponseInterface.php @@ -14,6 +14,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search; use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterInterface; +use Pagerfanta\Pagerfanta; interface ResponseInterface extends \IteratorAggregate, \Countable { @@ -21,4 +22,6 @@ interface ResponseInterface extends \IteratorAggregate, \Countable * @return FilterInterface[] */ public function getFilters(): array; + + public function getPaginator(): Pagerfanta; } diff --git a/src/Search/Search.php b/src/Search/Search.php index 87893c6d..2af5467f 100644 --- a/src/Search/Search.php +++ b/src/Search/Search.php @@ -15,6 +15,7 @@ use JoliCode\Elastically\Factory; use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use Pagerfanta\Elastica\ElasticaAdapter; use Sylius\Component\Locale\Context\LocaleContextInterface; use Symfony\Component\Serializer\SerializerInterface; @@ -30,7 +31,7 @@ public function __construct(SerializerInterface $serializer, LocaleContextInterf $this->localeContext = $localeContext; } - public function query(RequestInterface $request): ResponseInterface + public function query(RequestConfiguration $requestConfiguration, RequestInterface $request): ResponseInterface { $indexName = $this->getIndexName($request->getDocumentable(), $this->localeContext->getLocaleCode()); $factory = new Factory([ @@ -41,7 +42,7 @@ public function query(RequestInterface $request): ResponseInterface ]); $client = $factory->buildClient(); - return new Response(new ElasticaAdapter($client->getIndex($indexName), $request->getQuery())); + return new Response($requestConfiguration, new ElasticaAdapter($client->getIndex($indexName), $request->getQuery())); } private function getIndexName(DocumentableInterface $documentable, ?string $locale = null): string diff --git a/src/Search/SearchInterface.php b/src/Search/SearchInterface.php index df0dd0c6..5cbe2675 100644 --- a/src/Search/SearchInterface.php +++ b/src/Search/SearchInterface.php @@ -13,7 +13,9 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; + interface SearchInterface { - public function query(RequestInterface $request): ResponseInterface; + public function query(RequestConfiguration $requestConfiguration, RequestInterface $request): ResponseInterface; } From 19efc36106e0e7a64446ac940384d4c00276cf2a Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Thu, 25 Nov 2021 16:12:31 +0100 Subject: [PATCH 040/142] feat: add limit --- src/Controller/SearchController.php | 1 + .../views/Search/_pagination.html.twig | 16 ++++++++++++++++ src/Resources/views/Search/result.html.twig | 2 +- src/Search/Request/RequestConfiguration.php | 18 ++++++++++++++++++ src/Search/Response.php | 2 +- 5 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 src/Resources/views/Search/_pagination.html.twig diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php index 0f4f92e2..9b09cf67 100644 --- a/src/Controller/SearchController.php +++ b/src/Controller/SearchController.php @@ -46,6 +46,7 @@ public function searchAction(Request $request, string $query): Response 'documentable' => $elasticsearchRequest->getDocumentable(), 'query' => $query, 'result' => $result, + 'limits' => $requestConfiguration->getAvailableLimits(), ]); } } diff --git a/src/Resources/views/Search/_pagination.html.twig b/src/Resources/views/Search/_pagination.html.twig new file mode 100644 index 00000000..ac68ee1b --- /dev/null +++ b/src/Resources/views/Search/_pagination.html.twig @@ -0,0 +1,16 @@ +{% set paginationLimits = limits %} + +
+
+ +
+
diff --git a/src/Resources/views/Search/result.html.twig b/src/Resources/views/Search/result.html.twig index afb2a9d3..b9d802f9 100644 --- a/src/Resources/views/Search/result.html.twig +++ b/src/Resources/views/Search/result.html.twig @@ -25,7 +25,7 @@ {% else %} -{# {% include '@MonsieurBizSyliusSearchPlugin/Search/_pagination.html.twig' %}#} + {% include '@MonsieurBizSyliusSearchPlugin/Search/_pagination.html.twig' %} {% include '@MonsieurBizSyliusSearchPlugin/Search/_sorting.html.twig' %} diff --git a/src/Search/Request/RequestConfiguration.php b/src/Search/Request/RequestConfiguration.php index c1be13a4..cbefe190 100644 --- a/src/Search/Request/RequestConfiguration.php +++ b/src/Search/Request/RequestConfiguration.php @@ -49,4 +49,22 @@ public function getPage(): int { return (int) $this->request->get('page', 1); } + + public function getLimit(): int + { + $limit = (int) $this->request->get('limit'); + $availableLimits = $this->getAvailableLimits(); + + if (!\in_array($limit, $availableLimits, true)) { + $limit = reset($availableLimits); + } + + return $limit; + } + + public function getAvailableLimits(): array + { + // TODO define this in config (by query type?) + return [9, 18, 27]; + } } diff --git a/src/Search/Response.php b/src/Search/Response.php index 25ba07b2..da21ac0b 100644 --- a/src/Search/Response.php +++ b/src/Search/Response.php @@ -53,7 +53,7 @@ public function getPaginator(): Pagerfanta if (null === $this->paginator) { $this->paginator = new Pagerfanta($this->adapter); $this->paginator->setCurrentPage($this->requestConfiguration->getPage()); - $this->paginator->setMaxPerPage(10); // @todo + $this->paginator->setMaxPerPage($this->requestConfiguration->getLimit()); } return $this->paginator; From d60ee37d2b4842af099cbd3cf1a08554b9a0ab46 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Thu, 25 Nov 2021 16:13:18 +0100 Subject: [PATCH 041/142] feat: add fuzziness in product search query --- src/Search/Request/Search/Product.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Search/Request/Search/Product.php b/src/Search/Request/Search/Product.php index c411519b..b72399f2 100644 --- a/src/Search/Request/Search/Product.php +++ b/src/Search/Request/Search/Product.php @@ -86,6 +86,7 @@ public function getQuery(): Query ]); $nameAndDescriptionQuery->setQuery($this->configuration->getQueryText()); $nameAndDescriptionQuery->setType(MultiMatch::TYPE_MOST_FIELDS); + $nameAndDescriptionQuery->setFuzziness(MultiMatch::FUZZINESS_AUTO); $searchQuery = new Query\BoolQuery(); $searchQuery @@ -107,7 +108,6 @@ public function getQuery(): Query $esQuery->addAggregation($this->getMainTaxonAggregation()); // Manage sorting -// $channelCode = $this->channelContext->getChannel()->getCode(); foreach ($this->configuration->getSorting() as $field => $order) { $sort = $this->getSort($field, $order); if (0 !== \count($sort)) { @@ -137,6 +137,7 @@ private function addAttributesQueries(Query\BoolQuery $searchQuery): void sprintf('attributes.%s.value^%d', $productAttribute->getCode(), $productAttribute->getSearchWeight()), ]); $attributeValueQuery->setQuery($this->configuration->getQueryText()); + $attributeValueQuery->setFuzziness(MultiMatch::FUZZINESS_AUTO); $attributeQuery = new Query\Nested(); $attributeQuery->setPath(sprintf('attributes.%s', $productAttribute->getCode()))->setQuery($attributeValueQuery); From eb54be2f8b86e8a42b81ebc3d8d4a085926660f9 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Fri, 26 Nov 2021 16:25:18 +0100 Subject: [PATCH 042/142] feat: add price filter --- src/Controller/SearchController.php | 10 +- .../views/Search/Filter/default.html.twig | 28 ++++ .../views/Search/Filter/range.html.twig | 27 ++++ src/Resources/views/Search/_filter.html.twig | 35 +---- src/Search/Filter/RangeFilter.php | 122 ++++++++++++++++++ src/Search/Request/RequestConfiguration.php | 1 + src/Search/Request/Search/Product.php | 54 +++++++- src/Search/Response.php | 14 ++ 8 files changed, 260 insertions(+), 31 deletions(-) create mode 100644 src/Resources/views/Search/Filter/default.html.twig create mode 100644 src/Resources/views/Search/Filter/range.html.twig create mode 100644 src/Search/Filter/RangeFilter.php diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php index 9b09cf67..e091a3b8 100644 --- a/src/Controller/SearchController.php +++ b/src/Controller/SearchController.php @@ -17,19 +17,26 @@ use MonsieurBiz\SyliusSearchPlugin\Search\RequestFactory; use MonsieurBiz\SyliusSearchPlugin\Search\RequestInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Search; +use Sylius\Component\Currency\Context\CurrencyContextInterface; +use Sylius\Component\Locale\Context\LocaleContextInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Intl\Currencies; class SearchController extends AbstractController { private RequestFactory $requestFactory; private Search $search; + private CurrencyContextInterface $currencyContext; + private LocaleContextInterface $localeContext; - public function __construct(RequestFactory $requestFactory, Search $search) + public function __construct(RequestFactory $requestFactory, Search $search, CurrencyContextInterface $currencyContext, LocaleContextInterface $localeContext) { $this->requestFactory = $requestFactory; $this->search = $search; + $this->currencyContext = $currencyContext; + $this->localeContext = $localeContext; } // TODO add an optional parameter $documentType (nullable => get the default document type) @@ -47,6 +54,7 @@ public function searchAction(Request $request, string $query): Response 'query' => $query, 'result' => $result, 'limits' => $requestConfiguration->getAvailableLimits(), + 'currencySymbol' => Currencies::getSymbol($this->currencyContext->getCurrencyCode(), $this->localeContext->getLocaleCode()), ]); } } diff --git a/src/Resources/views/Search/Filter/default.html.twig b/src/Resources/views/Search/Filter/default.html.twig new file mode 100644 index 00000000..33b4b2cf --- /dev/null +++ b/src/Resources/views/Search/Filter/default.html.twig @@ -0,0 +1,28 @@ +{% set appliedAttributes = app.request.query.get(filter.type) %} +{% set currentValues = (appliedAttributes[filter.code] is defined) ? appliedAttributes[filter.code] : [] %} +{% if filter.values|length or currentValues is not empty %} +
+
+
{{ filter.label | trans }}
+
+ {% for value in filter.values %} + {% set valueIsApplied = (value.slug in currentValues) %} +
+
+ +
+
+ {% endfor %} +
+
+
+{% endif %} diff --git a/src/Resources/views/Search/Filter/range.html.twig b/src/Resources/views/Search/Filter/range.html.twig new file mode 100644 index 00000000..2427155f --- /dev/null +++ b/src/Resources/views/Search/Filter/range.html.twig @@ -0,0 +1,27 @@ +
+ {% set currentValue = app.request.query.get(filter.code) %} + {% set minValue = (currentValue['min'] is defined) ? currentValue['min'] : filter.min %} + {% set maxValue = (currentValue['max'] is defined) ? currentValue['max'] : filter.max %} + +
+
{{ filter.label | trans }}
+
+ +
+ +
+
{{ currencySymbol }}
+ +
+
+ +
+ +
+
{{ currencySymbol }}
+ +
+
+
+
+
diff --git a/src/Resources/views/Search/_filter.html.twig b/src/Resources/views/Search/_filter.html.twig index 33b4b2cf..e1cba8b2 100644 --- a/src/Resources/views/Search/_filter.html.twig +++ b/src/Resources/views/Search/_filter.html.twig @@ -1,28 +1,7 @@ -{% set appliedAttributes = app.request.query.get(filter.type) %} -{% set currentValues = (appliedAttributes[filter.code] is defined) ? appliedAttributes[filter.code] : [] %} -{% if filter.values|length or currentValues is not empty %} -
-
-
{{ filter.label | trans }}
-
- {% for value in filter.values %} - {% set valueIsApplied = (value.slug in currentValues) %} -
-
- -
-
- {% endfor %} -
-
-
-{% endif %} +{% include [ + '@MonsieurBizSyliusSearchPlugin/Search/Filter/'~filter.type~'.html.twig', + '@MonsieurBizSyliusSearchPlugin/Search/Filter/default.html.twig' + ] with { + 'filter': filter, + 'currencySymbol': currencySymbol +} %} diff --git a/src/Search/Filter/RangeFilter.php b/src/Search/Filter/RangeFilter.php new file mode 100644 index 00000000..6a582152 --- /dev/null +++ b/src/Search/Filter/RangeFilter.php @@ -0,0 +1,122 @@ +code = $code; + $this->label = $label; + $this->minLabel = $minLabel; + $this->maxLabel = $maxLabel; + $this->min = $min; + $this->max = $max; + } + + /** + * @return string + */ + public function getCode(): string + { + return $this->code; + } + + /** + * @return string + */ + public function getLabel(): string + { + return $this->label; + } + + /** + * @return string + */ + public function getMinLabel(): string + { + return $this->minLabel; + } + + /** + * @return string + */ + public function getMaxLabel(): string + { + return $this->maxLabel; + } + + /** + * @return int + */ + public function getMin(): int + { + return $this->min; + } + + /** + * @return int + */ + public function getMax(): int + { + return $this->max; + } + + public function getType(): string + { + return 'range'; + } +} diff --git a/src/Search/Request/RequestConfiguration.php b/src/Search/Request/RequestConfiguration.php index cbefe190..9aa7d619 100644 --- a/src/Search/Request/RequestConfiguration.php +++ b/src/Search/Request/RequestConfiguration.php @@ -33,6 +33,7 @@ public function getAppliedFilters($type = null): array { $appliedFilters = [ 'taxon' => $this->request->get('taxon', []), + 'price' =>$this->request->get('price', null), 'attributes' => $this->request->get('attributes', []), 'options' => $this->request->get('options', []), ]; diff --git a/src/Search/Request/Search/Product.php b/src/Search/Request/Search/Product.php index b72399f2..e57fb35d 100644 --- a/src/Search/Request/Search/Product.php +++ b/src/Search/Request/Search/Product.php @@ -106,6 +106,7 @@ public function getQuery(): Query $esQuery->setPostFilter($boolFilter); $this->addAggregations($esQuery); $esQuery->addAggregation($this->getMainTaxonAggregation()); + $esQuery->addAggregation($this->getPriceAggregation()); // Manage sorting foreach ($this->configuration->getSorting() as $field => $order) { @@ -115,7 +116,7 @@ public function getQuery(): Query } } - dump($esQuery->toArray()); + dump(json_encode($esQuery->toArray(), 1)); return $esQuery; } @@ -236,7 +237,29 @@ private function getMainTaxonAggregation(): Aggregation\AbstractAggregation ; } - private function getFilters($currentAttribute = null, array $filtreTypes = ['attributes', 'options', 'taxon']): Query\BoolQuery + private function getPriceAggregation(): Aggregation\AbstractAggregation + { + $qb = new \Elastica\QueryBuilder(); + + return $qb->aggregation() + ->nested('prices', 'prices') + ->addAggregation( + $qb->aggregation() + ->filter('prices') + ->setFilter( + $qb->query()->term() + ->setTerm('prices.channel_code', $this->channelContext->getChannel()->getCode()) + ) + ->addAggregation( + $qb->aggregation() + ->stats('prices_stats') + ->setField('prices.price') + ) + ) + ; + } + + private function getFilters($currentAttribute = null, array $filtreTypes = ['attributes', 'options', 'taxon', 'price']): Query\BoolQuery { $bool = new Query\BoolQuery(); @@ -265,6 +288,33 @@ private function getFilters($currentAttribute = null, array $filtreTypes = ['att } } + //todo + if (\in_array('price', $filtreTypes, true) && 0 !== count($this->configuration->getAppliedFilters('price'))) { + $qb = new \Elastica\QueryBuilder(); + + // channel filter + $channelPriceFilter = $qb->query() + ->term(['prices.channel_code' => $this->channelContext->getChannel()->getCode()]) + ; + $priceValue = $this->configuration->getAppliedFilters('price'); + $priceQuery = $qb->query() + ->range('prices.price', [ + 'gte' => $priceValue['min'] * 100, + 'lte' => $priceValue['max'] * 100, + ]) + ; + $bool->addMust( + $qb->query() + ->nested() + ->setPath('prices') + ->setQuery( + $qb->query()->bool() + ->addMust($channelPriceFilter) + ->addMust($priceQuery) + ) + ); + } + if (\in_array('attributes', $filtreTypes, true)) { foreach ($this->configuration->getAppliedFilters('attributes') as $field => $values) { if ($currentAttribute == $field) { diff --git a/src/Search/Response.php b/src/Search/Response.php index da21ac0b..d83c375e 100644 --- a/src/Search/Response.php +++ b/src/Search/Response.php @@ -15,6 +15,7 @@ use Elastica\ResultSet; use MonsieurBiz\SyliusSearchPlugin\Search\Filter\Filter; +use MonsieurBiz\SyliusSearchPlugin\Search\Filter\RangeFilter; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use Pagerfanta\Adapter\AdapterInterface; use Pagerfanta\Pagerfanta; @@ -102,6 +103,19 @@ private function buildFilters(): void } } + // todo price + $priceAggregation = $aggregations['prices']['prices'] ?? null; + if ($priceAggregation && $priceAggregation['doc_count'] > 0) { + $this->filters[] = new RangeFilter( + 'price', + 'monsieurbiz_searchplugin.filters.price_filter', + 'monsieurbiz_searchplugin.filters.price_min', + 'monsieurbiz_searchplugin.filters.price_max', + (int) floor(($priceAggregation['prices_stats']['min'] ?? 0) / 100), + (int) ceil(($priceAggregation['prices_stats']['max'] ?? 0) / 100) + ); + } + // Retrieve filters in aggregations foreach (['attributes', 'options'] as $aggregationType) { $attributeAggregations = $aggregations[$aggregationType] ?? []; From 3d1938d74b17923d63f4c650dfd8dff65e859584 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Fri, 26 Nov 2021 16:29:20 +0100 Subject: [PATCH 043/142] fix: search command --- src/Command/SearchCommand.php | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/Command/SearchCommand.php b/src/Command/SearchCommand.php index 0673dbd4..98aacd50 100644 --- a/src/Command/SearchCommand.php +++ b/src/Command/SearchCommand.php @@ -14,33 +14,43 @@ namespace MonsieurBiz\SyliusSearchPlugin\Command; use MonsieurBiz\SyliusSearchPlugin\Model\Product\ProductDTO; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use MonsieurBiz\SyliusSearchPlugin\Search\RequestFactory; use MonsieurBiz\SyliusSearchPlugin\Search\RequestInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Search; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; class SearchCommand extends Command { protected static $defaultName = 'monsieurbiz:search:search'; private RequestFactory $requestFactory; private Search $search; + private RequestStack $requestStack; public function __construct( - RequestFactory $requestFactory, Search $search, $name = null + RequestFactory $requestFactory, + Search $search, + RequestStack $requestStack, + $name = null ) { parent::__construct($name); $this->requestFactory = $requestFactory; $this->search = $search; + $this->requestStack = $requestStack; } protected function configure(): void { parent::configure(); $this->addArgument('query', InputArgument::REQUIRED, 'Search query'); + $this->addOption('channel', 'c', InputOption::VALUE_OPTIONAL, 'Channel code', 'FASHION_WEB'); } protected function execute(InputInterface $input, OutputInterface $output) @@ -48,10 +58,15 @@ protected function execute(InputInterface $input, OutputInterface $output) $io = new SymfonyStyle($input, $output); $query = $input->getArgument('query'); - $request = $this->requestFactory->create(RequestInterface::SEARCH_TYPE, 'monsieurbiz_product'); - $request->setQueryParameters(['query_text' => $query]); - $result = $this->search->query($request); + $request = new Request(['query' => $query, '_channel_code' => $input->getOption('channel')]); + $this->requestStack->push($request); + + $requestConfiguration = new RequestConfiguration($request); + $elasticsearchRequest = $this->requestFactory->create(RequestInterface::SEARCH_TYPE, 'monsieurbiz_product'); + $elasticsearchRequest->setConfiguration($requestConfiguration); + + $result = $this->search->query($requestConfiguration, $elasticsearchRequest); $io->title('Search result for: ' . $query); $io->section('Nb results: ' . $result->count()); $documents = []; From 62e2bbc293e456f4bdac4aea5bbf8fd7392ee05f Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Fri, 26 Nov 2021 16:39:50 +0100 Subject: [PATCH 044/142] refactor: rename the request factory to request handler --- src/Command/SearchCommand.php | 14 +++----------- src/Controller/SearchController.php | 17 +++++------------ .../MonsieurBizSyliusSearchExtension.php | 2 +- src/Resources/config/services.yaml | 2 +- src/Search/Request/RequestConfiguration.php | 18 ++++++++++++++++-- .../RequestHandler.php} | 11 +++++++---- src/Search/{ => Request}/RequestInterface.php | 2 +- src/Search/Request/Search/Product.php | 2 +- src/Search/Response.php | 10 +++++++++- src/Search/ResponseInterface.php | 3 +++ src/Search/Search.php | 16 +++++++++++++--- src/Search/SearchInterface.php | 2 +- 12 files changed, 61 insertions(+), 38 deletions(-) rename src/Search/{RequestFactory.php => Request/RequestHandler.php} (68%) rename src/Search/{ => Request}/RequestInterface.php (94%) diff --git a/src/Command/SearchCommand.php b/src/Command/SearchCommand.php index 98aacd50..d13704f5 100644 --- a/src/Command/SearchCommand.php +++ b/src/Command/SearchCommand.php @@ -15,8 +15,7 @@ use MonsieurBiz\SyliusSearchPlugin\Model\Product\ProductDTO; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; -use MonsieurBiz\SyliusSearchPlugin\Search\RequestFactory; -use MonsieurBiz\SyliusSearchPlugin\Search\RequestInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Search; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; @@ -30,18 +29,15 @@ class SearchCommand extends Command { protected static $defaultName = 'monsieurbiz:search:search'; - private RequestFactory $requestFactory; private Search $search; private RequestStack $requestStack; public function __construct( - RequestFactory $requestFactory, Search $search, RequestStack $requestStack, $name = null ) { parent::__construct($name); - $this->requestFactory = $requestFactory; $this->search = $search; $this->requestStack = $requestStack; } @@ -58,15 +54,11 @@ protected function execute(InputInterface $input, OutputInterface $output) $io = new SymfonyStyle($input, $output); $query = $input->getArgument('query'); - $request = new Request(['query' => $query, '_channel_code' => $input->getOption('channel')]); $this->requestStack->push($request); + $requestConfiguration = new RequestConfiguration($request, RequestInterface::SEARCH_TYPE, 'monsieurbiz_product'); - $requestConfiguration = new RequestConfiguration($request); - $elasticsearchRequest = $this->requestFactory->create(RequestInterface::SEARCH_TYPE, 'monsieurbiz_product'); - $elasticsearchRequest->setConfiguration($requestConfiguration); - - $result = $this->search->query($requestConfiguration, $elasticsearchRequest); + $result = $this->search->search($requestConfiguration); $io->title('Search result for: ' . $query); $io->section('Nb results: ' . $result->count()); $documents = []; diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php index e091a3b8..5e913d82 100644 --- a/src/Controller/SearchController.php +++ b/src/Controller/SearchController.php @@ -14,8 +14,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Controller; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; -use MonsieurBiz\SyliusSearchPlugin\Search\RequestFactory; -use MonsieurBiz\SyliusSearchPlugin\Search\RequestInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Search; use Sylius\Component\Currency\Context\CurrencyContextInterface; use Sylius\Component\Locale\Context\LocaleContextInterface; @@ -26,14 +25,12 @@ class SearchController extends AbstractController { - private RequestFactory $requestFactory; private Search $search; private CurrencyContextInterface $currencyContext; private LocaleContextInterface $localeContext; - public function __construct(RequestFactory $requestFactory, Search $search, CurrencyContextInterface $currencyContext, LocaleContextInterface $localeContext) + public function __construct(Search $search, CurrencyContextInterface $currencyContext, LocaleContextInterface $localeContext) { - $this->requestFactory = $requestFactory; $this->search = $search; $this->currencyContext = $currencyContext; $this->localeContext = $localeContext; @@ -42,15 +39,11 @@ public function __construct(RequestFactory $requestFactory, Search $search, Curr // TODO add an optional parameter $documentType (nullable => get the default document type) public function searchAction(Request $request, string $query): Response { - $requestConfiguration = new RequestConfiguration($request); - // TODO create a requestConfiguration (like \Sylius\Bundle\ResourceBundle\Controller\RequestConfiguration without metadata) - $elasticsearchRequest = $this->requestFactory->create(RequestInterface::SEARCH_TYPE, 'monsieurbiz_product'); - $elasticsearchRequest->setConfiguration($requestConfiguration); - - $result = $this->search->query($requestConfiguration, $elasticsearchRequest); + $requestConfiguration = new RequestConfiguration($request, RequestInterface::SEARCH_TYPE, 'monsieurbiz_product'); + $result = $this->search->search($requestConfiguration); return $this->render('@MonsieurBizSyliusSearchPlugin/Search/result.html.twig', [ - 'documentable' => $elasticsearchRequest->getDocumentable(), + 'documentable' => $result->getDocumentable(), 'query' => $query, 'result' => $result, 'limits' => $requestConfiguration->getAvailableLimits(), diff --git a/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php b/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php index 100162aa..a63d9ee3 100644 --- a/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php +++ b/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php @@ -13,7 +13,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\DependencyInjection; -use MonsieurBiz\SyliusSearchPlugin\Search\RequestInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\Extension; diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index 04843dc3..edba9290 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -1,6 +1,6 @@ parameters: monsieurbiz.search.model.documentable.interface: MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface - monsieurbiz.search.request.interface: MonsieurBiz\SyliusSearchPlugin\Search\RequestInterface + monsieurbiz.search.request.interface: MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface services: diff --git a/src/Search/Request/RequestConfiguration.php b/src/Search/Request/RequestConfiguration.php index 9aa7d619..4bd657e5 100644 --- a/src/Search/Request/RequestConfiguration.php +++ b/src/Search/Request/RequestConfiguration.php @@ -18,10 +18,14 @@ final class RequestConfiguration { private Request $request; + private string $type; + private string $documentType; - public function __construct(Request $request) + public function __construct(Request $request, string $type, string $documentType) { $this->request = $request; + $this->type = $type; + $this->documentType = $documentType; } public function getQueryText(): string @@ -33,7 +37,7 @@ public function getAppliedFilters($type = null): array { $appliedFilters = [ 'taxon' => $this->request->get('taxon', []), - 'price' =>$this->request->get('price', null), + 'price' =>$this->request->get('price', []), 'attributes' => $this->request->get('attributes', []), 'options' => $this->request->get('options', []), ]; @@ -68,4 +72,14 @@ public function getAvailableLimits(): array // TODO define this in config (by query type?) return [9, 18, 27]; } + + public function getType(): string + { + return $this->type; + } + + public function getDocumentType(): string + { + return $this->documentType; + } } diff --git a/src/Search/RequestFactory.php b/src/Search/Request/RequestHandler.php similarity index 68% rename from src/Search/RequestFactory.php rename to src/Search/Request/RequestHandler.php index ca731e1f..aeb36bea 100644 --- a/src/Search/RequestFactory.php +++ b/src/Search/Request/RequestHandler.php @@ -11,11 +11,12 @@ declare(strict_types=1); -namespace MonsieurBiz\SyliusSearchPlugin\Search; +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request; +use Elastica\Query; use Sylius\Component\Registry\ServiceRegistryInterface; -class RequestFactory +class RequestHandler { private ServiceRegistryInterface $searchRequestsRegistry; @@ -24,11 +25,13 @@ public function __construct(ServiceRegistryInterface $searchRequestsRegistry) $this->searchRequestsRegistry = $searchRequestsRegistry; } - public function create(string $type, string $documentType): RequestInterface + public function getRequest(RequestConfiguration $requestConfiguration): RequestInterface { /** @var RequestInterface $request */ foreach ($this->searchRequestsRegistry->all() as $request) { - if ($request->supports($type, $documentType)) { + if ($request->supports($requestConfiguration->getType(), $requestConfiguration->getDocumentType())) { + $request->setConfiguration($requestConfiguration); + return $request; } } diff --git a/src/Search/RequestInterface.php b/src/Search/Request/RequestInterface.php similarity index 94% rename from src/Search/RequestInterface.php rename to src/Search/Request/RequestInterface.php index a5286474..c6c62f98 100644 --- a/src/Search/RequestInterface.php +++ b/src/Search/Request/RequestInterface.php @@ -11,7 +11,7 @@ declare(strict_types=1); -namespace MonsieurBiz\SyliusSearchPlugin\Search; +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request; use Elastica\Query; use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; diff --git a/src/Search/Request/Search/Product.php b/src/Search/Request/Search/Product.php index e57fb35d..fc40bfa1 100644 --- a/src/Search/Request/Search/Product.php +++ b/src/Search/Request/Search/Product.php @@ -23,7 +23,7 @@ use MonsieurBiz\SyliusSearchPlugin\Repository\ProductAttributeRepositoryInterface; use MonsieurBiz\SyliusSearchPlugin\Repository\ProductOptionRepositoryInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; -use MonsieurBiz\SyliusSearchPlugin\Search\RequestInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; use Sylius\Component\Channel\Context\ChannelContextInterface; use Sylius\Component\Registry\ServiceRegistryInterface; diff --git a/src/Search/Response.php b/src/Search/Response.php index d83c375e..12765cef 100644 --- a/src/Search/Response.php +++ b/src/Search/Response.php @@ -14,6 +14,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search; use Elastica\ResultSet; +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Filter\Filter; use MonsieurBiz\SyliusSearchPlugin\Search\Filter\RangeFilter; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; @@ -24,13 +25,15 @@ class Response implements ResponseInterface { private RequestConfiguration $requestConfiguration; private AdapterInterface $adapter; + private DocumentableInterface $documentable; private ?Pagerfanta $paginator = null; private array $filters = []; - public function __construct(RequestConfiguration $requestConfiguration, AdapterInterface $adapter) + public function __construct(RequestConfiguration $requestConfiguration, AdapterInterface $adapter, DocumentableInterface $documentable) { $this->requestConfiguration = $requestConfiguration; $this->adapter = $adapter; + $this->documentable = $documentable; $this->buildFilters(); } @@ -60,6 +63,11 @@ public function getPaginator(): Pagerfanta return $this->paginator; } + public function getDocumentable(): DocumentableInterface + { + return $this->documentable; + } + private function buildFilters(): void { /** @var ResultSet $results */ diff --git a/src/Search/ResponseInterface.php b/src/Search/ResponseInterface.php index 28e28052..18d479b1 100644 --- a/src/Search/ResponseInterface.php +++ b/src/Search/ResponseInterface.php @@ -13,6 +13,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search; +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterInterface; use Pagerfanta\Pagerfanta; @@ -24,4 +25,6 @@ interface ResponseInterface extends \IteratorAggregate, \Countable public function getFilters(): array; public function getPaginator(): Pagerfanta; + + public function getDocumentable(): DocumentableInterface; } diff --git a/src/Search/Search.php b/src/Search/Search.php index 2af5467f..56ee2f92 100644 --- a/src/Search/Search.php +++ b/src/Search/Search.php @@ -15,7 +15,9 @@ use JoliCode\Elastically\Factory; use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestHandler; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; use Pagerfanta\Elastica\ElasticaAdapter; use Sylius\Component\Locale\Context\LocaleContextInterface; use Symfony\Component\Serializer\SerializerInterface; @@ -24,15 +26,19 @@ class Search implements SearchInterface { private SerializerInterface $serializer; private LocaleContextInterface $localeContext; + private RequestHandler $requestHandler; - public function __construct(SerializerInterface $serializer, LocaleContextInterface $localeContext) + public function __construct(SerializerInterface $serializer, LocaleContextInterface $localeContext, RequestHandler $requestHandler) { $this->serializer = $serializer; $this->localeContext = $localeContext; + $this->requestHandler = $requestHandler; } - public function query(RequestConfiguration $requestConfiguration, RequestInterface $request): ResponseInterface + public function search(RequestConfiguration $requestConfiguration): ResponseInterface { + $request = $this->requestHandler->getRequest($requestConfiguration); + $indexName = $this->getIndexName($request->getDocumentable(), $this->localeContext->getLocaleCode()); $factory = new Factory([ Factory::CONFIG_INDEX_CLASS_MAPPING => [ @@ -42,7 +48,11 @@ public function query(RequestConfiguration $requestConfiguration, RequestInterfa ]); $client = $factory->buildClient(); - return new Response($requestConfiguration, new ElasticaAdapter($client->getIndex($indexName), $request->getQuery())); + return new Response( + $requestConfiguration, + new ElasticaAdapter($client->getIndex($indexName), $request->getQuery()), + $request->getDocumentable() + ); } private function getIndexName(DocumentableInterface $documentable, ?string $locale = null): string diff --git a/src/Search/SearchInterface.php b/src/Search/SearchInterface.php index 5cbe2675..afa1fd72 100644 --- a/src/Search/SearchInterface.php +++ b/src/Search/SearchInterface.php @@ -17,5 +17,5 @@ interface SearchInterface { - public function query(RequestConfiguration $requestConfiguration, RequestInterface $request): ResponseInterface; + public function search(RequestConfiguration $requestConfiguration): ResponseInterface; } From eeabbfb64d6e0efff8976808cb67f72be0e50fe9 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Fri, 26 Nov 2021 16:50:47 +0100 Subject: [PATCH 045/142] fix: main taxon and price filter add other filters in es query --- src/Search/Request/Search/Product.php | 48 +++++++++++++++++---------- src/Search/Response.php | 4 +-- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/src/Search/Request/Search/Product.php b/src/Search/Request/Search/Product.php index fc40bfa1..12a41e3b 100644 --- a/src/Search/Request/Search/Product.php +++ b/src/Search/Request/Search/Product.php @@ -154,7 +154,7 @@ private function addAggregations(Query $query): void { $attributesAgg = new Nested('attributes', 'attributes'); $filtredAttributesAgg = new Aggregation\Filter('attributes'); - $filtredAttributesAgg->setFilter($this->getFilters(null, ['options', 'taxon'])); + $filtredAttributesAgg->setFilter($this->getFilters(null, ['options', 'taxon', 'price'])); $filtredAttributesAgg->addAggregation($attributesAgg); foreach ($this->productAttributeRepository->findIsSearchableOrFilterable() as $productAttribute) { if (!$productAttribute->isFilterable()) { @@ -180,7 +180,7 @@ private function addAggregations(Query $query): void $optionsAgg = new Nested('options', 'variants.options'); $filtredOptionsAgg = new Aggregation\Filter('options'); - $filtredOptionsAgg->setFilter($this->getFilters(null, ['attributes', 'taxon'])); + $filtredOptionsAgg->setFilter($this->getFilters(null, ['attributes', 'taxon', 'price'])); $filtredOptionsAgg->addAggregation($optionsAgg); foreach ($this->productOptionRepository->findIsSearchableOrFilterable() as $productOption) { if (!$productOption->isFilterable()) { @@ -217,20 +217,27 @@ private function getMainTaxonAggregation(): Aggregation\AbstractAggregation { $qb = new \Elastica\QueryBuilder(); + $otherFilters = $this->getFilters(null, ['attributes', 'options', 'price']); + return $qb->aggregation() - ->nested('main_taxon', 'main_taxon') + ->filter('main_taxon') + ->setFilter($otherFilters) ->addAggregation( $qb->aggregation() - ->terms('codes') - ->setField('main_taxon.code') + ->nested('main_taxon', 'main_taxon') ->addAggregation( $qb->aggregation() - ->terms('levels') - ->setField('main_taxon.level') + ->terms('codes') + ->setField('main_taxon.code') ->addAggregation( $qb->aggregation() - ->terms('names') - ->setField('main_taxon.name') + ->terms('levels') + ->setField('main_taxon.level') + ->addAggregation( + $qb->aggregation() + ->terms('names') + ->setField('main_taxon.name') + ) ) ) ) @@ -241,19 +248,26 @@ private function getPriceAggregation(): Aggregation\AbstractAggregation { $qb = new \Elastica\QueryBuilder(); + $otherFilters = $this->getFilters(null, ['attributes', 'options', 'taxon']); + return $qb->aggregation() - ->nested('prices', 'prices') + ->filter('prices') + ->setFilter($otherFilters) ->addAggregation( $qb->aggregation() - ->filter('prices') - ->setFilter( - $qb->query()->term() - ->setTerm('prices.channel_code', $this->channelContext->getChannel()->getCode()) - ) + ->nested('prices', 'prices') ->addAggregation( $qb->aggregation() - ->stats('prices_stats') - ->setField('prices.price') + ->filter('prices') + ->setFilter( + $qb->query()->term() + ->setTerm('prices.channel_code', $this->channelContext->getChannel()->getCode()) + ) + ->addAggregation( + $qb->aggregation() + ->stats('prices_stats') + ->setField('prices.price') + ) ) ) ; diff --git a/src/Search/Response.php b/src/Search/Response.php index 12765cef..a4177d67 100644 --- a/src/Search/Response.php +++ b/src/Search/Response.php @@ -79,7 +79,7 @@ private function buildFilters(): void } // todo main taxon - $taxonAggregation = $aggregations['main_taxon'] ?? null; + $taxonAggregation = $aggregations['main_taxon']['main_taxon'] ?? null; if ($taxonAggregation && $taxonAggregation['doc_count'] > 0) { $filter = new Filter('main_taxon', 'monsieurbiz_searchplugin.filters.taxon_filter', $taxonAggregation['doc_count'], 'taxon'); @@ -112,7 +112,7 @@ private function buildFilters(): void } // todo price - $priceAggregation = $aggregations['prices']['prices'] ?? null; + $priceAggregation = $aggregations['prices']['prices']['prices'] ?? null; if ($priceAggregation && $priceAggregation['doc_count'] > 0) { $this->filters[] = new RangeFilter( 'price', From 8b365e12c1b1830e63a76d4e6dc637c7ddd98443 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Fri, 26 Nov 2021 17:39:02 +0100 Subject: [PATCH 046/142] fix: display the min and max price value in placeholder --- src/Controller/SearchController.php | 1 + .../views/Search/Filter/range.html.twig | 10 +++++----- src/Search/Request/RequestConfiguration.php | 2 +- src/Search/Request/Search/Product.php | 16 ++++++++++------ 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php index 5e913d82..fe4dae9e 100644 --- a/src/Controller/SearchController.php +++ b/src/Controller/SearchController.php @@ -44,6 +44,7 @@ public function searchAction(Request $request, string $query): Response return $this->render('@MonsieurBizSyliusSearchPlugin/Search/result.html.twig', [ 'documentable' => $result->getDocumentable(), + 'requestConfiguration' => $requestConfiguration, 'query' => $query, 'result' => $result, 'limits' => $requestConfiguration->getAvailableLimits(), diff --git a/src/Resources/views/Search/Filter/range.html.twig b/src/Resources/views/Search/Filter/range.html.twig index 2427155f..b067ef63 100644 --- a/src/Resources/views/Search/Filter/range.html.twig +++ b/src/Resources/views/Search/Filter/range.html.twig @@ -1,7 +1,7 @@
- {% set currentValue = app.request.query.get(filter.code) %} - {% set minValue = (currentValue['min'] is defined) ? currentValue['min'] : filter.min %} - {% set maxValue = (currentValue['max'] is defined) ? currentValue['max'] : filter.max %} + {% set currentValue = requestConfiguration.appliedFilters(filter.code) %} + {% set minValue = (currentValue['min'] is defined) ? currentValue['min'] : '' %} + {% set maxValue = (currentValue['max'] is defined) ? currentValue['max'] : '' %}
{{ filter.label | trans }}
@@ -11,7 +11,7 @@
{{ currencySymbol }}
- +
@@ -19,7 +19,7 @@
{{ currencySymbol }}
- +
diff --git a/src/Search/Request/RequestConfiguration.php b/src/Search/Request/RequestConfiguration.php index 4bd657e5..56fb59d2 100644 --- a/src/Search/Request/RequestConfiguration.php +++ b/src/Search/Request/RequestConfiguration.php @@ -37,7 +37,7 @@ public function getAppliedFilters($type = null): array { $appliedFilters = [ 'taxon' => $this->request->get('taxon', []), - 'price' =>$this->request->get('price', []), + 'price' => array_filter($this->request->get('price', [])), 'attributes' => $this->request->get('attributes', []), 'options' => $this->request->get('options', []), ]; diff --git a/src/Search/Request/Search/Product.php b/src/Search/Request/Search/Product.php index 12a41e3b..07f5e566 100644 --- a/src/Search/Request/Search/Product.php +++ b/src/Search/Request/Search/Product.php @@ -303,19 +303,23 @@ private function getFilters($currentAttribute = null, array $filtreTypes = ['att } //todo - if (\in_array('price', $filtreTypes, true) && 0 !== count($this->configuration->getAppliedFilters('price'))) { + $priceValue = $this->configuration->getAppliedFilters('price'); + if (\in_array('price', $filtreTypes, true) && 0 !== \count($priceValue)) { $qb = new \Elastica\QueryBuilder(); // channel filter $channelPriceFilter = $qb->query() ->term(['prices.channel_code' => $this->channelContext->getChannel()->getCode()]) ; - $priceValue = $this->configuration->getAppliedFilters('price'); + $conditions = []; + if (\array_key_exists('min', $priceValue)) { + $conditions['gte'] = $priceValue['min'] * 100; + } + if (\array_key_exists('max', $priceValue)) { + $conditions['lte'] = $priceValue['max'] * 100; + } $priceQuery = $qb->query() - ->range('prices.price', [ - 'gte' => $priceValue['min'] * 100, - 'lte' => $priceValue['max'] * 100, - ]) + ->range('prices.price', $conditions) ; $bool->addMust( $qb->query() From e4b8089811759ff89f23bb919ea4181efb4415fc Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Fri, 26 Nov 2021 18:11:08 +0100 Subject: [PATCH 047/142] fix: use taxon code in filter value --- src/Search/Filter/Filter.php | 8 ++------ src/Search/Filter/FilterValue.php | 12 ++++++++++-- src/Search/Request/Search/Product.php | 2 +- src/Search/Response.php | 2 +- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Search/Filter/Filter.php b/src/Search/Filter/Filter.php index 9a01a76a..6851df4e 100644 --- a/src/Search/Filter/Filter.php +++ b/src/Search/Filter/Filter.php @@ -78,13 +78,9 @@ public function getValues(): array return $this->values; } - /** - * @param $value - * @param $count - */ - public function addValue($value, $count): void + public function addValue(string $label, int $count, ?string $value = null): void { - $this->values[] = new FilterValue($value, $count); + $this->values[] = new FilterValue($label, $count, $value); } /** diff --git a/src/Search/Filter/FilterValue.php b/src/Search/Filter/FilterValue.php index 5e0c5eba..27e21c84 100644 --- a/src/Search/Filter/FilterValue.php +++ b/src/Search/Filter/FilterValue.php @@ -32,15 +32,18 @@ class FilterValue */ private $count; + private string $value; + /** * Filter constructor. * * @param string $label * @param int $count */ - public function __construct(string $label, int $count) + public function __construct(string $label, int $count, string $value = null) { - $this->slug = SlugHelper::toSlug($label); + $this->value = $value ?? $label; + $this->slug = SlugHelper::toSlug($this->value); $this->label = $label; $this->count = $count; } @@ -68,4 +71,9 @@ public function getCount(): int { return $this->count; } + + public function getValue(): string + { + return $this->value; + } } diff --git a/src/Search/Request/Search/Product.php b/src/Search/Request/Search/Product.php index 07f5e566..9e8d70b5 100644 --- a/src/Search/Request/Search/Product.php +++ b/src/Search/Request/Search/Product.php @@ -288,7 +288,7 @@ private function getFilters($currentAttribute = null, array $filtreTypes = ['att $mainTaxonQuery->addShould( $qb->query() ->term() - ->setTerm(sprintf('%s.name', $field), SlugHelper::toLabel($value)) + ->setTerm(sprintf('%s.code', $field), SlugHelper::toLabel($value)) ); } $bool->addMust( diff --git a/src/Search/Response.php b/src/Search/Response.php index a4177d67..ac1af525 100644 --- a/src/Search/Response.php +++ b/src/Search/Response.php @@ -99,7 +99,7 @@ private function buildFilters(): void $taxonNameBuckets = $taxonLevelBucket['names']['buckets'] ?? []; foreach ($taxonNameBuckets as $taxonNameBucket) { $taxonName = $taxonNameBucket['key']; - $filter->addValue($taxonName ?? $taxonCode, $taxonCodeBucket['doc_count']); + $filter->addValue($taxonName ?? $taxonCode, $taxonCodeBucket['doc_count'], $taxonCode); break 2; } } From 3299c11ccd045fefd781184a1a5a5ad86d8fb73d Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 29 Nov 2021 16:22:17 +0100 Subject: [PATCH 048/142] refactor: add aggregation builders --- src/Resources/config/services.yaml | 16 ++ .../AggregationBuilderInterface.php | 24 ++ .../Aggregation/MainTaxonAggregation.php | 65 +++++ .../Request/Aggregation/PriceAggregation.php | 71 +++++ .../ProductAttributeAggregation.php | 61 +++++ .../ProductAttributesAggregation.php | 74 +++++ .../Aggregation/ProductOptionAggregation.php | 62 +++++ .../Aggregation/ProductOptionsAggregation.php | 77 ++++++ src/Search/Request/AggregationBuilder.php | 56 ++++ src/Search/Request/Search/Product.php | 255 +++++------------- 10 files changed, 573 insertions(+), 188 deletions(-) create mode 100644 src/Search/Request/Aggregation/AggregationBuilderInterface.php create mode 100644 src/Search/Request/Aggregation/MainTaxonAggregation.php create mode 100644 src/Search/Request/Aggregation/PriceAggregation.php create mode 100644 src/Search/Request/Aggregation/ProductAttributeAggregation.php create mode 100644 src/Search/Request/Aggregation/ProductAttributesAggregation.php create mode 100644 src/Search/Request/Aggregation/ProductOptionAggregation.php create mode 100644 src/Search/Request/Aggregation/ProductOptionsAggregation.php create mode 100644 src/Search/Request/AggregationBuilder.php diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index edba9290..b5a2b815 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -90,3 +90,19 @@ services: select: '@MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader\SelectReader', date: '@MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader\DateReader' } + + # Define aggregation builders + MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation\MainTaxonAggregation: + tags: { name: 'monsieurbiz.sarch.aggregation_builder' } + + MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation\PriceAggregation: + tags: { name: 'monsieurbiz.sarch.aggregation_builder' } + + MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation\ProductAttributesAggregation: + tags: { name: 'monsieurbiz.sarch.aggregation_builder' } + + MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation\ProductOptionsAggregation: + tags: { name: 'monsieurbiz.sarch.aggregation_builder' } + + MonsieurBiz\SyliusSearchPlugin\Search\Request\AggregationBuilder: + arguments: [ !tagged_iterator { tag: 'monsieurbiz.sarch.aggregation_builder' } ] diff --git a/src/Search/Request/Aggregation/AggregationBuilderInterface.php b/src/Search/Request/Aggregation/AggregationBuilderInterface.php new file mode 100644 index 00000000..a45d9dc2 --- /dev/null +++ b/src/Search/Request/Aggregation/AggregationBuilderInterface.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; + +use Elastica\Aggregation\AbstractAggregation; + +interface AggregationBuilderInterface +{ + /** + * @param string|array $aggregation + */ + public function build($aggregation, array $filters): ?AbstractAggregation; +} diff --git a/src/Search/Request/Aggregation/MainTaxonAggregation.php b/src/Search/Request/Aggregation/MainTaxonAggregation.php new file mode 100644 index 00000000..47a933d3 --- /dev/null +++ b/src/Search/Request/Aggregation/MainTaxonAggregation.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; + +use Elastica\Aggregation\AbstractAggregation; + +class MainTaxonAggregation implements AggregationBuilderInterface +{ + public function build($aggregation, array $filters): ?AbstractAggregation + { + if (!$this->isSupported($aggregation)) { + return null; + } + + $qb = new \Elastica\QueryBuilder(); + + $filters = array_filter($filters, function($key) { + return false === strpos($key, 'main_taxon'); + }, \ARRAY_FILTER_USE_KEY); + $filterQuery = $qb->query()->bool(); + foreach ($filters as $filter) { + $filterQuery->addMust($filter); + } + + return $qb->aggregation() + ->filter('main_taxon') + ->setFilter($filterQuery) + ->addAggregation( + $qb->aggregation() + ->nested('main_taxon', 'main_taxon') + ->addAggregation( + $qb->aggregation() + ->terms('codes') + ->setField('main_taxon.code') + ->addAggregation( + $qb->aggregation() + ->terms('levels') + ->setField('main_taxon.level') + ->addAggregation( + $qb->aggregation() + ->terms('names') + ->setField('main_taxon.name') + ) + ) + ) + ) + ; + } + + protected function isSupported($aggregation): bool + { + return 'main_taxon' === $aggregation; + } +} diff --git a/src/Search/Request/Aggregation/PriceAggregation.php b/src/Search/Request/Aggregation/PriceAggregation.php new file mode 100644 index 00000000..1a694834 --- /dev/null +++ b/src/Search/Request/Aggregation/PriceAggregation.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; + +use Elastica\Aggregation\AbstractAggregation; +use Sylius\Component\Channel\Context\ChannelContextInterface; + +class PriceAggregation implements AggregationBuilderInterface +{ + private ChannelContextInterface $channelContext; + + public function __construct(ChannelContextInterface $channelContext) + { + $this->channelContext = $channelContext; + } + + public function build($aggregation, array $filters): ?AbstractAggregation + { + if (!$this->isSupported($aggregation)) { + return null; + } + + $qb = new \Elastica\QueryBuilder(); + + $filters = array_filter($filters, function($key) { + return false === strpos($key, 'price'); + }, \ARRAY_FILTER_USE_KEY); + $filterQuery = $qb->query()->bool(); + foreach ($filters as $filter) { + $filterQuery->addMust($filter); + } + + return $qb->aggregation() + ->filter('prices') + ->setFilter($filterQuery) + ->addAggregation( + $qb->aggregation() + ->nested('prices', 'prices') + ->addAggregation( + $qb->aggregation() + ->filter('prices') + ->setFilter( + $qb->query()->term() + ->setTerm('prices.channel_code', $this->channelContext->getChannel()->getCode()) + ) + ->addAggregation( + $qb->aggregation() + ->stats('prices_stats') + ->setField('prices.price') + ) + ) + ) + ; + } + + private function isSupported($aggregation): bool + { + return 'price' === $aggregation; + } +} diff --git a/src/Search/Request/Aggregation/ProductAttributeAggregation.php b/src/Search/Request/Aggregation/ProductAttributeAggregation.php new file mode 100644 index 00000000..2303cf48 --- /dev/null +++ b/src/Search/Request/Aggregation/ProductAttributeAggregation.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; + +use Elastica\Aggregation\AbstractAggregation; +use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; +use Sylius\Component\Product\Model\ProductAttributeInterface; + +class ProductAttributeAggregation implements AggregationBuilderInterface +{ + public function build($aggregation, array $filters): ?AbstractAggregation + { + /** @var ProductAttributeInterface&SearchableInterface $aggregation */ + if (!$this->isSupport($aggregation) || !$aggregation->isFilterable()) { + return null; + } + + $qb = new \Elastica\QueryBuilder(); + + $filters = array_filter($filters, function($key) use ($aggregation) { + return false !== strpos($key, 'attributes.') && 'attributes.' . $aggregation->getCode() !== $key; + }, \ARRAY_FILTER_USE_KEY); + $filterQuery = $qb->query()->bool(); + foreach ($filters as $filter) { + $filterQuery->addMust($filter); + } + + $qb = new \Elastica\QueryBuilder(); + + return $qb->aggregation()->filter($aggregation->getCode()) + ->setFilter($filterQuery) + ->addAggregation( + $qb->aggregation()->nested($aggregation->getCode(), sprintf('attributes.%s', $aggregation->getCode())) + ->addAggregation( + $qb->aggregation()->terms('names') + ->setField(sprintf('attributes.%s.name', $aggregation->getCode())) + ->addAggregation( + $qb->aggregation()->terms('values') + ->setField(sprintf('attributes.%s.value.keyword', $aggregation->getCode())) + ) + ) + ) + ; + } + + private function isSupport($aggregation): bool + { + return $aggregation instanceof ProductAttributeInterface; + } +} diff --git a/src/Search/Request/Aggregation/ProductAttributesAggregation.php b/src/Search/Request/Aggregation/ProductAttributesAggregation.php new file mode 100644 index 00000000..2d20ccdd --- /dev/null +++ b/src/Search/Request/Aggregation/ProductAttributesAggregation.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; + +use Elastica\Aggregation\AbstractAggregation; +use Sylius\Component\Product\Model\ProductAttributeInterface; + +class ProductAttributesAggregation implements AggregationBuilderInterface +{ + public function __construct() + { + $this->productAttributeAggregationBuilder = new ProductAttributeAggregation(); + } + + public function build($aggregation, array $filters): ?AbstractAggregation + { + if (!$this->isSupport($aggregation)) { + return null; + } + + $qb = new \Elastica\QueryBuilder(); + + $currentFilters = array_filter($filters, function($key) { + return false === strpos($key, 'attributes.'); + }, \ARRAY_FILTER_USE_KEY); + $filterQuery = $qb->query()->bool(); + foreach ($currentFilters as $filter) { + $filterQuery->addMust($filter); + } + + $attributesAggregation = $qb->aggregation()->nested('attributes', 'attributes'); + foreach ($aggregation as $subAggregation) { + $subAggregationObject = $this->productAttributeAggregationBuilder->build($subAggregation, $filters); + if (null === $subAggregationObject) { + continue; + } + $attributesAggregation->addAggregation($subAggregationObject); + } + + if (0 == \count($attributesAggregation->getAggs())) { + return null; + } + + return $qb->aggregation()->filter('attributes') + ->setFilter($filterQuery) + ->addAggregation($attributesAggregation) + ; + } + + private function isSupport($aggregation): bool + { + if (!\is_array($aggregation)) { + return false; + } + foreach ($aggregation as $subAggregation) { + if ($subAggregation instanceof ProductAttributeInterface) { + return true; + } + } + + return false; + } +} diff --git a/src/Search/Request/Aggregation/ProductOptionAggregation.php b/src/Search/Request/Aggregation/ProductOptionAggregation.php new file mode 100644 index 00000000..8a63463e --- /dev/null +++ b/src/Search/Request/Aggregation/ProductOptionAggregation.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; + +use Elastica\Aggregation\AbstractAggregation; +use Elastica\QueryBuilder; +use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; +use Sylius\Component\Product\Model\ProductOptionInterface; + +class ProductOptionAggregation implements AggregationBuilderInterface +{ + public function build($aggregation, array $filters): ?AbstractAggregation + { + /** @var ProductOptionInterface&SearchableInterface $aggregation */ + if (!$this->isSupport($aggregation) || !$aggregation->isFilterable()) { + return null; + } + + $qb = new QueryBuilder(); + + $filters = array_filter($filters, function($key) use ($aggregation): bool { + return false !== strpos($key, 'options.') && 'options.' . $aggregation->getCode() !== $key; + }, \ARRAY_FILTER_USE_KEY); + $filterQuery = $qb->query()->bool(); + foreach ($filters as $filter) { + $filterQuery->addMust($filter); + } + + $qb = new QueryBuilder(); + + return $qb->aggregation()->filter($aggregation->getCode()) + ->setFilter($filterQuery) + ->addAggregation( + $qb->aggregation()->nested($aggregation->getCode(), sprintf('variants.options.%s', $aggregation->getCode())) + ->addAggregation( + $qb->aggregation()->terms('names') + ->setField(sprintf('variants.options.%s.name', $aggregation->getCode())) + ->addAggregation( + $qb->aggregation()->terms('values') + ->setField(sprintf('variants.options.%s.value.keyword', $aggregation->getCode())) + ) + ) + ) + ; + } + + private function isSupport($aggregation): bool + { + return $aggregation instanceof ProductOptionInterface; + } +} diff --git a/src/Search/Request/Aggregation/ProductOptionsAggregation.php b/src/Search/Request/Aggregation/ProductOptionsAggregation.php new file mode 100644 index 00000000..2f3134a0 --- /dev/null +++ b/src/Search/Request/Aggregation/ProductOptionsAggregation.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; + +use Elastica\Aggregation\AbstractAggregation; +use Sylius\Component\Product\Model\ProductOptionInterface; + +class ProductOptionsAggregation implements AggregationBuilderInterface +{ + private ProductOptionAggregation $productOptionAggregationBuilder; + + public function __construct(ProductOptionAggregation $productOptionAggregationBuilder) + { + $this->productOptionAggregationBuilder = $productOptionAggregationBuilder; + } + + public function build($aggregation, array $filters): ?AbstractAggregation + { + if (!$this->isSupport($aggregation)) { + return null; + } + + $qb = new \Elastica\QueryBuilder(); + + $currentFilters = array_filter($filters, function($key): bool { + return false === strpos($key, 'options.'); + }, \ARRAY_FILTER_USE_KEY); + $filterQuery = $qb->query()->bool(); + foreach ($currentFilters as $filter) { + $filterQuery->addMust($filter); + } + + $optionsAggregation = $qb->aggregation()->nested('options', 'variants.options'); + foreach ($aggregation as $subAggregation) { + $subAggregationObject = $this->productOptionAggregationBuilder->build($subAggregation, $filters); + if (null === $subAggregationObject) { + continue; + } + $optionsAggregation->addAggregation($subAggregationObject); + } + + if (0 == \count($optionsAggregation->getAggs())) { + return null; + } + + return $qb->aggregation()->filter('options') + ->setFilter($filterQuery) + ->addAggregation($optionsAggregation) + ; + } + + private function isSupport($aggregation): bool + { + if (!\is_array($aggregation)) { + return false; + } + + foreach ($aggregation as $subAggregation) { + if ($subAggregation instanceof ProductOptionInterface) { + return true; + } + } + + return false; + } +} diff --git a/src/Search/Request/AggregationBuilder.php b/src/Search/Request/AggregationBuilder.php new file mode 100644 index 00000000..5f5f0073 --- /dev/null +++ b/src/Search/Request/AggregationBuilder.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request; + +use Elastica\Aggregation\AbstractAggregation; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation\AggregationBuilderInterface; + +class AggregationBuilder +{ + /** + * @var iterable + */ + private iterable $aggregationBuilders; + + public function __construct(iterable $aggregationBuilders) + { + $this->aggregationBuilders = $aggregationBuilders; + } + + public function buildAggregations(array $aggregations, array $filters): array + { + $buckets = []; + + foreach ($aggregations as $aggregation) { + $buckets[] = $this->buildAggregation($aggregation, $filters); + } + + return array_filter($buckets); + } + + /** + * @param string|array $aggregation + */ + private function buildAggregation($aggregation, array $filters): AbstractAggregation + { + foreach ($this->aggregationBuilders as $aggregationBuilder) { + $aggregationQuery = $aggregationBuilder->build($aggregation, $filters); + if (null !== $aggregationQuery) { + return $aggregationQuery; + } + } + + throw new \RuntimeException('Aggregation can be build'); + } +} diff --git a/src/Search/Request/Search/Product.php b/src/Search/Request/Search/Product.php index 9e8d70b5..77876de9 100644 --- a/src/Search/Request/Search/Product.php +++ b/src/Search/Request/Search/Product.php @@ -13,15 +13,13 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Search; -use Elastica\Aggregation; -use Elastica\Aggregation\Nested; -use Elastica\Aggregation\Terms; use Elastica\Query; use Elastica\Query\MultiMatch; use MonsieurBiz\SyliusSearchPlugin\Helper\SlugHelper; use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; use MonsieurBiz\SyliusSearchPlugin\Repository\ProductAttributeRepositoryInterface; use MonsieurBiz\SyliusSearchPlugin\Repository\ProductOptionRepositoryInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\AggregationBuilder; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; use Sylius\Component\Channel\Context\ChannelContextInterface; @@ -30,23 +28,25 @@ class Product implements RequestInterface { private DocumentableInterface $documentable; - private RequestConfiguration $configuration; private ProductAttributeRepositoryInterface $productAttributeRepository; private ProductOptionRepositoryInterface $productOptionRepository; private ChannelContextInterface $channelContext; + private AggregationBuilder $aggregationBuilder; public function __construct( ServiceRegistryInterface $documentableRegistry, ProductAttributeRepositoryInterface $productAttributeRepository, ProductOptionRepositoryInterface $productOptionRepository, - ChannelContextInterface $channelContext + ChannelContextInterface $channelContext, + AggregationBuilder $aggregationBuilder ) { //TODO check if exist, return a dummy documentable if not $this->documentable = $documentableRegistry->get('search.documentable.monsieurbiz_product'); $this->productAttributeRepository = $productAttributeRepository; $this->productOptionRepository = $productOptionRepository; $this->channelContext = $channelContext; + $this->aggregationBuilder = $aggregationBuilder; } public function getType(): string @@ -102,11 +102,12 @@ public function getQuery(): Query $bool->addMust($searchQuery); $esQuery = Query::create($bool); - $boolFilter = $this->getFilters(); + $boolFilter = new Query\BoolQuery(); + foreach ($this->getFilters() as $filter) { + $boolFilter->addMust($filter); + } $esQuery->setPostFilter($boolFilter); $this->addAggregations($esQuery); - $esQuery->addAggregation($this->getMainTaxonAggregation()); - $esQuery->addAggregation($this->getPriceAggregation()); // Manage sorting foreach ($this->configuration->getSorting() as $field => $order) { @@ -152,159 +153,49 @@ private function addAttributesQueries(Query\BoolQuery $searchQuery): void private function addAggregations(Query $query): void { - $attributesAgg = new Nested('attributes', 'attributes'); - $filtredAttributesAgg = new Aggregation\Filter('attributes'); - $filtredAttributesAgg->setFilter($this->getFilters(null, ['options', 'taxon', 'price'])); - $filtredAttributesAgg->addAggregation($attributesAgg); - foreach ($this->productAttributeRepository->findIsSearchableOrFilterable() as $productAttribute) { - if (!$productAttribute->isFilterable()) { - continue; - } - $attributeValuesAgg = new Terms('values'); - $attributeValuesAgg->setField(sprintf('attributes.%s.value.keyword', $productAttribute->getCode())); - - $attributeCodesAgg = new Terms('names'); - $attributeCodesAgg->setField(sprintf('attributes.%s.name', $productAttribute->getCode())); - $attributeCodesAgg->addAggregation($attributeValuesAgg); - - $attributeAgg = new Nested($productAttribute->getCode(), sprintf('attributes.%s', $productAttribute->getCode())); - $attributeAgg->addAggregation($attributeCodesAgg); - - $boolFilter = $this->getFilters($productAttribute->getCode(), ['attributes']); - $filter = new Aggregation\Filter($productAttribute->getCode()); - $filter->setFilter($boolFilter); - $filter->addAggregation($attributeAgg); - - $attributesAgg->addAggregation($filter); - } - - $optionsAgg = new Nested('options', 'variants.options'); - $filtredOptionsAgg = new Aggregation\Filter('options'); - $filtredOptionsAgg->setFilter($this->getFilters(null, ['attributes', 'taxon', 'price'])); - $filtredOptionsAgg->addAggregation($optionsAgg); - foreach ($this->productOptionRepository->findIsSearchableOrFilterable() as $productOption) { - if (!$productOption->isFilterable()) { - continue; - } - $attributeValuesAgg = new Terms('values'); - $attributeValuesAgg->setField(sprintf('variants.options.%s.value.keyword', $productOption->getCode())); - - $attributeCodesAgg = new Terms('names'); - $attributeCodesAgg->setField(sprintf('variants.options.%s.name', $productOption->getCode())); - $attributeCodesAgg->addAggregation($attributeValuesAgg); - - $attributeAgg = new Nested($productOption->getCode(), sprintf('variants.options.%s', $productOption->getCode())); - $attributeAgg->addAggregation($attributeCodesAgg); - - $boolFilter = $this->getFilters($productOption->getCode(), ['options']); - $filter = new Aggregation\Filter($productOption->getCode()); - $filter->setFilter($boolFilter); - $filter->addAggregation($attributeAgg); - - $optionsAgg->addAggregation($filter); - } - - if (0 < \count($attributesAgg->getAggs())) { - $query->addAggregation($filtredAttributesAgg); - } - - if (0 < \count($optionsAgg->getAggs())) { - $query->addAggregation($filtredOptionsAgg); + $newAggs = $this->aggregationBuilder->buildAggregations( + [ + $this->productAttributeRepository->findIsSearchableOrFilterable(), + $this->productOptionRepository->findIsSearchableOrFilterable(), + 'main_taxon', + 'price', + ], + $this->getFilters() + ); + + foreach ($newAggs as $aggregation) { + $query->addAggregation($aggregation); } } - private function getMainTaxonAggregation(): Aggregation\AbstractAggregation + private function getFilters(): array { - $qb = new \Elastica\QueryBuilder(); + $filters = []; - $otherFilters = $this->getFilters(null, ['attributes', 'options', 'price']); - - return $qb->aggregation() - ->filter('main_taxon') - ->setFilter($otherFilters) - ->addAggregation( - $qb->aggregation() - ->nested('main_taxon', 'main_taxon') - ->addAggregation( - $qb->aggregation() - ->terms('codes') - ->setField('main_taxon.code') - ->addAggregation( - $qb->aggregation() - ->terms('levels') - ->setField('main_taxon.level') - ->addAggregation( - $qb->aggregation() - ->terms('names') - ->setField('main_taxon.name') - ) - ) - ) - ) - ; - } - - private function getPriceAggregation(): Aggregation\AbstractAggregation - { $qb = new \Elastica\QueryBuilder(); - $otherFilters = $this->getFilters(null, ['attributes', 'options', 'taxon']); - - return $qb->aggregation() - ->filter('prices') - ->setFilter($otherFilters) - ->addAggregation( - $qb->aggregation() - ->nested('prices', 'prices') - ->addAggregation( - $qb->aggregation() - ->filter('prices') - ->setFilter( - $qb->query()->term() - ->setTerm('prices.channel_code', $this->channelContext->getChannel()->getCode()) - ) - ->addAggregation( - $qb->aggregation() - ->stats('prices_stats') - ->setField('prices.price') - ) - ) - ) - ; - } - - private function getFilters($currentAttribute = null, array $filtreTypes = ['attributes', 'options', 'taxon', 'price']): Query\BoolQuery - { - $bool = new Query\BoolQuery(); - - //todo - if (\in_array('taxon', $filtreTypes, true)) { - $qb = new \Elastica\QueryBuilder(); - foreach ($this->configuration->getAppliedFilters('taxon') as $field => $values) { - $mainTaxonQuery = $qb->query() - ->bool() - ; - foreach ($values as $value) { - $mainTaxonQuery->addShould( - $qb->query() - ->term() - ->setTerm(sprintf('%s.code', $field), SlugHelper::toLabel($value)) - ); - } - $bool->addMust( + foreach ($this->configuration->getAppliedFilters('taxon') as $field => $values) { + $mainTaxonQuery = $qb->query() + ->bool() + ; + foreach ($values as $value) { + $mainTaxonQuery->addShould( $qb->query() - ->nested() - ->setPath($field) - ->setQuery( - $mainTaxonQuery - ) + ->term() + ->setTerm(sprintf('%s.code', $field), SlugHelper::toLabel($value)) ); } + $filters['main_taxons'] = $qb->query() + ->nested() + ->setPath($field) + ->setQuery( + $mainTaxonQuery + ) + ; } - //todo $priceValue = $this->configuration->getAppliedFilters('price'); - if (\in_array('price', $filtreTypes, true) && 0 !== \count($priceValue)) { + if (0 !== \count($priceValue)) { $qb = new \Elastica\QueryBuilder(); // channel filter @@ -321,58 +212,46 @@ private function getFilters($currentAttribute = null, array $filtreTypes = ['att $priceQuery = $qb->query() ->range('prices.price', $conditions) ; - $bool->addMust( - $qb->query() - ->nested() - ->setPath('prices') - ->setQuery( - $qb->query()->bool() - ->addMust($channelPriceFilter) - ->addMust($priceQuery) - ) - ); + $filters['price'] = $qb->query() + ->nested() + ->setPath('prices') + ->setQuery( + $qb->query()->bool() + ->addMust($channelPriceFilter) + ->addMust($priceQuery) + ) + ; } - if (\in_array('attributes', $filtreTypes, true)) { - foreach ($this->configuration->getAppliedFilters('attributes') as $field => $values) { - if ($currentAttribute == $field) { - continue; - } - $attributeValueQuery = new Query\BoolQuery(); + foreach ($this->configuration->getAppliedFilters('attributes') as $field => $values) { + $attributeValueQuery = new Query\BoolQuery(); - foreach ($values as $value) { - $termQuery = new Query\Terms(sprintf('attributes.%s.value.keyword', $field), [SlugHelper::toLabel($value)]); - $attributeValueQuery->addShould($termQuery); // todo configure the "and" or "or" - } + foreach ($values as $value) { + $termQuery = new Query\Terms(sprintf('attributes.%s.value.keyword', $field), [SlugHelper::toLabel($value)]); + $attributeValueQuery->addShould($termQuery); // todo configure the "and" or "or" + } - $attributeQuery = new Query\Nested(); - $attributeQuery->setPath(sprintf('attributes.%s', $field))->setQuery($attributeValueQuery); + $attributeQuery = new Query\Nested(); + $attributeQuery->setPath(sprintf('attributes.%s', $field))->setQuery($attributeValueQuery); - $bool->addMust($attributeQuery); - } + $filters['attributes.' . $field] = $attributeQuery; } - if (\in_array('options', $filtreTypes, true)) { - foreach ($this->configuration->getAppliedFilters('options') as $field => $values) { - if ($currentAttribute == $field) { - continue; - } - - $attributeValueQuery = new Query\BoolQuery(); + foreach ($this->configuration->getAppliedFilters('options') as $field => $values) { + $attributeValueQuery = new Query\BoolQuery(); - foreach ($values as $value) { - $termQuery = new Query\Terms(sprintf('variants.options.%s.value.keyword', $field), [SlugHelper::toLabel($value)]); - $attributeValueQuery->addShould($termQuery); // todo configure the "and" or "or" - } + foreach ($values as $value) { + $termQuery = new Query\Terms(sprintf('variants.options.%s.value.keyword', $field), [SlugHelper::toLabel($value)]); + $attributeValueQuery->addShould($termQuery); // todo configure the "and" or "or" + } - $attributeQuery = new Query\Nested(); - $attributeQuery->setPath(sprintf('variants.options.%s', $field))->setQuery($attributeValueQuery); + $attributeQuery = new Query\Nested(); + $attributeQuery->setPath(sprintf('variants.options.%s', $field))->setQuery($attributeValueQuery); - $bool->addMust($attributeQuery); - } + $filters['options.' . $field] = $attributeQuery; } - return $bool; + return $filters; } // todo find solution to get more extendable From 35f9071c2d3cb80f8197bab13954f68e51d04094 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 29 Nov 2021 16:27:34 +0100 Subject: [PATCH 049/142] fix: file comment header & remove unused import --- src/AutoMapper/ProductAttributeValueReader/DefaultReader.php | 4 ++-- .../ProductAttributeValueReader/ReaderInterface.php | 4 ++-- src/Search/Filter/RangeFilter.php | 4 ++-- src/Search/Request/RequestConfiguration.php | 2 +- src/Search/Request/RequestHandler.php | 1 - src/Search/Request/RequestInterface.php | 1 - src/Search/Search.php | 3 +-- 7 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/AutoMapper/ProductAttributeValueReader/DefaultReader.php b/src/AutoMapper/ProductAttributeValueReader/DefaultReader.php index 4364e605..5423edab 100644 --- a/src/AutoMapper/ProductAttributeValueReader/DefaultReader.php +++ b/src/AutoMapper/ProductAttributeValueReader/DefaultReader.php @@ -1,9 +1,9 @@ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/src/AutoMapper/ProductAttributeValueReader/ReaderInterface.php b/src/AutoMapper/ProductAttributeValueReader/ReaderInterface.php index bf1ad168..74fdb651 100644 --- a/src/AutoMapper/ProductAttributeValueReader/ReaderInterface.php +++ b/src/AutoMapper/ProductAttributeValueReader/ReaderInterface.php @@ -1,9 +1,9 @@ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/src/Search/Filter/RangeFilter.php b/src/Search/Filter/RangeFilter.php index 6a582152..94700b97 100644 --- a/src/Search/Filter/RangeFilter.php +++ b/src/Search/Filter/RangeFilter.php @@ -1,9 +1,9 @@ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/src/Search/Request/RequestConfiguration.php b/src/Search/Request/RequestConfiguration.php index 56fb59d2..013f683f 100644 --- a/src/Search/Request/RequestConfiguration.php +++ b/src/Search/Request/RequestConfiguration.php @@ -57,7 +57,7 @@ public function getPage(): int public function getLimit(): int { - $limit = (int) $this->request->get('limit'); + $limit = (int) $this->request->get('limit'); $availableLimits = $this->getAvailableLimits(); if (!\in_array($limit, $availableLimits, true)) { diff --git a/src/Search/Request/RequestHandler.php b/src/Search/Request/RequestHandler.php index aeb36bea..5b81b456 100644 --- a/src/Search/Request/RequestHandler.php +++ b/src/Search/Request/RequestHandler.php @@ -13,7 +13,6 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request; -use Elastica\Query; use Sylius\Component\Registry\ServiceRegistryInterface; class RequestHandler diff --git a/src/Search/Request/RequestInterface.php b/src/Search/Request/RequestInterface.php index c6c62f98..186abec4 100644 --- a/src/Search/Request/RequestInterface.php +++ b/src/Search/Request/RequestInterface.php @@ -15,7 +15,6 @@ use Elastica\Query; use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; -use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; interface RequestInterface { diff --git a/src/Search/Search.php b/src/Search/Search.php index 56ee2f92..253877e7 100644 --- a/src/Search/Search.php +++ b/src/Search/Search.php @@ -15,9 +15,8 @@ use JoliCode\Elastically\Factory; use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; -use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestHandler; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; -use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestHandler; use Pagerfanta\Elastica\ElasticaAdapter; use Sylius\Component\Locale\Context\LocaleContextInterface; use Symfony\Component\Serializer\SerializerInterface; From b9dfc7fe2fbde62d5f37d29d9e7df457c6520c18 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 29 Nov 2021 17:06:20 +0100 Subject: [PATCH 050/142] feat: add search form and translation --- src/Controller/SearchController.php | 20 +++++++ src/Form/Type/SearchType.php | 57 ++++++++++++++++++++ src/Resources/config/config.yaml | 8 +++ src/Resources/config/routing/shop.yaml | 6 +++ src/Resources/translations/messages.en.yml | 31 +++++++++++ src/Resources/translations/messages.fr.yml | 31 +++++++++++ src/Resources/translations/messages.it.yml | 31 +++++++++++ src/Resources/views/Header/form.html.twig | 1 + src/Resources/views/Search/_form.html.twig | 9 ++++ src/Twig/Extension/RenderSearchForm.php | 61 ++++++++++++++++++++++ 10 files changed, 255 insertions(+) create mode 100644 src/Form/Type/SearchType.php create mode 100644 src/Resources/translations/messages.en.yml create mode 100644 src/Resources/translations/messages.fr.yml create mode 100644 src/Resources/translations/messages.it.yml create mode 100644 src/Resources/views/Header/form.html.twig create mode 100644 src/Resources/views/Search/_form.html.twig create mode 100644 src/Twig/Extension/RenderSearchForm.php diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php index fe4dae9e..e6a83e53 100644 --- a/src/Controller/SearchController.php +++ b/src/Controller/SearchController.php @@ -19,6 +19,7 @@ use Sylius\Component\Currency\Context\CurrencyContextInterface; use Sylius\Component\Locale\Context\LocaleContextInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Intl\Currencies; @@ -51,4 +52,23 @@ public function searchAction(Request $request, string $query): Response 'currencySymbol' => Currencies::getSymbol($this->currencyContext->getCurrencyCode(), $this->localeContext->getLocaleCode()), ]); } + + /** + * Post search. + * + * @param Request $request + * + * @return RedirectResponse + */ + public function postAction(Request $request) + { + $query = $request->request->get('monsieurbiz_searchplugin_search')['query'] ?? ''; + + return $this->redirect( + $this->generateUrl( + 'monsieurbiz_search_search', + ['query' => urlencode($query)] + ) + ); + } } diff --git a/src/Form/Type/SearchType.php b/src/Form/Type/SearchType.php new file mode 100644 index 00000000..f6ab7d31 --- /dev/null +++ b/src/Form/Type/SearchType.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Form\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\SubmitType; +use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Validator\Constraints\NotBlank; +use Symfony\Component\Validator\Constraints\Required; + +class SearchType extends AbstractType +{ + /** + * @param FormBuilderInterface $builder + * @param array $options + */ + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder + ->add('query', TextType::class, [ + 'required' => true, + 'label' => 'monsieurbiz_searchplugin.form.query', + 'attr' => [ + 'placeholder' => 'monsieurbiz_searchplugin.form.query_placeholder', + ], + 'constraints' => [ + new NotBlank(), + new Required(), + ], + ]) + ->add('submit', SubmitType::class, [ + 'attr' => ['class' => 'submit'], + 'label' => 'monsieurbiz_searchplugin.form.submit', + ]) + ; + } + + /** + * {@inheritdoc} + */ + public function getBlockPrefix() + { + return 'monsieurbiz_searchplugin_search'; + } +} diff --git a/src/Resources/config/config.yaml b/src/Resources/config/config.yaml index b512429a..8c83c080 100644 --- a/src/Resources/config/config.yaml +++ b/src/Resources/config/config.yaml @@ -1,3 +1,11 @@ imports: - { resource: "services.yaml" } - { resource: "monsieurbiz_search.yaml" } + +sylius_ui: + events: + sylius.shop.layout.header.content: + blocks: + search: + template: "@MonsieurBizSyliusSearchPlugin/Header/form.html.twig" + priority: 20 diff --git a/src/Resources/config/routing/shop.yaml b/src/Resources/config/routing/shop.yaml index be353e8a..2efedd2a 100644 --- a/src/Resources/config/routing/shop.yaml +++ b/src/Resources/config/routing/shop.yaml @@ -5,3 +5,9 @@ monsieurbiz_search_search: _controller: MonsieurBiz\SyliusSearchPlugin\Controller\SearchController:searchAction requirements: query: .+ + +monsieurbiz_search_post: + path: /search + methods: [POST] + defaults: + _controller: MonsieurBiz\SyliusSearchPlugin\Controller\SearchController:postAction diff --git a/src/Resources/translations/messages.en.yml b/src/Resources/translations/messages.en.yml new file mode 100644 index 00000000..166c9670 --- /dev/null +++ b/src/Resources/translations/messages.en.yml @@ -0,0 +1,31 @@ +monsieurbiz_searchplugin: + admin: + product_attribute: + form: + title: Search + filterable: Filterable + product_option: + form: + title: Search + filterable: Filterable + form: + query: 'Search' + query_placeholder: 'Search…' + submit: 'Submit search' + search: + result: + search_result: 'Search results for "%query%" (%count%)' + no_result: 'No result.' + instant: + result: + search_result: 'Search results for "%query%" (%count%)' + no_result: 'No result.' + filters: + filter_results: 'Filter results' + apply_filters: 'Apply filters' + no_filter: 'No filter available' + taxon_filter: 'Categories' + price_filter: 'Price' + price_min: 'Min price' + price_max: 'Max price' + loading: 'We are looking for the best results according to your criteria' diff --git a/src/Resources/translations/messages.fr.yml b/src/Resources/translations/messages.fr.yml new file mode 100644 index 00000000..72b5aeb4 --- /dev/null +++ b/src/Resources/translations/messages.fr.yml @@ -0,0 +1,31 @@ +monsieurbiz_searchplugin: + admin: + product_attribute: + form: + title: Recherche + filterable: Filtrable + product_option: + form: + title: Recherche + filterable: Filtrable + form: + query: 'Recherche' + query_placeholder: 'Rechercher…' + submit: 'Lancer la recherche' + search: + result: + search_result: 'Résultats de recherche pour "%query%" (%count%)' + no_result: 'Pas de résultat.' + instant: + result: + search_result: 'Résultats de recherche pour "%query%" (%count%)' + no_result: 'Pas de résultat.' + filters: + filter_results: 'Filtrer les résultats' + apply_filters: 'Appliquer les filtres' + no_filter: 'Aucun filtre disponible' + taxon_filter: 'Catégories' + price_filter: 'Prix' + price_min: 'Prix minimum' + price_max: 'Prix maximum' + loading: 'Nous recherchons les meilleurs résultats selon vos critères' diff --git a/src/Resources/translations/messages.it.yml b/src/Resources/translations/messages.it.yml new file mode 100644 index 00000000..fb1bf7a6 --- /dev/null +++ b/src/Resources/translations/messages.it.yml @@ -0,0 +1,31 @@ +monsieurbiz_searchplugin: + admin: + product_attribute: + form: + title: Ricerca + filterable: Filtrabile + product_option: + form: + title: Ricerca + filterable: Filtrabile + form: + query: 'Cerca' + query_placeholder: 'Cerca…' + submit: 'Cerca' + search: + result: + search_result: 'Risultati per la ricerca "%query%" (%count%)' + no_result: 'Nessun risultato.' + instant: + result: + search_result: 'Risultati per la ricerca "%query%" (%count%)' + no_result: 'Nessun risultato.' + filters: + filter_results: 'Filtra i risultati' + apply_filters: 'Applica i filtri' + no_filter: 'Nessun filtro disponibile' + taxon_filter: 'Categorie' + price_filter: 'Prezzo' + price_min: 'Prezzo Minimo' + price_max: 'Prezzo Massimo' + loading: 'Stiamo cercando i risultati migliori in base ai filtri che hai applicato' diff --git a/src/Resources/views/Header/form.html.twig b/src/Resources/views/Header/form.html.twig new file mode 100644 index 00000000..9ecbbd9a --- /dev/null +++ b/src/Resources/views/Header/form.html.twig @@ -0,0 +1 @@ +{{ search_form() }} diff --git a/src/Resources/views/Search/_form.html.twig b/src/Resources/views/Search/_form.html.twig new file mode 100644 index 00000000..a3a78c6e --- /dev/null +++ b/src/Resources/views/Search/_form.html.twig @@ -0,0 +1,9 @@ +{% form_theme form '@SyliusShop/Form/theme.html.twig' %} +
+ {{ form_start(form, {'action': path('monsieurbiz_search_post'), 'method': 'POST', 'attr': {'class': 'ui search item autocomplete-search'}}) }} + {{ form_errors(form) }} + {{ form_row(form.query, {'value': query, 'label': false}) }} + {{ form_row(form.submit, {'attr': {'class': 'ui primary button'}}) }} +
+ {{ form_end(form) }} +
diff --git a/src/Twig/Extension/RenderSearchForm.php b/src/Twig/Extension/RenderSearchForm.php new file mode 100644 index 00000000..f6fa0e20 --- /dev/null +++ b/src/Twig/Extension/RenderSearchForm.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Twig\Extension; + +use MonsieurBiz\SyliusSearchPlugin\Form\Type\SearchType; +use Symfony\Component\Form\FormFactoryInterface; +use Symfony\Component\HttpFoundation\RequestStack; +use Twig\Environment; +use Twig\Extension\AbstractExtension; +use Twig\Markup; +use Twig\TwigFunction; + +class RenderSearchForm extends AbstractExtension +{ + /** @var FormFactoryInterface */ + private $formFactory; + + /** @var Environment */ + private $templatingEngine; + + /** @var RequestStack */ + private $requestStack; + + public function __construct( + FormFactoryInterface $formFactory, + Environment $templatingEngine, + RequestStack $requestStack + ) { + $this->formFactory = $formFactory; + $this->templatingEngine = $templatingEngine; + $this->requestStack = $requestStack; + } + + public function getFunctions() + { + return [ + new TwigFunction('search_form', [$this, 'createForm']), + ]; + } + + public function createForm($template = null) + { + $template = $template ?? '@MonsieurBizSyliusSearchPlugin/Search/_form.html.twig'; + + return new Markup($this->templatingEngine->render($template, [ + 'form' => $this->formFactory->create(SearchType::class)->createView(), + 'query' => urldecode($this->requestStack->getCurrentRequest()->get('query') ?? ''), + ]), 'UTF-8'); + } +} From c46b9c42417f6ca8afa1897010837b46e0f80c15 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 30 Nov 2021 16:40:28 +0100 Subject: [PATCH 051/142] feat: add webpack encore configuration --- .gitignore | 1 + Makefile | 6 ++--- assets/js/app.js | 0 package.json | 25 +++++++++++++++++++ src/Exception/UnknownRequestTypeException.php | 19 ++++++++++++++ src/Resources/config/sylius/ui.yaml | 0 .../views/Instant/Product/_box.html.twig | 0 src/Resources/views/Instant/result.html.twig | 0 src/Resources/views/_scripts.html.twig | 0 src/Search/Request/InstantSearch/Product.php | 17 +++++++++++++ webpack.config.js | 24 ++++++++++++++++++ 11 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 assets/js/app.js create mode 100644 package.json create mode 100644 src/Exception/UnknownRequestTypeException.php create mode 100644 src/Resources/config/sylius/ui.yaml create mode 100644 src/Resources/views/Instant/Product/_box.html.twig create mode 100644 src/Resources/views/Instant/result.html.twig create mode 100644 src/Resources/views/_scripts.html.twig create mode 100644 src/Search/Request/InstantSearch/Product.php create mode 100644 webpack.config.js diff --git a/.gitignore b/.gitignore index 97137b9b..0d68c09e 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ /.php-version /.phpunit.result.cache /node_modules +yarn.lock diff --git a/Makefile b/Makefile index 133c9f33..3a4fce79 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ COMPOSER=symfony composer CONSOLE=${SYMFONY} console export COMPOSE_PROJECT_NAME=search COMPOSE=docker-compose -YARN=cd ${APP_DIR} && yarn +YARN=yarn PHPSTAN=symfony php vendor/bin/phpstan PHPUNIT=symfony php vendor/bin/phpunit PHPSPEC=symfony php vendor/bin/phpspec @@ -51,9 +51,9 @@ endif yarn.install: ${APP_DIR}/yarn.lock ${APP_DIR}/yarn.lock: - ln -sf ${APP_DIR}/node_modules node_modules + cd ${APP_DIR} && ${YARN} install && ${YARN} build ${YARN} install - ${YARN} build + ${YARN} encore prod node_modules: ${APP_DIR}/node_modules ## Install the Node dependencies using yarn diff --git a/assets/js/app.js b/assets/js/app.js new file mode 100644 index 00000000..e69de29b diff --git a/package.json b/package.json new file mode 100644 index 00000000..9b4d6622 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "monsieurbiz-sylius-search-plugin", + "version": "0.0.1", + "description": "Add instant search on the search form", + "main": "webpack.config.js", + "scripts": { + "build": "encore production", + "watch": "encore dev --watch" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/monsieurbiz/SyliusSearchPlugin.git" + }, + "author": "Monsieur Biz ", + "license": "MIT", + "bugs": { + "url": "https://github.com/monsieurbiz/SyliusSearchPlugin/issues" + }, + "homepage": "https://github.com/monsieurbiz/SyliusSearchPlugin#readme", + "devDependencies": { + "@symfony/webpack-encore": "^0.28.1" + }, + "dependencies": { + } +} diff --git a/src/Exception/UnknownRequestTypeException.php b/src/Exception/UnknownRequestTypeException.php new file mode 100644 index 00000000..6c7cee0a --- /dev/null +++ b/src/Exception/UnknownRequestTypeException.php @@ -0,0 +1,19 @@ + Date: Tue, 30 Nov 2021 16:41:39 +0100 Subject: [PATCH 052/142] feat: add instant search --- assets/js/app.js | 52 +++++ src/Controller/SearchController.php | 34 +++- src/DependencyInjection/Configuration.php | 1 + src/Exception/UnknownRequestTypeException.php | 9 +- src/Resources/config/config.yaml | 9 +- src/Resources/config/monsieurbiz_search.yaml | 1 + src/Resources/config/routing/shop.yaml | 6 + src/Resources/config/sylius/ui.yaml | 11 ++ src/Resources/public/entrypoints.json | 9 + src/Resources/public/js/monsieurbiz-search.js | 181 ++++++++++++++++++ src/Resources/public/manifest.json | 3 + src/Resources/translations/messages.en.yml | 1 + .../views/Instant/Product/_box.html.twig | 21 ++ src/Resources/views/Instant/result.html.twig | 21 ++ src/Resources/views/Search/_form.html.twig | 2 +- .../views/Search/product/_box.html.twig | 1 - src/Resources/views/_scripts.html.twig | 12 ++ src/Search/Request/InstantSearch/Product.php | 124 +++++++++++- src/Search/Request/RequestHandler.php | 6 +- src/Search/Search.php | 4 + 20 files changed, 486 insertions(+), 22 deletions(-) create mode 100644 src/Resources/public/entrypoints.json create mode 100644 src/Resources/public/js/monsieurbiz-search.js create mode 100644 src/Resources/public/manifest.json diff --git a/assets/js/app.js b/assets/js/app.js index e69de29b..8503ff7d 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -0,0 +1,52 @@ +global.MonsieurBizInstantSearch = class { + constructor( + instantUrl, + searchInputSelector, + resultClosestSelector, + resultFindSelector, + keyUpTimeOut, + minQueryLength + ) { + // Init a timeout variable to be used below + var instantSearchTimeout = null; + document.querySelector(searchInputSelector).addEventListener('keyup', function (e) { + clearTimeout(instantSearchTimeout); + var query = e.currentTarget.value; + var resultElement = e.currentTarget.closest(resultClosestSelector).querySelector(resultFindSelector); + instantSearchTimeout = setTimeout(function () { + if (query.length >= minQueryLength) { + var httpRequest = new XMLHttpRequest(); + httpRequest.onload = function() { + if (this.status === 200) { + resultElement.innerHTML = this.responseText; + resultElement.style.display = 'block'; + } + }; + httpRequest.open("POST", instantUrl); + httpRequest.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + httpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + httpRequest.send(new URLSearchParams({query: query}).toString()); + } + }, keyUpTimeOut); + }); + + // Hide results when user leave the search field + document.querySelector(searchInputSelector).addEventListener('focusout', function (e) { + var resultElement = e.currentTarget.closest(resultClosestSelector).querySelector(resultFindSelector); + setTimeout(function () { + resultElement.style.display = 'none'; + }, 100); // Add timeout to keep the click on the result + }); + } +} + +document.addEventListener("DOMContentLoaded", function() { + new MonsieurBizInstantSearch( + monsieurbizSearchPlugin.instantUrl, + monsieurbizSearchPlugin.searchInputSelector, + monsieurbizSearchPlugin.resultClosestSelector, + monsieurbizSearchPlugin.resultFindSelector, + monsieurbizSearchPlugin.keyUpTimeOut, + monsieurbizSearchPlugin.minQueryLength + ); +}); diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php index e6a83e53..aa61d968 100644 --- a/src/Controller/SearchController.php +++ b/src/Controller/SearchController.php @@ -13,11 +13,14 @@ namespace MonsieurBiz\SyliusSearchPlugin\Controller; +use MonsieurBiz\SyliusSearchPlugin\Exception\UnknownRequestTypeException; +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Search; use Sylius\Component\Currency\Context\CurrencyContextInterface; use Sylius\Component\Locale\Context\LocaleContextInterface; +use Sylius\Component\Registry\ServiceRegistryInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; @@ -55,12 +58,8 @@ public function searchAction(Request $request, string $query): Response /** * Post search. - * - * @param Request $request - * - * @return RedirectResponse */ - public function postAction(Request $request) + public function postAction(Request $request): RedirectResponse { $query = $request->request->get('monsieurbiz_searchplugin_search')['query'] ?? ''; @@ -71,4 +70,29 @@ public function postAction(Request $request) ) ); } + + /** + * Perform the instant search action & display results. + */ + public function instantAction(Request $request, ServiceRegistryInterface $documentableRegistry): Response + { + $results = []; + /** @var DocumentableInterface $documentable */ + foreach ($documentableRegistry->all() as $documentable) { + $requestConfiguration = new RequestConfiguration( + $request, + RequestInterface::INSTANT_TYPE, + $documentable->getIndexCode() + ); + try { + $results[] = $this->search->search($requestConfiguration); + } catch (UnknownRequestTypeException $e) { + continue; + } + } + + return $this->render('@MonsieurBizSyliusSearchPlugin/Instant/result.html.twig', [ + 'results' => $results, + ]); + } } diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 02fbc05a..7f418e19 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -42,6 +42,7 @@ public function getConfigTreeBuilder(): TreeBuilder ->addDefaultsIfNotSet() ->children() ->scalarNode('item')->isRequired()->cannotBeEmpty()->end() + ->scalarNode('instant')->isRequired()->cannotBeEmpty()->end() ->end() ->end() ->end() diff --git a/src/Exception/UnknownRequestTypeException.php b/src/Exception/UnknownRequestTypeException.php index 6c7cee0a..d9f813b2 100644 --- a/src/Exception/UnknownRequestTypeException.php +++ b/src/Exception/UnknownRequestTypeException.php @@ -1,9 +1,9 @@ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ namespace MonsieurBiz\SyliusSearchPlugin\Exception; -class UnknownRequestType -{ +use Exception; +class UnknownRequestTypeException extends Exception +{ } diff --git a/src/Resources/config/config.yaml b/src/Resources/config/config.yaml index 8c83c080..3878d152 100644 --- a/src/Resources/config/config.yaml +++ b/src/Resources/config/config.yaml @@ -1,11 +1,4 @@ imports: - { resource: "services.yaml" } - { resource: "monsieurbiz_search.yaml" } - -sylius_ui: - events: - sylius.shop.layout.header.content: - blocks: - search: - template: "@MonsieurBizSyliusSearchPlugin/Header/form.html.twig" - priority: 20 + - { resource: "sylius/ui.yaml" } diff --git a/src/Resources/config/monsieurbiz_search.yaml b/src/Resources/config/monsieurbiz_search.yaml index 55d8ead5..d393edbc 100644 --- a/src/Resources/config/monsieurbiz_search.yaml +++ b/src/Resources/config/monsieurbiz_search.yaml @@ -5,6 +5,7 @@ monsieurbiz_sylius_search: target: 'MonsieurBiz\SyliusSearchPlugin\Model\Product\ProductDTO' templates: item: '@MonsieurBizSyliusSearchPlugin/Search/product/_box.html.twig' + instant: '@MonsieurBizSyliusSearchPlugin/Instant/Product/_box.html.twig' #mapping_provider: '...' # by default monsieurbiz.search.mapper_provider #dataprovider: '...' # by default MonsieurBiz\SyliusSearchPlugin\Model\Datasource\RepositoryDatasource automapper_classes: diff --git a/src/Resources/config/routing/shop.yaml b/src/Resources/config/routing/shop.yaml index 2efedd2a..afbc1e91 100644 --- a/src/Resources/config/routing/shop.yaml +++ b/src/Resources/config/routing/shop.yaml @@ -11,3 +11,9 @@ monsieurbiz_search_post: methods: [POST] defaults: _controller: MonsieurBiz\SyliusSearchPlugin\Controller\SearchController:postAction + +monsieurbiz_search_instant: + path: /instant + methods: [POST] + defaults: + _controller: MonsieurBiz\SyliusSearchPlugin\Controller\SearchController:instantAction diff --git a/src/Resources/config/sylius/ui.yaml b/src/Resources/config/sylius/ui.yaml index e69de29b..2841374a 100644 --- a/src/Resources/config/sylius/ui.yaml +++ b/src/Resources/config/sylius/ui.yaml @@ -0,0 +1,11 @@ +sylius_ui: + events: + sylius.shop.layout.javascripts: + blocks: + monsieurbiz_toolbox_javascripts: + template: "@MonsieurBizSyliusSearchPlugin/_scripts.html.twig" + sylius.shop.layout.header.content: + blocks: + search: + template: "@MonsieurBizSyliusSearchPlugin/Header/form.html.twig" + priority: 20 diff --git a/src/Resources/public/entrypoints.json b/src/Resources/public/entrypoints.json new file mode 100644 index 00000000..78b0e007 --- /dev/null +++ b/src/Resources/public/entrypoints.json @@ -0,0 +1,9 @@ +{ + "entrypoints": { + "monsieurbiz-search": { + "js": [ + "/public/js/monsieurbiz-search.js" + ] + } + } +} \ No newline at end of file diff --git a/src/Resources/public/js/monsieurbiz-search.js b/src/Resources/public/js/monsieurbiz-search.js new file mode 100644 index 00000000..7ac4f884 --- /dev/null +++ b/src/Resources/public/js/monsieurbiz-search.js @@ -0,0 +1,181 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = "/public/"; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = "./assets/js/app.js"); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ "./assets/js/app.js": +/*!**************************!*\ + !*** ./assets/js/app.js ***! + \**************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +/* WEBPACK VAR INJECTION */(function(global) {function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +global.MonsieurBizInstantSearch = /*#__PURE__*/function () { + "use strict"; + + function _class(instantUrl, searchInputSelector, resultClosestSelector, resultFindSelector, keyUpTimeOut, minQueryLength) { + _classCallCheck(this, _class); + + // Init a timeout variable to be used below + var instantSearchTimeout = null; + document.querySelector(searchInputSelector).addEventListener('keyup', function (e) { + clearTimeout(instantSearchTimeout); + var query = e.currentTarget.value; + var resultElement = e.currentTarget.closest(resultClosestSelector).querySelector(resultFindSelector); + instantSearchTimeout = setTimeout(function () { + if (query.length >= minQueryLength) { + var httpRequest = new XMLHttpRequest(); + + httpRequest.onload = function () { + if (this.status === 200) { + resultElement.innerHTML = this.responseText; + resultElement.style.display = 'block'; + } + }; + + httpRequest.open("POST", instantUrl); + httpRequest.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + httpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + httpRequest.send(new URLSearchParams({ + query: query + }).toString()); + } + }, keyUpTimeOut); + }); // Hide results when user leave the search field + + document.querySelector(searchInputSelector).addEventListener('focusout', function (e) { + var resultElement = e.currentTarget.closest(resultClosestSelector).querySelector(resultFindSelector); + setTimeout(function () { + resultElement.style.display = 'none'; + }, 100); // Add timeout to keep the click on the result + }); + } + + return _class; +}(); + +document.addEventListener("DOMContentLoaded", function () { + new MonsieurBizInstantSearch(monsieurbizSearchPlugin.instantUrl, monsieurbizSearchPlugin.searchInputSelector, monsieurbizSearchPlugin.resultClosestSelector, monsieurbizSearchPlugin.resultFindSelector, monsieurbizSearchPlugin.keyUpTimeOut, monsieurbizSearchPlugin.minQueryLength); +}); +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../node_modules/webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js"))) + +/***/ }), + +/***/ "./node_modules/webpack/buildin/global.js": +/*!***********************************!*\ + !*** (webpack)/buildin/global.js ***! + \***********************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +var g; + +// This works in non-strict mode +g = (function() { + return this; +})(); + +try { + // This works if eval is allowed (see CSP) + g = g || new Function("return this")(); +} catch (e) { + // This works if the window reference is available + if (typeof window === "object") g = window; +} + +// g can still be undefined, but nothing to do about it... +// We return undefined, instead of nothing here, so it's +// easier to handle this case. if(!global) { ...} + +module.exports = g; + + +/***/ }) + +/******/ }); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vYXNzZXRzL2pzL2FwcC5qcyIsIndlYnBhY2s6Ly8vKHdlYnBhY2spL2J1aWxkaW4vZ2xvYmFsLmpzIl0sIm5hbWVzIjpbImdsb2JhbCIsIk1vbnNpZXVyQml6SW5zdGFudFNlYXJjaCIsImluc3RhbnRVcmwiLCJzZWFyY2hJbnB1dFNlbGVjdG9yIiwicmVzdWx0Q2xvc2VzdFNlbGVjdG9yIiwicmVzdWx0RmluZFNlbGVjdG9yIiwia2V5VXBUaW1lT3V0IiwibWluUXVlcnlMZW5ndGgiLCJpbnN0YW50U2VhcmNoVGltZW91dCIsImRvY3VtZW50IiwicXVlcnlTZWxlY3RvciIsImFkZEV2ZW50TGlzdGVuZXIiLCJlIiwiY2xlYXJUaW1lb3V0IiwicXVlcnkiLCJjdXJyZW50VGFyZ2V0IiwidmFsdWUiLCJyZXN1bHRFbGVtZW50IiwiY2xvc2VzdCIsInNldFRpbWVvdXQiLCJsZW5ndGgiLCJodHRwUmVxdWVzdCIsIlhNTEh0dHBSZXF1ZXN0Iiwib25sb2FkIiwic3RhdHVzIiwiaW5uZXJIVE1MIiwicmVzcG9uc2VUZXh0Iiwic3R5bGUiLCJkaXNwbGF5Iiwib3BlbiIsInNldFJlcXVlc3RIZWFkZXIiLCJzZW5kIiwiVVJMU2VhcmNoUGFyYW1zIiwidG9TdHJpbmciLCJtb25zaWV1cmJpelNlYXJjaFBsdWdpbiJdLCJtYXBwaW5ncyI6IjtRQUFBO1FBQ0E7O1FBRUE7UUFDQTs7UUFFQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTs7UUFFQTtRQUNBOztRQUVBO1FBQ0E7O1FBRUE7UUFDQTtRQUNBOzs7UUFHQTtRQUNBOztRQUVBO1FBQ0E7O1FBRUE7UUFDQTtRQUNBO1FBQ0EsMENBQTBDLGdDQUFnQztRQUMxRTtRQUNBOztRQUVBO1FBQ0E7UUFDQTtRQUNBLHdEQUF3RCxrQkFBa0I7UUFDMUU7UUFDQSxpREFBaUQsY0FBYztRQUMvRDs7UUFFQTtR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QzNCLGNBTjVCO0FBUUgsQ0FURCxFOzs7Ozs7Ozs7Ozs7QUMxQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsQ0FBQzs7QUFFRDtBQUNBO0FBQ0E7QUFDQSxDQUFDO0FBQ0Q7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSw0Q0FBNEM7O0FBRTVDIiwiZmlsZSI6ImpzL21vbnNpZXVyYml6LXNlYXJjaC5qcyIsInNvdXJjZXNDb250ZW50IjpbIiBcdC8vIFRoZSBtb2R1bGUgY2FjaGVcbiBcdHZhciBpbnN0YWxsZWRNb2R1bGVzID0ge307XG5cbiBcdC8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG4gXHRmdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cbiBcdFx0Ly8gQ2hlY2sgaWYgbW9kdWxlIGlzIGluIGNhY2hlXG4gXHRcdGlmKGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdKSB7XG4gXHRcdFx0cmV0dXJuIGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdLmV4cG9ydHM7XG4gXHRcdH1cbiBcdFx0Ly8gQ3JlYXRlIGEgbmV3IG1vZHVsZSAoYW5kIHB1dCBpdCBpbnRvIHRoZSBjYWNoZSlcbiBcdFx0dmFyIG1vZHVsZSA9IGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdID0ge1xuIFx0XHRcdGk6IG1vZHVsZUlkLFxuIFx0XHRcdGw6IGZhbHNlLFxuIFx0XHRcdGV4cG9ydHM6IHt9XG4gXHRcdH07XG5cbiBcdFx0Ly8gRXhlY3V0ZSB0aGUgbW9kdWxlIGZ1bmN0aW9uXG4gXHRcdG1vZHVsZXNbbW9kdWxlSWRdLmNhbGwobW9kdWxlLmV4cG9ydHMsIG1vZHVsZSwgbW9kdWxlLmV4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pO1xuXG4gXHRcdC8vIEZsYWcgdGhlIG1vZHVsZSBhcyBsb2FkZWRcbiBcdFx0bW9kdWxlLmwgPSB0cnVlO1xuXG4gXHRcdC8vIFJldHVybiB0aGUgZXhwb3J0cyBvZiB0aGUgbW9kdWxlXG4gXHRcdHJldHVybiBtb2R1bGUuZXhwb3J0cztcbiBcdH1cblxuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZXMgb2JqZWN0IChfX3dlYnBhY2tfbW9kdWxlc19fKVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5tID0gbW9kdWxlcztcblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGUgY2FjaGVcbiBcdF9fd2VicGFja19yZXF1aXJlX18uYyA9IGluc3RhbGxlZE1vZHVsZXM7XG5cbiBcdC8vIGRlZmluZSBnZXR0ZXIgZnVuY3Rpb24gZm9yIGhhcm1vbnkgZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kID0gZnVuY3Rpb24oZXhwb3J0cywgbmFtZSwgZ2V0dGVyKSB7XG4gXHRcdGlmKCFfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZXhwb3J0cywgbmFtZSkpIHtcbiBcdFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgbmFtZSwgeyBlbnVtZXJhYmxlOiB0cnVlLCBnZXQ6IGdldHRlciB9KTtcbiBcdFx0fVxuIFx0fTtcblxuIFx0Ly8gZGVmaW5lIF9fZXNNb2R1bGUgb24gZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5yID0gZnVuY3Rpb24oZXhwb3J0cykge1xuIFx0XHRpZih0eXBlb2YgU3ltYm9sICE9PSAndW5kZWZpbmVkJyAmJiBTeW1ib2wudG9TdHJpbmdUYWcpIHtcbiBcdFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgU3ltYm9sLnRvU3RyaW5nVGFnLCB7IHZhbHVlOiAnTW9kdWxlJyB9KTtcbiBcdFx0fVxuIFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7IHZhbHVlOiB0cnVlIH0pO1xuIFx0fTtcblxuIFx0Ly8gY3JlYXRlIGEgZmFrZSBuYW1lc3BhY2Ugb2JqZWN0XG4gXHQvLyBtb2RlICYgMTogdmFsdWUgaXMgYSBtb2R1bGUgaWQsIHJlcXVpcmUgaXRcbiBcdC8vIG1vZGUgJiAyOiBtZXJnZSBhbGwgcHJvcGVydGllcyBvZiB2YWx1ZSBpbnRvIHRoZSBuc1xuIFx0Ly8gbW9kZSAmIDQ6IHJldHVybiB2YWx1ZSB3aGVuIGFscmVhZHkgbnMgb2JqZWN0XG4gXHQvLyBtb2RlICYgOHwxOiBiZWhhdmUgbGlrZSByZXF1aXJlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnQgPSBmdW5jdGlvbih2YWx1ZSwgbW9kZSkge1xuIFx0XHRpZihtb2RlICYgMSkgdmFsdWUgPSBfX3dlYnBhY2tfcmVxdWlyZV9fKHZhbHVlKTtcbiBcdFx0aWYobW9kZSAmIDgpIHJldHVybiB2YWx1ZTtcbiBcdFx0aWYoKG1vZGUgJiA0KSAmJiB0eXBlb2YgdmFsdWUgPT09ICdvYmplY3QnICYmIHZhbHVlICYmIHZhbHVlLl9fZXNNb2R1bGUpIHJldHVybiB2YWx1ZTtcbiBcdFx0dmFyIG5zID0gT2JqZWN0LmNyZWF0ZShudWxsKTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5yKG5zKTtcbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KG5zLCAnZGVmYXVsdCcsIHsgZW51bWVyYWJsZTogdHJ1ZSwgdmFsdWU6IHZhbHVlIH0pO1xuIFx0XHRpZihtb2RlICYgMiAmJiB0eXBlb2YgdmFsdWUgIT0gJ3N0cmluZycpIGZvcih2YXIga2V5IGluIHZhbHVlKSBfX3dlYnBhY2tfcmVxdWlyZV9fLmQobnMsIGtleSwgZnVuY3Rpb24oa2V5KSB7IHJldHVybiB2YWx1ZVtrZXldOyB9LmJpbmQobnVsbCwga2V5KSk7XG4gXHRcdHJldHVybiBucztcbiBcdH07XG5cbiBcdC8vIGdldERlZmF1bHRFeHBvcnQgZnVuY3Rpb24gZm9yIGNvbXBhdGliaWxpdHkgd2l0aCBub24taGFybW9ueSBtb2R1bGVzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm4gPSBmdW5jdGlvbihtb2R1bGUpIHtcbiBcdFx0dmFyIGdldHRlciA9IG1vZHVsZSAmJiBtb2R1bGUuX19lc01vZHVsZSA/XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0RGVmYXVsdCgpIHsgcmV0dXJuIG1vZHVsZVsnZGVmYXVsdCddOyB9IDpcbiBcdFx0XHRmdW5jdGlvbiBnZXRNb2R1bGVFeHBvcnRzKCkgeyByZXR1cm4gbW9kdWxlOyB9O1xuIFx0XHRfX3dlYnBhY2tfcmVxdWlyZV9fLmQoZ2V0dGVyLCAnYScsIGdldHRlcik7XG4gXHRcdHJldHVybiBnZXR0ZXI7XG4gXHR9O1xuXG4gXHQvLyBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGxcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubyA9IGZ1bmN0aW9uKG9iamVjdCwgcHJvcGVydHkpIHsgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmplY3QsIHByb3BlcnR5KTsgfTtcblxuIFx0Ly8gX193ZWJwYWNrX3B1YmxpY19wYXRoX19cbiBcdF9fd2VicGFja19yZXF1aXJlX18ucCA9IFwiL3B1YmxpYy9cIjtcblxuXG4gXHQvLyBMb2FkIGVudHJ5IG1vZHVsZSBhbmQgcmV0dXJuIGV4cG9ydHNcbiBcdHJldHVybiBfX3dlYnBhY2tfcmVxdWlyZV9fKF9fd2VicGFja19yZXF1aXJlX18ucyA9IFwiLi9hc3NldHMvanMvYXBwLmpzXCIpO1xuIiwiZ2xvYmFsLk1vbnNpZXVyQml6SW5zdGFudFNlYXJjaCA9IGNsYXNzIHtcbiAgICBjb25zdHJ1Y3RvcihcbiAgICAgICAgaW5zdGFudFVybCxcbiAgICAgICAgc2VhcmNoSW5wdXRTZWxlY3RvcixcbiAgICAgICAgcmVzdWx0Q2xvc2VzdFNlbGVjdG9yLFxuICAgICAgICByZXN1bHRGaW5kU2VsZWN0b3IsXG4gICAgICAgIGtleVVwVGltZU91dCxcbiAgICAgICAgbWluUXVlcnlMZW5ndGhcbiAgICApIHtcbiAgICAgICAgLy8gSW5pdCBhIHRpbWVvdXQgdmFyaWFibGUgdG8gYmUgdXNlZCBiZWxvd1xuICAgICAgICB2YXIgaW5zdGFudFNlYXJjaFRpbWVvdXQgPSBudWxsO1xuICAgICAgICBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKHNlYXJjaElucHV0U2VsZWN0b3IpLmFkZEV2ZW50TGlzdGVuZXIoJ2tleXVwJywgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGNsZWFyVGltZW91dChpbnN0YW50U2VhcmNoVGltZW91dCk7XG4gICAgICAgICAgICB2YXIgcXVlcnkgPSBlLmN1cnJlbnRUYXJnZXQudmFsdWU7XG4gICAgICAgICAgICB2YXIgcmVzdWx0RWxlbWVudCA9IGUuY3VycmVudFRhcmdldC5jbG9zZXN0KHJlc3VsdENsb3Nlc3RTZWxlY3RvcikucXVlcnlTZWxlY3RvcihyZXN1bHRGaW5kU2VsZWN0b3IpO1xuICAgICAgICAgICAgaW5zdGFudFNlYXJjaFRpbWVvdXQgPSBzZXRUaW1lb3V0KGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBpZiAocXVlcnkubGVuZ3RoID49IG1pblF1ZXJ5TGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBodHRwUmVxdWVzdCA9IG5ldyBYTUxIdHRwUmVxdWVzdCgpO1xuICAgICAgICAgICAgICAgICAgICBodHRwUmVxdWVzdC5vbmxvYWQgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLnN0YXR1cyA9PT0gMjAwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0RWxlbWVudC5pbm5lckhUTUwgPSB0aGlzLnJlc3BvbnNlVGV4dDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXN1bHRFbGVtZW50LnN0eWxlLmRpc3BsYXkgPSAnYmxvY2snO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgICAgICBodHRwUmVxdWVzdC5vcGVuKFwiUE9TVFwiLCBpbnN0YW50VXJsKTtcbiAgICAgICAgICAgICAgICAgICAgaHR0cFJlcXVlc3Quc2V0UmVxdWVzdEhlYWRlcihcIlgtUmVxdWVzdGVkLVdpdGhcIiwgXCJYTUxIdHRwUmVxdWVzdFwiKTtcbiAgICAgICAgICAgICAgICAgICAgaHR0cFJlcXVlc3Quc2V0UmVxdWVzdEhlYWRlcihcIkNvbnRlbnQtVHlwZVwiLCBcImFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZFwiKTtcbiAgICAgICAgICAgICAgICAgICAgaHR0cFJlcXVlc3Quc2VuZChuZXcgVVJMU2VhcmNoUGFyYW1zKHtxdWVyeTogcXVlcnl9KS50b1N0cmluZygpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LCBrZXlVcFRpbWVPdXQpO1xuICAgICAgICB9KTtcblxuICAgICAgICAvLyBIaWRlIHJlc3VsdHMgd2hlbiB1c2VyIGxlYXZlIHRoZSBzZWFyY2ggZmllbGRcbiAgICAgICAgZG9jdW1lbnQucXVlcnlTZWxlY3RvcihzZWFyY2hJbnB1dFNlbGVjdG9yKS5hZGRFdmVudExpc3RlbmVyKCdmb2N1c291dCcsIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICAgICB2YXIgcmVzdWx0RWxlbWVudCA9IGUuY3VycmVudFRhcmdldC5jbG9zZXN0KHJlc3VsdENsb3Nlc3RTZWxlY3RvcikucXVlcnlTZWxlY3RvcihyZXN1bHRGaW5kU2VsZWN0b3IpO1xuICAgICAgICAgICAgc2V0VGltZW91dChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgcmVzdWx0RWxlbWVudC5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xuICAgICAgICAgICAgfSwgMTAwKTsgLy8gQWRkIHRpbWVvdXQgdG8ga2VlcCB0aGUgY2xpY2sgb24gdGhlIHJlc3VsdFxuICAgICAgICB9KTtcbiAgICB9XG59XG5cbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoXCJET01Db250ZW50TG9hZGVkXCIsIGZ1bmN0aW9uKCkge1xuICAgIG5ldyBNb25zaWV1ckJpekluc3RhbnRTZWFyY2goXG4gICAgICAgIG1vbnNpZXVyYml6U2VhcmNoUGx1Z2luLmluc3RhbnRVcmwsXG4gICAgICAgIG1vbnNpZXVyYml6U2VhcmNoUGx1Z2luLnNlYXJjaElucHV0U2VsZWN0b3IsXG4gICAgICAgIG1vbnNpZXVyYml6U2VhcmNoUGx1Z2luLnJlc3VsdENsb3Nlc3RTZWxlY3RvcixcbiAgICAgICAgbW9uc2lldXJiaXpTZWFyY2hQbHVnaW4ucmVzdWx0RmluZFNlbGVjdG9yLFxuICAgICAgICBtb25zaWV1cmJpelNlYXJjaFBsdWdpbi5rZXlVcFRpbWVPdXQsXG4gICAgICAgIG1vbnNpZXVyYml6U2VhcmNoUGx1Z2luLm1pblF1ZXJ5TGVuZ3RoXG4gICAgKTtcbn0pO1xuIiwidmFyIGc7XG5cbi8vIFRoaXMgd29ya3MgaW4gbm9uLXN0cmljdCBtb2RlXG5nID0gKGZ1bmN0aW9uKCkge1xuXHRyZXR1cm4gdGhpcztcbn0pKCk7XG5cbnRyeSB7XG5cdC8vIFRoaXMgd29ya3MgaWYgZXZhbCBpcyBhbGxvd2VkIChzZWUgQ1NQKVxuXHRnID0gZyB8fCBuZXcgRnVuY3Rpb24oXCJyZXR1cm4gdGhpc1wiKSgpO1xufSBjYXRjaCAoZSkge1xuXHQvLyBUaGlzIHdvcmtzIGlmIHRoZSB3aW5kb3cgcmVmZXJlbmNlIGlzIGF2YWlsYWJsZVxuXHRpZiAodHlwZW9mIHdpbmRvdyA9PT0gXCJvYmplY3RcIikgZyA9IHdpbmRvdztcbn1cblxuLy8gZyBjYW4gc3RpbGwgYmUgdW5kZWZpbmVkLCBidXQgbm90aGluZyB0byBkbyBhYm91dCBpdC4uLlxuLy8gV2UgcmV0dXJuIHVuZGVmaW5lZCwgaW5zdGVhZCBvZiBub3RoaW5nIGhlcmUsIHNvIGl0J3Ncbi8vIGVhc2llciB0byBoYW5kbGUgdGhpcyBjYXNlLiBpZighZ2xvYmFsKSB7IC4uLn1cblxubW9kdWxlLmV4cG9ydHMgPSBnO1xuIl0sInNvdXJjZVJvb3QiOiIifQ== \ No newline at end of file diff --git a/src/Resources/public/manifest.json b/src/Resources/public/manifest.json new file mode 100644 index 00000000..0462ffdf --- /dev/null +++ b/src/Resources/public/manifest.json @@ -0,0 +1,3 @@ +{ + "public/monsieurbiz-search.js": "/public/js/monsieurbiz-search.js" +} \ No newline at end of file diff --git a/src/Resources/translations/messages.en.yml b/src/Resources/translations/messages.en.yml index 166c9670..ba419b58 100644 --- a/src/Resources/translations/messages.en.yml +++ b/src/Resources/translations/messages.en.yml @@ -20,6 +20,7 @@ monsieurbiz_searchplugin: result: search_result: 'Search results for "%query%" (%count%)' no_result: 'No result.' + monsieurbiz_product: 'Products' filters: filter_results: 'Filter results' apply_filters: 'Apply filters' diff --git a/src/Resources/views/Instant/Product/_box.html.twig b/src/Resources/views/Instant/Product/_box.html.twig index e69de29b..3714b911 100644 --- a/src/Resources/views/Instant/Product/_box.html.twig +++ b/src/Resources/views/Instant/Product/_box.html.twig @@ -0,0 +1,21 @@ +{% import "@SyliusShop/Common/Macro/money.html.twig" as money %} + +
+ + {% if item.images|first %} + {% set path = item.images|first.path|imagine_filter(filter|default('sylius_shop_product_thumbnail')) %} + {% else %} + {% set path = '//placehold.it/200x200' %} + {% endif %} + + {{ item.name }} + +
+ {{ item.name }} + + {% if item.prices is not empty %} + {% set pricing = item.prices|filter(price => price.channel_code == sylius.channel.code)|first %} +
{{ money.convertAndFormat(pricing.price) }}
+ {% endif %} +
+
diff --git a/src/Resources/views/Instant/result.html.twig b/src/Resources/views/Instant/result.html.twig index e69de29b..b91b310b 100644 --- a/src/Resources/views/Instant/result.html.twig +++ b/src/Resources/views/Instant/result.html.twig @@ -0,0 +1,21 @@ +{% block content %} + {% set noResult = true %} + {% for result in results %} + {% if result.count != 0 %} + {% set noResult = false %} +
+
{{ ('monsieurbiz_searchplugin.instant.result.' ~ result.documentable.indexCode)|trans }}
+
+ {% for item in result %} + {% include result.documentable.template('instant') with {'item': item.model} %} + {% endfor %} +
+
+ {% endif %} + {% endfor %} + {% if noResult %} +
+
{{ 'monsieurbiz_searchplugin.instant.result.no_result'|trans }}
+
+ {% endif %} +{% endblock %} diff --git a/src/Resources/views/Search/_form.html.twig b/src/Resources/views/Search/_form.html.twig index a3a78c6e..732a45bb 100644 --- a/src/Resources/views/Search/_form.html.twig +++ b/src/Resources/views/Search/_form.html.twig @@ -1,6 +1,6 @@ {% form_theme form '@SyliusShop/Form/theme.html.twig' %}
- {{ form_start(form, {'action': path('monsieurbiz_search_post'), 'method': 'POST', 'attr': {'class': 'ui search item autocomplete-search'}}) }} + {{ form_start(form, {'action': path('monsieurbiz_search_post'), 'method': 'POST', 'attr': {'class': 'ui search item category autocomplete-search'}}) }} {{ form_errors(form) }} {{ form_row(form.query, {'value': query, 'label': false}) }} {{ form_row(form.submit, {'attr': {'class': 'ui primary button'}}) }} diff --git a/src/Resources/views/Search/product/_box.html.twig b/src/Resources/views/Search/product/_box.html.twig index b019f888..17585ccd 100644 --- a/src/Resources/views/Search/product/_box.html.twig +++ b/src/Resources/views/Search/product/_box.html.twig @@ -1,7 +1,6 @@ {% import "@SyliusShop/Common/Macro/money.html.twig" as money %}
- {# TODO find the locale... #}
diff --git a/src/Resources/views/_scripts.html.twig b/src/Resources/views/_scripts.html.twig index e69de29b..85a70a81 100644 --- a/src/Resources/views/_scripts.html.twig +++ b/src/Resources/views/_scripts.html.twig @@ -0,0 +1,12 @@ + +{% include '@SyliusUi/_javascripts.html.twig' with {'path': 'bundles/monsieurbizsyliussearchplugin/js/monsieurbiz-search.js'} %} diff --git a/src/Search/Request/InstantSearch/Product.php b/src/Search/Request/InstantSearch/Product.php index 2f977897..b119c322 100644 --- a/src/Search/Request/InstantSearch/Product.php +++ b/src/Search/Request/InstantSearch/Product.php @@ -3,15 +3,135 @@ /* * This file is part of Monsieur Biz' Search plugin for Sylius. * - * (c) Monsieur Biz + * (c) Monsieur Biz * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\InstantSearch; -class Product +use Elastica\Query; +use Elastica\Query\MultiMatch; +use Elastica\QueryBuilder; +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use MonsieurBiz\SyliusSearchPlugin\Repository\ProductAttributeRepositoryInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; +use Sylius\Component\Channel\Context\ChannelContextInterface; +use Sylius\Component\Registry\ServiceRegistryInterface; + +class Product implements RequestInterface { + private DocumentableInterface $documentable; + private ?RequestConfiguration $configuration; + private ChannelContextInterface $channelContext; + private ProductAttributeRepositoryInterface $productAttributeRepository; + + public function __construct( + ServiceRegistryInterface $documentableRegistry, + ChannelContextInterface $channelContext, + ProductAttributeRepositoryInterface $productAttributeRepository + ) { + $this->documentable = $documentableRegistry->get('search.documentable.monsieurbiz_product'); + $this->channelContext = $channelContext; + $this->productAttributeRepository = $productAttributeRepository; + } + + public function getType(): string + { + return RequestInterface::INSTANT_TYPE; + } + + public function getDocumentable(): DocumentableInterface + { + return $this->documentable; + } + + public function getQuery(): Query + { + if (!$this->configuration || '' === $this->configuration->getQueryText()) { + throw new \Exception('missing query text'); //todo + } + + $queryText = $this->configuration->getQueryText(); + $qb = $this->getQueryBuilder(); + $searchQuery = $qb->query()->bool() + ->addShould($qb->query()->term(['code' => ['value' => $queryText]])) + ->addShould( + $qb->query()->multi_match() + ->setFields([ + 'name^5', // todo configuration + 'description', // move to should ? score impact but not include in result + ]) + ->setQuery($queryText) + ->setType(MultiMatch::TYPE_MOST_FIELDS) + ->setFuzziness(MultiMatch::FUZZINESS_AUTO) + ) + ; + + foreach ($this->getAttributeQueries($queryText) as $attributeQuery) { + $searchQuery->addShould($attributeQuery); + } + + $boolQuery = $qb->query()->bool() + ->addFilter($qb->query()->term(['enabled' => ['value' => true]])) + ->addFilter( + $qb->query()->nested() + ->setPath('channels') + ->setQuery( + $qb->query()->term(['channels.code' => ['value' => $this->channelContext->getChannel()->getCode()]]) + ) + ) + ->addMust($searchQuery) + ; + + return Query::create($boolQuery); + } + + public function supports(string $type, string $documentableCode): bool + { + return RequestInterface::INSTANT_TYPE == $type && $this->getDocumentable()->getIndexCode() == $documentableCode; + } + + public function setConfiguration(RequestConfiguration $configuration): void + { + $this->configuration = $configuration; + } + + private function getAttributeQueries(string $queryText): array + { + $attributeQueries = []; + $qb = $this->getQueryBuilder(); + foreach ($this->productAttributeRepository->findIsSearchableOrFilterable() as $productAttribute) { + if (!$productAttribute->isSearchable()) { + continue; + } + + $attributeQueries[] = $qb->query()->nested() + ->setPath('attributes') + ->setQuery( + $qb->query()->nested() + ->setPath(sprintf('attributes.%s', $productAttribute->getCode())) + ->setQuery( + $qb->query()->multi_match() + ->setFields([ + sprintf('attributes.%s.value^%d', $productAttribute->getCode(), $productAttribute->getSearchWeight()), + ]) + ->setQuery($queryText) + ->setFuzziness(MultiMatch::FUZZINESS_AUTO) + ) + ) + ; + } + + return $attributeQueries; + } + private function getQueryBuilder(): QueryBuilder + { + return new QueryBuilder(); + } } diff --git a/src/Search/Request/RequestHandler.php b/src/Search/Request/RequestHandler.php index 5b81b456..b5bbb894 100644 --- a/src/Search/Request/RequestHandler.php +++ b/src/Search/Request/RequestHandler.php @@ -13,6 +13,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request; +use MonsieurBiz\SyliusSearchPlugin\Exception\UnknownRequestTypeException; use Sylius\Component\Registry\ServiceRegistryInterface; class RequestHandler @@ -24,6 +25,9 @@ public function __construct(ServiceRegistryInterface $searchRequestsRegistry) $this->searchRequestsRegistry = $searchRequestsRegistry; } + /** + * @throws UnknownRequestTypeException + */ public function getRequest(RequestConfiguration $requestConfiguration): RequestInterface { /** @var RequestInterface $request */ @@ -35,6 +39,6 @@ public function getRequest(RequestConfiguration $requestConfiguration): RequestI } } - throw new \Exception('Unknow request type'); // TODO + throw new UnknownRequestTypeException(); } } diff --git a/src/Search/Search.php b/src/Search/Search.php index 253877e7..195b43ff 100644 --- a/src/Search/Search.php +++ b/src/Search/Search.php @@ -14,6 +14,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search; use JoliCode\Elastically\Factory; +use MonsieurBiz\SyliusSearchPlugin\Exception\UnknownRequestTypeException; use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestHandler; @@ -34,6 +35,9 @@ public function __construct(SerializerInterface $serializer, LocaleContextInterf $this->requestHandler = $requestHandler; } + /** + * @throws UnknownRequestTypeException + */ public function search(RequestConfiguration $requestConfiguration): ResponseInterface { $request = $this->requestHandler->getRequest($requestConfiguration); From ea274d89fa1c96bc59ec86d49b9a9c9dcea3d0aa Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Wed, 1 Dec 2021 16:48:38 +0100 Subject: [PATCH 053/142] feat: require the settings plugin --- composer.json | 1 + tests/Application/config/bundles.php | 1 + .../config/packages/monsieurbiz_settings_plugin.yaml | 2 ++ .../Application/config/routes/monsieurbiz_settings_plugin.yaml | 3 +++ 4 files changed, 7 insertions(+) create mode 100644 tests/Application/config/packages/monsieurbiz_settings_plugin.yaml create mode 100644 tests/Application/config/routes/monsieurbiz_settings_plugin.yaml diff --git a/composer.json b/composer.json index 755ada0e..3e9bdf5d 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,7 @@ "jacquesbh/eater": "^2.0", "jane-php/automapper-bundle": "^7.1", "jolicode/elastically": "dev-master", + "monsieurbiz/sylius-settings-plugin": "^1.0", "sylius/sylius": "^1.9" }, "require-dev": { diff --git a/tests/Application/config/bundles.php b/tests/Application/config/bundles.php index 2ea97401..3fa47d8a 100644 --- a/tests/Application/config/bundles.php +++ b/tests/Application/config/bundles.php @@ -72,4 +72,5 @@ SyliusLabs\Polyfill\Symfony\Security\Bundle\SyliusLabsPolyfillSymfonySecurityBundle::class => ['all' => true], MonsieurBiz\SyliusSearchPlugin\MonsieurBizSyliusSearchPlugin::class => ['all' => true], Jane\Bundle\AutoMapperBundle\JaneAutoMapperBundle::class => ['all' => true], + MonsieurBiz\SyliusSettingsPlugin\MonsieurBizSyliusSettingsPlugin::class => ['all' => true], ]; diff --git a/tests/Application/config/packages/monsieurbiz_settings_plugin.yaml b/tests/Application/config/packages/monsieurbiz_settings_plugin.yaml new file mode 100644 index 00000000..02c10948 --- /dev/null +++ b/tests/Application/config/packages/monsieurbiz_settings_plugin.yaml @@ -0,0 +1,2 @@ +imports: + - { resource: "@MonsieurBizSyliusSettingsPlugin/Resources/config/config.yaml" } diff --git a/tests/Application/config/routes/monsieurbiz_settings_plugin.yaml b/tests/Application/config/routes/monsieurbiz_settings_plugin.yaml new file mode 100644 index 00000000..3b012295 --- /dev/null +++ b/tests/Application/config/routes/monsieurbiz_settings_plugin.yaml @@ -0,0 +1,3 @@ +monsieurbiz_sylius_settings_admin: + resource: "@MonsieurBizSyliusSettingsPlugin/Resources/config/routes/admin.yaml" + prefix: /%sylius_admin.path_name% From a1e1e88f251662be65b8a2da6fe57efef40ae351 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Wed, 1 Dec 2021 16:49:02 +0100 Subject: [PATCH 054/142] feat: add instant search configuration --- src/Controller/SearchController.php | 38 ++++++++++-- src/DependencyInjection/Configuration.php | 17 ++++++ .../DocumentableRegistryPass.php | 1 + .../MonsieurBizSyliusSearchExtension.php | 17 ++++++ src/Form/Type/Settings/LimitsSearchType.php | 61 +++++++++++++++++++ src/Form/Type/Settings/SettingsSearchType.php | 53 ++++++++++++++++ src/Model/Documentable/Documentable.php | 20 +++++- .../Documentable/DocumentableInterface.php | 2 + src/Resources/config/config.yaml | 1 + .../config/monsieurbiz/settings.yaml | 14 +++++ src/Resources/config/monsieurbiz_search.yaml | 4 ++ src/Search/Request/RequestConfiguration.php | 37 ++++++++--- src/Search/Request/RequestInterface.php | 2 +- 13 files changed, 249 insertions(+), 18 deletions(-) create mode 100644 src/Form/Type/Settings/LimitsSearchType.php create mode 100644 src/Form/Type/Settings/SettingsSearchType.php create mode 100644 src/Resources/config/monsieurbiz/settings.yaml diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php index aa61d968..1e8d6162 100644 --- a/src/Controller/SearchController.php +++ b/src/Controller/SearchController.php @@ -18,6 +18,8 @@ use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Search; +use MonsieurBiz\SyliusSettingsPlugin\Settings\SettingsInterface; +use Sylius\Component\Channel\Context\ChannelContextInterface; use Sylius\Component\Currency\Context\CurrencyContextInterface; use Sylius\Component\Locale\Context\LocaleContextInterface; use Sylius\Component\Registry\ServiceRegistryInterface; @@ -32,18 +34,36 @@ class SearchController extends AbstractController private Search $search; private CurrencyContextInterface $currencyContext; private LocaleContextInterface $localeContext; + private ChannelContextInterface $channelContext; + private SettingsInterface $searchSettings; + private ServiceRegistryInterface $documentableRegistry; - public function __construct(Search $search, CurrencyContextInterface $currencyContext, LocaleContextInterface $localeContext) - { + public function __construct( + Search $search, + CurrencyContextInterface $currencyContext, + LocaleContextInterface $localeContext, + ChannelContextInterface $channelContext, + SettingsInterface $searchSettings, + ServiceRegistryInterface $documentableRegistry + ) { $this->search = $search; $this->currencyContext = $currencyContext; $this->localeContext = $localeContext; + $this->channelContext = $channelContext; + $this->searchSettings = $searchSettings; + $this->documentableRegistry = $documentableRegistry; } // TODO add an optional parameter $documentType (nullable => get the default document type) public function searchAction(Request $request, string $query): Response { - $requestConfiguration = new RequestConfiguration($request, RequestInterface::SEARCH_TYPE, 'monsieurbiz_product'); + $requestConfiguration = new RequestConfiguration( + $request, + RequestInterface::SEARCH_TYPE, + $this->documentableRegistry->get('search.documentable.monsieurbiz_product'), + $this->searchSettings, + $this->channelContext + ); $result = $this->search->search($requestConfiguration); return $this->render('@MonsieurBizSyliusSearchPlugin/Search/result.html.twig', [ @@ -74,15 +94,21 @@ public function postAction(Request $request): RedirectResponse /** * Perform the instant search action & display results. */ - public function instantAction(Request $request, ServiceRegistryInterface $documentableRegistry): Response + public function instantAction(Request $request): Response { $results = []; /** @var DocumentableInterface $documentable */ - foreach ($documentableRegistry->all() as $documentable) { + foreach ($this->documentableRegistry->all() as $documentable) { + if (!(bool) $this->searchSettings->getCurrentValue($this->channelContext->getChannel(), null, 'instant_search_enabled__' . $documentable->getIndexCode())) { + continue; + } + $requestConfiguration = new RequestConfiguration( $request, RequestInterface::INSTANT_TYPE, - $documentable->getIndexCode() + $documentable, + $this->searchSettings, + $this->channelContext ); try { $results[] = $this->search->search($requestConfiguration); diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 7f418e19..1bcfb35a 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -34,6 +34,7 @@ public function getConfigTreeBuilder(): TreeBuilder ->defaultValue([]) ->arrayPrototype() ->children() + ->scalarNode('instant_search_enabled')->defaultValue(false)->end() ->scalarNode('source')->isRequired()->cannotBeEmpty()->end() ->scalarNode('target')->isRequired()->cannotBeEmpty()->end() ->scalarNode('mapping_provider')->defaultValue(YamlWithLocaleProvider::class)->end() @@ -45,6 +46,22 @@ public function getConfigTreeBuilder(): TreeBuilder ->scalarNode('instant')->isRequired()->cannotBeEmpty()->end() ->end() ->end() + + // Limits + ->arrayNode('limits') + ->children() + ->arrayNode('search') + ->performNoDeepMerging() + ->integerPrototype()->end() + ->defaultValue([9, 18, 27]) + ->end() + ->arrayNode('instant_search') + ->performNoDeepMerging() + ->integerPrototype()->end() + ->defaultValue([9, 18, 27]) + ->end() + ->end() + ->end() ->end() ->end() ->end() diff --git a/src/DependencyInjection/DocumentableRegistryPass.php b/src/DependencyInjection/DocumentableRegistryPass.php index 045a5378..f0171188 100644 --- a/src/DependencyInjection/DocumentableRegistryPass.php +++ b/src/DependencyInjection/DocumentableRegistryPass.php @@ -41,6 +41,7 @@ public function process(ContainerBuilder $container): void '$sourceClass' => $documentableConfiguration['source'], '$targetClass' => $documentableConfiguration['target'], '$templates' => $documentableConfiguration['templates'], + '$limits' => $documentableConfiguration['limits'], ]) ; $documentableDefinition = $container->setDefinition($documentableServiceId, $documentableDefinition); diff --git a/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php b/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php index a63d9ee3..fff2bc2d 100644 --- a/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php +++ b/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php @@ -28,6 +28,9 @@ public function load(array $configs, ContainerBuilder $container): void $config = $this->processConfiguration($this->getConfiguration([], $container), $configs); foreach ($config as $name => $value) { $container->setParameter(self::EXTENSION_CONFIG_NAME . '.' . $name, $value); + if ('documents' === $name) { + $this->addDocumentsConfiguration(self::EXTENSION_CONFIG_NAME . '.' . $name, $value, $container); + } } $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); @@ -45,4 +48,18 @@ public function getAlias() { return str_replace(['monsieur_biz'], ['monsieurbiz'], parent::getAlias()); } + + private function addDocumentsConfiguration(string $name, array $values, ContainerBuilder $container): void + { + foreach ($values as $documentIndexName => $documentValues) { + $this->addDocumentConfiguration($name . '.' . $documentIndexName, $documentValues, $container); + } + } + + private function addDocumentConfiguration(string $name, array $values, ContainerBuilder $container): void + { + foreach ($values as $configName => $configValue) { + $container->setParameter($name . '.' . $configName, $configValue); + } + } } diff --git a/src/Form/Type/Settings/LimitsSearchType.php b/src/Form/Type/Settings/LimitsSearchType.php new file mode 100644 index 00000000..f47d0bcb --- /dev/null +++ b/src/Form/Type/Settings/LimitsSearchType.php @@ -0,0 +1,61 @@ +addWithDefaultCheckbox( + $builder, + 'search', + CollectionType::class, + [ + 'entry_type' => NumberType::class, + 'label' => $documentable->getIndexCode() . ' search', + 'required' => true, + 'allow_add' => true, + 'allow_delete' => true, + ] + ); + $this->addWithDefaultCheckbox( + $builder, + 'instant_search', + CollectionType::class, + [ + 'entry_type' => NumberType::class, + 'label' => $documentable->getIndexCode() . ' instant_search', + 'required' => true, + 'allow_add' => true, + 'allow_delete' => true, + ] + ); + } + + public function configureOptions(OptionsResolver $resolver): void + { + parent::configureOptions($resolver); + $resolver->setRequired('documentable'); + } +} diff --git a/src/Form/Type/Settings/SettingsSearchType.php b/src/Form/Type/Settings/SettingsSearchType.php new file mode 100644 index 00000000..1ff27524 --- /dev/null +++ b/src/Form/Type/Settings/SettingsSearchType.php @@ -0,0 +1,53 @@ +documentableRegistry = $documentableRegistry; + } + + public function buildForm(FormBuilderInterface $builder, array $options): void + { + /** @var DocumentableInterface $documentable */ + foreach ($this->documentableRegistry->all() as $documentable) { + $this->addWithDefaultCheckbox( + $builder, + 'instant_search_enabled__' . $documentable->getIndexCode(), + CheckboxType::class, + [ + 'required' => false, + ] + ); + $subOptions = $options; + $subOptions['data'] = $subOptions['data']['limits__' . $documentable->getIndexCode()] ?? []; + $subOptions['documentable'] = $documentable; + $builder->add( + 'limits__' . $documentable->getIndexCode(), + LimitsSearchType::class, + $subOptions + ); + } + } +} diff --git a/src/Model/Documentable/Documentable.php b/src/Model/Documentable/Documentable.php index 6a50f859..064cfc3e 100644 --- a/src/Model/Documentable/Documentable.php +++ b/src/Model/Documentable/Documentable.php @@ -26,13 +26,20 @@ class Documentable implements DocumentableInterface * @var array */ private array $templates; + private array $limits; - public function __construct(string $indexCode, string $sourceClass, string $targetClass, array $templates) - { + public function __construct( + string $indexCode, + string $sourceClass, + string $targetClass, + array $templates, + array $limits + ) { $this->indexCode = $indexCode; $this->sourceClass = $sourceClass; $this->targetClass = $targetClass; $this->templates = $templates; + $this->limits = $limits; } public function getIndexCode(): string @@ -61,4 +68,13 @@ public function getTemplate(string $type): ?string { return $this->templates[$type] ?? null; } + + public function getLimits(?string $queryType = null): array + { + if (null == $queryType) { + return $this->limits; + } + + return $this->limits[$queryType] ?? []; + } } diff --git a/src/Model/Documentable/DocumentableInterface.php b/src/Model/Documentable/DocumentableInterface.php index 22fa8b6e..e793c8f4 100644 --- a/src/Model/Documentable/DocumentableInterface.php +++ b/src/Model/Documentable/DocumentableInterface.php @@ -36,4 +36,6 @@ public function getDatasource(): DatasourceInterface; public function isTranslatable(): bool; public function getTemplate(string $type): ?string; + + public function getLimits(?string $queryType): array; } diff --git a/src/Resources/config/config.yaml b/src/Resources/config/config.yaml index 3878d152..4c23edaf 100644 --- a/src/Resources/config/config.yaml +++ b/src/Resources/config/config.yaml @@ -2,3 +2,4 @@ imports: - { resource: "services.yaml" } - { resource: "monsieurbiz_search.yaml" } - { resource: "sylius/ui.yaml" } + - { resource: "monsieurbiz/settings.yaml" } diff --git a/src/Resources/config/monsieurbiz/settings.yaml b/src/Resources/config/monsieurbiz/settings.yaml new file mode 100644 index 00000000..5b7971e3 --- /dev/null +++ b/src/Resources/config/monsieurbiz/settings.yaml @@ -0,0 +1,14 @@ +monsieurbiz_sylius_settings: + plugins: + monsieurbiz.search: + vendor_name: Monsieur Biz + vendor_url: + plugin_name: Search + description: Search configuration + icon: search + use_locales: false + classes: + form: MonsieurBiz\SyliusSearchPlugin\Form\Type\Settings\SettingsSearchType + default_values: + instant_search_enabled__monsieurbiz_product: '%monsieurbiz.search.config.documents.monsieurbiz_product.instant_search_enabled%' + limits__monsieurbiz_product: '%monsieurbiz.search.config.documents.monsieurbiz_product.limits%' diff --git a/src/Resources/config/monsieurbiz_search.yaml b/src/Resources/config/monsieurbiz_search.yaml index d393edbc..1039a6f1 100644 --- a/src/Resources/config/monsieurbiz_search.yaml +++ b/src/Resources/config/monsieurbiz_search.yaml @@ -1,6 +1,10 @@ monsieurbiz_sylius_search: documents: monsieurbiz_product: + instant_search_enabled: true # by default false + limits: + search: [9, 18, 27] + instant_search: [5] source: 'Sylius\Component\Core\Model\ProductInterface' target: 'MonsieurBiz\SyliusSearchPlugin\Model\Product\ProductDTO' templates: diff --git a/src/Search/Request/RequestConfiguration.php b/src/Search/Request/RequestConfiguration.php index 013f683f..81e91287 100644 --- a/src/Search/Request/RequestConfiguration.php +++ b/src/Search/Request/RequestConfiguration.php @@ -13,19 +13,33 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request; +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use MonsieurBiz\SyliusSettingsPlugin\Settings\SettingsInterface; +use Sylius\Component\Channel\Context\ChannelContextInterface; use Symfony\Component\HttpFoundation\Request; final class RequestConfiguration { + public const FALLBACK_LIMIT = 9; + private Request $request; private string $type; - private string $documentType; - - public function __construct(Request $request, string $type, string $documentType) - { + private DocumentableInterface $documentable; + private SettingsInterface $searchSettings; + private ChannelContextInterface $channelContext; + + public function __construct( + Request $request, + string $type, + DocumentableInterface $documentable, + SettingsInterface $searchSettings, + ChannelContextInterface $channelContext + ) { $this->request = $request; $this->type = $type; - $this->documentType = $documentType; + $this->documentable = $documentable; + $this->searchSettings = $searchSettings; + $this->channelContext = $channelContext; } public function getQueryText(): string @@ -64,13 +78,18 @@ public function getLimit(): int $limit = reset($availableLimits); } - return $limit; + return $limit ?? self::FALLBACK_LIMIT; } public function getAvailableLimits(): array { - // TODO define this in config (by query type?) - return [9, 18, 27]; + $configLimits = $this->searchSettings->getCurrentValue( + $this->channelContext->getChannel(), + null, + 'limits__' . $this->getDocumentType() + ); + + return $configLimits[$this->getType()] ?? $this->documentable->getLimits($this->getType()); } public function getType(): string @@ -80,6 +99,6 @@ public function getType(): string public function getDocumentType(): string { - return $this->documentType; + return $this->documentable->getIndexCode(); } } diff --git a/src/Search/Request/RequestInterface.php b/src/Search/Request/RequestInterface.php index 186abec4..3cf98b90 100644 --- a/src/Search/Request/RequestInterface.php +++ b/src/Search/Request/RequestInterface.php @@ -20,7 +20,7 @@ interface RequestInterface { public const SEARCH_TYPE = 'search'; public const TAXON_TYPE = 'taxon'; - public const INSTANT_TYPE = 'instant'; + public const INSTANT_TYPE = 'instant_search'; public function getType(): string; From d0ec22dc92d2b51694ba31f8f686d7bc0509c1bf Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Fri, 3 Dec 2021 09:16:35 +0100 Subject: [PATCH 055/142] feat: add taxons display from ES --- src/AutoMapper/ProductMapperConfiguration.php | 1 + src/Controller/SearchController.php | 27 +- src/Form/Type/Settings/LimitsSearchType.php | 6 +- src/Form/Type/Settings/SettingsSearchType.php | 4 +- .../monsieurbiz_product_mapping.yaml | 2 +- src/Resources/config/routing/shop.yaml | 10 + src/Resources/config/services.yaml | 4 + .../views/Search/Filter/default.html.twig | 16 +- .../views/Search/_pagination.html.twig | 2 +- src/Resources/views/Taxon/result.html.twig | 44 +++ src/Search/Filter/Filter.php | 6 +- .../Request/Aggregation/TaxonsAggregation.php | 69 ++++ src/Search/Request/RequestConfiguration.php | 16 +- src/Search/Request/Taxon/Product.php | 313 ++++++++++++++++++ src/Search/Response.php | 25 ++ 15 files changed, 526 insertions(+), 19 deletions(-) create mode 100644 src/Resources/views/Taxon/result.html.twig create mode 100644 src/Search/Request/Aggregation/TaxonsAggregation.php create mode 100644 src/Search/Request/Taxon/Product.php diff --git a/src/AutoMapper/ProductMapperConfiguration.php b/src/AutoMapper/ProductMapperConfiguration.php index c0e87bc6..32c10b1b 100644 --- a/src/AutoMapper/ProductMapperConfiguration.php +++ b/src/AutoMapper/ProductMapperConfiguration.php @@ -98,6 +98,7 @@ public function process(MapperGeneratorMetadataInterface $metadata): void $metadata->forMember('product_taxons', function(ProductInterface $product): array { return array_map(function(ProductTaxonInterface $productTaxon) { + // todo add parent taxon in Taxon object with automapper return $this->autoMapper->map($productTaxon, ProductTaxon::class); }, $product->getProductTaxons()->toArray()); }); diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php index 1e8d6162..14a2dbb1 100644 --- a/src/Controller/SearchController.php +++ b/src/Controller/SearchController.php @@ -19,6 +19,8 @@ use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Search; use MonsieurBiz\SyliusSettingsPlugin\Settings\SettingsInterface; +use Sylius\Bundle\ResourceBundle\Controller\Parameters; +use Sylius\Bundle\ResourceBundle\Controller\ParametersParserInterface; use Sylius\Component\Channel\Context\ChannelContextInterface; use Sylius\Component\Currency\Context\CurrencyContextInterface; use Sylius\Component\Locale\Context\LocaleContextInterface; @@ -37,6 +39,7 @@ class SearchController extends AbstractController private ChannelContextInterface $channelContext; private SettingsInterface $searchSettings; private ServiceRegistryInterface $documentableRegistry; + private ParametersParserInterface $parametersParser; public function __construct( Search $search, @@ -44,7 +47,8 @@ public function __construct( LocaleContextInterface $localeContext, ChannelContextInterface $channelContext, SettingsInterface $searchSettings, - ServiceRegistryInterface $documentableRegistry + ServiceRegistryInterface $documentableRegistry, + ParametersParserInterface $parametersParser ) { $this->search = $search; $this->currencyContext = $currencyContext; @@ -52,6 +56,7 @@ public function __construct( $this->channelContext = $channelContext; $this->searchSettings = $searchSettings; $this->documentableRegistry = $documentableRegistry; + $this->parametersParser = $parametersParser; } // TODO add an optional parameter $documentType (nullable => get the default document type) @@ -71,7 +76,6 @@ public function searchAction(Request $request, string $query): Response 'requestConfiguration' => $requestConfiguration, 'query' => $query, 'result' => $result, - 'limits' => $requestConfiguration->getAvailableLimits(), 'currencySymbol' => Currencies::getSymbol($this->currencyContext->getCurrencyCode(), $this->localeContext->getLocaleCode()), ]); } @@ -121,4 +125,23 @@ public function instantAction(Request $request): Response 'results' => $results, ]); } + + public function taxonAction(Request $request): Response + { + $requestConfiguration = new RequestConfiguration( + $request, + RequestInterface::TAXON_TYPE, + $this->documentableRegistry->get('search.documentable.monsieurbiz_product'), + $this->searchSettings, + $this->channelContext, + new Parameters($this->parametersParser->parseRequestValues($request->attributes->get('_sylius', []), $request)) + ); + $result = $this->search->search($requestConfiguration); + + return $this->render('@MonsieurBizSyliusSearchPlugin/Taxon/result.html.twig', [ + 'requestConfiguration' => $requestConfiguration, + 'result' => $result, + 'currencySymbol' => Currencies::getSymbol($this->currencyContext->getCurrencyCode(), $this->localeContext->getLocaleCode()), + ]); + } } diff --git a/src/Form/Type/Settings/LimitsSearchType.php b/src/Form/Type/Settings/LimitsSearchType.php index f47d0bcb..921ab619 100644 --- a/src/Form/Type/Settings/LimitsSearchType.php +++ b/src/Form/Type/Settings/LimitsSearchType.php @@ -1,9 +1,9 @@ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -22,7 +22,7 @@ class LimitsSearchType extends AbstractSettingsType { - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { /** @var DocumentableInterface $documentable */ $documentable = $options['documentable']; diff --git a/src/Form/Type/Settings/SettingsSearchType.php b/src/Form/Type/Settings/SettingsSearchType.php index 1ff27524..2f46184d 100644 --- a/src/Form/Type/Settings/SettingsSearchType.php +++ b/src/Form/Type/Settings/SettingsSearchType.php @@ -1,9 +1,9 @@ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml b/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml index 1a75597c..ed6318b0 100644 --- a/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml +++ b/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml @@ -36,7 +36,7 @@ mappings: type: integer level: type: integer - product_taxon: + product_taxons: type: nested properties: taxon: diff --git a/src/Resources/config/routing/shop.yaml b/src/Resources/config/routing/shop.yaml index afbc1e91..10b90786 100644 --- a/src/Resources/config/routing/shop.yaml +++ b/src/Resources/config/routing/shop.yaml @@ -17,3 +17,13 @@ monsieurbiz_search_instant: methods: [POST] defaults: _controller: MonsieurBiz\SyliusSearchPlugin\Controller\SearchController:instantAction + +monsieurbiz_sylius_search_taxon: + path: /taxons/{slug} + methods: [GET] + defaults: + _controller: MonsieurBiz\SyliusSearchPlugin\Controller\SearchController:taxonAction + _sylius: + taxon: "expr:notFoundOnNull(service('sylius.repository.taxon').findOneBySlug($slug, service('sylius.context.locale').getLocaleCode()))" + requirements: + slug: .+ diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index b5a2b815..c23a715b 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -9,6 +9,7 @@ services: autoconfigure: true public: false bind: + Sylius\Bundle\ResourceBundle\Controller\ParametersParserInterface: '@sylius.resource_controller.parameters_parser' $localeProvider: '@sylius.translation_locale_provider' $productVariantResolver: '@sylius.product_variant_resolver.default' $documentableRegistry: '@monsieurbiz.search.registry.documentable' @@ -95,6 +96,9 @@ services: MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation\MainTaxonAggregation: tags: { name: 'monsieurbiz.sarch.aggregation_builder' } + MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation\TaxonsAggregation: + tags: { name: 'monsieurbiz.sarch.aggregation_builder' } + MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation\PriceAggregation: tags: { name: 'monsieurbiz.sarch.aggregation_builder' } diff --git a/src/Resources/views/Search/Filter/default.html.twig b/src/Resources/views/Search/Filter/default.html.twig index 33b4b2cf..f212d94a 100644 --- a/src/Resources/views/Search/Filter/default.html.twig +++ b/src/Resources/views/Search/Filter/default.html.twig @@ -1,5 +1,15 @@ -{% set appliedAttributes = app.request.query.get(filter.type) %} -{% set currentValues = (appliedAttributes[filter.code] is defined) ? appliedAttributes[filter.code] : [] %} +{% set inputName = filter.code %} +{% set filterName = filter.code %} +{% if filter.type is not empty %} + {% set inputName = filter.type ~ '[' ~ inputName ~ ']' %} + {% set filterName = filter.type %} +{% endif %} +{% set appliedAttributes = requestConfiguration.appliedFilters(filterName) %} + +{% set currentValues = appliedAttributes ?? [] %} +{% if filter.type is not empty %} + {% set currentValues = (appliedAttributes[filter.code] is defined) ? appliedAttributes[filter.code] : [] %} +{% endif %} {% if filter.values|length or currentValues is not empty %}
@@ -12,7 +22,7 @@
diff --git a/src/Resources/views/Taxon/result.html.twig b/src/Resources/views/Taxon/result.html.twig new file mode 100644 index 00000000..f677c0eb --- /dev/null +++ b/src/Resources/views/Taxon/result.html.twig @@ -0,0 +1,44 @@ +{% extends '@SyliusShop/layout.html.twig' %} +{% import '@SyliusUi/Macro/pagination.html.twig' as pagination %} + +{% block content %} + {% include '@SyliusShop/Product/Index/_header.html.twig' %} + +
+
+
+

{{ 'monsieurbiz_searchplugin.filters.loading' | trans }}

+
+
+
+ {% include '@MonsieurBizSyliusSearchPlugin/Search/_sidebar.html.twig' %} +
+
+ {% if result.count == 0 %} +
+
+
+

+ {{ 'monsieurbiz_searchplugin.search.result.no_result'|trans }} +

+
+
+
+ {% else %} + {% include '@MonsieurBizSyliusSearchPlugin/Search/_pagination.html.twig' %} + {% include '@MonsieurBizSyliusSearchPlugin/Search/_sorting.html.twig' %} + + +
+ {% for item in result %} + {% include result.documentable.template('item') with {'item': item.model} %} + {% endfor %} +
+ + + + {{ pagination.simple(result.paginator) }} + {% endif %} +
+
+{% endblock %} diff --git a/src/Search/Filter/Filter.php b/src/Search/Filter/Filter.php index 6851df4e..f9ca98bc 100644 --- a/src/Search/Filter/Filter.php +++ b/src/Search/Filter/Filter.php @@ -41,12 +41,8 @@ class Filter implements FilterInterface /** * Filter constructor. - * - * @param string $code - * @param string $label - * @param int $count */ - public function __construct(string $code, string $label, int $count, string $type) + public function __construct(string $code, string $label, int $count, string $type = '') { $this->code = $code; $this->label = $label; diff --git a/src/Search/Request/Aggregation/TaxonsAggregation.php b/src/Search/Request/Aggregation/TaxonsAggregation.php new file mode 100644 index 00000000..ac38631d --- /dev/null +++ b/src/Search/Request/Aggregation/TaxonsAggregation.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; + +use Elastica\Aggregation\AbstractAggregation; +use Sylius\Component\Core\Model\TaxonInterface; + +class TaxonsAggregation implements AggregationBuilderInterface +{ + public function build($aggregation, array $filters): ?AbstractAggregation + { + if (!$this->isSupported($aggregation)) { + return null; + } + /** @var TaxonInterface $currentTaxon */ + $currentTaxon = $aggregation['taxons']; + $qb = new \Elastica\QueryBuilder(); + + $filters = array_filter($filters, function($key) { + return false === strpos($key, 'taxons'); + }, \ARRAY_FILTER_USE_KEY); + $filterQuery = $qb->query()->bool(); + foreach ($filters as $filter) { + $filterQuery->addMust($filter); + } + + return $qb->aggregation() + ->filter('taxons') + ->setFilter($filterQuery) + ->addAggregation( + $qb->aggregation() + ->nested('taxons', 'product_taxons') + ->addAggregation( + $qb->aggregation() + ->nested('taxons', 'product_taxons.taxon') + ->addAggregation( + $qb->aggregation() + ->filter('taxons', $qb->query()->term(['product_taxons.taxon.level' => ['value' => $currentTaxon->getLevel() + 1]])) + ->addAggregation( + $qb->aggregation() + ->terms('codes') + ->setField('product_taxons.taxon.code') + ->addAggregation( + $qb->aggregation()->terms('names') + ->setField('product_taxons.taxon.name') + ) + ) + ) + ) + ) + ; + } + + protected function isSupported($aggregation): bool + { + return \array_key_exists('taxons', $aggregation); + } +} diff --git a/src/Search/Request/RequestConfiguration.php b/src/Search/Request/RequestConfiguration.php index 81e91287..de106b0a 100644 --- a/src/Search/Request/RequestConfiguration.php +++ b/src/Search/Request/RequestConfiguration.php @@ -15,7 +15,9 @@ use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; use MonsieurBiz\SyliusSettingsPlugin\Settings\SettingsInterface; +use Sylius\Bundle\ResourceBundle\Controller\Parameters; use Sylius\Component\Channel\Context\ChannelContextInterface; +use Sylius\Component\Core\Model\TaxonInterface; use Symfony\Component\HttpFoundation\Request; final class RequestConfiguration @@ -27,19 +29,22 @@ final class RequestConfiguration private DocumentableInterface $documentable; private SettingsInterface $searchSettings; private ChannelContextInterface $channelContext; + private ?Parameters $parameters; public function __construct( Request $request, string $type, DocumentableInterface $documentable, SettingsInterface $searchSettings, - ChannelContextInterface $channelContext + ChannelContextInterface $channelContext, + ?Parameters $parameters = null ) { $this->request = $request; $this->type = $type; $this->documentable = $documentable; $this->searchSettings = $searchSettings; $this->channelContext = $channelContext; + $this->parameters = $parameters ?? new Parameters(); } public function getQueryText(): string @@ -54,6 +59,7 @@ public function getAppliedFilters($type = null): array 'price' => array_filter($this->request->get('price', [])), 'attributes' => $this->request->get('attributes', []), 'options' => $this->request->get('options', []), + 'taxons' => $this->request->get('taxons', []), ]; return null !== $type ? ($appliedFilters[$type] ?? []) : $appliedFilters; @@ -78,7 +84,7 @@ public function getLimit(): int $limit = reset($availableLimits); } - return $limit ?? self::FALLBACK_LIMIT; + return $limit ?: self::FALLBACK_LIMIT; } public function getAvailableLimits(): array @@ -101,4 +107,10 @@ public function getDocumentType(): string { return $this->documentable->getIndexCode(); } + + public function getTaxon(): TaxonInterface + { + // todo throw exception if not exist + return $this->parameters->get('taxon'); + } } diff --git a/src/Search/Request/Taxon/Product.php b/src/Search/Request/Taxon/Product.php new file mode 100644 index 00000000..a8251e69 --- /dev/null +++ b/src/Search/Request/Taxon/Product.php @@ -0,0 +1,313 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Taxon; + +use Elastica\Query; +use Elastica\QueryBuilder; +use MonsieurBiz\SyliusSearchPlugin\Helper\SlugHelper; +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use MonsieurBiz\SyliusSearchPlugin\Repository\ProductAttributeRepositoryInterface; +use MonsieurBiz\SyliusSearchPlugin\Repository\ProductOptionRepositoryInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\AggregationBuilder; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; +use Sylius\Component\Channel\Context\ChannelContextInterface; +use Sylius\Component\Registry\ServiceRegistryInterface; + +class Product implements RequestInterface +{ + private DocumentableInterface $documentable; + private ProductAttributeRepositoryInterface $productAttributeRepository; + private ProductOptionRepositoryInterface $productOptionRepository; + private ChannelContextInterface $channelContext; + private AggregationBuilder $aggregationBuilder; + private ?RequestConfiguration $configuration; + + public function __construct( + ServiceRegistryInterface $documentableRegistry, + ProductAttributeRepositoryInterface $productAttributeRepository, + ProductOptionRepositoryInterface $productOptionRepository, + ChannelContextInterface $channelContext, + AggregationBuilder $aggregationBuilder + ) { + $this->documentable = $documentableRegistry->get('search.documentable.monsieurbiz_product'); + $this->productAttributeRepository = $productAttributeRepository; + $this->productOptionRepository = $productOptionRepository; + $this->channelContext = $channelContext; + $this->aggregationBuilder = $aggregationBuilder; + } + + public function getType(): string + { + return RequestInterface::TAXON_TYPE; + } + + public function getDocumentable(): DocumentableInterface + { + return $this->documentable; + } + + public function getQuery(): Query + { + $qb = new QueryBuilder(); + $searchQuery = $qb->query()->nested() + ->setPath('product_taxons') + ->setQuery( + $qb->query()->nested() + ->setPath('product_taxons.taxon') + ->setQuery( + $qb->query()->term(['product_taxons.taxon.code' => ['value' => $this->configuration->getTaxon()->getCode()]]) + ) + ) + ; + + if ($this->configuration->getTaxon()->isRoot()) { + $searchQuery = $qb->query()->bool(); + } + + $boolQuery = $qb->query()->bool() + ->addFilter($qb->query()->term(['enabled' => ['value' => true]])) + ->addFilter( + $qb->query()->nested() + ->setPath('channels') + ->setQuery( + $qb->query()->term(['channels.code' => ['value' => $this->channelContext->getChannel()->getCode()]]) + ) + ) + ->addMust($searchQuery) + ; + + $query = Query::create($boolQuery); + + $postFilter = new Query\BoolQuery(); + foreach ($this->getFilters() as $filter) { + $postFilter->addMust($filter); + } + $query->setPostFilter($postFilter); + $this->addAggregations($query); + + // Manage sorting + $sorts = $this->configuration->getSorting() ?: ['position' => 'desc']; + foreach ($sorts as $field => $order) { + $sort = $this->getSort($field, $order); + if (0 !== \count($sort)) { + $query->addSort($sort); + } + } + + dump(json_encode($query->toArray(), 1)); + + return $query; + } + + public function supports(string $type, string $documentableCode): bool + { + return RequestInterface::TAXON_TYPE === $type && $documentableCode === $this->getDocumentable()->getIndexCode(); + } + + public function setConfiguration(RequestConfiguration $configuration): void + { + $this->configuration = $configuration; + } + + private function getFilters(): array + { + $filters = []; + + $qb = new \Elastica\QueryBuilder(); + + foreach ($this->configuration->getAppliedFilters('taxon') as $field => $values) { + $mainTaxonQuery = $qb->query() + ->bool() + ; + foreach ($values as $value) { + $mainTaxonQuery->addShould( + $qb->query() + ->term() + ->setTerm(sprintf('%s.code', $field), SlugHelper::toLabel($value)) + ); + } + $filters['main_taxons'] = $qb->query() + ->nested() + ->setPath($field) + ->setQuery( + $mainTaxonQuery + ) + ; + } + + $taxonsSelected = $this->configuration->getAppliedFilters('taxons'); + if (0 !== \count($taxonsSelected)) { + $taxonQuery = $qb->query() + ->bool() + ; + foreach ($this->configuration->getAppliedFilters('taxons') as $value) { + $taxonQuery->addShould( + $qb->query() + ->term() + ->setTerm('product_taxons.taxon.code', SlugHelper::toLabel($value)) + ); + } + + $filters['taxons'] = $qb->query() + ->nested() + ->setPath('product_taxons') + ->setQuery( + $qb->query()->nested() + ->setPath('product_taxons.taxon') + ->setQuery($taxonQuery) + ) + ; + } + + $priceValue = $this->configuration->getAppliedFilters('price'); + if (0 !== \count($priceValue)) { + $qb = new \Elastica\QueryBuilder(); + + // channel filter + $channelPriceFilter = $qb->query() + ->term(['prices.channel_code' => $this->channelContext->getChannel()->getCode()]) + ; + $conditions = []; + if (\array_key_exists('min', $priceValue)) { + $conditions['gte'] = $priceValue['min'] * 100; + } + if (\array_key_exists('max', $priceValue)) { + $conditions['lte'] = $priceValue['max'] * 100; + } + $priceQuery = $qb->query() + ->range('prices.price', $conditions) + ; + $filters['price'] = $qb->query() + ->nested() + ->setPath('prices') + ->setQuery( + $qb->query()->bool() + ->addMust($channelPriceFilter) + ->addMust($priceQuery) + ) + ; + } + + foreach ($this->configuration->getAppliedFilters('attributes') as $field => $values) { + $attributeValueQuery = new Query\BoolQuery(); + + foreach ($values as $value) { + $termQuery = new Query\Terms(sprintf('attributes.%s.value.keyword', $field), [SlugHelper::toLabel($value)]); + $attributeValueQuery->addShould($termQuery); // todo configure the "and" or "or" + } + + $attributeQuery = new Query\Nested(); + $attributeQuery->setPath(sprintf('attributes.%s', $field))->setQuery($attributeValueQuery); + + $filters['attributes.' . $field] = $attributeQuery; + } + + foreach ($this->configuration->getAppliedFilters('options') as $field => $values) { + $attributeValueQuery = new Query\BoolQuery(); + + foreach ($values as $value) { + $termQuery = new Query\Terms(sprintf('variants.options.%s.value.keyword', $field), [SlugHelper::toLabel($value)]); + $attributeValueQuery->addShould($termQuery); // todo configure the "and" or "or" + } + + $attributeQuery = new Query\Nested(); + $attributeQuery->setPath(sprintf('variants.options.%s', $field))->setQuery($attributeValueQuery); + + $filters['options.' . $field] = $attributeQuery; + } + +// dump($filters);die; + + return $filters; + } + + private function addAggregations(Query $query): void + { + $newAggs = $this->aggregationBuilder->buildAggregations( + [ + $this->productAttributeRepository->findIsSearchableOrFilterable(), + $this->productOptionRepository->findIsSearchableOrFilterable(), + ['taxons' => $this->configuration->getTaxon()], + 'price', + ], + $this->getFilters() + ); + + foreach ($newAggs as $aggregation) { + $query->addAggregation($aggregation); + } + } + + // todo find solution to get more extendable + private function getSort(string $field, string $order) + { + $fieldName = $field; + if ('name' == $field) { + $fieldName = $field . '.keyword'; + } + + switch ($field) { + case 'name': + case 'created_at': + return $this->buildSort($fieldName, $order); + case 'price': + return self::buildSort( + 'prices.price', + $order, + 'prices', + 'prices.channel_code', $this->channelContext->getChannel()->getCode() + ); + case 'position': + default: + $qb = new QueryBuilder(); + $filter = $qb->query()->nested() + ->setPath('product_taxons.taxon') + ->setQuery( + $qb->query()->term(['product_taxons.taxon.code' => ['value' => $this->configuration->getTaxon()->getCode()]]) + ) + ; + + return $this->buildSort('product_taxons.position', 'asc', 'product_taxons', null, $filter); + } + } + + /** + * @param string|Query\AbstractQuery|null $sortFilterValue + */ + private function buildSort( + string $field, + string $order, + ?string $nestedPath = null, + ?string $sortFilterField = null, + $sortFilterValue = null + ): array { + $sort = [$field => ['order' => $order]]; + if (null !== $nestedPath) { + $sort[$field]['nested']['path'] = $nestedPath; + if ($sortFilterValue instanceof Query\AbstractQuery) { + $filter = $sortFilterValue->toArray(); + } else { + $filter = [ + 'term' => [ + $sortFilterField => $sortFilterValue, + ], + ]; + } + $sort[$field]['nested']['filter'] = $filter; + } + + return $sort; + } +} diff --git a/src/Search/Response.php b/src/Search/Response.php index ac1af525..d11b42d1 100644 --- a/src/Search/Response.php +++ b/src/Search/Response.php @@ -111,6 +111,31 @@ private function buildFilters(): void } } + // todo taxons + $taxonAggregation = $aggregations['taxons']['taxons']['taxons']['taxons'] ?? null; + if ($taxonAggregation && $taxonAggregation['doc_count'] > 0) { + $filter = new Filter('taxons', 'monsieurbiz_searchplugin.filters.taxon_filter', $taxonAggregation['doc_count']); + + // Get main taxon code in aggregation + $taxonCodeBuckets = $taxonAggregation['codes']['buckets'] ?? []; + foreach ($taxonCodeBuckets as $taxonCodeBucket) { + if (0 === $taxonCodeBucket['doc_count']) { + continue; + } + $taxonCode = $taxonCodeBucket['key']; + $taxonName = null; + $taxonNameBuckets = $taxonCodeBucket['names']['buckets'] ?? []; + foreach ($taxonNameBuckets as $taxonNameBucket) { + $taxonName = $taxonNameBucket['key']; + $filter->addValue($taxonName ?? $taxonCode, $taxonCodeBucket['doc_count'], $taxonCode); + } + } + + // Put taxon filter in first if contains value + if (0 !== \count($filter->getValues())) { + $this->filters[] = $filter; + } + } // todo price $priceAggregation = $aggregations['prices']['prices']['prices'] ?? null; if ($priceAggregation && $priceAggregation['doc_count'] > 0) { From 256ac07d04ddddc7d03ab0307bf63297b7a2f4ab Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 6 Dec 2021 09:10:39 +0100 Subject: [PATCH 056/142] feat: reindex product from admin actions --- .../ReindexProductEventSubscriber.php | 113 ++++++++++++++++++ src/Index/Indexer.php | 37 +++++- src/Message/ProductReindexFromIds.php | 34 ++++++ src/Message/ProductReindexFromTaxon.php | 31 +++++ .../ProductReindexFromIdsHandler.php | 47 ++++++++ .../ProductReindexFromTaxonHandler.php | 51 ++++++++ src/Resources/config/config.yaml | 1 + src/Resources/config/messenger.yaml | 8 ++ tests/Application/.env | 2 + 9 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 src/EventSubscriber/ReindexProductEventSubscriber.php create mode 100644 src/Message/ProductReindexFromIds.php create mode 100644 src/Message/ProductReindexFromTaxon.php create mode 100644 src/MessageHandler/ProductReindexFromIdsHandler.php create mode 100644 src/MessageHandler/ProductReindexFromTaxonHandler.php create mode 100644 src/Resources/config/messenger.yaml diff --git a/src/EventSubscriber/ReindexProductEventSubscriber.php b/src/EventSubscriber/ReindexProductEventSubscriber.php new file mode 100644 index 00000000..e49044c7 --- /dev/null +++ b/src/EventSubscriber/ReindexProductEventSubscriber.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\EventSubscriber; + +use Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Event\OnFlushEventArgs; +use Doctrine\ORM\Events; +use MonsieurBiz\SyliusSearchPlugin\Message\ProductReindexFromIds; +use MonsieurBiz\SyliusSearchPlugin\Message\ProductReindexFromTaxon; +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerAwareTrait; +use Sylius\Component\Core\Model\ChannelPricingInterface; +use Sylius\Component\Core\Model\ProductImageInterface; +use Sylius\Component\Core\Model\ProductInterface; +use Sylius\Component\Core\Model\ProductTaxonInterface; +use Sylius\Component\Core\Model\ProductTranslationInterface; +use Sylius\Component\Product\Model\ProductAttributeValueInterface; +use Sylius\Component\Product\Model\ProductVariantInterface; +use Sylius\Component\Product\Model\ProductVariantTranslationInterface; +use Symfony\Component\Messenger\MessageBusInterface; + +class ReindexProductEventSubscriber implements EventSubscriberInterface, LoggerAwareInterface +{ + use LoggerAwareTrait; + + private MessageBusInterface $messageBus; + + public function __construct(MessageBusInterface $messageBus) + { + $this->messageBus = $messageBus; + } + + public function getSubscribedEvents() + { + return [ + Events::onFlush => 'onFlush', + ]; + } + + public function onFlush(OnFlushEventArgs $eventArgs): void + { + $eventArgs->getEntityManager()->getEventManager()->removeEventListener(Events::onFlush, $this); + $productReindexFormIdsMessage = new ProductReindexFromIds(); + $unitOfWork = $eventArgs->getEntityManager()->getUnitOfWork(); + + $collections = array_merge($unitOfWork->getScheduledCollectionUpdates(), $unitOfWork->getScheduledCollectionDeletions()); + foreach ($collections as $collection) { + if ($collection->getOwner() instanceof ProductInterface) { + $productReindexFormIdsMessage->addProductId($collection->getOwner()->getId()); + } + } + + $entities = array_merge($unitOfWork->getScheduledEntityInsertions(), $unitOfWork->getScheduledEntityUpdates(), $unitOfWork->getScheduledEntityDeletions()); + foreach ($entities as $entity) { + if ($entity instanceof ProductTaxonInterface) { + $this->messageBus->dispatch(new ProductReindexFromTaxon($entity->getTaxon()->getId())); + continue; + } + $product = $this->getProduct($eventArgs->getEntityManager(), $entity); + if (null !== $product) { + $productReindexFormIdsMessage->addProductId($product->getId()); + } + } + + if (0 !== count($productReindexFormIdsMessage->getProductIds())) { + $this->logger->info('Schedule reindex for:'. implode(', ', $productReindexFormIdsMessage->getProductIds()), ['monsieurbiz.search']); + $this->messageBus->dispatch($productReindexFormIdsMessage); + } + + // todo delete : product + + // in other event subscriber ... + // todo reindex all data when: change/create/remove attribute/option, add/remove channel, add/remove locale + } + + private function getProduct(EntityManagerInterface $entityManager, $entity): ?\Sylius\Component\Product\Model\ProductInterface + { + $this->logger->info(\get_class($entity) . ': ' . implode(', ', class_implements($entity) ?? []), ['monsieurbiz.search']); + + switch (true) { + case $entity instanceof ProductInterface: + return $entity; + case $entity instanceof ProductVariantInterface: + return $entity->getProduct(); + case $entity instanceof ProductTaxonInterface: + return $entity->getProduct(); + case $entity instanceof ProductTranslationInterface && $entity->getTranslatable() instanceof ProductInterface: + return $entity->getTranslatable(); + case $entity instanceof ProductAttributeValueInterface: + return $entity->getProduct(); + case $entity instanceof ProductImageInterface && $entity->getOwner() instanceof ProductInterface: + return $entity->getOwner(); + case $entity instanceof ChannelPricingInterface && $entity->getProductVariant() instanceof ProductVariantInterface: + return $entity->getProductVariant()->getProduct(); + case $entity instanceof ProductVariantTranslationInterface && $entity->getTranslatable() instanceof ProductVariantInterface: + return $entity->getTranslatable()->getProduct(); + } + + return null; + } +} diff --git a/src/Index/Indexer.php b/src/Index/Indexer.php index d2507259..9557c712 100644 --- a/src/Index/Indexer.php +++ b/src/Index/Indexer.php @@ -18,14 +18,18 @@ use Jane\Component\AutoMapper\AutoMapperInterface; use JoliCode\Elastically\Factory; use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerAwareTrait; use Sylius\Component\Locale\Model\LocaleInterface; use Sylius\Component\Registry\ServiceRegistryInterface; use Sylius\Component\Resource\Model\TranslatableInterface; use Sylius\Component\Resource\Repository\RepositoryInterface; use Symfony\Component\Serializer\SerializerInterface; -final class Indexer +final class Indexer implements LoggerAwareInterface { + use LoggerAwareTrait; + private ServiceRegistryInterface $documentableRegistry; private RepositoryInterface $localeRepository; private array $locales = []; @@ -76,6 +80,37 @@ public function indexAll(): void } } + public function indexByDocuments(DocumentableInterface $documentable, array $documents, ?string $locale = null, ?\JoliCode\Elastically\Indexer $indexer = null): void + { + if (null === $indexer) { + $factory = new Factory([ + Factory::CONFIG_MAPPINGS_PROVIDER => $documentable->getMappingProvider(), + Factory::CONFIG_SERIALIZER => $this->serializer, + ]); + $indexer = $factory->buildIndexer(); + } + + if (null === $locale && $documentable->isTranslatable()) { + foreach ($this->getLocales() as $localeCode) { + $this->indexByDocuments($documentable, $documents, $localeCode, $indexer); + } + + $indexer->flush(); + $this->logger->info('flush', ['monsieurbiz.search"']); + + return; + } + $indexName = $this->getIndexName($documentable, $locale); + foreach ($documents as $document) { + if (null !== $locale && $document instanceof TranslatableInterface) { + $document->setCurrentLocale($locale); + } + $dto = $this->autoMapper->map($document, $documentable->getTargetClass()); + $indexer->scheduleIndex($indexName, new Document((string) $document->getId(), $dto)); + $this->logger->info('index: ' . $document->getId(), ['monsieurbiz.search"']); + } + } + private function indexDocumentable(DocumentableInterface $documentable, ?string $locale = null): void { if (null === $locale && $documentable->isTranslatable()) { diff --git a/src/Message/ProductReindexFromIds.php b/src/Message/ProductReindexFromIds.php new file mode 100644 index 00000000..78b5adb7 --- /dev/null +++ b/src/Message/ProductReindexFromIds.php @@ -0,0 +1,34 @@ +productIds = $productIds; + } + + public function getProductIds(): array + { + return array_unique($this->productIds); + } + + public function addProductId(int $productIds): void + { + $this->productIds[] = $productIds; + } +} diff --git a/src/Message/ProductReindexFromTaxon.php b/src/Message/ProductReindexFromTaxon.php new file mode 100644 index 00000000..c7667eaa --- /dev/null +++ b/src/Message/ProductReindexFromTaxon.php @@ -0,0 +1,31 @@ +taxonId = $taxonId; + } + + public function getTaxonId(): int + { + return $this->taxonId; + } +} diff --git a/src/MessageHandler/ProductReindexFromIdsHandler.php b/src/MessageHandler/ProductReindexFromIdsHandler.php new file mode 100644 index 00000000..86bcac16 --- /dev/null +++ b/src/MessageHandler/ProductReindexFromIdsHandler.php @@ -0,0 +1,47 @@ +productRepository = $productRepository; + $this->indexer = $indexer; + $this->documentableRegistry = $documentableRegistry; + } + + public function __invoke(ProductReindexFromIds $message) + { + $products = $this->productRepository->findBy(['id' => $message->getProductIds()]); + + $this->indexer->indexByDocuments( + $this->documentableRegistry->get('search.documentable.monsieurbiz_product'), + $products + ); + } +} diff --git a/src/MessageHandler/ProductReindexFromTaxonHandler.php b/src/MessageHandler/ProductReindexFromTaxonHandler.php new file mode 100644 index 00000000..fa5dfd5e --- /dev/null +++ b/src/MessageHandler/ProductReindexFromTaxonHandler.php @@ -0,0 +1,51 @@ +productRepository = $productRepository; + $this->indexer = $indexer; + $this->documentableRegistry = $documentableRegistry; + } + + public function __invoke(ProductReindexFromTaxon $message) + { + $products = $this->productRepository->createQueryBuilder('o') + ->innerJoin('o.productTaxons', 'productTaxon') + ->andWhere('productTaxon.taxon = :taxonId') + ->setParameter('taxonId', $message->getTaxonId())->getQuery()->getResult() + ; + + $this->indexer->indexByDocuments( + $this->documentableRegistry->get('search.documentable.monsieurbiz_product'), + $products + ); + } +} diff --git a/src/Resources/config/config.yaml b/src/Resources/config/config.yaml index 4c23edaf..e128d434 100644 --- a/src/Resources/config/config.yaml +++ b/src/Resources/config/config.yaml @@ -3,3 +3,4 @@ imports: - { resource: "monsieurbiz_search.yaml" } - { resource: "sylius/ui.yaml" } - { resource: "monsieurbiz/settings.yaml" } + - { resource: "messenger.yaml" } diff --git a/src/Resources/config/messenger.yaml b/src/Resources/config/messenger.yaml new file mode 100644 index 00000000..9d0d8b4c --- /dev/null +++ b/src/Resources/config/messenger.yaml @@ -0,0 +1,8 @@ +framework: + messenger: + transports: + async_search: '%env(MESSENGER_TRANSPORT_DSN)%' + + routing: + MonsieurBiz\SyliusSearchPlugin\Message\ProductReindexFromTaxon: async_search + MonsieurBiz\SyliusSearchPlugin\Message\ProductReindexFromIds: async_search diff --git a/tests/Application/.env b/tests/Application/.env index 0e8d7966..437d68f3 100644 --- a/tests/Application/.env +++ b/tests/Application/.env @@ -30,3 +30,5 @@ JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private-test.pem JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public-test.pem JWT_PASSPHRASE=ALL_THAT_IS_GOLD_DOES_NOT_GLITTER_NOT_ALL_THOSE_WHO_WANDER_ARE_LOST ###< lexik/jwt-authentication-bundle ### + +MESSENGER_TRANSPORT_DSN=doctrine://default From d4db59cbf52cd9c104cce6341de292da5cd924f5 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 6 Dec 2021 10:20:09 +0100 Subject: [PATCH 057/142] feat: add deletion documents method and dispatch an product document message from admin --- src/AutoMapper/ProductMapperConfiguration.php | 4 +- .../ReindexProductEventSubscriber.php | 74 ++++++++++++++----- src/Index/Indexer.php | 35 ++++++++- src/Message/ProductToDeleteFromIds.php | 34 +++++++++ .../ProductToDeleteFromIdsHandler.php | 47 ++++++++++++ 5 files changed, 173 insertions(+), 21 deletions(-) create mode 100644 src/Message/ProductToDeleteFromIds.php create mode 100644 src/MessageHandler/ProductToDeleteFromIdsHandler.php diff --git a/src/AutoMapper/ProductMapperConfiguration.php b/src/AutoMapper/ProductMapperConfiguration.php index 32c10b1b..16132d54 100644 --- a/src/AutoMapper/ProductMapperConfiguration.php +++ b/src/AutoMapper/ProductMapperConfiguration.php @@ -92,8 +92,8 @@ public function process(MapperGeneratorMetadataInterface $metadata): void return $images; }); - $metadata->forMember('mainTaxon', function(ProductInterface $product): Taxon { - return $this->autoMapper->map($product->getMainTaxon(), Taxon::class); + $metadata->forMember('mainTaxon', function(ProductInterface $product): ?Taxon { + return $product->getMainTaxon() ? $this->autoMapper->map($product->getMainTaxon(), Taxon::class) : null; }); $metadata->forMember('product_taxons', function(ProductInterface $product): array { diff --git a/src/EventSubscriber/ReindexProductEventSubscriber.php b/src/EventSubscriber/ReindexProductEventSubscriber.php index e49044c7..1fbaae15 100644 --- a/src/EventSubscriber/ReindexProductEventSubscriber.php +++ b/src/EventSubscriber/ReindexProductEventSubscriber.php @@ -14,11 +14,12 @@ namespace MonsieurBiz\SyliusSearchPlugin\EventSubscriber; use Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface; -use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Event\OnFlushEventArgs; +use Doctrine\ORM\Event\PostFlushEventArgs; use Doctrine\ORM\Events; use MonsieurBiz\SyliusSearchPlugin\Message\ProductReindexFromIds; use MonsieurBiz\SyliusSearchPlugin\Message\ProductReindexFromTaxon; +use MonsieurBiz\SyliusSearchPlugin\Message\ProductToDeleteFromIds; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareTrait; use Sylius\Component\Core\Model\ChannelPricingInterface; @@ -35,6 +36,11 @@ class ReindexProductEventSubscriber implements EventSubscriberInterface, LoggerA { use LoggerAwareTrait; + /** + * @var ProductInterface[] + */ + private array $productsToReindex = []; + private array $productsToBeDelete = []; private MessageBusInterface $messageBus; public function __construct(MessageBusInterface $messageBus) @@ -46,46 +52,80 @@ public function getSubscribedEvents() { return [ Events::onFlush => 'onFlush', + Events::postFlush => 'postFlush', ]; } public function onFlush(OnFlushEventArgs $eventArgs): void { $eventArgs->getEntityManager()->getEventManager()->removeEventListener(Events::onFlush, $this); - $productReindexFormIdsMessage = new ProductReindexFromIds(); $unitOfWork = $eventArgs->getEntityManager()->getUnitOfWork(); $collections = array_merge($unitOfWork->getScheduledCollectionUpdates(), $unitOfWork->getScheduledCollectionDeletions()); foreach ($collections as $collection) { if ($collection->getOwner() instanceof ProductInterface) { - $productReindexFormIdsMessage->addProductId($collection->getOwner()->getId()); + $this->productsToReindex[] = $collection->getOwner(); + } + } + + $entities = array_merge($unitOfWork->getScheduledEntityInsertions(), $unitOfWork->getScheduledEntityUpdates()); + $this->onFlushEntities($entities); + $this->onFlushEntities($unitOfWork->getScheduledEntityDeletions(), 'deletions'); + + if (0 !== \count($this->productsToBeDelete)) { + $productToDeleteMessage = new ProductToDeleteFromIds(); + array_map(function(ProductInterface $product) use ($productToDeleteMessage): void { + foreach ($this->productsToReindex as $key => $productsToReindex) { + if ($productsToReindex->getId() === $product->getId()) { + unset($this->productsToReindex[$key]); + } + } + $productToDeleteMessage->addProductId($product->getId()); + }, $this->productsToBeDelete); + $this->messageBus->dispatch($productToDeleteMessage); + } + + // in other event subscriber ... + // todo reindex all data when: change/create/remove attribute/option, add/remove channel, add/remove locale + } + + public function postFlush(PostFlushEventArgs $eventArgs): void + { + $productReindexFormIdsMessage = new ProductReindexFromIds(); + + foreach ($this->productsToReindex as $productsToReindex) { + if (null === $productsToReindex->getId()) { + continue; } + $productReindexFormIdsMessage->addProductId($productsToReindex->getId()); } + $this->productsToReindex = []; - $entities = array_merge($unitOfWork->getScheduledEntityInsertions(), $unitOfWork->getScheduledEntityUpdates(), $unitOfWork->getScheduledEntityDeletions()); + if (0 !== \count($productReindexFormIdsMessage->getProductIds())) { + $this->logger->info('Schedule reindex for: ' . implode(', ', $productReindexFormIdsMessage->getProductIds()), ['monsieurbiz.search']); + $this->messageBus->dispatch($productReindexFormIdsMessage); + } + } + + private function onFlushEntities(array $entities, string $type = 'insertionsOrUpdate'): void + { foreach ($entities as $entity) { + if ($entity instanceof ProductInterface && 'deletions' === $type) { + $this->productsToBeDelete[] = $entity; + continue; + } if ($entity instanceof ProductTaxonInterface) { $this->messageBus->dispatch(new ProductReindexFromTaxon($entity->getTaxon()->getId())); continue; } - $product = $this->getProduct($eventArgs->getEntityManager(), $entity); + $product = $this->getProduct($entity); if (null !== $product) { - $productReindexFormIdsMessage->addProductId($product->getId()); + $this->productsToReindex[] = $product; } } - - if (0 !== count($productReindexFormIdsMessage->getProductIds())) { - $this->logger->info('Schedule reindex for:'. implode(', ', $productReindexFormIdsMessage->getProductIds()), ['monsieurbiz.search']); - $this->messageBus->dispatch($productReindexFormIdsMessage); - } - - // todo delete : product - - // in other event subscriber ... - // todo reindex all data when: change/create/remove attribute/option, add/remove channel, add/remove locale } - private function getProduct(EntityManagerInterface $entityManager, $entity): ?\Sylius\Component\Product\Model\ProductInterface + private function getProduct($entity): ?\Sylius\Component\Product\Model\ProductInterface { $this->logger->info(\get_class($entity) . ': ' . implode(', ', class_implements($entity) ?? []), ['monsieurbiz.search']); diff --git a/src/Index/Indexer.php b/src/Index/Indexer.php index 9557c712..45e37a39 100644 --- a/src/Index/Indexer.php +++ b/src/Index/Indexer.php @@ -96,7 +96,7 @@ public function indexByDocuments(DocumentableInterface $documentable, array $doc } $indexer->flush(); - $this->logger->info('flush', ['monsieurbiz.search"']); + $this->logger->info('flush', ['monsieurbiz.search']); return; } @@ -107,7 +107,38 @@ public function indexByDocuments(DocumentableInterface $documentable, array $doc } $dto = $this->autoMapper->map($document, $documentable->getTargetClass()); $indexer->scheduleIndex($indexName, new Document((string) $document->getId(), $dto)); - $this->logger->info('index: ' . $document->getId(), ['monsieurbiz.search"']); + $this->logger->info('index - ' . $locale . ': ' . $document->getId(), ['monsieurbiz.search']); + } + } + + public function deleteByDocuments(DocumentableInterface $documentable, array $documents, ?string $locale = null, ?\JoliCode\Elastically\Indexer $indexer = null): void + { + if (null === $indexer) { + $factory = new Factory([ + Factory::CONFIG_MAPPINGS_PROVIDER => $documentable->getMappingProvider(), + Factory::CONFIG_SERIALIZER => $this->serializer, + ]); + $indexer = $factory->buildIndexer(); + } + + if (null === $locale && $documentable->isTranslatable()) { + foreach ($this->getLocales() as $localeCode) { + $this->deleteByDocuments($documentable, $documents, $localeCode, $indexer); + } + + $indexer->flush(); + $this->logger->info('flush', ['monsieurbiz.search']); + + return; + } + + $indexName = $this->getIndexName($documentable, $locale); + foreach ($documents as $document) { + if (null !== $locale && $document instanceof TranslatableInterface) { + $document->setCurrentLocale($locale); + } + $indexer->scheduleDelete($indexName, (string) $document->getId()); + $this->logger->info('delete - ' . $locale . ': ' . $document->getId(), ['monsieurbiz.search']); } } diff --git a/src/Message/ProductToDeleteFromIds.php b/src/Message/ProductToDeleteFromIds.php new file mode 100644 index 00000000..941463e2 --- /dev/null +++ b/src/Message/ProductToDeleteFromIds.php @@ -0,0 +1,34 @@ +productIds = $productIds; + } + + public function getProductIds(): array + { + return array_unique($this->productIds); + } + + public function addProductId(int $productIds): void + { + $this->productIds[] = $productIds; + } +} diff --git a/src/MessageHandler/ProductToDeleteFromIdsHandler.php b/src/MessageHandler/ProductToDeleteFromIdsHandler.php new file mode 100644 index 00000000..11382042 --- /dev/null +++ b/src/MessageHandler/ProductToDeleteFromIdsHandler.php @@ -0,0 +1,47 @@ +productRepository = $productRepository; + $this->indexer = $indexer; + $this->documentableRegistry = $documentableRegistry; + } + + public function __invoke(ProductToDeleteFromIds $message) + { + $products = $this->productRepository->findBy(['id' => $message->getProductIds()]); + + $this->indexer->deleteByDocuments( + $this->documentableRegistry->get('search.documentable.monsieurbiz_product'), + $products + ); + } +} From f67eb2d2ee8acb655f682e9eb7f0ad78f5002fa3 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 6 Dec 2021 14:51:17 +0100 Subject: [PATCH 058/142] fix: use the last version of jane elastically & require messenger --- composer.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 3e9bdf5d..1ee46911 100644 --- a/composer.json +++ b/composer.json @@ -9,9 +9,10 @@ "babdev/pagerfanta-bundle": "^2.5", "jacquesbh/eater": "^2.0", "jane-php/automapper-bundle": "^7.1", - "jolicode/elastically": "dev-master", + "jolicode/elastically": "^1.4.0", "monsieurbiz/sylius-settings-plugin": "^1.0", - "sylius/sylius": "^1.9" + "sylius/sylius": "^1.9", + "symfony/messenger": "^4.4 || ^5.2" }, "require-dev": { "behat/behat": "^3.6.1", From ced470f9b0818fe9a198053e97da6bbf7026dbbc Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 7 Dec 2021 11:00:36 +0100 Subject: [PATCH 059/142] refactor: add own client builder --- src/Index/Indexer.php | 29 ++++-------- src/Resources/config/services.yaml | 32 ++----------- src/Search/ClientFactory.php | 73 ++++++++++++++++++++++++++++++ src/Search/Search.php | 21 ++------- 4 files changed, 91 insertions(+), 64 deletions(-) create mode 100644 src/Search/ClientFactory.php diff --git a/src/Index/Indexer.php b/src/Index/Indexer.php index 45e37a39..04aa6318 100644 --- a/src/Index/Indexer.php +++ b/src/Index/Indexer.php @@ -18,6 +18,7 @@ use Jane\Component\AutoMapper\AutoMapperInterface; use JoliCode\Elastically\Factory; use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\ClientFactory; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareTrait; use Sylius\Component\Locale\Model\LocaleInterface; @@ -35,20 +36,20 @@ final class Indexer implements LoggerAwareInterface private array $locales = []; private EntityManagerInterface $entityManager; private AutoMapperInterface $autoMapper; - private SerializerInterface $serializer; + private ClientFactory $clientFactory; public function __construct( ServiceRegistryInterface $documentableRegistry, RepositoryInterface $localeRepository, EntityManagerInterface $entityManager, AutoMapperInterface $autoMapper, - SerializerInterface $serializer + ClientFactory $clientFactory ) { $this->documentableRegistry = $documentableRegistry; $this->localeRepository = $localeRepository; $this->entityManager = $entityManager; $this->autoMapper = $autoMapper; - $this->serializer = $serializer; + $this->clientFactory = $clientFactory; } /** @@ -83,11 +84,7 @@ public function indexAll(): void public function indexByDocuments(DocumentableInterface $documentable, array $documents, ?string $locale = null, ?\JoliCode\Elastically\Indexer $indexer = null): void { if (null === $indexer) { - $factory = new Factory([ - Factory::CONFIG_MAPPINGS_PROVIDER => $documentable->getMappingProvider(), - Factory::CONFIG_SERIALIZER => $this->serializer, - ]); - $indexer = $factory->buildIndexer(); + $indexer = $this->clientFactory->getIndexer($documentable, $locale); } if (null === $locale && $documentable->isTranslatable()) { @@ -114,11 +111,7 @@ public function indexByDocuments(DocumentableInterface $documentable, array $doc public function deleteByDocuments(DocumentableInterface $documentable, array $documents, ?string $locale = null, ?\JoliCode\Elastically\Indexer $indexer = null): void { if (null === $indexer) { - $factory = new Factory([ - Factory::CONFIG_MAPPINGS_PROVIDER => $documentable->getMappingProvider(), - Factory::CONFIG_SERIALIZER => $this->serializer, - ]); - $indexer = $factory->buildIndexer(); + $indexer = $this->clientFactory->getIndexer($documentable, $locale); } if (null === $locale && $documentable->isTranslatable()) { @@ -151,18 +144,14 @@ private function indexDocumentable(DocumentableInterface $documentable, ?string return; } - $indexName = $this->getIndexName($documentable, $locale); - $factory = new Factory([ - Factory::CONFIG_MAPPINGS_PROVIDER => $documentable->getMappingProvider(), - Factory::CONFIG_SERIALIZER => $this->serializer, - ]); - $indexBuilder = $factory->buildIndexBuilder(); + $indexName = $this->clientFactory->getIndexName($documentable, $locale); + $indexBuilder = $this->clientFactory->getIndexBuilder($documentable, $locale); $newIndex = $indexBuilder->createIndex($indexName, [ 'index_code' => $documentable->getIndexCode(), 'locale' => $locale ? strtolower($locale) : null, ]); - $indexer = $factory->buildIndexer(); + $indexer = $this->clientFactory->getIndexer($documentable, $locale); foreach ($documentable->getDatasource()->getItems($documentable->getSourceClass()) as $item) { if (null !== $locale && $item instanceof TranslatableInterface) { $item->setCurrentLocale($locale); diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index c23a715b..f00a2955 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -28,49 +28,25 @@ services: MonsieurBiz\SyliusSearchPlugin\Generated\: resource: '../../../generated' - JoliCode\Elastically\IndexNameMapper: - arguments: - $prefix: null # or a string to prefix index name - $indexClassMapping: {} - - JoliCode\Elastically\Serializer\StaticContextBuilder: - arguments: - $mapping: {} - - JoliCode\Elastically\ResultSetBuilder: - arguments: - $indexNameMapper: '@JoliCode\Elastically\IndexNameMapper' - $contextBuilder: '@JoliCode\Elastically\Serializer\StaticContextBuilder' - $denormalizer: '@serializer' - # ES Client configuration - monsieurbiz.search.elasticsearch.client: - class: JoliCode\Elastically\Client + MonsieurBiz\SyliusSearchPlugin\Search\ClientFactory: arguments: $config: host: '%env(ELASTICSEARCH_HOST)%' - $logger: '@logger' - $resultSetBuilder: '@JoliCode\Elastically\ResultSetBuilder' - $indexNameMapper: '@JoliCode\Elastically\IndexNameMapper' - - JoliCode\Elastically\Mapping\YamlProvider: ~ + port: '%env(ELASTICSEARCH_PORT)%' MonsieurBiz\SyliusSearchPlugin\Repository\ProductAttributeRepository: arguments: $attributeRepository: '@sylius.repository.product_attribute' # Define our mapping provider + JoliCode\Elastically\Mapping\YamlProvider: ~ MonsieurBiz\SyliusSearchPlugin\Mapping\YamlWithLocaleProvider: decorates: JoliCode\Elastically\Mapping\YamlProvider arguments: $decorated: '@.inner' - # Replace the mapping provider by our mapping provider - JoliCode\Elastically\IndexBuilder: - arguments: - $client: '@monsieurbiz.search.elasticsearch.client' - $mappingProvider: '@MonsieurBiz\SyliusSearchPlugin\Mapping\YamlWithLocaleProvider' - + # Defines our registries monsieurbiz.search.registry.documentable: class: Sylius\Component\Registry\ServiceRegistry arguments: diff --git a/src/Search/ClientFactory.php b/src/Search/ClientFactory.php new file mode 100644 index 00000000..dffe8c06 --- /dev/null +++ b/src/Search/ClientFactory.php @@ -0,0 +1,73 @@ +config = $config; + $this->serializer = $serializer; + } + + public function getClient(DocumentableInterface $documentable, ?string $localeCode = null): Client + { + $factory = new Factory($this->getConfig($documentable, $localeCode)); + + return $factory->buildClient(); + } + + public function getIndexBuilder(DocumentableInterface $documentable, ?string $localeCode = null): IndexBuilder + { + $factory = new Factory($this->getConfig($documentable, $localeCode)); + + return $factory->buildIndexBuilder(); + } + + public function getIndexer(DocumentableInterface $documentable, ?string $localeCode = null): Indexer + { + $factory = new Factory($this->getConfig($documentable, $localeCode)); + + return $factory->buildIndexer(); + } + + public function getIndexName(DocumentableInterface $documentable, ?string $locale): string + { + return $documentable->getIndexCode() . strtolower(null !== $locale ? '_' . $locale : ''); + } + + private function getConfig(DocumentableInterface $documentable, ?string $localeCode): array + { + $indexName = $this->getIndexName($documentable, $localeCode); + $additionalConfig = [ + Factory::CONFIG_INDEX_CLASS_MAPPING => [ + $indexName => $documentable->getTargetClass(), + ], + Factory::CONFIG_MAPPINGS_PROVIDER => $documentable->getMappingProvider(), + Factory::CONFIG_SERIALIZER => $this->serializer, + ]; + + return array_merge($this->config, $additionalConfig); + } +} diff --git a/src/Search/Search.php b/src/Search/Search.php index 195b43ff..73d25a11 100644 --- a/src/Search/Search.php +++ b/src/Search/Search.php @@ -24,15 +24,15 @@ class Search implements SearchInterface { - private SerializerInterface $serializer; private LocaleContextInterface $localeContext; private RequestHandler $requestHandler; + private ClientFactory $clientFactory; - public function __construct(SerializerInterface $serializer, LocaleContextInterface $localeContext, RequestHandler $requestHandler) + public function __construct(ClientFactory $clientFactory, LocaleContextInterface $localeContext, RequestHandler $requestHandler) { - $this->serializer = $serializer; $this->localeContext = $localeContext; $this->requestHandler = $requestHandler; + $this->clientFactory = $clientFactory; } /** @@ -42,14 +42,8 @@ public function search(RequestConfiguration $requestConfiguration): ResponseInte { $request = $this->requestHandler->getRequest($requestConfiguration); - $indexName = $this->getIndexName($request->getDocumentable(), $this->localeContext->getLocaleCode()); - $factory = new Factory([ - Factory::CONFIG_INDEX_CLASS_MAPPING => [ - $indexName => $request->getDocumentable()->getTargetClass(), - ], - Factory::CONFIG_SERIALIZER => $this->serializer, - ]); - $client = $factory->buildClient(); + $indexName = $this->clientFactory->getIndexName($request->getDocumentable(), $this->localeContext->getLocaleCode()); + $client = $this->clientFactory->getClient($request->getDocumentable(), $this->localeContext->getLocaleCode()); return new Response( $requestConfiguration, @@ -57,9 +51,4 @@ public function search(RequestConfiguration $requestConfiguration): ResponseInte $request->getDocumentable() ); } - - private function getIndexName(DocumentableInterface $documentable, ?string $locale = null): string - { - return $documentable->getIndexCode() . strtolower((null !== $locale && $documentable->isTranslatable()) ? '_' . $locale : ''); - } } From 78c923050a2e3183360c9202a1e2a1d0611039cc Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 7 Dec 2021 11:03:50 +0100 Subject: [PATCH 060/142] fix: prevent locale code use only 2 characters --- src/Mapping/YamlWithLocaleProvider.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mapping/YamlWithLocaleProvider.php b/src/Mapping/YamlWithLocaleProvider.php index 97cf2148..6a72a920 100644 --- a/src/Mapping/YamlWithLocaleProvider.php +++ b/src/Mapping/YamlWithLocaleProvider.php @@ -78,9 +78,9 @@ private function appendLocaleAnalyzers(array $mapping, string $locale): array private function getLocaleCode(string $locale): array { - return [ + return array_unique([ current(explode('_', $locale)), $locale, - ]; + ]); } } From be676c73ad7f173449bda7cfe756442852265e64 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 7 Dec 2021 11:14:33 +0100 Subject: [PATCH 061/142] fix: use custom ES env variables --- src/Resources/config/services.yaml | 4 ++-- tests/Application/.env | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index f00a2955..d4bd0f27 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -32,8 +32,8 @@ services: MonsieurBiz\SyliusSearchPlugin\Search\ClientFactory: arguments: $config: - host: '%env(ELASTICSEARCH_HOST)%' - port: '%env(ELASTICSEARCH_PORT)%' + host: '%env(MONSIEURBIZ_SEARCHPLUGIN_ES_HOST)%' + port: '%env(MONSIEURBIZ_SEARCHPLUGIN_ES_PORT)%' MonsieurBiz\SyliusSearchPlugin\Repository\ProductAttributeRepository: arguments: diff --git a/tests/Application/.env b/tests/Application/.env index 437d68f3..6bf99ef4 100644 --- a/tests/Application/.env +++ b/tests/Application/.env @@ -31,4 +31,6 @@ JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public-test.pem JWT_PASSPHRASE=ALL_THAT_IS_GOLD_DOES_NOT_GLITTER_NOT_ALL_THOSE_WHO_WANDER_ARE_LOST ###< lexik/jwt-authentication-bundle ### -MESSENGER_TRANSPORT_DSN=doctrine://default +MONSIEURBIZ_SEARCHPLUGIN_MESSENGER_TRANSPORT_DSN=doctrine://default +MONSIEURBIZ_SEARCHPLUGIN_ES_HOST=${ELASTICSEARCH_HOST:-localhost} +MONSIEURBIZ_SEARCHPLUGIN_ES_PORT=${ELASTICSEARCH_PORT:-9200} From 50e2201f38f8999838d3fa239b41511c77133e3e Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 7 Dec 2021 11:24:10 +0100 Subject: [PATCH 062/142] fix: allows to build the aggregation of attributes and options only if at least one entity is filterable --- .../Aggregation/AggregationBuilderInterface.php | 4 +++- .../Request/Aggregation/MainTaxonAggregation.php | 2 +- src/Search/Request/Aggregation/PriceAggregation.php | 2 +- .../Aggregation/ProductAttributeAggregation.php | 2 +- .../Aggregation/ProductAttributesAggregation.php | 4 ++-- .../Request/Aggregation/ProductOptionAggregation.php | 2 +- .../Aggregation/ProductOptionsAggregation.php | 4 ++-- src/Search/Request/Aggregation/TaxonsAggregation.php | 2 +- src/Search/Request/AggregationBuilder.php | 12 +++++++++--- 9 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/Search/Request/Aggregation/AggregationBuilderInterface.php b/src/Search/Request/Aggregation/AggregationBuilderInterface.php index a45d9dc2..99753b6e 100644 --- a/src/Search/Request/Aggregation/AggregationBuilderInterface.php +++ b/src/Search/Request/Aggregation/AggregationBuilderInterface.php @@ -19,6 +19,8 @@ interface AggregationBuilderInterface { /** * @param string|array $aggregation + * + * @return AbstractAggregation|bool|null */ - public function build($aggregation, array $filters): ?AbstractAggregation; + public function build($aggregation, array $filters); } diff --git a/src/Search/Request/Aggregation/MainTaxonAggregation.php b/src/Search/Request/Aggregation/MainTaxonAggregation.php index 47a933d3..979bc47a 100644 --- a/src/Search/Request/Aggregation/MainTaxonAggregation.php +++ b/src/Search/Request/Aggregation/MainTaxonAggregation.php @@ -17,7 +17,7 @@ class MainTaxonAggregation implements AggregationBuilderInterface { - public function build($aggregation, array $filters): ?AbstractAggregation + public function build($aggregation, array $filters) { if (!$this->isSupported($aggregation)) { return null; diff --git a/src/Search/Request/Aggregation/PriceAggregation.php b/src/Search/Request/Aggregation/PriceAggregation.php index 1a694834..16d8e652 100644 --- a/src/Search/Request/Aggregation/PriceAggregation.php +++ b/src/Search/Request/Aggregation/PriceAggregation.php @@ -25,7 +25,7 @@ public function __construct(ChannelContextInterface $channelContext) $this->channelContext = $channelContext; } - public function build($aggregation, array $filters): ?AbstractAggregation + public function build($aggregation, array $filters) { if (!$this->isSupported($aggregation)) { return null; diff --git a/src/Search/Request/Aggregation/ProductAttributeAggregation.php b/src/Search/Request/Aggregation/ProductAttributeAggregation.php index 2303cf48..326f01e5 100644 --- a/src/Search/Request/Aggregation/ProductAttributeAggregation.php +++ b/src/Search/Request/Aggregation/ProductAttributeAggregation.php @@ -19,7 +19,7 @@ class ProductAttributeAggregation implements AggregationBuilderInterface { - public function build($aggregation, array $filters): ?AbstractAggregation + public function build($aggregation, array $filters) { /** @var ProductAttributeInterface&SearchableInterface $aggregation */ if (!$this->isSupport($aggregation) || !$aggregation->isFilterable()) { diff --git a/src/Search/Request/Aggregation/ProductAttributesAggregation.php b/src/Search/Request/Aggregation/ProductAttributesAggregation.php index 2d20ccdd..f32d1b12 100644 --- a/src/Search/Request/Aggregation/ProductAttributesAggregation.php +++ b/src/Search/Request/Aggregation/ProductAttributesAggregation.php @@ -23,7 +23,7 @@ public function __construct() $this->productAttributeAggregationBuilder = new ProductAttributeAggregation(); } - public function build($aggregation, array $filters): ?AbstractAggregation + public function build($aggregation, array $filters) { if (!$this->isSupport($aggregation)) { return null; @@ -49,7 +49,7 @@ public function build($aggregation, array $filters): ?AbstractAggregation } if (0 == \count($attributesAggregation->getAggs())) { - return null; + return false; } return $qb->aggregation()->filter('attributes') diff --git a/src/Search/Request/Aggregation/ProductOptionAggregation.php b/src/Search/Request/Aggregation/ProductOptionAggregation.php index 8a63463e..f7fd14ce 100644 --- a/src/Search/Request/Aggregation/ProductOptionAggregation.php +++ b/src/Search/Request/Aggregation/ProductOptionAggregation.php @@ -20,7 +20,7 @@ class ProductOptionAggregation implements AggregationBuilderInterface { - public function build($aggregation, array $filters): ?AbstractAggregation + public function build($aggregation, array $filters) { /** @var ProductOptionInterface&SearchableInterface $aggregation */ if (!$this->isSupport($aggregation) || !$aggregation->isFilterable()) { diff --git a/src/Search/Request/Aggregation/ProductOptionsAggregation.php b/src/Search/Request/Aggregation/ProductOptionsAggregation.php index 2f3134a0..12223bc6 100644 --- a/src/Search/Request/Aggregation/ProductOptionsAggregation.php +++ b/src/Search/Request/Aggregation/ProductOptionsAggregation.php @@ -25,7 +25,7 @@ public function __construct(ProductOptionAggregation $productOptionAggregationBu $this->productOptionAggregationBuilder = $productOptionAggregationBuilder; } - public function build($aggregation, array $filters): ?AbstractAggregation + public function build($aggregation, array $filters) { if (!$this->isSupport($aggregation)) { return null; @@ -51,7 +51,7 @@ public function build($aggregation, array $filters): ?AbstractAggregation } if (0 == \count($optionsAggregation->getAggs())) { - return null; + return false; } return $qb->aggregation()->filter('options') diff --git a/src/Search/Request/Aggregation/TaxonsAggregation.php b/src/Search/Request/Aggregation/TaxonsAggregation.php index ac38631d..9e913ae0 100644 --- a/src/Search/Request/Aggregation/TaxonsAggregation.php +++ b/src/Search/Request/Aggregation/TaxonsAggregation.php @@ -18,7 +18,7 @@ class TaxonsAggregation implements AggregationBuilderInterface { - public function build($aggregation, array $filters): ?AbstractAggregation + public function build($aggregation, array $filters) { if (!$this->isSupported($aggregation)) { return null; diff --git a/src/Search/Request/AggregationBuilder.php b/src/Search/Request/AggregationBuilder.php index 5f5f0073..f74090b3 100644 --- a/src/Search/Request/AggregationBuilder.php +++ b/src/Search/Request/AggregationBuilder.php @@ -33,7 +33,11 @@ public function buildAggregations(array $aggregations, array $filters): array $buckets = []; foreach ($aggregations as $aggregation) { - $buckets[] = $this->buildAggregation($aggregation, $filters); + $aggregationQuery = $this->buildAggregation($aggregation, $filters); + if (false === $aggregationQuery) { + continue; + } + $buckets[] = $aggregationQuery; } return array_filter($buckets); @@ -41,8 +45,10 @@ public function buildAggregations(array $aggregations, array $filters): array /** * @param string|array $aggregation + * + * @return AbstractAggregation|bool|null */ - private function buildAggregation($aggregation, array $filters): AbstractAggregation + private function buildAggregation($aggregation, array $filters) { foreach ($this->aggregationBuilders as $aggregationBuilder) { $aggregationQuery = $aggregationBuilder->build($aggregation, $filters); @@ -51,6 +57,6 @@ private function buildAggregation($aggregation, array $filters): AbstractAggrega } } - throw new \RuntimeException('Aggregation can be build'); + throw new \RuntimeException('Aggregation can be build'); // it's throw an exception if we have not filtreable attribute } } From e59f6906406188164593ff23e08c4722b8cfac38 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Wed, 8 Dec 2021 11:36:53 +0100 Subject: [PATCH 063/142] feat: add applied value in filter --- .../views/Search/Filter/default.html.twig | 12 +-- .../views/Search/Filter/range.html.twig | 29 +++---- src/Search/Filter/Filter.php | 28 ++++++- src/Search/Filter/FilterValue.php | 22 +++-- src/Search/Filter/RangeFilter.php | 81 +++++++++++-------- src/Search/Response.php | 7 +- 6 files changed, 103 insertions(+), 76 deletions(-) diff --git a/src/Resources/views/Search/Filter/default.html.twig b/src/Resources/views/Search/Filter/default.html.twig index f212d94a..33222ab4 100644 --- a/src/Resources/views/Search/Filter/default.html.twig +++ b/src/Resources/views/Search/Filter/default.html.twig @@ -1,22 +1,14 @@ {% set inputName = filter.code %} -{% set filterName = filter.code %} {% if filter.type is not empty %} {% set inputName = filter.type ~ '[' ~ inputName ~ ']' %} - {% set filterName = filter.type %} {% endif %} -{% set appliedAttributes = requestConfiguration.appliedFilters(filterName) %} -{% set currentValues = appliedAttributes ?? [] %} -{% if filter.type is not empty %} - {% set currentValues = (appliedAttributes[filter.code] is defined) ? appliedAttributes[filter.code] : [] %} -{% endif %} -{% if filter.values|length or currentValues is not empty %} +{% if filter.values|length %}
{{ filter.label | trans }}
{% for value in filter.values %} - {% set valueIsApplied = (value.slug in currentValues) %}
diff --git a/src/Resources/views/Search/Filter/range.html.twig b/src/Resources/views/Search/Filter/range.html.twig index b067ef63..a4f7e287 100644 --- a/src/Resources/views/Search/Filter/range.html.twig +++ b/src/Resources/views/Search/Filter/range.html.twig @@ -1,27 +1,20 @@
- {% set currentValue = requestConfiguration.appliedFilters(filter.code) %} - {% set minValue = (currentValue['min'] is defined) ? currentValue['min'] : '' %} - {% set maxValue = (currentValue['max'] is defined) ? currentValue['max'] : '' %} -
{{ filter.label | trans }}
-
- -
-
{{ currencySymbol }}
- -
-
- -
- -
-
{{ currencySymbol }}
- + {% for value in filter.values %} + {% set valueType = filter.valueType(value.label) %} + {% set inputValue = value.isApplied ? value.value : '' %} + {% set placeholderValue = value.isApplied ? filter.defaultValue(valueType) : value.value %} +
+ +
+
{{ currencySymbol }}
+ +
-
+ {% endfor %}
diff --git a/src/Search/Filter/Filter.php b/src/Search/Filter/Filter.php index f9ca98bc..8c664290 100644 --- a/src/Search/Filter/Filter.php +++ b/src/Search/Filter/Filter.php @@ -13,6 +13,8 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Filter; +use MonsieurBiz\SyliusSearchPlugin\Helper\SlugHelper; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterInterface; class Filter implements FilterInterface @@ -38,16 +40,18 @@ class Filter implements FilterInterface private $count; private string $type; + private RequestConfiguration $requestConfiguration; /** * Filter constructor. */ - public function __construct(string $code, string $label, int $count, string $type = '') + public function __construct(RequestConfiguration $requestConfiguration, string $code, string $label, int $count, string $type = '') { $this->code = $code; $this->label = $label; $this->count = $count; $this->type = $type; + $this->requestConfiguration = $requestConfiguration; } /** @@ -76,7 +80,12 @@ public function getValues(): array public function addValue(string $label, int $count, ?string $value = null): void { - $this->values[] = new FilterValue($label, $count, $value); + $this->values[] = new FilterValue( + $label, + $count, + $value, + in_array(SlugHelper::toSlug($value ?? $label), $this->getCurrentValues()) + ); } /** @@ -102,4 +111,19 @@ public function setType(string $type): void { $this->type = $type; } + + public function getAppliedValues(): array + { + return array_filter($this->getValues(), function(FilterValue $filterValue): bool { + return $filterValue->isApplied(); + }); + } + + protected function getCurrentValues(): array + { + $appliedFilters = $this->requestConfiguration->getAppliedFilters(); + $appliedFilters = $appliedFilters[$this->getType()] ?? $appliedFilters[$this->getCode()]; + + return $appliedFilters[$this->getCode()] ?? $appliedFilters; + } } diff --git a/src/Search/Filter/FilterValue.php b/src/Search/Filter/FilterValue.php index 27e21c84..aefbb8fe 100644 --- a/src/Search/Filter/FilterValue.php +++ b/src/Search/Filter/FilterValue.php @@ -17,11 +17,6 @@ class FilterValue { - /** - * @var string - */ - private $slug; - /** * @var string */ @@ -33,6 +28,7 @@ class FilterValue private $count; private string $value; + private bool $isApplied; /** * Filter constructor. @@ -40,12 +36,12 @@ class FilterValue * @param string $label * @param int $count */ - public function __construct(string $label, int $count, string $value = null) + public function __construct(string $label, int $count, string $value = null, bool $isApplied = false) { $this->value = $value ?? $label; - $this->slug = SlugHelper::toSlug($this->value); $this->label = $label; $this->count = $count; + $this->isApplied = $isApplied; } /** @@ -53,7 +49,7 @@ public function __construct(string $label, int $count, string $value = null) */ public function getSlug(): string { - return $this->slug; + return SlugHelper::toSlug($this->value); } /** @@ -76,4 +72,14 @@ public function getValue(): string { return $this->value; } + + public function setValue($value): void + { + $this->value = $value; + } + + public function isApplied(): bool + { + return $this->isApplied; + } } diff --git a/src/Search/Filter/RangeFilter.php b/src/Search/Filter/RangeFilter.php index 94700b97..75179d84 100644 --- a/src/Search/Filter/RangeFilter.php +++ b/src/Search/Filter/RangeFilter.php @@ -13,6 +13,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Filter; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterInterface; class RangeFilter implements FilterInterface @@ -32,11 +33,6 @@ class RangeFilter implements FilterInterface */ private $minLabel; - /** - * @var string - */ - private $maxLabel; - /** * @var int */ @@ -47,24 +43,23 @@ class RangeFilter implements FilterInterface */ private $max; + private array $values = []; + private RequestConfiguration $requestConfiguration; + /** * Filter constructor. - * - * @param string $code - * @param string $label - * @param string $minLabel - * @param string $maxLabel - * @param int $min - * @param int $max */ - public function __construct(string $code, string $label, string $minLabel, string $maxLabel, int $min, int $max) + public function __construct(RequestConfiguration $requestConfiguration, string $code, string $label, string $minLabel, string $maxLabel, int $min, int $max) { + $this->requestConfiguration = $requestConfiguration; $this->code = $code; $this->label = $label; $this->minLabel = $minLabel; - $this->maxLabel = $maxLabel; $this->min = $min; $this->max = $max; + + $this->addValue($minLabel, 0, (string) $min); + $this->addValue($maxLabel, 0, (string) $max); } /** @@ -83,40 +78,56 @@ public function getLabel(): string return $this->label; } - /** - * @return string - */ - public function getMinLabel(): string + public function addValue(string $label, int $count, ?string $value = null): void { - return $this->minLabel; + $currentValueType = $this->getValueType($label); + $currentValues = $this->getCurrentValues(); + $isApplied = \array_key_exists($currentValueType, $currentValues); + $value = $isApplied ? $currentValues[$currentValueType] : $value; + + $this->values[] = new FilterValue($label, $count, $value, $isApplied); } - /** - * @return string - */ - public function getMaxLabel(): string + public function getValues(): array { - return $this->maxLabel; + return $this->values; } - /** - * @return int - */ - public function getMin(): int + public function getType(): string { - return $this->min; + return 'range'; } - /** - * @return int - */ - public function getMax(): int + public function getAppliedValues(): array { + return array_filter($this->values, function(FilterValue $filterValue): bool { + return $filterValue->isApplied(); + }); + } + + public function getDefaultValue(string $type): int + { + if ('min' == $type) { + return $this->min; + } + return $this->max; } - public function getType(): string + public function getValueType($valueLabel): string { - return 'range'; + if ($valueLabel == $this->minLabel) { + return 'min'; + } + + return 'max'; + } + + protected function getCurrentValues(): array + { + $appliedFilters = $this->requestConfiguration->getAppliedFilters(); + $appliedFilters = $appliedFilters[$this->getType()] ?? $appliedFilters[$this->getCode()]; + + return $appliedFilters[$this->getCode()] ?? $appliedFilters; } } diff --git a/src/Search/Response.php b/src/Search/Response.php index d11b42d1..ace639eb 100644 --- a/src/Search/Response.php +++ b/src/Search/Response.php @@ -81,7 +81,7 @@ private function buildFilters(): void // todo main taxon $taxonAggregation = $aggregations['main_taxon']['main_taxon'] ?? null; if ($taxonAggregation && $taxonAggregation['doc_count'] > 0) { - $filter = new Filter('main_taxon', 'monsieurbiz_searchplugin.filters.taxon_filter', $taxonAggregation['doc_count'], 'taxon'); + $filter = new Filter($this->requestConfiguration, 'main_taxon', 'monsieurbiz_searchplugin.filters.taxon_filter', $taxonAggregation['doc_count'], 'taxon'); // Get main taxon code in aggregation $taxonCodeBuckets = $taxonAggregation['codes']['buckets'] ?? []; @@ -114,7 +114,7 @@ private function buildFilters(): void // todo taxons $taxonAggregation = $aggregations['taxons']['taxons']['taxons']['taxons'] ?? null; if ($taxonAggregation && $taxonAggregation['doc_count'] > 0) { - $filter = new Filter('taxons', 'monsieurbiz_searchplugin.filters.taxon_filter', $taxonAggregation['doc_count']); + $filter = new Filter($this->requestConfiguration, 'taxons', 'monsieurbiz_searchplugin.filters.taxon_filter', $taxonAggregation['doc_count']); // Get main taxon code in aggregation $taxonCodeBuckets = $taxonAggregation['codes']['buckets'] ?? []; @@ -140,6 +140,7 @@ private function buildFilters(): void $priceAggregation = $aggregations['prices']['prices']['prices'] ?? null; if ($priceAggregation && $priceAggregation['doc_count'] > 0) { $this->filters[] = new RangeFilter( + $this->requestConfiguration, 'price', 'monsieurbiz_searchplugin.filters.price_filter', 'monsieurbiz_searchplugin.filters.price_min', @@ -161,7 +162,7 @@ private function buildFilters(): void $attributeNameBuckets = $attributeAggregation['names']['buckets'] ?? []; foreach ($attributeNameBuckets as $attributeNameBucket) { $attributeValueBuckets = $attributeNameBucket['values']['buckets'] ?? []; - $filter = new Filter($attributeCode, $attributeNameBucket['key'], $attributeNameBucket['doc_count'], $aggregationType); + $filter = new Filter($this->requestConfiguration, $attributeCode, $attributeNameBucket['key'], $attributeNameBucket['doc_count'], $aggregationType); foreach ($attributeValueBuckets as $attributeValueBucket) { if (0 === $attributeValueBucket['doc_count']) { continue; From 550ef05adb9c7f49bd9f22141e730b59c06d281f Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Fri, 10 Dec 2021 17:58:37 +0100 Subject: [PATCH 064/142] fix: url decode the query in template --- src/Controller/SearchController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php index 14a2dbb1..b73a4b64 100644 --- a/src/Controller/SearchController.php +++ b/src/Controller/SearchController.php @@ -74,7 +74,7 @@ public function searchAction(Request $request, string $query): Response return $this->render('@MonsieurBizSyliusSearchPlugin/Search/result.html.twig', [ 'documentable' => $result->getDocumentable(), 'requestConfiguration' => $requestConfiguration, - 'query' => $query, + 'query' => urldecode($query), 'result' => $result, 'currencySymbol' => Currencies::getSymbol($this->currencyContext->getCurrencyCode(), $this->localeContext->getLocaleCode()), ]); From 51757024611606a2b23b337370fbfc7f8ac4f1ce Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 13 Dec 2021 10:11:03 +0100 Subject: [PATCH 065/142] feat: change input search type --- src/Form/Type/SearchType.php | 3 ++- src/Resources/views/_scripts.html.twig | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Form/Type/SearchType.php b/src/Form/Type/SearchType.php index f6ab7d31..cb286c29 100644 --- a/src/Form/Type/SearchType.php +++ b/src/Form/Type/SearchType.php @@ -14,6 +14,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Form\Type; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\SearchType as SymfonySearchType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; @@ -29,7 +30,7 @@ class SearchType extends AbstractType public function buildForm(FormBuilderInterface $builder, array $options): void { $builder - ->add('query', TextType::class, [ + ->add('query', SymfonySearchType::class, [ 'required' => true, 'label' => 'monsieurbiz_searchplugin.form.query', 'attr' => [ diff --git a/src/Resources/views/_scripts.html.twig b/src/Resources/views/_scripts.html.twig index 85a70a81..ed1d3b06 100644 --- a/src/Resources/views/_scripts.html.twig +++ b/src/Resources/views/_scripts.html.twig @@ -2,7 +2,7 @@ var monsieurbizSearchPlugin = { instantEnabled: true, instantUrl: '{{ path('monsieurbiz_search_instant') | escape('js') }}', // URL for instant search - searchInputSelector: '.autocomplete-search input[type=text]', // Selector for search input text field + searchInputSelector: '.autocomplete-search input[type=search]', // Selector for search input text field resultClosestSelector: '.autocomplete-search', // Selector used to display results, take the closest parent of searchInputSelector resultFindSelector: '.autocomplete-results', // Selector used to display results, take the closest parent of searchInputSelector keyUpTimeOut: 500, // Time in millisecond to wait to be sure the user ended typing From 1406a268d089c70db5bb8023f76a9d1ad09677ff Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 13 Dec 2021 10:11:54 +0100 Subject: [PATCH 066/142] feat: add taxon limits configuration --- src/DependencyInjection/Configuration.php | 7 ++++++- src/Resources/config/monsieurbiz_search.yaml | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 1bcfb35a..f57d8f5a 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -55,11 +55,16 @@ public function getConfigTreeBuilder(): TreeBuilder ->integerPrototype()->end() ->defaultValue([9, 18, 27]) ->end() - ->arrayNode('instant_search') + ->arrayNode('taxon') ->performNoDeepMerging() ->integerPrototype()->end() ->defaultValue([9, 18, 27]) ->end() + ->arrayNode('instant_search') + ->performNoDeepMerging() + ->integerPrototype()->end() + ->defaultValue([10]) + ->end() ->end() ->end() ->end() diff --git a/src/Resources/config/monsieurbiz_search.yaml b/src/Resources/config/monsieurbiz_search.yaml index 1039a6f1..315500d3 100644 --- a/src/Resources/config/monsieurbiz_search.yaml +++ b/src/Resources/config/monsieurbiz_search.yaml @@ -4,6 +4,7 @@ monsieurbiz_sylius_search: instant_search_enabled: true # by default false limits: search: [9, 18, 27] + taxon: [9, 18, 27] instant_search: [5] source: 'Sylius\Component\Core\Model\ProductInterface' target: 'MonsieurBiz\SyliusSearchPlugin\Model\Product\ProductDTO' From 54ef88fdd70a42d513924a947b4882c35c4379f9 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 13 Dec 2021 16:38:23 +0100 Subject: [PATCH 067/142] fix: normalize product prices --- src/Normalizer/Product/ProductDTONormalizer.php | 10 ++++++++++ src/Resources/views/Search/product/_box.html.twig | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Normalizer/Product/ProductDTONormalizer.php b/src/Normalizer/Product/ProductDTONormalizer.php index cab8e59c..2e0be5f4 100644 --- a/src/Normalizer/Product/ProductDTONormalizer.php +++ b/src/Normalizer/Product/ProductDTONormalizer.php @@ -15,6 +15,7 @@ use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Channel; use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Image; +use MonsieurBiz\SyliusSearchPlugin\Generated\Model\PricingDTO; use MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductAttribute; use MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductTaxon; use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Taxon; @@ -78,6 +79,15 @@ public function denormalize($data, string $type, string $format = null, array $c unset($data['channels']); } + if (\array_key_exists('prices', $data)) { + $values = []; + foreach ($data['prices'] as $key => $value) { + $values[$key] = $this->denormalizer->denormalize($value, PricingDTO::class, 'json', $context); + } + $object->setPrices($values); + unset($data['channels']); + } + return $object; } diff --git a/src/Resources/views/Search/product/_box.html.twig b/src/Resources/views/Search/product/_box.html.twig index 17585ccd..8b359128 100644 --- a/src/Resources/views/Search/product/_box.html.twig +++ b/src/Resources/views/Search/product/_box.html.twig @@ -21,7 +21,7 @@ {{ item.name }} {% if item.prices is not empty %} - {% set pricing = item.prices|filter(price => price.channel_code == sylius.channel.code)|first %} + {% set pricing = item.prices|filter(price => price.channelCode == sylius.channel.code)|first %}
{{ money.convertAndFormat(pricing.price) }}
{% endif %}
From 2e15b25351e61a14739d849ee53f93b6b6c50551 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 13 Dec 2021 16:39:46 +0100 Subject: [PATCH 068/142] feat: add the configuration of target classes --- src/AutoMapper/Configuration.php | 19 +++++- .../ProductAttributeValueConfiguration.php | 3 +- src/AutoMapper/ProductMapperConfiguration.php | 31 ++++------ src/AutoMapper/VariantMapperConfiguration.php | 3 +- .../AutomapperConfigurationRegistryPass.php | 5 +- src/DependencyInjection/Configuration.php | 15 ++++- .../Product/ProductDTONormalizer.php | 60 ++++++++++++++----- src/Resources/config/monsieurbiz_search.yaml | 17 +++++- 8 files changed, 108 insertions(+), 45 deletions(-) diff --git a/src/AutoMapper/Configuration.php b/src/AutoMapper/Configuration.php index 04f866de..a9f3286b 100644 --- a/src/AutoMapper/Configuration.php +++ b/src/AutoMapper/Configuration.php @@ -13,9 +13,12 @@ namespace MonsieurBiz\SyliusSearchPlugin\AutoMapper; +use RuntimeException; + final class Configuration { private array $sourceClasses = []; + private array $targetClasses = []; public function addSourceClass(string $identifier, string $className): void { @@ -25,9 +28,23 @@ public function addSourceClass(string $identifier, string $className): void public function getSourceClass($identifier): string { if (!\array_key_exists($identifier, $this->sourceClasses)) { - throw new \Exception('Unknown source class for: ' . $identifier); + throw new RuntimeException('Unknown source class for: ' . $identifier); } return $this->sourceClasses[$identifier]; } + + public function addTargetClass(string $identifier, string $className): void + { + $this->targetClasses[$identifier] = $className; + } + + public function getTargetClass($identifier): string + { + if (!\array_key_exists($identifier, $this->targetClasses)) { + throw new RuntimeException('Unknown target class for: ' . $identifier); + } + + return $this->targetClasses[$identifier]; + } } diff --git a/src/AutoMapper/ProductAttributeValueConfiguration.php b/src/AutoMapper/ProductAttributeValueConfiguration.php index a2fdae85..75d7abe9 100644 --- a/src/AutoMapper/ProductAttributeValueConfiguration.php +++ b/src/AutoMapper/ProductAttributeValueConfiguration.php @@ -16,7 +16,6 @@ use Jane\Bundle\AutoMapperBundle\Configuration\MapperConfigurationInterface; use Jane\Component\AutoMapper\MapperGeneratorMetadataInterface; use MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader\ReaderInterface; -use MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductAttribute; use RuntimeException; use Sylius\Component\Product\Model\ProductAttributeValueInterface; @@ -58,6 +57,6 @@ public function getSource(): string public function getTarget(): string { - return ProductAttribute::class; + return $this->configuration->getTargetClass('product_attribute'); } } diff --git a/src/AutoMapper/ProductMapperConfiguration.php b/src/AutoMapper/ProductMapperConfiguration.php index 16132d54..1f00f5a8 100644 --- a/src/AutoMapper/ProductMapperConfiguration.php +++ b/src/AutoMapper/ProductMapperConfiguration.php @@ -18,14 +18,6 @@ use Jane\Component\AutoMapper\MapperGeneratorMetadataInterface; use Jane\Component\AutoMapper\MapperMetadata; use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; -use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Channel; -use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Image; -use MonsieurBiz\SyliusSearchPlugin\Generated\Model\PricingDTO; -use MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductAttribute; -use MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductTaxon; -use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Taxon; -use MonsieurBiz\SyliusSearchPlugin\Model\Product\ProductDTO; -use MonsieurBiz\SyliusSearchPlugin\Model\Product\VariantDTO; use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\ProductInterface; use Sylius\Component\Core\Model\ProductTaxonInterface; @@ -85,33 +77,35 @@ public function process(MapperGeneratorMetadataInterface $metadata): void $metadata->forMember('images', function(ProductInterface $product): array { $images = []; + $imageDTOClass = $this->configuration->getTargetClass('image'); foreach ($product->getImages() as $image) { - $images[] = $this->autoMapper->map($image, Image::class); // rename the target class to DTO + $images[] = $this->autoMapper->map($image, $imageDTOClass); } return $images; }); - $metadata->forMember('mainTaxon', function(ProductInterface $product): ?Taxon { - return $product->getMainTaxon() ? $this->autoMapper->map($product->getMainTaxon(), Taxon::class) : null; + $metadata->forMember('mainTaxon', function(ProductInterface $product) { + return $product->getMainTaxon() ? $this->autoMapper->map($product->getMainTaxon(), $this->configuration->getTargetClass('taxon')) : null; }); $metadata->forMember('product_taxons', function(ProductInterface $product): array { return array_map(function(ProductTaxonInterface $productTaxon) { // todo add parent taxon in Taxon object with automapper - return $this->autoMapper->map($productTaxon, ProductTaxon::class); + return $this->autoMapper->map($productTaxon, $this->configuration->getTargetClass('product_taxon')); }, $product->getProductTaxons()->toArray()); }); $metadata->forMember('channels', function(ProductInterface $product): array { return array_map(function(ChannelInterface $channel) { - return $this->autoMapper->map($channel, Channel::class); + return $this->autoMapper->map($channel, $this->configuration->getTargetClass('channel')); }, $product->getChannels()->toArray()); }); $metadata->forMember('attributes', function(ProductInterface $product): array { $attributes = []; - $currentLocale = $product->getTranslation()->getLocale(); // TODO default locale if it's null? + $currentLocale = $product->getTranslation()->getLocale(); + $productAttributeDTOClass = $this->configuration->getTargetClass('product_attribute'); foreach ($product->getAttributesByLocale($currentLocale, $currentLocale) as $attributeValue) { if (null === $attributeValue->getName() || null === $attributeValue->getValue()) { continue; @@ -120,7 +114,7 @@ public function process(MapperGeneratorMetadataInterface $metadata): void if (!$attribute instanceof SearchableInterface || (!$attribute->isSearchable() && !$attribute->isFilterable())) { continue; } - $attributes[$attributeValue->getCode()] = $this->autoMapper->map($attributeValue, ProductAttribute::class); + $attributes[$attributeValue->getCode()] = $this->autoMapper->map($attributeValue, $productAttributeDTOClass); } return $attributes; @@ -128,8 +122,9 @@ public function process(MapperGeneratorMetadataInterface $metadata): void $metadata->forMember('variants', function(ProductInterface $product): array { $variants = []; + $productVariantDTOClass = $this->configuration->getTargetClass('product_variant'); foreach ($product->getEnabledVariants() as $variant) { - $variants[] = $this->autoMapper->map($variant, VariantDTO::class); + $variants[] = $this->autoMapper->map($variant, $productVariantDTOClass); } return $variants; @@ -140,7 +135,7 @@ public function process(MapperGeneratorMetadataInterface $metadata): void /** @var ProductVariantInterface $variant */ $variant = $this->productVariantResolver->getVariant($product); foreach ($variant->getChannelPricings() as $channelPricing) { - $prices[] = $this->autoMapper->map($channelPricing, PricingDTO::class); + $prices[] = $this->autoMapper->map($channelPricing, $this->configuration->getTargetClass('pricing')); } return $prices; @@ -154,6 +149,6 @@ public function getSource(): string public function getTarget(): string { - return ProductDTO::class; + return $this->configuration->getTargetClass('product'); } } diff --git a/src/AutoMapper/VariantMapperConfiguration.php b/src/AutoMapper/VariantMapperConfiguration.php index a5039c98..7fa2dc08 100644 --- a/src/AutoMapper/VariantMapperConfiguration.php +++ b/src/AutoMapper/VariantMapperConfiguration.php @@ -15,7 +15,6 @@ use Jane\Bundle\AutoMapperBundle\Configuration\MapperConfigurationInterface; use Jane\Component\AutoMapper\MapperGeneratorMetadataInterface; -use MonsieurBiz\SyliusSearchPlugin\Model\Product\VariantDTO; use Sylius\Component\Core\Model\ProductVariantInterface; use Sylius\Component\Inventory\Checker\AvailabilityCheckerInterface; @@ -58,6 +57,6 @@ public function getSource(): string public function getTarget(): string { - return VariantDTO::class; + return $this->configuration->getTargetClass('product_variant'); } } diff --git a/src/DependencyInjection/AutomapperConfigurationRegistryPass.php b/src/DependencyInjection/AutomapperConfigurationRegistryPass.php index e9a86dff..7aed6b7f 100644 --- a/src/DependencyInjection/AutomapperConfigurationRegistryPass.php +++ b/src/DependencyInjection/AutomapperConfigurationRegistryPass.php @@ -22,8 +22,11 @@ public function process(ContainerBuilder $container): void { $automapperConfig = $container->getDefinition(\MonsieurBiz\SyliusSearchPlugin\AutoMapper\Configuration::class); $automapperClasses = $container->getParameter('monsieurbiz.search.config.automapper_classes'); - foreach ($automapperClasses as $identifier => $sourceClass) { + foreach ($automapperClasses['sources'] as $identifier => $sourceClass) { $automapperConfig->addMethodCall('addSourceClass', [$identifier, $sourceClass]); } + foreach ($automapperClasses['targets'] as $identifier => $sourceClass) { + $automapperConfig->addMethodCall('addTargetClass', [$identifier, $sourceClass]); + } } } diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index f57d8f5a..c0b1917d 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -71,9 +71,18 @@ public function getConfigTreeBuilder(): TreeBuilder ->end() ->end() ->arrayNode('automapper_classes') - ->useAttributeAsKey('code', false) - ->defaultValue([]) - ->prototype('scalar')->end() + ->children() + ->arrayNode('sources') + ->useAttributeAsKey('code', false) + ->defaultValue([]) + ->prototype('scalar')->end() + ->end() + ->arrayNode('targets') + ->useAttributeAsKey('code', false) + ->defaultValue([]) + ->prototype('scalar')->end() + ->end() + ->end() ->end() ->end() ; diff --git a/src/Normalizer/Product/ProductDTONormalizer.php b/src/Normalizer/Product/ProductDTONormalizer.php index 2e0be5f4..b8b600d8 100644 --- a/src/Normalizer/Product/ProductDTONormalizer.php +++ b/src/Normalizer/Product/ProductDTONormalizer.php @@ -13,13 +13,13 @@ namespace MonsieurBiz\SyliusSearchPlugin\Normalizer\Product; -use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Channel; -use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Image; -use MonsieurBiz\SyliusSearchPlugin\Generated\Model\PricingDTO; -use MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductAttribute; -use MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductTaxon; -use MonsieurBiz\SyliusSearchPlugin\Generated\Model\Taxon; -use MonsieurBiz\SyliusSearchPlugin\Model\Product\ProductDTO; +use Jacquesbh\Eater\EaterInterface; +use MonsieurBiz\SyliusSearchPlugin\AutoMapper\Configuration; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; +use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; +use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface; +use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; +use Symfony\Component\Serializer\NameConverter\NameConverterInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; @@ -33,20 +33,46 @@ final class ProductDTONormalizer extends ObjectNormalizer implements Denormalize use DenormalizerAwareTrait; use NormalizerAwareTrait; + private Configuration $automapperConfiguration; + + public function __construct( + Configuration $automapperConfiguration, + ClassMetadataFactoryInterface $classMetadataFactory = null, + NameConverterInterface $nameConverter = null, + PropertyAccessorInterface $propertyAccessor = null, + PropertyTypeExtractorInterface $propertyTypeExtractor = null, + ClassDiscriminatorResolverInterface $classDiscriminatorResolver = null, + callable $objectClassResolver = null, + array $defaultContext = [] + ) { + parent::__construct( + $classMetadataFactory, + $nameConverter, + $propertyAccessor, + $propertyTypeExtractor, + $classDiscriminatorResolver, + $objectClassResolver, + $defaultContext + ); + $this->automapperConfiguration = $automapperConfiguration; + } + public function denormalize($data, string $type, string $format = null, array $context = []) { - /** @var ProductDTO $object */ + /** @var EaterInterface $object */ $object = parent::denormalize($data, $type, $format, $context); if (\array_key_exists('main_taxon', $data)) { - $object->setMainTaxon($this->denormalizer->denormalize($data['main_taxon'], Taxon::class, 'json', $context)); + $taxonDTOClass = $this->automapperConfiguration->getTargetClass('taxon'); + $object->setMainTaxon($this->denormalizer->denormalize($data['main_taxon'], $taxonDTOClass, 'json', $context)); unset($data['main_taxon']); } if (\array_key_exists('product_taxons', $data)) { $values = []; + $productTaxonDTOClass = $this->automapperConfiguration->getTargetClass('product_taxon'); foreach ($data['product_taxons'] as $value) { - $values[] = $this->denormalizer->denormalize($value, ProductTaxon::class, 'json', $context); + $values[] = $this->denormalizer->denormalize($value, $productTaxonDTOClass, 'json', $context); } $object->setProductTaxons($values); unset($data['product_taxons']); @@ -54,8 +80,9 @@ public function denormalize($data, string $type, string $format = null, array $c if (\array_key_exists('images', $data) && null !== $data['images']) { $values = []; + $imageDTOClass = $this->automapperConfiguration->getTargetClass('image'); foreach ($data['images'] as $value) { - $values[] = $this->denormalizer->denormalize($value, Image::class, 'json', $context); + $values[] = $this->denormalizer->denormalize($value, $imageDTOClass, 'json', $context); } $object->setImages($values); unset($data['product_taxons']); @@ -63,8 +90,9 @@ public function denormalize($data, string $type, string $format = null, array $c if (\array_key_exists('channels', $data)) { $values = []; + $channelDTOClass = $this->automapperConfiguration->getTargetClass('channel'); foreach ($data['channels'] as $value) { - $values[] = $this->denormalizer->denormalize($value, Channel::class, 'json', $context); + $values[] = $this->denormalizer->denormalize($value, $channelDTOClass, 'json', $context); } $object->setChannels($values); unset($data['channels']); @@ -72,8 +100,9 @@ public function denormalize($data, string $type, string $format = null, array $c if (\array_key_exists('attributes', $data)) { $values = []; + $productAttributeDTOClass = $this->automapperConfiguration->getTargetClass('product_attribute'); foreach ($data['attributes'] as $key => $value) { - $values[$key] = $this->denormalizer->denormalize($value, ProductAttribute::class, 'json', $context); + $values[$key] = $this->denormalizer->denormalize($value, $productAttributeDTOClass, 'json', $context); } $object->setAttributes($values); unset($data['channels']); @@ -81,8 +110,9 @@ public function denormalize($data, string $type, string $format = null, array $c if (\array_key_exists('prices', $data)) { $values = []; + $pricingDTOClass = $this->automapperConfiguration->getTargetClass('pricing'); foreach ($data['prices'] as $key => $value) { - $values[$key] = $this->denormalizer->denormalize($value, PricingDTO::class, 'json', $context); + $values[$key] = $this->denormalizer->denormalize($value, $pricingDTOClass, 'json', $context); } $object->setPrices($values); unset($data['channels']); @@ -93,7 +123,7 @@ public function denormalize($data, string $type, string $format = null, array $c public function supportsDenormalization($data, string $type, string $format = null) { - return ProductDTO::class === $type; + return $this->automapperConfiguration->getTargetClass('product') === $type; } public function supportsNormalization($data, string $format = null) diff --git a/src/Resources/config/monsieurbiz_search.yaml b/src/Resources/config/monsieurbiz_search.yaml index 315500d3..6343f04d 100644 --- a/src/Resources/config/monsieurbiz_search.yaml +++ b/src/Resources/config/monsieurbiz_search.yaml @@ -14,6 +14,17 @@ monsieurbiz_sylius_search: #mapping_provider: '...' # by default monsieurbiz.search.mapper_provider #dataprovider: '...' # by default MonsieurBiz\SyliusSearchPlugin\Model\Datasource\RepositoryDatasource automapper_classes: - product: '%sylius.model.product.class%' - product_variant: '%sylius.model.product_variant.class%' - product_attribute_value: '%sylius.model.product_attribute_value.class%' + sources: + product: '%sylius.model.product.class%' + product_variant: '%sylius.model.product_variant.class%' + product_attribute_value: '%sylius.model.product_attribute_value.class%' + targets: + product: 'MonsieurBiz\SyliusSearchPlugin\Model\Product\ProductDTO' + image: 'MonsieurBiz\SyliusSearchPlugin\Generated\Model\Image' # todo rename the target class to DTO + taxon: 'MonsieurBiz\SyliusSearchPlugin\Generated\Model\Taxon' + product_taxon: 'MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductTaxon' + channel: 'MonsieurBiz\SyliusSearchPlugin\Generated\Model\Channel' + product_attribute: 'MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductAttribute' + product_variant: 'MonsieurBiz\SyliusSearchPlugin\Model\Product\VariantDTO' + pricing: 'MonsieurBiz\SyliusSearchPlugin\Generated\Model\PricingDTO' + From b415c0d856ab648072eaceea2c783873d3b7bc8f Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 14 Dec 2021 09:57:33 +0100 Subject: [PATCH 069/142] fix: search command after the request configuration refacto --- src/Command/SearchCommand.php | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/Command/SearchCommand.php b/src/Command/SearchCommand.php index d13704f5..fe975c24 100644 --- a/src/Command/SearchCommand.php +++ b/src/Command/SearchCommand.php @@ -13,10 +13,13 @@ namespace MonsieurBiz\SyliusSearchPlugin\Command; -use MonsieurBiz\SyliusSearchPlugin\Model\Product\ProductDTO; +use Jacquesbh\Eater\EaterInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Search; +use MonsieurBiz\SyliusSettingsPlugin\Settings\SettingsInterface; +use Sylius\Component\Channel\Context\ChannelContextInterface; +use Sylius\Component\Registry\ServiceRegistryInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -31,15 +34,24 @@ class SearchCommand extends Command protected static $defaultName = 'monsieurbiz:search:search'; private Search $search; private RequestStack $requestStack; + private ChannelContextInterface $channelContext; + private SettingsInterface $searchSettings; + private ServiceRegistryInterface $documentableRegistry; public function __construct( Search $search, RequestStack $requestStack, + ChannelContextInterface $channelContext, + SettingsInterface $searchSettings, + ServiceRegistryInterface $documentableRegistry, $name = null ) { parent::__construct($name); $this->search = $search; $this->requestStack = $requestStack; + $this->channelContext = $channelContext; + $this->searchSettings = $searchSettings; + $this->documentableRegistry = $documentableRegistry; } protected function configure(): void @@ -56,14 +68,20 @@ protected function execute(InputInterface $input, OutputInterface $output) $query = $input->getArgument('query'); $request = new Request(['query' => $query, '_channel_code' => $input->getOption('channel')]); $this->requestStack->push($request); - $requestConfiguration = new RequestConfiguration($request, RequestInterface::SEARCH_TYPE, 'monsieurbiz_product'); + $requestConfiguration = new RequestConfiguration( + $request, + RequestInterface::SEARCH_TYPE, + $this->documentableRegistry->get('search.documentable.monsieurbiz_product'), + $this->searchSettings, + $this->channelContext + ); $result = $this->search->search($requestConfiguration); $io->title('Search result for: ' . $query); $io->section('Nb results: ' . $result->count()); $documents = []; foreach ($result->getIterator() as $resultItem) { - /** @var ProductDTO $productDTO */ + /** @var EaterInterface $productDTO */ $productDTO = $resultItem->getModel(); $documents[] = [$resultItem->getScore(), $productDTO->getId()]; } From 6d223d8e35af92b76d27ae9800df24f7f9148066 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 14 Dec 2021 09:58:28 +0100 Subject: [PATCH 070/142] feat: add jane generate target in makefile --- Makefile | 6 ++++++ composer.json | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3a4fce79..a0a982ab 100644 --- a/Makefile +++ b/Makefile @@ -115,6 +115,12 @@ sylius.assets: ## Install all assets with symlinks ${CONSOLE} sylius:install:assets ${CONSOLE} sylius:theme:assets:install --symlink +### +### JANE +### ¯¯¯¯¯¯ +jane.generate: ## Generate Models classes with Jane + ${COMPOSER} run -- jane-generate + ### ### PLATFORM ### ¯¯¯¯¯¯¯¯ diff --git a/composer.json b/composer.json index 1ee46911..95b1d773 100644 --- a/composer.json +++ b/composer.json @@ -63,7 +63,8 @@ } }, "scripts": { - "phpcs": "php-cs-fixer fix --using-cache=no" + "phpcs": "php-cs-fixer fix --using-cache=no", + "jane-generate": "jane generate --config-file=src/Resources/config/jane/jane-configuration.php" }, "config": { "sort-packages": true From 615527a273f7a9d7173a7cb3d884802ffd1ab11a Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 14 Dec 2021 09:59:40 +0100 Subject: [PATCH 071/142] feat(docker): dynamic elasticsearch port and env variable for kibana port --- tests/Application/.env | 4 ++++ tests/Application/docker-compose.yaml | 5 ++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/Application/.env b/tests/Application/.env index 6bf99ef4..ccf16d2a 100644 --- a/tests/Application/.env +++ b/tests/Application/.env @@ -31,6 +31,10 @@ JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public-test.pem JWT_PASSPHRASE=ALL_THAT_IS_GOLD_DOES_NOT_GLITTER_NOT_ALL_THOSE_WHO_WANDER_ARE_LOST ###< lexik/jwt-authentication-bundle ### +###> Docker ### +KIBANA_PORT=15601 +###< Docker ### + MONSIEURBIZ_SEARCHPLUGIN_MESSENGER_TRANSPORT_DSN=doctrine://default MONSIEURBIZ_SEARCHPLUGIN_ES_HOST=${ELASTICSEARCH_HOST:-localhost} MONSIEURBIZ_SEARCHPLUGIN_ES_PORT=${ELASTICSEARCH_PORT:-9200} diff --git a/tests/Application/docker-compose.yaml b/tests/Application/docker-compose.yaml index b5302995..e708b493 100644 --- a/tests/Application/docker-compose.yaml +++ b/tests/Application/docker-compose.yaml @@ -30,8 +30,7 @@ services: soft: -1 hard: -1 ports: - - "9200:9200" - - "9300:9300" + - "9200" kibana: image: docker.elastic.co/kibana/kibana:7.15.0 environment: @@ -44,7 +43,7 @@ services: links: - elasticsearch ports: - - "5601:5601" + - "${KIBANA_PORT:-5601}:5601" volumes: database: {} From 0e02d8d62b0c9aa8f5223031b49ed0a4ac8a2078 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 14 Dec 2021 10:02:02 +0100 Subject: [PATCH 072/142] feat(recipe): add the 2.0 version and replace messenger variable --- .../monsieurbiz_sylius_search_plugin.yaml | 2 ++ recipes/2.0/manifest.json | 15 +++++++++++++++ src/Resources/config/messenger.yaml | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 recipes/2.0/config/packages/monsieurbiz_sylius_search_plugin.yaml create mode 100644 recipes/2.0/manifest.json diff --git a/recipes/2.0/config/packages/monsieurbiz_sylius_search_plugin.yaml b/recipes/2.0/config/packages/monsieurbiz_sylius_search_plugin.yaml new file mode 100644 index 00000000..759ac0d9 --- /dev/null +++ b/recipes/2.0/config/packages/monsieurbiz_sylius_search_plugin.yaml @@ -0,0 +1,2 @@ +imports: + - { resource: "@MonsieurBizSyliusSearchPlugin/Resources/config/config.yaml" } diff --git a/recipes/2.0/manifest.json b/recipes/2.0/manifest.json new file mode 100644 index 00000000..084337a5 --- /dev/null +++ b/recipes/2.0/manifest.json @@ -0,0 +1,15 @@ +{ + "bundles": { + "MonsieurBiz\\SyliusSearchPlugin\\MonsieurBizSyliusSearchPlugin::class": [ + "all" + ] + }, + "copy-from-recipe": { + "config/": "%CONFIG_DIR%/" + }, + "env": { + "MONSIEURBIZ_SEARCHPLUGIN_ES_HOST": "${ELASTICSEARCH_HOST:-localhost}", + "MONSIEURBIZ_SEARCHPLUGIN_ES_PORT": "${ELASTICSEARCH_PORT:-9200}", + "MONSIEURBIZ_SEARCHPLUGIN_MESSENGER_TRANSPORT_DSN": "doctrine://default" + } +} diff --git a/src/Resources/config/messenger.yaml b/src/Resources/config/messenger.yaml index 9d0d8b4c..37bcdb6b 100644 --- a/src/Resources/config/messenger.yaml +++ b/src/Resources/config/messenger.yaml @@ -1,7 +1,7 @@ framework: messenger: transports: - async_search: '%env(MESSENGER_TRANSPORT_DSN)%' + async_search: '%env(MONSIEURBIZ_SEARCHPLUGIN_MESSENGER_TRANSPORT_DSN)%' routing: MonsieurBiz\SyliusSearchPlugin\Message\ProductReindexFromTaxon: async_search From 9a513a43a8b8fcb8cb714e01edfc432e7ba12a2f Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 14 Dec 2021 10:32:28 +0100 Subject: [PATCH 073/142] feat(docker): update elasticsearch version --- Makefile | 5 +++++ tests/Application/docker-compose.yaml | 4 +++- tests/Application/docker/elasticsearch/Dockerfile | 8 +++++--- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index a0a982ab..e416f418 100644 --- a/Makefile +++ b/Makefile @@ -130,6 +130,11 @@ platform: .php-version up ## Setup the platform tools docker.pull: ## Pull the docker images cd ${APP_DIR} && ${COMPOSE} pull +.PHONY: docker.pull + +docker.build: ## Build (and pull) the docker images + cd ${APP_DIR} && ${COMPOSE} build --pull +.PHONY: docker.build docker.up: ## Start the docker containers cd ${APP_DIR} && ${COMPOSE} up -d diff --git a/tests/Application/docker-compose.yaml b/tests/Application/docker-compose.yaml index e708b493..3a73d79e 100644 --- a/tests/Application/docker-compose.yaml +++ b/tests/Application/docker-compose.yaml @@ -17,6 +17,8 @@ services: elasticsearch: build: context: ./docker/elasticsearch/ + args: + - ELASTICSEARCH_VERSION=7.16.1 volumes: - esdata:/usr/share/elasticsearch/data:rw environment: @@ -32,7 +34,7 @@ services: ports: - "9200" kibana: - image: docker.elastic.co/kibana/kibana:7.15.0 + image: docker.elastic.co/kibana/kibana:7.16.1 environment: - "ELASTICSEARCH_HOSTS=http://elasticsearch:9200" - "XPACK_GRAPH_ENABLED=false" diff --git a/tests/Application/docker/elasticsearch/Dockerfile b/tests/Application/docker/elasticsearch/Dockerfile index c5764255..a3d7ebf5 100644 --- a/tests/Application/docker/elasticsearch/Dockerfile +++ b/tests/Application/docker/elasticsearch/Dockerfile @@ -1,5 +1,7 @@ -FROM docker.elastic.co/elasticsearch/elasticsearch:7.15.0 +ARG ELASTICSEARCH_VERSION + +FROM docker.elastic.co/elasticsearch/elasticsearch:$ELASTICSEARCH_VERSION # Install ES plugins -RUN bin/elasticsearch-plugin install analysis-phonetic -RUN bin/elasticsearch-plugin install analysis-icu +RUN bin/elasticsearch-plugin install analysis-phonetic && \ + bin/elasticsearch-plugin install analysis-icu From 6ca048316b2e5dee4def8266eb7d6ad618d5f8e2 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 14 Dec 2021 10:41:41 +0100 Subject: [PATCH 074/142] refactor: rename DTO classes with DTO suffix --- generated/Model/{Channel.php => ChannelDTO.php} | 2 +- generated/Model/{Image.php => ImageDTO.php} | 2 +- ...{ProductAttribute.php => ProductAttributeDTO.php} | 2 +- .../Model/{ProductTaxon.php => ProductTaxonDTO.php} | 12 ++++++------ generated/Model/{Taxon.php => TaxonDTO.php} | 2 +- ...hannelNormalizer.php => ChannelDTONormalizer.php} | 8 ++++---- .../{ImageNormalizer.php => ImageDTONormalizer.php} | 8 ++++---- generated/Normalizer/JaneObjectNormalizer.php | 2 +- ...malizer.php => ProductAttributeDTONormalizer.php} | 8 ++++---- ...nNormalizer.php => ProductTaxonDTONormalizer.php} | 10 +++++----- .../{TaxonNormalizer.php => TaxonDTONormalizer.php} | 8 ++++---- src/Resources/config/jane/json-schema.json | 12 ++++++------ src/Resources/config/monsieurbiz_search.yaml | 10 +++++----- 13 files changed, 43 insertions(+), 43 deletions(-) rename generated/Model/{Channel.php => ChannelDTO.php} (96%) rename generated/Model/{Image.php => ImageDTO.php} (97%) rename generated/Model/{ProductAttribute.php => ProductAttributeDTO.php} (97%) rename generated/Model/{ProductTaxon.php => ProductTaxonDTO.php} (79%) rename generated/Model/{Taxon.php => TaxonDTO.php} (99%) rename generated/Normalizer/{ChannelNormalizer.php => ChannelDTONormalizer.php} (90%) rename generated/Normalizer/{ImageNormalizer.php => ImageDTONormalizer.php} (92%) rename generated/Normalizer/{ProductAttributeNormalizer.php => ProductAttributeDTONormalizer.php} (92%) rename generated/Normalizer/{ProductTaxonNormalizer.php => ProductTaxonDTONormalizer.php} (91%) rename generated/Normalizer/{TaxonNormalizer.php => TaxonDTONormalizer.php} (92%) diff --git a/generated/Model/Channel.php b/generated/Model/ChannelDTO.php similarity index 96% rename from generated/Model/Channel.php rename to generated/Model/ChannelDTO.php index 81a074e8..9fe20849 100644 --- a/generated/Model/Channel.php +++ b/generated/Model/ChannelDTO.php @@ -2,7 +2,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Generated\Model; -class Channel +class ChannelDTO { /** * diff --git a/generated/Model/Image.php b/generated/Model/ImageDTO.php similarity index 97% rename from generated/Model/Image.php rename to generated/Model/ImageDTO.php index 0ca33727..59146cf4 100644 --- a/generated/Model/Image.php +++ b/generated/Model/ImageDTO.php @@ -2,7 +2,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Generated\Model; -class Image +class ImageDTO { /** * diff --git a/generated/Model/ProductAttribute.php b/generated/Model/ProductAttributeDTO.php similarity index 97% rename from generated/Model/ProductAttribute.php rename to generated/Model/ProductAttributeDTO.php index 80b2a4f7..d2b9643e 100644 --- a/generated/Model/ProductAttribute.php +++ b/generated/Model/ProductAttributeDTO.php @@ -2,7 +2,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Generated\Model; -class ProductAttribute +class ProductAttributeDTO { /** * diff --git a/generated/Model/ProductTaxon.php b/generated/Model/ProductTaxonDTO.php similarity index 79% rename from generated/Model/ProductTaxon.php rename to generated/Model/ProductTaxonDTO.php index 686ac83c..bcadb042 100644 --- a/generated/Model/ProductTaxon.php +++ b/generated/Model/ProductTaxonDTO.php @@ -2,12 +2,12 @@ namespace MonsieurBiz\SyliusSearchPlugin\Generated\Model; -class ProductTaxon +class ProductTaxonDTO { /** * * - * @var Taxon + * @var TaxonDTO */ protected $taxon; /** @@ -19,20 +19,20 @@ class ProductTaxon /** * * - * @return Taxon + * @return TaxonDTO */ - public function getTaxon() : Taxon + public function getTaxon() : TaxonDTO { return $this->taxon; } /** * * - * @param Taxon $taxon + * @param TaxonDTO $taxon * * @return self */ - public function setTaxon(Taxon $taxon) : self + public function setTaxon(TaxonDTO $taxon) : self { $this->taxon = $taxon; return $this; diff --git a/generated/Model/Taxon.php b/generated/Model/TaxonDTO.php similarity index 99% rename from generated/Model/Taxon.php rename to generated/Model/TaxonDTO.php index 11df4559..87f86f39 100644 --- a/generated/Model/Taxon.php +++ b/generated/Model/TaxonDTO.php @@ -2,7 +2,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Generated\Model; -class Taxon +class TaxonDTO { /** * diff --git a/generated/Normalizer/ChannelNormalizer.php b/generated/Normalizer/ChannelDTONormalizer.php similarity index 90% rename from generated/Normalizer/ChannelNormalizer.php rename to generated/Normalizer/ChannelDTONormalizer.php index 550774ac..135a422b 100644 --- a/generated/Normalizer/ChannelNormalizer.php +++ b/generated/Normalizer/ChannelDTONormalizer.php @@ -11,18 +11,18 @@ use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; -class ChannelNormalizer implements DenormalizerInterface, NormalizerInterface, DenormalizerAwareInterface, NormalizerAwareInterface +class ChannelDTONormalizer implements DenormalizerInterface, NormalizerInterface, DenormalizerAwareInterface, NormalizerAwareInterface { use DenormalizerAwareTrait; use NormalizerAwareTrait; use CheckArray; public function supportsDenormalization($data, $type, $format = null) { - return $type === 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Channel'; + return $type === 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ChannelDTO'; } public function supportsNormalization($data, $format = null) { - return $data instanceof \MonsieurBiz\SyliusSearchPlugin\Generated\Model\Channel; + return $data instanceof \MonsieurBiz\SyliusSearchPlugin\Generated\Model\ChannelDTO; } public function denormalize($data, $class, $format = null, array $context = array()) { @@ -32,7 +32,7 @@ public function denormalize($data, $class, $format = null, array $context = arra if (isset($data['$recursiveRef'])) { return new Reference($data['$recursiveRef'], $context['document-origin']); } - $object = new \MonsieurBiz\SyliusSearchPlugin\Generated\Model\Channel(); + $object = new \MonsieurBiz\SyliusSearchPlugin\Generated\Model\ChannelDTO(); if (null === $data || false === \is_array($data)) { return $object; } diff --git a/generated/Normalizer/ImageNormalizer.php b/generated/Normalizer/ImageDTONormalizer.php similarity index 92% rename from generated/Normalizer/ImageNormalizer.php rename to generated/Normalizer/ImageDTONormalizer.php index 233442ec..3847cb45 100644 --- a/generated/Normalizer/ImageNormalizer.php +++ b/generated/Normalizer/ImageDTONormalizer.php @@ -11,18 +11,18 @@ use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; -class ImageNormalizer implements DenormalizerInterface, NormalizerInterface, DenormalizerAwareInterface, NormalizerAwareInterface +class ImageDTONormalizer implements DenormalizerInterface, NormalizerInterface, DenormalizerAwareInterface, NormalizerAwareInterface { use DenormalizerAwareTrait; use NormalizerAwareTrait; use CheckArray; public function supportsDenormalization($data, $type, $format = null) { - return $type === 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Image'; + return $type === 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ImageDTO'; } public function supportsNormalization($data, $format = null) { - return $data instanceof \MonsieurBiz\SyliusSearchPlugin\Generated\Model\Image; + return $data instanceof \MonsieurBiz\SyliusSearchPlugin\Generated\Model\ImageDTO; } public function denormalize($data, $class, $format = null, array $context = array()) { @@ -32,7 +32,7 @@ public function denormalize($data, $class, $format = null, array $context = arra if (isset($data['$recursiveRef'])) { return new Reference($data['$recursiveRef'], $context['document-origin']); } - $object = new \MonsieurBiz\SyliusSearchPlugin\Generated\Model\Image(); + $object = new \MonsieurBiz\SyliusSearchPlugin\Generated\Model\ImageDTO(); if (null === $data || false === \is_array($data)) { return $object; } diff --git a/generated/Normalizer/JaneObjectNormalizer.php b/generated/Normalizer/JaneObjectNormalizer.php index 9d3bde0c..c63e8f20 100644 --- a/generated/Normalizer/JaneObjectNormalizer.php +++ b/generated/Normalizer/JaneObjectNormalizer.php @@ -14,7 +14,7 @@ class JaneObjectNormalizer implements DenormalizerInterface, NormalizerInterface use DenormalizerAwareTrait; use NormalizerAwareTrait; use CheckArray; - protected $normalizers = array('MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Image' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ImageNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Channel' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ChannelNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductTaxon' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductTaxonNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Taxon' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\TaxonNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductAttribute' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductAttributeNormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\PricingDTO' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\PricingDTONormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\MonsieurBiz\\SyliusSearchPlugin\\Generated\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + protected $normalizers = array('MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ImageDTO' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ImageDTONormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ChannelDTO' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ChannelDTONormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductTaxonDTO' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductTaxonDTONormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\TaxonDTO' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\TaxonDTONormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductAttributeDTO' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductAttributeDTONormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\PricingDTO' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\PricingDTONormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\MonsieurBiz\\SyliusSearchPlugin\\Generated\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); public function supportsDenormalization($data, $type, $format = null) { return array_key_exists($type, $this->normalizers); diff --git a/generated/Normalizer/ProductAttributeNormalizer.php b/generated/Normalizer/ProductAttributeDTONormalizer.php similarity index 92% rename from generated/Normalizer/ProductAttributeNormalizer.php rename to generated/Normalizer/ProductAttributeDTONormalizer.php index 2108ca67..ccbc4e1e 100644 --- a/generated/Normalizer/ProductAttributeNormalizer.php +++ b/generated/Normalizer/ProductAttributeDTONormalizer.php @@ -11,18 +11,18 @@ use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; -class ProductAttributeNormalizer implements DenormalizerInterface, NormalizerInterface, DenormalizerAwareInterface, NormalizerAwareInterface +class ProductAttributeDTONormalizer implements DenormalizerInterface, NormalizerInterface, DenormalizerAwareInterface, NormalizerAwareInterface { use DenormalizerAwareTrait; use NormalizerAwareTrait; use CheckArray; public function supportsDenormalization($data, $type, $format = null) { - return $type === 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductAttribute'; + return $type === 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductAttributeDTO'; } public function supportsNormalization($data, $format = null) { - return $data instanceof \MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductAttribute; + return $data instanceof \MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductAttributeDTO; } public function denormalize($data, $class, $format = null, array $context = array()) { @@ -32,7 +32,7 @@ public function denormalize($data, $class, $format = null, array $context = arra if (isset($data['$recursiveRef'])) { return new Reference($data['$recursiveRef'], $context['document-origin']); } - $object = new \MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductAttribute(); + $object = new \MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductAttributeDTO(); if (null === $data || false === \is_array($data)) { return $object; } diff --git a/generated/Normalizer/ProductTaxonNormalizer.php b/generated/Normalizer/ProductTaxonDTONormalizer.php similarity index 91% rename from generated/Normalizer/ProductTaxonNormalizer.php rename to generated/Normalizer/ProductTaxonDTONormalizer.php index e8dc17dd..77920aa7 100644 --- a/generated/Normalizer/ProductTaxonNormalizer.php +++ b/generated/Normalizer/ProductTaxonDTONormalizer.php @@ -11,18 +11,18 @@ use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; -class ProductTaxonNormalizer implements DenormalizerInterface, NormalizerInterface, DenormalizerAwareInterface, NormalizerAwareInterface +class ProductTaxonDTONormalizer implements DenormalizerInterface, NormalizerInterface, DenormalizerAwareInterface, NormalizerAwareInterface { use DenormalizerAwareTrait; use NormalizerAwareTrait; use CheckArray; public function supportsDenormalization($data, $type, $format = null) { - return $type === 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductTaxon'; + return $type === 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductTaxonDTO'; } public function supportsNormalization($data, $format = null) { - return $data instanceof \MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductTaxon; + return $data instanceof \MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductTaxonDTO; } public function denormalize($data, $class, $format = null, array $context = array()) { @@ -32,12 +32,12 @@ public function denormalize($data, $class, $format = null, array $context = arra if (isset($data['$recursiveRef'])) { return new Reference($data['$recursiveRef'], $context['document-origin']); } - $object = new \MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductTaxon(); + $object = new \MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductTaxonDTO(); if (null === $data || false === \is_array($data)) { return $object; } if (\array_key_exists('taxon', $data)) { - $object->setTaxon($this->denormalizer->denormalize($data['taxon'], 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Taxon', 'json', $context)); + $object->setTaxon($this->denormalizer->denormalize($data['taxon'], 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\TaxonDTO', 'json', $context)); } if (\array_key_exists('position', $data) && $data['position'] !== null) { $value = $data['position']; diff --git a/generated/Normalizer/TaxonNormalizer.php b/generated/Normalizer/TaxonDTONormalizer.php similarity index 92% rename from generated/Normalizer/TaxonNormalizer.php rename to generated/Normalizer/TaxonDTONormalizer.php index 03edf1ea..21bc2440 100644 --- a/generated/Normalizer/TaxonNormalizer.php +++ b/generated/Normalizer/TaxonDTONormalizer.php @@ -11,18 +11,18 @@ use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; -class TaxonNormalizer implements DenormalizerInterface, NormalizerInterface, DenormalizerAwareInterface, NormalizerAwareInterface +class TaxonDTONormalizer implements DenormalizerInterface, NormalizerInterface, DenormalizerAwareInterface, NormalizerAwareInterface { use DenormalizerAwareTrait; use NormalizerAwareTrait; use CheckArray; public function supportsDenormalization($data, $type, $format = null) { - return $type === 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\Taxon'; + return $type === 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\TaxonDTO'; } public function supportsNormalization($data, $format = null) { - return $data instanceof \MonsieurBiz\SyliusSearchPlugin\Generated\Model\Taxon; + return $data instanceof \MonsieurBiz\SyliusSearchPlugin\Generated\Model\TaxonDTO; } public function denormalize($data, $class, $format = null, array $context = array()) { @@ -32,7 +32,7 @@ public function denormalize($data, $class, $format = null, array $context = arra if (isset($data['$recursiveRef'])) { return new Reference($data['$recursiveRef'], $context['document-origin']); } - $object = new \MonsieurBiz\SyliusSearchPlugin\Generated\Model\Taxon(); + $object = new \MonsieurBiz\SyliusSearchPlugin\Generated\Model\TaxonDTO(); if (null === $data || false === \is_array($data)) { return $object; } diff --git a/src/Resources/config/jane/json-schema.json b/src/Resources/config/jane/json-schema.json index 6de22381..dc30a8c9 100644 --- a/src/Resources/config/jane/json-schema.json +++ b/src/Resources/config/jane/json-schema.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/2019-09/schema#", "definitions": { - "Image": { + "ImageDTO": { "type": "object", "properties": { "path": { @@ -12,7 +12,7 @@ } } }, - "Channel": { + "ChannelDTO": { "type": "object", "properties": { "code": { @@ -20,11 +20,11 @@ } } }, - "ProductTaxon": { + "ProductTaxonDTO": { "type": "object", "properties": { "taxon": { - "$ref": "#/definitions/Taxon" + "$ref": "#/definitions/TaxonDTO" }, "position": { "type": [ @@ -34,7 +34,7 @@ } } }, - "Taxon": { + "TaxonDTO": { "type": "object", "properties": { "name": { @@ -51,7 +51,7 @@ } } }, - "ProductAttribute": { + "ProductAttributeDTO": { "type": "object", "properties": { "code": { diff --git a/src/Resources/config/monsieurbiz_search.yaml b/src/Resources/config/monsieurbiz_search.yaml index 6343f04d..47efa2f8 100644 --- a/src/Resources/config/monsieurbiz_search.yaml +++ b/src/Resources/config/monsieurbiz_search.yaml @@ -20,11 +20,11 @@ monsieurbiz_sylius_search: product_attribute_value: '%sylius.model.product_attribute_value.class%' targets: product: 'MonsieurBiz\SyliusSearchPlugin\Model\Product\ProductDTO' - image: 'MonsieurBiz\SyliusSearchPlugin\Generated\Model\Image' # todo rename the target class to DTO - taxon: 'MonsieurBiz\SyliusSearchPlugin\Generated\Model\Taxon' - product_taxon: 'MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductTaxon' - channel: 'MonsieurBiz\SyliusSearchPlugin\Generated\Model\Channel' - product_attribute: 'MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductAttribute' + image: 'MonsieurBiz\SyliusSearchPlugin\Generated\Model\ImageDTO' + taxon: 'MonsieurBiz\SyliusSearchPlugin\Generated\Model\TaxonDTO' + product_taxon: 'MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductTaxonDTO' + channel: 'MonsieurBiz\SyliusSearchPlugin\Generated\Model\ChannelDTO' + product_attribute: 'MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductAttributeDTO' product_variant: 'MonsieurBiz\SyliusSearchPlugin\Model\Product\VariantDTO' pricing: 'MonsieurBiz\SyliusSearchPlugin\Generated\Model\PricingDTO' From 74a488e1a8f60265c31d57b1ff7fd7f40dd43d21 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 14 Dec 2021 10:59:09 +0100 Subject: [PATCH 075/142] fix: phpcs errors --- src/Form/Type/SearchType.php | 1 - src/Index/Indexer.php | 2 -- src/Message/ProductReindexFromIds.php | 4 ++-- src/Message/ProductReindexFromTaxon.php | 4 ++-- src/Message/ProductToDeleteFromIds.php | 4 ++-- src/MessageHandler/ProductReindexFromIdsHandler.php | 6 +++--- src/MessageHandler/ProductReindexFromTaxonHandler.php | 6 +++--- src/MessageHandler/ProductToDeleteFromIdsHandler.php | 6 +++--- src/Search/ClientFactory.php | 4 ++-- src/Search/Filter/Filter.php | 2 +- src/Search/Request/Aggregation/MainTaxonAggregation.php | 2 -- src/Search/Request/Aggregation/PriceAggregation.php | 1 - .../Request/Aggregation/ProductAttributeAggregation.php | 1 - .../Request/Aggregation/ProductAttributesAggregation.php | 1 - src/Search/Request/Aggregation/ProductOptionAggregation.php | 1 - .../Request/Aggregation/ProductOptionsAggregation.php | 1 - src/Search/Request/Aggregation/TaxonsAggregation.php | 1 - src/Search/Search.php | 3 --- 18 files changed, 18 insertions(+), 32 deletions(-) diff --git a/src/Form/Type/SearchType.php b/src/Form/Type/SearchType.php index cb286c29..0f452f1e 100644 --- a/src/Form/Type/SearchType.php +++ b/src/Form/Type/SearchType.php @@ -16,7 +16,6 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\SearchType as SymfonySearchType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; -use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\Required; diff --git a/src/Index/Indexer.php b/src/Index/Indexer.php index 04aa6318..18a08bb8 100644 --- a/src/Index/Indexer.php +++ b/src/Index/Indexer.php @@ -16,7 +16,6 @@ use Doctrine\ORM\EntityManagerInterface; use Elastica\Document; use Jane\Component\AutoMapper\AutoMapperInterface; -use JoliCode\Elastically\Factory; use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; use MonsieurBiz\SyliusSearchPlugin\Search\ClientFactory; use Psr\Log\LoggerAwareInterface; @@ -25,7 +24,6 @@ use Sylius\Component\Registry\ServiceRegistryInterface; use Sylius\Component\Resource\Model\TranslatableInterface; use Sylius\Component\Resource\Repository\RepositoryInterface; -use Symfony\Component\Serializer\SerializerInterface; final class Indexer implements LoggerAwareInterface { diff --git a/src/Message/ProductReindexFromIds.php b/src/Message/ProductReindexFromIds.php index 78b5adb7..b15527eb 100644 --- a/src/Message/ProductReindexFromIds.php +++ b/src/Message/ProductReindexFromIds.php @@ -1,9 +1,9 @@ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/src/Message/ProductReindexFromTaxon.php b/src/Message/ProductReindexFromTaxon.php index c7667eaa..ac367484 100644 --- a/src/Message/ProductReindexFromTaxon.php +++ b/src/Message/ProductReindexFromTaxon.php @@ -1,9 +1,9 @@ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/src/Message/ProductToDeleteFromIds.php b/src/Message/ProductToDeleteFromIds.php index 941463e2..b46e8967 100644 --- a/src/Message/ProductToDeleteFromIds.php +++ b/src/Message/ProductToDeleteFromIds.php @@ -1,9 +1,9 @@ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/src/MessageHandler/ProductReindexFromIdsHandler.php b/src/MessageHandler/ProductReindexFromIdsHandler.php index 86bcac16..9309a428 100644 --- a/src/MessageHandler/ProductReindexFromIdsHandler.php +++ b/src/MessageHandler/ProductReindexFromIdsHandler.php @@ -1,9 +1,9 @@ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -35,7 +35,7 @@ public function __construct( $this->documentableRegistry = $documentableRegistry; } - public function __invoke(ProductReindexFromIds $message) + public function __invoke(ProductReindexFromIds $message): void { $products = $this->productRepository->findBy(['id' => $message->getProductIds()]); diff --git a/src/MessageHandler/ProductReindexFromTaxonHandler.php b/src/MessageHandler/ProductReindexFromTaxonHandler.php index fa5dfd5e..b33a66c0 100644 --- a/src/MessageHandler/ProductReindexFromTaxonHandler.php +++ b/src/MessageHandler/ProductReindexFromTaxonHandler.php @@ -1,9 +1,9 @@ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -35,7 +35,7 @@ public function __construct( $this->documentableRegistry = $documentableRegistry; } - public function __invoke(ProductReindexFromTaxon $message) + public function __invoke(ProductReindexFromTaxon $message): void { $products = $this->productRepository->createQueryBuilder('o') ->innerJoin('o.productTaxons', 'productTaxon') diff --git a/src/MessageHandler/ProductToDeleteFromIdsHandler.php b/src/MessageHandler/ProductToDeleteFromIdsHandler.php index 11382042..bc86af5f 100644 --- a/src/MessageHandler/ProductToDeleteFromIdsHandler.php +++ b/src/MessageHandler/ProductToDeleteFromIdsHandler.php @@ -1,9 +1,9 @@ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -35,7 +35,7 @@ public function __construct( $this->documentableRegistry = $documentableRegistry; } - public function __invoke(ProductToDeleteFromIds $message) + public function __invoke(ProductToDeleteFromIds $message): void { $products = $this->productRepository->findBy(['id' => $message->getProductIds()]); diff --git a/src/Search/ClientFactory.php b/src/Search/ClientFactory.php index dffe8c06..c44ff4dc 100644 --- a/src/Search/ClientFactory.php +++ b/src/Search/ClientFactory.php @@ -1,9 +1,9 @@ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/src/Search/Filter/Filter.php b/src/Search/Filter/Filter.php index 8c664290..a47fee8e 100644 --- a/src/Search/Filter/Filter.php +++ b/src/Search/Filter/Filter.php @@ -84,7 +84,7 @@ public function addValue(string $label, int $count, ?string $value = null): void $label, $count, $value, - in_array(SlugHelper::toSlug($value ?? $label), $this->getCurrentValues()) + \in_array(SlugHelper::toSlug($value ?? $label), $this->getCurrentValues(), true) ); } diff --git a/src/Search/Request/Aggregation/MainTaxonAggregation.php b/src/Search/Request/Aggregation/MainTaxonAggregation.php index 979bc47a..c09de708 100644 --- a/src/Search/Request/Aggregation/MainTaxonAggregation.php +++ b/src/Search/Request/Aggregation/MainTaxonAggregation.php @@ -13,8 +13,6 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; -use Elastica\Aggregation\AbstractAggregation; - class MainTaxonAggregation implements AggregationBuilderInterface { public function build($aggregation, array $filters) diff --git a/src/Search/Request/Aggregation/PriceAggregation.php b/src/Search/Request/Aggregation/PriceAggregation.php index 16d8e652..014a5eec 100644 --- a/src/Search/Request/Aggregation/PriceAggregation.php +++ b/src/Search/Request/Aggregation/PriceAggregation.php @@ -13,7 +13,6 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; -use Elastica\Aggregation\AbstractAggregation; use Sylius\Component\Channel\Context\ChannelContextInterface; class PriceAggregation implements AggregationBuilderInterface diff --git a/src/Search/Request/Aggregation/ProductAttributeAggregation.php b/src/Search/Request/Aggregation/ProductAttributeAggregation.php index 326f01e5..3e2e891e 100644 --- a/src/Search/Request/Aggregation/ProductAttributeAggregation.php +++ b/src/Search/Request/Aggregation/ProductAttributeAggregation.php @@ -13,7 +13,6 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; -use Elastica\Aggregation\AbstractAggregation; use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; use Sylius\Component\Product\Model\ProductAttributeInterface; diff --git a/src/Search/Request/Aggregation/ProductAttributesAggregation.php b/src/Search/Request/Aggregation/ProductAttributesAggregation.php index f32d1b12..afe5b8a5 100644 --- a/src/Search/Request/Aggregation/ProductAttributesAggregation.php +++ b/src/Search/Request/Aggregation/ProductAttributesAggregation.php @@ -13,7 +13,6 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; -use Elastica\Aggregation\AbstractAggregation; use Sylius\Component\Product\Model\ProductAttributeInterface; class ProductAttributesAggregation implements AggregationBuilderInterface diff --git a/src/Search/Request/Aggregation/ProductOptionAggregation.php b/src/Search/Request/Aggregation/ProductOptionAggregation.php index f7fd14ce..9de69fbd 100644 --- a/src/Search/Request/Aggregation/ProductOptionAggregation.php +++ b/src/Search/Request/Aggregation/ProductOptionAggregation.php @@ -13,7 +13,6 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; -use Elastica\Aggregation\AbstractAggregation; use Elastica\QueryBuilder; use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; use Sylius\Component\Product\Model\ProductOptionInterface; diff --git a/src/Search/Request/Aggregation/ProductOptionsAggregation.php b/src/Search/Request/Aggregation/ProductOptionsAggregation.php index 12223bc6..f09b0e61 100644 --- a/src/Search/Request/Aggregation/ProductOptionsAggregation.php +++ b/src/Search/Request/Aggregation/ProductOptionsAggregation.php @@ -13,7 +13,6 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; -use Elastica\Aggregation\AbstractAggregation; use Sylius\Component\Product\Model\ProductOptionInterface; class ProductOptionsAggregation implements AggregationBuilderInterface diff --git a/src/Search/Request/Aggregation/TaxonsAggregation.php b/src/Search/Request/Aggregation/TaxonsAggregation.php index 9e913ae0..2e4f90fd 100644 --- a/src/Search/Request/Aggregation/TaxonsAggregation.php +++ b/src/Search/Request/Aggregation/TaxonsAggregation.php @@ -13,7 +13,6 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; -use Elastica\Aggregation\AbstractAggregation; use Sylius\Component\Core\Model\TaxonInterface; class TaxonsAggregation implements AggregationBuilderInterface diff --git a/src/Search/Search.php b/src/Search/Search.php index 73d25a11..99ee2d55 100644 --- a/src/Search/Search.php +++ b/src/Search/Search.php @@ -13,14 +13,11 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search; -use JoliCode\Elastically\Factory; use MonsieurBiz\SyliusSearchPlugin\Exception\UnknownRequestTypeException; -use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestHandler; use Pagerfanta\Elastica\ElasticaAdapter; use Sylius\Component\Locale\Context\LocaleContextInterface; -use Symfony\Component\Serializer\SerializerInterface; class Search implements SearchInterface { From a31728023ac942cb40da7ac23c7dc0f210194e0e Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 14 Dec 2021 10:59:41 +0100 Subject: [PATCH 076/142] feat: prevent http exception during the command search --- src/Command/SearchCommand.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Command/SearchCommand.php b/src/Command/SearchCommand.php index fe975c24..bef80573 100644 --- a/src/Command/SearchCommand.php +++ b/src/Command/SearchCommand.php @@ -13,6 +13,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Command; +use Elastica\Exception\Connection\HttpException; use Jacquesbh\Eater\EaterInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; @@ -76,7 +77,14 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->channelContext ); - $result = $this->search->search($requestConfiguration); + try { + $result = $this->search->search($requestConfiguration); + } catch (HttpException $exception) { + $io->error('Error with the HTTP request: ' . $exception->getMessage()); + + return Command::FAILURE; + } + $io->title('Search result for: ' . $query); $io->section('Nb results: ' . $result->count()); $documents = []; From 4adaea3c79fd16898b54cc49629fe0b39f75ca27 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 14 Dec 2021 11:29:35 +0100 Subject: [PATCH 077/142] fix: fix the instant search product template with the pricing refactor --- src/Resources/views/Instant/Product/_box.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Resources/views/Instant/Product/_box.html.twig b/src/Resources/views/Instant/Product/_box.html.twig index 3714b911..dda699eb 100644 --- a/src/Resources/views/Instant/Product/_box.html.twig +++ b/src/Resources/views/Instant/Product/_box.html.twig @@ -14,7 +14,7 @@
{{ item.name }} {% if item.prices is not empty %} - {% set pricing = item.prices|filter(price => price.channel_code == sylius.channel.code)|first %} + {% set pricing = item.prices|filter(price => price.channelCode == sylius.channel.code)|first %}
{{ money.convertAndFormat(pricing.price) }}
{% endif %}
From 00902700ab9d7d263497f75b170029c278311d80 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Wed, 15 Dec 2021 15:43:57 +0100 Subject: [PATCH 078/142] refactor: add filter, post filter and sorting classes, and refactor the product request classes --- src/Resources/config/services.yaml | 55 +++ .../Aggregation/MainTaxonAggregation.php | 9 +- .../Request/Aggregation/PriceAggregation.php | 9 +- .../ProductAttributeAggregation.php | 11 +- .../ProductAttributesAggregation.php | 10 +- .../Aggregation/ProductOptionAggregation.php | 12 +- .../Aggregation/ProductOptionsAggregation.php | 10 +- .../Request/Aggregation/TaxonsAggregation.php | 9 +- src/Search/Request/InstantSearch/Product.php | 137 -------- .../PostFilter/PostFilterInterface.php | 22 ++ .../PostFilterRegistryInterface.php | 20 ++ .../Product/AttributesPostFilter.php | 41 +++ .../Product/MainTaxonPostFilter.php | 48 +++ .../PostFilter/Product/OptionsPostFilter.php | 41 +++ .../PostFilter/Product/PricePostFilter.php | 62 ++++ .../Product/ProductTaxonPostFilter.php | 52 +++ .../PostFilter/ProductTaxonRegistry.php | 28 ++ .../Request/ProductRequest/InstantSearch.php | 73 ++++ src/Search/Request/ProductRequest/Search.php | 127 +++++++ src/Search/Request/ProductRequest/Taxon.php | 125 +++++++ .../QueryFilter/Product/ChannelFilter.php | 43 +++ .../QueryFilter/Product/EnabledFilter.php | 31 ++ .../QueryFilter/Product/SearchTermFilter.php | 107 ++++++ .../QueryFilter/Product/TaxonFilter.php | 42 +++ .../QueryFilter/ProductSearchRegistry.php | 28 ++ .../QueryFilter/ProductTaxonRegistry.php | 28 ++ .../QueryFilter/QueryFilterInterface.php | 22 ++ .../QueryFilterRegistryInterface.php | 20 ++ src/Search/Request/Search/Product.php | 302 ----------------- .../Sorting/Product/CreatedAtSorter.php | 34 ++ .../Request/Sorting/Product/NameSorter.php | 34 ++ .../Sorting/Product/PositionSorter.php | 48 +++ .../Request/Sorting/Product/PriceSorter.php | 50 +++ .../Request/Sorting/ProductSorterRegistry.php | 28 ++ .../Request/Sorting/SorterBuilderTrait.php | 43 +++ .../Request/Sorting/SorterInterface.php | 22 ++ .../Sorting/SorterRegistryInterface.php | 20 ++ src/Search/Request/Taxon/Product.php | 313 ------------------ 38 files changed, 1334 insertions(+), 782 deletions(-) delete mode 100644 src/Search/Request/InstantSearch/Product.php create mode 100644 src/Search/Request/PostFilter/PostFilterInterface.php create mode 100644 src/Search/Request/PostFilter/PostFilterRegistryInterface.php create mode 100644 src/Search/Request/PostFilter/Product/AttributesPostFilter.php create mode 100644 src/Search/Request/PostFilter/Product/MainTaxonPostFilter.php create mode 100644 src/Search/Request/PostFilter/Product/OptionsPostFilter.php create mode 100644 src/Search/Request/PostFilter/Product/PricePostFilter.php create mode 100644 src/Search/Request/PostFilter/Product/ProductTaxonPostFilter.php create mode 100644 src/Search/Request/PostFilter/ProductTaxonRegistry.php create mode 100644 src/Search/Request/ProductRequest/InstantSearch.php create mode 100644 src/Search/Request/ProductRequest/Search.php create mode 100644 src/Search/Request/ProductRequest/Taxon.php create mode 100644 src/Search/Request/QueryFilter/Product/ChannelFilter.php create mode 100644 src/Search/Request/QueryFilter/Product/EnabledFilter.php create mode 100644 src/Search/Request/QueryFilter/Product/SearchTermFilter.php create mode 100644 src/Search/Request/QueryFilter/Product/TaxonFilter.php create mode 100644 src/Search/Request/QueryFilter/ProductSearchRegistry.php create mode 100644 src/Search/Request/QueryFilter/ProductTaxonRegistry.php create mode 100644 src/Search/Request/QueryFilter/QueryFilterInterface.php create mode 100644 src/Search/Request/QueryFilter/QueryFilterRegistryInterface.php delete mode 100644 src/Search/Request/Search/Product.php create mode 100644 src/Search/Request/Sorting/Product/CreatedAtSorter.php create mode 100644 src/Search/Request/Sorting/Product/NameSorter.php create mode 100644 src/Search/Request/Sorting/Product/PositionSorter.php create mode 100644 src/Search/Request/Sorting/Product/PriceSorter.php create mode 100644 src/Search/Request/Sorting/ProductSorterRegistry.php create mode 100644 src/Search/Request/Sorting/SorterBuilderTrait.php create mode 100644 src/Search/Request/Sorting/SorterInterface.php create mode 100644 src/Search/Request/Sorting/SorterRegistryInterface.php delete mode 100644 src/Search/Request/Taxon/Product.php diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index d4bd0f27..31f7e203 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -86,3 +86,58 @@ services: MonsieurBiz\SyliusSearchPlugin\Search\Request\AggregationBuilder: arguments: [ !tagged_iterator { tag: 'monsieurbiz.sarch.aggregation_builder' } ] + + # Define query filters + MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\ProductSearchRegistry: + arguments: + - [ + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\SearchTermFilter', + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\ChannelFilter', + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\EnabledFilter' + ] + + MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\ProductTaxonRegistry: + arguments: + - [ + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\ChannelFilter', + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\EnabledFilter', + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\TaxonFilter' + ] + + # Define post filters + MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\ProductTaxonRegistry: + arguments: + - [ + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\Product\AttributesPostFilter', + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\Product\MainTaxonPostFilter', + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\Product\OptionsPostFilter', + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\Product\PricePostFilter', + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\Product\ProductTaxonPostFilter', + ] + + # Define sorters + MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\ProductSorterRegistry: + arguments: + - [ + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\Product\PositionSorter', + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\Product\PriceSorter', + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\Product\NameSorter', + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\Product\CreatedAtSorter', + ] + + # Define the product queries + MonsieurBiz\SyliusSearchPlugin\Search\Request\ProductRequest\Search: + arguments: + $queryFilterRegistry: '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\ProductSearchRegistry' + $postFilterRegistry: '@MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\ProductTaxonRegistry' + $sorterRegistry: '@MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\ProductSorterRegistry' + + MonsieurBiz\SyliusSearchPlugin\Search\Request\ProductRequest\InstantSearch: + arguments: + $queryFilterRegistry: '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\ProductSearchRegistry' + + MonsieurBiz\SyliusSearchPlugin\Search\Request\ProductRequest\Taxon: + arguments: + $queryFilterRegistry: '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\ProductTaxonRegistry' + $postFilterRegistry: '@MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\ProductTaxonRegistry' + $sorterRegistry: '@MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\ProductSorterRegistry' diff --git a/src/Search/Request/Aggregation/MainTaxonAggregation.php b/src/Search/Request/Aggregation/MainTaxonAggregation.php index c09de708..0db45723 100644 --- a/src/Search/Request/Aggregation/MainTaxonAggregation.php +++ b/src/Search/Request/Aggregation/MainTaxonAggregation.php @@ -13,7 +13,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; -class MainTaxonAggregation implements AggregationBuilderInterface +final class MainTaxonAggregation implements AggregationBuilderInterface { public function build($aggregation, array $filters) { @@ -22,10 +22,9 @@ public function build($aggregation, array $filters) } $qb = new \Elastica\QueryBuilder(); - - $filters = array_filter($filters, function($key) { - return false === strpos($key, 'main_taxon'); - }, \ARRAY_FILTER_USE_KEY); + $filters = array_filter($filters, function($filter): bool { + return !$filter->hasParam('path') || 'main_taxon' !== $filter->getParam('path'); + }); $filterQuery = $qb->query()->bool(); foreach ($filters as $filter) { $filterQuery->addMust($filter); diff --git a/src/Search/Request/Aggregation/PriceAggregation.php b/src/Search/Request/Aggregation/PriceAggregation.php index 014a5eec..96eaa393 100644 --- a/src/Search/Request/Aggregation/PriceAggregation.php +++ b/src/Search/Request/Aggregation/PriceAggregation.php @@ -15,7 +15,7 @@ use Sylius\Component\Channel\Context\ChannelContextInterface; -class PriceAggregation implements AggregationBuilderInterface +final class PriceAggregation implements AggregationBuilderInterface { private ChannelContextInterface $channelContext; @@ -32,9 +32,10 @@ public function build($aggregation, array $filters) $qb = new \Elastica\QueryBuilder(); - $filters = array_filter($filters, function($key) { - return false === strpos($key, 'price'); - }, \ARRAY_FILTER_USE_KEY); + $filters = array_filter($filters, function($filter): bool { + return !$filter->hasParam('path') || 'prices' !== $filter->getParam('path'); + }); + $filterQuery = $qb->query()->bool(); foreach ($filters as $filter) { $filterQuery->addMust($filter); diff --git a/src/Search/Request/Aggregation/ProductAttributeAggregation.php b/src/Search/Request/Aggregation/ProductAttributeAggregation.php index 3e2e891e..2ce5d885 100644 --- a/src/Search/Request/Aggregation/ProductAttributeAggregation.php +++ b/src/Search/Request/Aggregation/ProductAttributeAggregation.php @@ -16,7 +16,7 @@ use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; use Sylius\Component\Product\Model\ProductAttributeInterface; -class ProductAttributeAggregation implements AggregationBuilderInterface +final class ProductAttributeAggregation implements AggregationBuilderInterface { public function build($aggregation, array $filters) { @@ -26,10 +26,13 @@ public function build($aggregation, array $filters) } $qb = new \Elastica\QueryBuilder(); + $filters = array_filter($filters, function($filter) use ($aggregation): bool { + return !$filter->hasParam('path') || ( + false !== strpos($filter->getParam('path'), 'attributes.') + && 'attributes.' . $aggregation->getCode() !== $filter->getParam('path') + ); + }); - $filters = array_filter($filters, function($key) use ($aggregation) { - return false !== strpos($key, 'attributes.') && 'attributes.' . $aggregation->getCode() !== $key; - }, \ARRAY_FILTER_USE_KEY); $filterQuery = $qb->query()->bool(); foreach ($filters as $filter) { $filterQuery->addMust($filter); diff --git a/src/Search/Request/Aggregation/ProductAttributesAggregation.php b/src/Search/Request/Aggregation/ProductAttributesAggregation.php index afe5b8a5..ba166cb8 100644 --- a/src/Search/Request/Aggregation/ProductAttributesAggregation.php +++ b/src/Search/Request/Aggregation/ProductAttributesAggregation.php @@ -13,9 +13,10 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; +use Elastica\Query\AbstractQuery; use Sylius\Component\Product\Model\ProductAttributeInterface; -class ProductAttributesAggregation implements AggregationBuilderInterface +final class ProductAttributesAggregation implements AggregationBuilderInterface { public function __construct() { @@ -30,9 +31,10 @@ public function build($aggregation, array $filters) $qb = new \Elastica\QueryBuilder(); - $currentFilters = array_filter($filters, function($key) { - return false === strpos($key, 'attributes.'); - }, \ARRAY_FILTER_USE_KEY); + $currentFilters = array_filter($filters, function(AbstractQuery $filter): bool { + return !$filter->hasParam('path') || false === strpos($filter->getParam('path'), 'attributes.'); + }); + $filterQuery = $qb->query()->bool(); foreach ($currentFilters as $filter) { $filterQuery->addMust($filter); diff --git a/src/Search/Request/Aggregation/ProductOptionAggregation.php b/src/Search/Request/Aggregation/ProductOptionAggregation.php index 9de69fbd..cd2bdcec 100644 --- a/src/Search/Request/Aggregation/ProductOptionAggregation.php +++ b/src/Search/Request/Aggregation/ProductOptionAggregation.php @@ -17,7 +17,7 @@ use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; use Sylius\Component\Product\Model\ProductOptionInterface; -class ProductOptionAggregation implements AggregationBuilderInterface +final class ProductOptionAggregation implements AggregationBuilderInterface { public function build($aggregation, array $filters) { @@ -28,9 +28,13 @@ public function build($aggregation, array $filters) $qb = new QueryBuilder(); - $filters = array_filter($filters, function($key) use ($aggregation): bool { - return false !== strpos($key, 'options.') && 'options.' . $aggregation->getCode() !== $key; - }, \ARRAY_FILTER_USE_KEY); + $filters = array_filter($filters, function($filter) use ($aggregation): bool { + return !$filter->hasParam('path') || ( + false !== strpos($filter->getParam('path'), 'options.') + && 'variants.options.' . $aggregation->getCode() !== $filter->getParam('path') + ); + }); + $filterQuery = $qb->query()->bool(); foreach ($filters as $filter) { $filterQuery->addMust($filter); diff --git a/src/Search/Request/Aggregation/ProductOptionsAggregation.php b/src/Search/Request/Aggregation/ProductOptionsAggregation.php index f09b0e61..47bba51a 100644 --- a/src/Search/Request/Aggregation/ProductOptionsAggregation.php +++ b/src/Search/Request/Aggregation/ProductOptionsAggregation.php @@ -13,9 +13,10 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; +use Elastica\Query\AbstractQuery; use Sylius\Component\Product\Model\ProductOptionInterface; -class ProductOptionsAggregation implements AggregationBuilderInterface +final class ProductOptionsAggregation implements AggregationBuilderInterface { private ProductOptionAggregation $productOptionAggregationBuilder; @@ -31,10 +32,9 @@ public function build($aggregation, array $filters) } $qb = new \Elastica\QueryBuilder(); - - $currentFilters = array_filter($filters, function($key): bool { - return false === strpos($key, 'options.'); - }, \ARRAY_FILTER_USE_KEY); + $currentFilters = array_filter($filters, function(AbstractQuery $filter): bool { + return !$filter->hasParam('path') || false === strpos($filter->getParam('path'), 'options.'); + }); $filterQuery = $qb->query()->bool(); foreach ($currentFilters as $filter) { $filterQuery->addMust($filter); diff --git a/src/Search/Request/Aggregation/TaxonsAggregation.php b/src/Search/Request/Aggregation/TaxonsAggregation.php index 2e4f90fd..1a0d8929 100644 --- a/src/Search/Request/Aggregation/TaxonsAggregation.php +++ b/src/Search/Request/Aggregation/TaxonsAggregation.php @@ -15,7 +15,7 @@ use Sylius\Component\Core\Model\TaxonInterface; -class TaxonsAggregation implements AggregationBuilderInterface +final class TaxonsAggregation implements AggregationBuilderInterface { public function build($aggregation, array $filters) { @@ -26,9 +26,10 @@ public function build($aggregation, array $filters) $currentTaxon = $aggregation['taxons']; $qb = new \Elastica\QueryBuilder(); - $filters = array_filter($filters, function($key) { - return false === strpos($key, 'taxons'); - }, \ARRAY_FILTER_USE_KEY); + $filters = array_filter($filters, function($filter) { + return !$filter->hasParam('path') || 'product_taxons' !== $filter->getParam('path'); + }); + $filterQuery = $qb->query()->bool(); foreach ($filters as $filter) { $filterQuery->addMust($filter); diff --git a/src/Search/Request/InstantSearch/Product.php b/src/Search/Request/InstantSearch/Product.php deleted file mode 100644 index b119c322..00000000 --- a/src/Search/Request/InstantSearch/Product.php +++ /dev/null @@ -1,137 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\InstantSearch; - -use Elastica\Query; -use Elastica\Query\MultiMatch; -use Elastica\QueryBuilder; -use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; -use MonsieurBiz\SyliusSearchPlugin\Repository\ProductAttributeRepositoryInterface; -use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; -use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; -use Sylius\Component\Channel\Context\ChannelContextInterface; -use Sylius\Component\Registry\ServiceRegistryInterface; - -class Product implements RequestInterface -{ - private DocumentableInterface $documentable; - private ?RequestConfiguration $configuration; - private ChannelContextInterface $channelContext; - private ProductAttributeRepositoryInterface $productAttributeRepository; - - public function __construct( - ServiceRegistryInterface $documentableRegistry, - ChannelContextInterface $channelContext, - ProductAttributeRepositoryInterface $productAttributeRepository - ) { - $this->documentable = $documentableRegistry->get('search.documentable.monsieurbiz_product'); - $this->channelContext = $channelContext; - $this->productAttributeRepository = $productAttributeRepository; - } - - public function getType(): string - { - return RequestInterface::INSTANT_TYPE; - } - - public function getDocumentable(): DocumentableInterface - { - return $this->documentable; - } - - public function getQuery(): Query - { - if (!$this->configuration || '' === $this->configuration->getQueryText()) { - throw new \Exception('missing query text'); //todo - } - - $queryText = $this->configuration->getQueryText(); - $qb = $this->getQueryBuilder(); - $searchQuery = $qb->query()->bool() - ->addShould($qb->query()->term(['code' => ['value' => $queryText]])) - ->addShould( - $qb->query()->multi_match() - ->setFields([ - 'name^5', // todo configuration - 'description', // move to should ? score impact but not include in result - ]) - ->setQuery($queryText) - ->setType(MultiMatch::TYPE_MOST_FIELDS) - ->setFuzziness(MultiMatch::FUZZINESS_AUTO) - ) - ; - - foreach ($this->getAttributeQueries($queryText) as $attributeQuery) { - $searchQuery->addShould($attributeQuery); - } - - $boolQuery = $qb->query()->bool() - ->addFilter($qb->query()->term(['enabled' => ['value' => true]])) - ->addFilter( - $qb->query()->nested() - ->setPath('channels') - ->setQuery( - $qb->query()->term(['channels.code' => ['value' => $this->channelContext->getChannel()->getCode()]]) - ) - ) - ->addMust($searchQuery) - ; - - return Query::create($boolQuery); - } - - public function supports(string $type, string $documentableCode): bool - { - return RequestInterface::INSTANT_TYPE == $type && $this->getDocumentable()->getIndexCode() == $documentableCode; - } - - public function setConfiguration(RequestConfiguration $configuration): void - { - $this->configuration = $configuration; - } - - private function getAttributeQueries(string $queryText): array - { - $attributeQueries = []; - $qb = $this->getQueryBuilder(); - foreach ($this->productAttributeRepository->findIsSearchableOrFilterable() as $productAttribute) { - if (!$productAttribute->isSearchable()) { - continue; - } - - $attributeQueries[] = $qb->query()->nested() - ->setPath('attributes') - ->setQuery( - $qb->query()->nested() - ->setPath(sprintf('attributes.%s', $productAttribute->getCode())) - ->setQuery( - $qb->query()->multi_match() - ->setFields([ - sprintf('attributes.%s.value^%d', $productAttribute->getCode(), $productAttribute->getSearchWeight()), - ]) - ->setQuery($queryText) - ->setFuzziness(MultiMatch::FUZZINESS_AUTO) - ) - ) - ; - } - - return $attributeQueries; - } - - private function getQueryBuilder(): QueryBuilder - { - return new QueryBuilder(); - } -} diff --git a/src/Search/Request/PostFilter/PostFilterInterface.php b/src/Search/Request/PostFilter/PostFilterInterface.php new file mode 100644 index 00000000..6b07522a --- /dev/null +++ b/src/Search/Request/PostFilter/PostFilterInterface.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter; + +use Elastica\Query\BoolQuery; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; + +interface PostFilterInterface +{ + public function apply(BoolQuery $boolQuery, RequestConfiguration $requestConfiguration): void; +} diff --git a/src/Search/Request/PostFilter/PostFilterRegistryInterface.php b/src/Search/Request/PostFilter/PostFilterRegistryInterface.php new file mode 100644 index 00000000..5813eb70 --- /dev/null +++ b/src/Search/Request/PostFilter/PostFilterRegistryInterface.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter; + +use Sylius\Component\Registry\ServiceRegistryInterface; + +interface PostFilterRegistryInterface extends ServiceRegistryInterface +{ +} diff --git a/src/Search/Request/PostFilter/Product/AttributesPostFilter.php b/src/Search/Request/PostFilter/Product/AttributesPostFilter.php new file mode 100644 index 00000000..5fd94db2 --- /dev/null +++ b/src/Search/Request/PostFilter/Product/AttributesPostFilter.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\Product; + +use Elastica\Query\BoolQuery; +use Elastica\QueryBuilder; +use MonsieurBiz\SyliusSearchPlugin\Helper\SlugHelper; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\PostFilterInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; + +final class AttributesPostFilter implements PostFilterInterface +{ + public function apply(BoolQuery $boolQuery, RequestConfiguration $requestConfiguration): void + { + $qb = new QueryBuilder(); + foreach ($requestConfiguration->getAppliedFilters('attributes') as $field => $values) { + $attributeValueQuery = $qb->query()->bool(); + + foreach ($values as $value) { + $termQuery = $qb->query()->term([sprintf('attributes.%s.value.keyword', $field) => SlugHelper::toLabel($value)]); + $attributeValueQuery->addShould($termQuery); // todo configure the "and" or "or" + } + + $attributeQuery = $qb->query()->nested(); + $attributeQuery->setPath(sprintf('attributes.%s', $field))->setQuery($attributeValueQuery); + + $boolQuery->addMust($attributeQuery); + } + } +} diff --git a/src/Search/Request/PostFilter/Product/MainTaxonPostFilter.php b/src/Search/Request/PostFilter/Product/MainTaxonPostFilter.php new file mode 100644 index 00000000..2af93bbc --- /dev/null +++ b/src/Search/Request/PostFilter/Product/MainTaxonPostFilter.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\Product; + +use Elastica\Query\BoolQuery; +use Elastica\QueryBuilder; +use MonsieurBiz\SyliusSearchPlugin\Helper\SlugHelper; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\PostFilterInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; + +final class MainTaxonPostFilter implements PostFilterInterface +{ + public function apply(BoolQuery $boolQuery, RequestConfiguration $requestConfiguration): void + { + $qb = new QueryBuilder(); + foreach ($requestConfiguration->getAppliedFilters('taxon') as $field => $values) { + $mainTaxonQuery = $qb->query() + ->bool() + ; + foreach ($values as $value) { + $mainTaxonQuery->addShould( + $qb->query() + ->term() + ->setTerm(sprintf('%s.code', $field), SlugHelper::toLabel($value)) + ); + } + $boolQuery->addMust( + $qb->query() + ->nested() + ->setPath($field) + ->setQuery( + $mainTaxonQuery + ) + ); + } + } +} diff --git a/src/Search/Request/PostFilter/Product/OptionsPostFilter.php b/src/Search/Request/PostFilter/Product/OptionsPostFilter.php new file mode 100644 index 00000000..d346d109 --- /dev/null +++ b/src/Search/Request/PostFilter/Product/OptionsPostFilter.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\Product; + +use Elastica\Query\BoolQuery; +use Elastica\QueryBuilder; +use MonsieurBiz\SyliusSearchPlugin\Helper\SlugHelper; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\PostFilterInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; + +final class OptionsPostFilter implements PostFilterInterface +{ + public function apply(BoolQuery $boolQuery, RequestConfiguration $requestConfiguration): void + { + $qb = new QueryBuilder(); + foreach ($requestConfiguration->getAppliedFilters('options') as $field => $values) { + $optionValueQuery = $qb->query()->bool(); + + foreach ($values as $value) { + $termQuery = $qb->query()->term([sprintf('variants.options.%s.value.keyword', $field) => SlugHelper::toLabel($value)]); + $optionValueQuery->addShould($termQuery); // todo configure the "and" or "or" + } + + $optionQuery = $qb->query()->nested(); + $optionQuery->setPath(sprintf('variants.options.%s', $field))->setQuery($optionValueQuery); + + $boolQuery->addMust($optionQuery); + } + } +} diff --git a/src/Search/Request/PostFilter/Product/PricePostFilter.php b/src/Search/Request/PostFilter/Product/PricePostFilter.php new file mode 100644 index 00000000..cfe686b1 --- /dev/null +++ b/src/Search/Request/PostFilter/Product/PricePostFilter.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\Product; + +use Elastica\Query\BoolQuery; +use Elastica\QueryBuilder; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\PostFilterInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; +use Sylius\Component\Channel\Context\ChannelContextInterface; + +final class PricePostFilter implements PostFilterInterface +{ + private ChannelContextInterface $channelContext; + + public function __construct(ChannelContextInterface $channelContext) + { + $this->channelContext = $channelContext; + } + + public function apply(BoolQuery $boolQuery, RequestConfiguration $requestConfiguration): void + { + $qb = new QueryBuilder(); + $priceValue = $requestConfiguration->getAppliedFilters('price'); + if (0 !== \count($priceValue)) { + $channelPriceFilter = $qb->query() + ->term(['prices.channel_code' => $this->channelContext->getChannel()->getCode()]) + ; + $conditions = []; + if (\array_key_exists('min', $priceValue)) { + $conditions['gte'] = $priceValue['min'] * 100; + } + if (\array_key_exists('max', $priceValue)) { + $conditions['lte'] = $priceValue['max'] * 100; + } + $priceQuery = $qb->query() + ->range('prices.price', $conditions) + ; + + $boolQuery->addMust( + $qb->query() + ->nested() + ->setPath('prices') + ->setQuery( + $qb->query()->bool() + ->addMust($channelPriceFilter) + ->addMust($priceQuery) + ) + ); + } + } +} diff --git a/src/Search/Request/PostFilter/Product/ProductTaxonPostFilter.php b/src/Search/Request/PostFilter/Product/ProductTaxonPostFilter.php new file mode 100644 index 00000000..cbc6753f --- /dev/null +++ b/src/Search/Request/PostFilter/Product/ProductTaxonPostFilter.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\Product; + +use Elastica\Query\BoolQuery; +use Elastica\QueryBuilder; +use MonsieurBiz\SyliusSearchPlugin\Helper\SlugHelper; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\PostFilterInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; + +final class ProductTaxonPostFilter implements PostFilterInterface +{ + public function apply(BoolQuery $boolQuery, RequestConfiguration $requestConfiguration): void + { + $qb = new QueryBuilder(); + $taxonsSelected = $requestConfiguration->getAppliedFilters('taxons'); + if (0 !== \count($taxonsSelected)) { + $taxonQuery = $qb->query() + ->bool() + ; + foreach ($taxonsSelected as $value) { + $taxonQuery->addShould( + $qb->query() + ->term() + ->setTerm('product_taxons.taxon.code', SlugHelper::toLabel($value)) + ); + } + + $boolQuery->addMust( + $qb->query() + ->nested() + ->setPath('product_taxons') + ->setQuery( + $qb->query()->nested() + ->setPath('product_taxons.taxon') + ->setQuery($taxonQuery) + ) + ); + } + } +} diff --git a/src/Search/Request/PostFilter/ProductTaxonRegistry.php b/src/Search/Request/PostFilter/ProductTaxonRegistry.php new file mode 100644 index 00000000..a2bd227c --- /dev/null +++ b/src/Search/Request/PostFilter/ProductTaxonRegistry.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter; + +use Sylius\Component\Registry\ServiceRegistry; + +final class ProductTaxonRegistry extends ServiceRegistry implements PostFilterRegistryInterface +{ + public function __construct(array $queryFilters = []) + { + parent::__construct(PostFilterInterface::class, 'monsieurbiz.search'); + + foreach ($queryFilters as $queryFilter) { + $this->register(\get_class($queryFilter), $queryFilter); + } + } +} diff --git a/src/Search/Request/ProductRequest/InstantSearch.php b/src/Search/Request/ProductRequest/InstantSearch.php new file mode 100644 index 00000000..eb7be112 --- /dev/null +++ b/src/Search/Request/ProductRequest/InstantSearch.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\ProductRequest; + +use Elastica\Query; +use Elastica\QueryBuilder; +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\QueryFilterRegistryInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; +use RuntimeException; +use Sylius\Component\Registry\ServiceRegistryInterface; + +final class InstantSearch implements RequestInterface +{ + private DocumentableInterface $documentable; + private ?RequestConfiguration $configuration; + private QueryFilterRegistryInterface $queryFilterRegistry; + + public function __construct( + ServiceRegistryInterface $documentableRegistry, + QueryFilterRegistryInterface $queryFilterRegistry + ) { + $this->documentable = $documentableRegistry->get('search.documentable.monsieurbiz_product'); + $this->queryFilterRegistry = $queryFilterRegistry; + } + + public function getType(): string + { + return RequestInterface::INSTANT_TYPE; + } + + public function getDocumentable(): DocumentableInterface + { + return $this->documentable; + } + + public function getQuery(): Query + { + if (null === $this->configuration || '' === $this->configuration->getQueryText()) { + throw new RuntimeException('missing query text'); + } + + $qb = new QueryBuilder(); + $boolQuery = $qb->query()->bool(); + foreach ($this->queryFilterRegistry->all() as $queryFilter) { + $queryFilter->apply($boolQuery, $this->configuration); + } + + return Query::create($boolQuery); + } + + public function supports(string $type, string $documentableCode): bool + { + return RequestInterface::INSTANT_TYPE == $type && $this->getDocumentable()->getIndexCode() == $documentableCode; + } + + public function setConfiguration(RequestConfiguration $configuration): void + { + $this->configuration = $configuration; + } +} diff --git a/src/Search/Request/ProductRequest/Search.php b/src/Search/Request/ProductRequest/Search.php new file mode 100644 index 00000000..847e87f6 --- /dev/null +++ b/src/Search/Request/ProductRequest/Search.php @@ -0,0 +1,127 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\ProductRequest; + +use Elastica\Query; +use Elastica\QueryBuilder; +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use MonsieurBiz\SyliusSearchPlugin\Repository\ProductAttributeRepositoryInterface; +use MonsieurBiz\SyliusSearchPlugin\Repository\ProductOptionRepositoryInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\AggregationBuilder; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\PostFilterRegistryInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\QueryFilterRegistryInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\SorterRegistryInterface; +use RuntimeException; +use Sylius\Component\Registry\ServiceRegistryInterface; + +final class Search implements RequestInterface +{ + private DocumentableInterface $documentable; + private RequestConfiguration $configuration; + private ProductAttributeRepositoryInterface $productAttributeRepository; + private ProductOptionRepositoryInterface $productOptionRepository; + private AggregationBuilder $aggregationBuilder; + private QueryFilterRegistryInterface $queryFilterRegistry; + private PostFilterRegistryInterface $postFilterRegistry; + private SorterRegistryInterface $sorterRegistry; + + public function __construct( + ServiceRegistryInterface $documentableRegistry, + ProductAttributeRepositoryInterface $productAttributeRepository, + ProductOptionRepositoryInterface $productOptionRepository, + AggregationBuilder $aggregationBuilder, + QueryFilterRegistryInterface $queryFilterRegistry, + PostFilterRegistryInterface $postFilterRegistry, + SorterRegistryInterface $sorterRegistry + ) { + //TODO check if exist, return a dummy documentable if not + $this->documentable = $documentableRegistry->get('search.documentable.monsieurbiz_product'); + $this->productAttributeRepository = $productAttributeRepository; + $this->productOptionRepository = $productOptionRepository; + $this->aggregationBuilder = $aggregationBuilder; + $this->queryFilterRegistry = $queryFilterRegistry; + $this->postFilterRegistry = $postFilterRegistry; + $this->sorterRegistry = $sorterRegistry; + } + + public function getType(): string + { + return RequestInterface::SEARCH_TYPE; + } + + public function getDocumentable(): DocumentableInterface + { + return $this->documentable; + } + + public function setConfiguration(RequestConfiguration $configuration): void + { + $this->configuration = $configuration; + } + + public function getQuery(): Query + { + if ('' === $this->configuration->getQueryText()) { + throw new RuntimeException('missing query text'); + } + + $qb = new QueryBuilder(); + + $boolQuery = $qb->query()->bool(); + foreach ($this->queryFilterRegistry->all() as $queryFilter) { + $queryFilter->apply($boolQuery, $this->configuration); + } + + $query = Query::create($boolQuery); + $postFilter = new Query\BoolQuery(); + foreach ($this->postFilterRegistry->all() as $postFilterApplier) { + $postFilterApplier->apply($postFilter, $this->configuration); + } + $query->setPostFilter($postFilter); + + $this->addAggregations($query, $postFilter); + + foreach ($this->sorterRegistry->all() as $sorter) { + $sorter->apply($query, $this->configuration); + } + + dump(json_encode($query->toArray(), 1)); + + return $query; + } + + public function supports(string $type, string $documentableCode): bool + { + return $type == $this->getType() && $this->getDocumentable()->getIndexCode() == $documentableCode; + } + + private function addAggregations(Query $query, Query\BoolQuery $postFilter): void + { + $aggregations = $this->aggregationBuilder->buildAggregations( + [ + $this->productAttributeRepository->findIsSearchableOrFilterable(), + $this->productOptionRepository->findIsSearchableOrFilterable(), + 'main_taxon', + 'price', + ], + $postFilter->hasParam('must') ? $postFilter->getParam('must') : [] + ); + + foreach ($aggregations as $aggregation) { + $query->addAggregation($aggregation); + } + } +} diff --git a/src/Search/Request/ProductRequest/Taxon.php b/src/Search/Request/ProductRequest/Taxon.php new file mode 100644 index 00000000..96518a9d --- /dev/null +++ b/src/Search/Request/ProductRequest/Taxon.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\ProductRequest; + +use Elastica\Query; +use Elastica\QueryBuilder; +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use MonsieurBiz\SyliusSearchPlugin\Repository\ProductAttributeRepositoryInterface; +use MonsieurBiz\SyliusSearchPlugin\Repository\ProductOptionRepositoryInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\AggregationBuilder; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\PostFilterRegistryInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\QueryFilterRegistryInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\SorterRegistryInterface; +use Sylius\Component\Channel\Context\ChannelContextInterface; +use Sylius\Component\Registry\ServiceRegistryInterface; + +final class Taxon implements RequestInterface +{ + private DocumentableInterface $documentable; + private ProductAttributeRepositoryInterface $productAttributeRepository; + private ProductOptionRepositoryInterface $productOptionRepository; + private ChannelContextInterface $channelContext; + private AggregationBuilder $aggregationBuilder; + private ?RequestConfiguration $configuration; + private QueryFilterRegistryInterface $queryFilterRegistry; + private PostFilterRegistryInterface $postFilterRegistry; + private SorterRegistryInterface $sorterRegistry; + + public function __construct( + ServiceRegistryInterface $documentableRegistry, + ProductAttributeRepositoryInterface $productAttributeRepository, + ProductOptionRepositoryInterface $productOptionRepository, + ChannelContextInterface $channelContext, + AggregationBuilder $aggregationBuilder, + QueryFilterRegistryInterface $queryFilterRegistry, + PostFilterRegistryInterface $postFilterRegistry, + SorterRegistryInterface $sorterRegistry + ) { + $this->documentable = $documentableRegistry->get('search.documentable.monsieurbiz_product'); + $this->productAttributeRepository = $productAttributeRepository; + $this->productOptionRepository = $productOptionRepository; + $this->channelContext = $channelContext; + $this->aggregationBuilder = $aggregationBuilder; + $this->queryFilterRegistry = $queryFilterRegistry; + $this->postFilterRegistry = $postFilterRegistry; + $this->sorterRegistry = $sorterRegistry; + } + + public function getType(): string + { + return RequestInterface::TAXON_TYPE; + } + + public function getDocumentable(): DocumentableInterface + { + return $this->documentable; + } + + public function getQuery(): Query + { + $qb = new QueryBuilder(); + + $boolQuery = $qb->query()->bool(); + foreach ($this->queryFilterRegistry->all() as $queryFilter) { + $queryFilter->apply($boolQuery, $this->configuration); + } + $query = Query::create($boolQuery); + $postFilter = new Query\BoolQuery(); + + foreach ($this->postFilterRegistry->all() as $postFilterApplier) { + $postFilterApplier->apply($postFilter, $this->configuration); + } + $query->setPostFilter($postFilter); + + $this->addAggregations($query, $postFilter); + + foreach ($this->sorterRegistry->all() as $sorter) { + $sorter->apply($query, $this->configuration); + } + + dump(json_encode($query->toArray(), 1)); + + return $query; + } + + public function supports(string $type, string $documentableCode): bool + { + return RequestInterface::TAXON_TYPE === $type && $documentableCode === $this->getDocumentable()->getIndexCode(); + } + + public function setConfiguration(RequestConfiguration $configuration): void + { + $this->configuration = $configuration; + } + + private function addAggregations(Query $query, Query\BoolQuery $postFilter): void + { + $aggregations = $this->aggregationBuilder->buildAggregations( + [ + $this->productAttributeRepository->findIsSearchableOrFilterable(), + $this->productOptionRepository->findIsSearchableOrFilterable(), + ['taxons' => $this->configuration->getTaxon()], + 'price', + ], + $postFilter->hasParam('must') ? $postFilter->getParam('must') : [] + ); + + foreach ($aggregations as $aggregation) { + $query->addAggregation($aggregation); + } + } +} diff --git a/src/Search/Request/QueryFilter/Product/ChannelFilter.php b/src/Search/Request/QueryFilter/Product/ChannelFilter.php new file mode 100644 index 00000000..b86f08ea --- /dev/null +++ b/src/Search/Request/QueryFilter/Product/ChannelFilter.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product; + +use Elastica\Query\BoolQuery; +use Elastica\QueryBuilder; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\QueryFilterInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; +use Sylius\Component\Channel\Context\ChannelContextInterface; + +final class ChannelFilter implements QueryFilterInterface +{ + private ChannelContextInterface $channelContext; + + public function __construct(ChannelContextInterface $channelContext) + { + $this->channelContext = $channelContext; + } + + public function apply(BoolQuery $boolQuery, RequestConfiguration $requestConfiguration): void + { + $qb = new QueryBuilder(); + + $boolQuery->addFilter( + $qb->query()->nested() + ->setPath('channels') + ->setQuery( + $qb->query()->term(['channels.code' => ['value' => $this->channelContext->getChannel()->getCode()]]) + ) + ); + } +} diff --git a/src/Search/Request/QueryFilter/Product/EnabledFilter.php b/src/Search/Request/QueryFilter/Product/EnabledFilter.php new file mode 100644 index 00000000..55f2857e --- /dev/null +++ b/src/Search/Request/QueryFilter/Product/EnabledFilter.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product; + +use Elastica\Query\BoolQuery; +use Elastica\QueryBuilder; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\QueryFilterInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; + +final class EnabledFilter implements QueryFilterInterface +{ + public function apply(BoolQuery $boolQuery, RequestConfiguration $requestConfiguration): void + { + $qb = new QueryBuilder(); + + $boolQuery->addFilter( + $qb->query()->term(['enabled' => ['value' => true]]) + ); + } +} diff --git a/src/Search/Request/QueryFilter/Product/SearchTermFilter.php b/src/Search/Request/QueryFilter/Product/SearchTermFilter.php new file mode 100644 index 00000000..781512f3 --- /dev/null +++ b/src/Search/Request/QueryFilter/Product/SearchTermFilter.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product; + +use Elastica\Query\BoolQuery; +use Elastica\Query\MultiMatch; +use Elastica\QueryBuilder; +use MonsieurBiz\SyliusSearchPlugin\Repository\ProductAttributeRepositoryInterface; +use MonsieurBiz\SyliusSearchPlugin\Repository\ProductOptionRepositoryInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\QueryFilterInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; + +final class SearchTermFilter implements QueryFilterInterface +{ + private ProductAttributeRepositoryInterface $productAttributeRepository; + private ProductOptionRepositoryInterface $productOptionRepository; + + public function __construct( + ProductAttributeRepositoryInterface $productAttributeRepository, + ProductOptionRepositoryInterface $productOptionRepository + ) { + $this->productAttributeRepository = $productAttributeRepository; + $this->productOptionRepository = $productOptionRepository; + } + + public function apply(BoolQuery $boolQuery, RequestConfiguration $requestConfiguration): void + { + $qb = new QueryBuilder(); + + $searchCode = $qb->query()->term(['code' => $requestConfiguration->getQueryText()]); + + $nameAndDescriptionQuery = $qb->query()->multi_match(); + $nameAndDescriptionQuery->setFields([ + 'name^5', // todo configuration + 'description', // move to should ? score impact but not include in result + ]); + $nameAndDescriptionQuery->setQuery($requestConfiguration->getQueryText()); + $nameAndDescriptionQuery->setType(MultiMatch::TYPE_MOST_FIELDS); + $nameAndDescriptionQuery->setFuzziness(MultiMatch::FUZZINESS_AUTO); + + $searchQuery = $qb->query()->bool(); + $searchQuery + ->addShould($searchCode) + ->addShould($nameAndDescriptionQuery) + ; + + $this->addAttributesQueries($searchQuery, $requestConfiguration); + $this->addOptionsQueries($searchQuery, $requestConfiguration); + + $boolQuery->addMust($searchQuery); + } + + protected function addAttributesQueries(BoolQuery $searchQuery, RequestConfiguration $requestConfiguration): void + { + $qb = new QueryBuilder(); + foreach ($this->productAttributeRepository->findIsSearchableOrFilterable() as $productAttribute) { + if (!$productAttribute->isSearchable()) { + continue; + } + + $attributeValueQuery = $qb->query()->multi_match(); + $attributeValueQuery->setFields([ + sprintf('attributes.%s.value^%d', $productAttribute->getCode(), $productAttribute->getSearchWeight()), + ]); + $attributeValueQuery->setQuery($requestConfiguration->getQueryText()); + $attributeValueQuery->setFuzziness(MultiMatch::FUZZINESS_AUTO); + + $attributeQuery = $qb->query()->nested(); + $attributeQuery->setPath(sprintf('attributes.%s', $productAttribute->getCode()))->setQuery($attributeValueQuery); + + $searchQuery->addShould($attributeQuery); + } + } + + protected function addOptionsQueries(BoolQuery $searchQuery, RequestConfiguration $requestConfiguration): void + { + $qb = new QueryBuilder(); + foreach ($this->productOptionRepository->findIsSearchableOrFilterable() as $productOption) { + if (!$productOption->isSearchable()) { + continue; + } + + $attributeValueQuery = $qb->query()->multi_match(); + $attributeValueQuery->setFields([ + sprintf('variants.options.%s.value^%d', $productOption->getCode(), $productOption->getSearchWeight()), + ]); + $attributeValueQuery->setQuery($requestConfiguration->getQueryText()); + $attributeValueQuery->setFuzziness(MultiMatch::FUZZINESS_AUTO); + + $attributeQuery = $qb->query()->nested(); + $attributeQuery->setPath(sprintf('variants.options.%s', $productOption->getCode()))->setQuery($attributeValueQuery); + + $searchQuery->addShould($attributeQuery); + } + } +} diff --git a/src/Search/Request/QueryFilter/Product/TaxonFilter.php b/src/Search/Request/QueryFilter/Product/TaxonFilter.php new file mode 100644 index 00000000..85ae883a --- /dev/null +++ b/src/Search/Request/QueryFilter/Product/TaxonFilter.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product; + +use Elastica\Query\BoolQuery; +use Elastica\QueryBuilder; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\QueryFilterInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; + +final class TaxonFilter implements QueryFilterInterface +{ + public function apply(BoolQuery $boolQuery, RequestConfiguration $requestConfiguration): void + { + $qb = new QueryBuilder(); + $searchQuery = $qb->query()->nested() + ->setPath('product_taxons') + ->setQuery( + $qb->query()->nested() + ->setPath('product_taxons.taxon') + ->setQuery( + $qb->query()->term(['product_taxons.taxon.code' => ['value' => $requestConfiguration->getTaxon()->getCode()]]) + ) + ) + ; + if ($requestConfiguration->getTaxon()->isRoot()) { + $searchQuery = $qb->query()->bool(); + } + + $boolQuery->addMust($searchQuery); + } +} diff --git a/src/Search/Request/QueryFilter/ProductSearchRegistry.php b/src/Search/Request/QueryFilter/ProductSearchRegistry.php new file mode 100644 index 00000000..7d2a151a --- /dev/null +++ b/src/Search/Request/QueryFilter/ProductSearchRegistry.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter; + +use Sylius\Component\Registry\ServiceRegistry; + +final class ProductSearchRegistry extends ServiceRegistry implements QueryFilterRegistryInterface +{ + public function __construct(array $queryFilters = []) + { + parent::__construct(QueryFilterInterface::class, 'monsieurbiz.search'); + + foreach ($queryFilters as $queryFilter) { + $this->register(\get_class($queryFilter), $queryFilter); + } + } +} diff --git a/src/Search/Request/QueryFilter/ProductTaxonRegistry.php b/src/Search/Request/QueryFilter/ProductTaxonRegistry.php new file mode 100644 index 00000000..acae5910 --- /dev/null +++ b/src/Search/Request/QueryFilter/ProductTaxonRegistry.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter; + +use Sylius\Component\Registry\ServiceRegistry; + +final class ProductTaxonRegistry extends ServiceRegistry implements QueryFilterRegistryInterface +{ + public function __construct(array $queryFilters = []) + { + parent::__construct(QueryFilterInterface::class, 'monsieurbiz.search'); + + foreach ($queryFilters as $queryFilter) { + $this->register(\get_class($queryFilter), $queryFilter); + } + } +} diff --git a/src/Search/Request/QueryFilter/QueryFilterInterface.php b/src/Search/Request/QueryFilter/QueryFilterInterface.php new file mode 100644 index 00000000..6e40a8b0 --- /dev/null +++ b/src/Search/Request/QueryFilter/QueryFilterInterface.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter; + +use Elastica\Query\BoolQuery; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; + +interface QueryFilterInterface +{ + public function apply(BoolQuery $boolQuery, RequestConfiguration $requestConfiguration): void; +} diff --git a/src/Search/Request/QueryFilter/QueryFilterRegistryInterface.php b/src/Search/Request/QueryFilter/QueryFilterRegistryInterface.php new file mode 100644 index 00000000..99926407 --- /dev/null +++ b/src/Search/Request/QueryFilter/QueryFilterRegistryInterface.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter; + +use Sylius\Component\Registry\ServiceRegistryInterface; + +interface QueryFilterRegistryInterface extends ServiceRegistryInterface +{ +} diff --git a/src/Search/Request/Search/Product.php b/src/Search/Request/Search/Product.php deleted file mode 100644 index 77876de9..00000000 --- a/src/Search/Request/Search/Product.php +++ /dev/null @@ -1,302 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Search; - -use Elastica\Query; -use Elastica\Query\MultiMatch; -use MonsieurBiz\SyliusSearchPlugin\Helper\SlugHelper; -use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; -use MonsieurBiz\SyliusSearchPlugin\Repository\ProductAttributeRepositoryInterface; -use MonsieurBiz\SyliusSearchPlugin\Repository\ProductOptionRepositoryInterface; -use MonsieurBiz\SyliusSearchPlugin\Search\Request\AggregationBuilder; -use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; -use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; -use Sylius\Component\Channel\Context\ChannelContextInterface; -use Sylius\Component\Registry\ServiceRegistryInterface; - -class Product implements RequestInterface -{ - private DocumentableInterface $documentable; - private RequestConfiguration $configuration; - private ProductAttributeRepositoryInterface $productAttributeRepository; - private ProductOptionRepositoryInterface $productOptionRepository; - private ChannelContextInterface $channelContext; - private AggregationBuilder $aggregationBuilder; - - public function __construct( - ServiceRegistryInterface $documentableRegistry, - ProductAttributeRepositoryInterface $productAttributeRepository, - ProductOptionRepositoryInterface $productOptionRepository, - ChannelContextInterface $channelContext, - AggregationBuilder $aggregationBuilder - ) { - //TODO check if exist, return a dummy documentable if not - $this->documentable = $documentableRegistry->get('search.documentable.monsieurbiz_product'); - $this->productAttributeRepository = $productAttributeRepository; - $this->productOptionRepository = $productOptionRepository; - $this->channelContext = $channelContext; - $this->aggregationBuilder = $aggregationBuilder; - } - - public function getType(): string - { - return RequestInterface::SEARCH_TYPE; - } - - public function getDocumentable(): DocumentableInterface - { - return $this->documentable; - } - - public function setConfiguration(RequestConfiguration $configuration): void - { - $this->configuration = $configuration; - } - - public function getQuery(): Query - { - if ('' === $this->configuration->getQueryText()) { - throw new \Exception('missing query text'); //todo - } - - $enableFilter = new Query\Terms('enabled', [true]); - $currentChannelFilter = new Query\Term(); - $currentChannelFilter->setTerm('channels.code', $this->channelContext->getChannel()->getCode()); - $channelFilter = new Query\Nested(); - $channelFilter->setPath('channels'); - $channelFilter->setQuery($currentChannelFilter); - - $searchCode = new Query\Terms('code', [$this->configuration->getQueryText()]); - - $nameAndDescriptionQuery = new MultiMatch(); - $nameAndDescriptionQuery->setFields([ - 'name^5', // todo configuration - 'description', // move to should ? score impact but not include in result - ]); - $nameAndDescriptionQuery->setQuery($this->configuration->getQueryText()); - $nameAndDescriptionQuery->setType(MultiMatch::TYPE_MOST_FIELDS); - $nameAndDescriptionQuery->setFuzziness(MultiMatch::FUZZINESS_AUTO); - - $searchQuery = new Query\BoolQuery(); - $searchQuery - ->addShould($searchCode) - ->addShould($nameAndDescriptionQuery) - ; - - $this->addAttributesQueries($searchQuery); - - $bool = new Query\BoolQuery(); - $bool->addFilter($enableFilter); - $bool->addFilter($channelFilter); - $bool->addMust($searchQuery); - - $esQuery = Query::create($bool); - $boolFilter = new Query\BoolQuery(); - foreach ($this->getFilters() as $filter) { - $boolFilter->addMust($filter); - } - $esQuery->setPostFilter($boolFilter); - $this->addAggregations($esQuery); - - // Manage sorting - foreach ($this->configuration->getSorting() as $field => $order) { - $sort = $this->getSort($field, $order); - if (0 !== \count($sort)) { - $esQuery->addSort($this->getSort($field, $order)); - } - } - - dump(json_encode($esQuery->toArray(), 1)); - - return $esQuery; - } - - public function supports(string $type, string $documentableCode): bool - { - return $type == $this->getType() && $this->getDocumentable()->getIndexCode() == $documentableCode; - } - - private function addAttributesQueries(Query\BoolQuery $searchQuery): void - { - foreach ($this->productAttributeRepository->findIsSearchableOrFilterable() as $productAttribute) { - if (!$productAttribute->isSearchable()) { - continue; - } - - $attributeValueQuery = new MultiMatch(); - $attributeValueQuery->setFields([ - sprintf('attributes.%s.value^%d', $productAttribute->getCode(), $productAttribute->getSearchWeight()), - ]); - $attributeValueQuery->setQuery($this->configuration->getQueryText()); - $attributeValueQuery->setFuzziness(MultiMatch::FUZZINESS_AUTO); - - $attributeQuery = new Query\Nested(); - $attributeQuery->setPath(sprintf('attributes.%s', $productAttribute->getCode()))->setQuery($attributeValueQuery); - - $attributesQuery = new Query\Nested(); - $attributesQuery->setPath('attributes')->setQuery($attributeQuery); - - $searchQuery->addShould($attributeQuery); - } - } - - private function addAggregations(Query $query): void - { - $newAggs = $this->aggregationBuilder->buildAggregations( - [ - $this->productAttributeRepository->findIsSearchableOrFilterable(), - $this->productOptionRepository->findIsSearchableOrFilterable(), - 'main_taxon', - 'price', - ], - $this->getFilters() - ); - - foreach ($newAggs as $aggregation) { - $query->addAggregation($aggregation); - } - } - - private function getFilters(): array - { - $filters = []; - - $qb = new \Elastica\QueryBuilder(); - - foreach ($this->configuration->getAppliedFilters('taxon') as $field => $values) { - $mainTaxonQuery = $qb->query() - ->bool() - ; - foreach ($values as $value) { - $mainTaxonQuery->addShould( - $qb->query() - ->term() - ->setTerm(sprintf('%s.code', $field), SlugHelper::toLabel($value)) - ); - } - $filters['main_taxons'] = $qb->query() - ->nested() - ->setPath($field) - ->setQuery( - $mainTaxonQuery - ) - ; - } - - $priceValue = $this->configuration->getAppliedFilters('price'); - if (0 !== \count($priceValue)) { - $qb = new \Elastica\QueryBuilder(); - - // channel filter - $channelPriceFilter = $qb->query() - ->term(['prices.channel_code' => $this->channelContext->getChannel()->getCode()]) - ; - $conditions = []; - if (\array_key_exists('min', $priceValue)) { - $conditions['gte'] = $priceValue['min'] * 100; - } - if (\array_key_exists('max', $priceValue)) { - $conditions['lte'] = $priceValue['max'] * 100; - } - $priceQuery = $qb->query() - ->range('prices.price', $conditions) - ; - $filters['price'] = $qb->query() - ->nested() - ->setPath('prices') - ->setQuery( - $qb->query()->bool() - ->addMust($channelPriceFilter) - ->addMust($priceQuery) - ) - ; - } - - foreach ($this->configuration->getAppliedFilters('attributes') as $field => $values) { - $attributeValueQuery = new Query\BoolQuery(); - - foreach ($values as $value) { - $termQuery = new Query\Terms(sprintf('attributes.%s.value.keyword', $field), [SlugHelper::toLabel($value)]); - $attributeValueQuery->addShould($termQuery); // todo configure the "and" or "or" - } - - $attributeQuery = new Query\Nested(); - $attributeQuery->setPath(sprintf('attributes.%s', $field))->setQuery($attributeValueQuery); - - $filters['attributes.' . $field] = $attributeQuery; - } - - foreach ($this->configuration->getAppliedFilters('options') as $field => $values) { - $attributeValueQuery = new Query\BoolQuery(); - - foreach ($values as $value) { - $termQuery = new Query\Terms(sprintf('variants.options.%s.value.keyword', $field), [SlugHelper::toLabel($value)]); - $attributeValueQuery->addShould($termQuery); // todo configure the "and" or "or" - } - - $attributeQuery = new Query\Nested(); - $attributeQuery->setPath(sprintf('variants.options.%s', $field))->setQuery($attributeValueQuery); - - $filters['options.' . $field] = $attributeQuery; - } - - return $filters; - } - - // todo find solution to get more extendable - private function getSort(string $field, string $order) - { - $fieldName = $field; - if ('name' == $field) { - $fieldName = $field . '.keyword'; - } - - switch ($field) { - case 'name': - case 'created_at': - return $this->buildSort($fieldName, $order); - case 'price': - return self::buildSort( - 'prices.price', - $order, - 'prices', - 'prices.channel_code', $this->channelContext->getChannel()->getCode() - ); - case 'position': - default: - // Dummy value to have null sorting in ES and keep ES results sorting - return $this->buildSort('_score', 'desc'); - } - } - - private function buildSort( - string $field, - string $order, - ?string $nestedPath = null, - ?string $sortFilterField = null, - ?string $sortFilterValue = null - ): array { - $sort = [$field => ['order' => $order]]; - if (null !== $nestedPath) { - $sort[$field]['nested_path'] = $nestedPath; - $sort[$field]['nested_filter'] = [ - 'term' => [ - $sortFilterField => $sortFilterValue, - ], - ]; - } - - return $sort; - } -} diff --git a/src/Search/Request/Sorting/Product/CreatedAtSorter.php b/src/Search/Request/Sorting/Product/CreatedAtSorter.php new file mode 100644 index 00000000..108c7094 --- /dev/null +++ b/src/Search/Request/Sorting/Product/CreatedAtSorter.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\Product; + +use Elastica\Query; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\SorterBuilderTrait; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\SorterInterface; + +final class CreatedAtSorter implements SorterInterface +{ + use SorterBuilderTrait; + + public function apply(Query $query, RequestConfiguration $requestConfiguration): void + { + $sorting = $requestConfiguration->getSorting(); + if (!\array_key_exists('created_at', $sorting)) { + return; + } + + $query->addSort($this->buildSort('created_at', $sorting['created_at'])); + } +} diff --git a/src/Search/Request/Sorting/Product/NameSorter.php b/src/Search/Request/Sorting/Product/NameSorter.php new file mode 100644 index 00000000..e2705de8 --- /dev/null +++ b/src/Search/Request/Sorting/Product/NameSorter.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\Product; + +use Elastica\Query; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\SorterBuilderTrait; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\SorterInterface; + +final class NameSorter implements SorterInterface +{ + use SorterBuilderTrait; + + public function apply(Query $query, RequestConfiguration $requestConfiguration): void + { + $sorting = $requestConfiguration->getSorting(); + if (!\array_key_exists('name', $sorting)) { + return; + } + + $query->addSort($this->buildSort('name.keyword', $sorting['name'])); + } +} diff --git a/src/Search/Request/Sorting/Product/PositionSorter.php b/src/Search/Request/Sorting/Product/PositionSorter.php new file mode 100644 index 00000000..32f3efa7 --- /dev/null +++ b/src/Search/Request/Sorting/Product/PositionSorter.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\Product; + +use Elastica\Query; +use Elastica\QueryBuilder; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\SorterBuilderTrait; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\SorterInterface; + +final class PositionSorter implements SorterInterface +{ + use SorterBuilderTrait; + + public function apply(Query $query, RequestConfiguration $requestConfiguration): void + { + $sorting = $requestConfiguration->getSorting(); + if (!\array_key_exists('position', $sorting) && 0 !== \count($sorting)) { + return; + } + + $sort = $this->buildSort('_score', 'desc'); + if (RequestInterface::TAXON_TYPE == $requestConfiguration->getType()) { + $qb = new QueryBuilder(); + $filter = $qb->query()->nested() + ->setPath('product_taxons.taxon') + ->setQuery( + $qb->query()->term(['product_taxons.taxon.code' => ['value' => $requestConfiguration->getTaxon()->getCode()]]) + ) + ; + $sort = $this->buildSort('product_taxons.position', 'asc', 'product_taxons', null, $filter); + } + + $query->addSort($sort); + } +} diff --git a/src/Search/Request/Sorting/Product/PriceSorter.php b/src/Search/Request/Sorting/Product/PriceSorter.php new file mode 100644 index 00000000..092eddd1 --- /dev/null +++ b/src/Search/Request/Sorting/Product/PriceSorter.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\Product; + +use Elastica\Query; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\SorterBuilderTrait; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\SorterInterface; +use Sylius\Component\Channel\Context\ChannelContextInterface; + +final class PriceSorter implements SorterInterface +{ + use SorterBuilderTrait; + + private ChannelContextInterface $channelContext; + + public function __construct(ChannelContextInterface $channelContext) + { + $this->channelContext = $channelContext; + } + + public function apply(Query $query, RequestConfiguration $requestConfiguration): void + { + $sorting = $requestConfiguration->getSorting(); + if (!\array_key_exists('price', $sorting)) { + return; + } + + $query->addSort( + $this->buildSort( + 'prices.price', + $sorting['price'], + 'prices', + 'prices.channel_code', + $this->channelContext->getChannel()->getCode() + ) + ); + } +} diff --git a/src/Search/Request/Sorting/ProductSorterRegistry.php b/src/Search/Request/Sorting/ProductSorterRegistry.php new file mode 100644 index 00000000..5a5c9924 --- /dev/null +++ b/src/Search/Request/Sorting/ProductSorterRegistry.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting; + +use Sylius\Component\Registry\ServiceRegistry; + +final class ProductSorterRegistry extends ServiceRegistry implements SorterRegistryInterface +{ + public function __construct(array $queryFilters = []) + { + parent::__construct(SorterInterface::class, 'monsieurbiz.search'); + + foreach ($queryFilters as $queryFilter) { + $this->register(\get_class($queryFilter), $queryFilter); + } + } +} diff --git a/src/Search/Request/Sorting/SorterBuilderTrait.php b/src/Search/Request/Sorting/SorterBuilderTrait.php new file mode 100644 index 00000000..798d41ff --- /dev/null +++ b/src/Search/Request/Sorting/SorterBuilderTrait.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting; + +use Elastica\Query\AbstractQuery; + +trait SorterBuilderTrait +{ + protected function buildSort( + string $field, + string $order, + ?string $nestedPath = null, + ?string $sortFilterField = null, + $sortFilterValue = null + ): array { + $sort = [$field => ['order' => $order]]; + if (null !== $nestedPath) { + $sort[$field]['nested']['path'] = $nestedPath; + $filter = [ + 'term' => [ + $sortFilterField => $sortFilterValue, + ], + ]; + if ($sortFilterValue instanceof AbstractQuery) { + $filter = $sortFilterValue->toArray(); + } + $sort[$field]['nested']['filter'] = $filter; + } + + return $sort; + } +} diff --git a/src/Search/Request/Sorting/SorterInterface.php b/src/Search/Request/Sorting/SorterInterface.php new file mode 100644 index 00000000..1487f469 --- /dev/null +++ b/src/Search/Request/Sorting/SorterInterface.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting; + +use Elastica\Query; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; + +interface SorterInterface +{ + public function apply(Query $query, RequestConfiguration $requestConfiguration): void; +} diff --git a/src/Search/Request/Sorting/SorterRegistryInterface.php b/src/Search/Request/Sorting/SorterRegistryInterface.php new file mode 100644 index 00000000..01d4e017 --- /dev/null +++ b/src/Search/Request/Sorting/SorterRegistryInterface.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting; + +use Sylius\Component\Registry\ServiceRegistryInterface; + +interface SorterRegistryInterface extends ServiceRegistryInterface +{ +} diff --git a/src/Search/Request/Taxon/Product.php b/src/Search/Request/Taxon/Product.php deleted file mode 100644 index a8251e69..00000000 --- a/src/Search/Request/Taxon/Product.php +++ /dev/null @@ -1,313 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Taxon; - -use Elastica\Query; -use Elastica\QueryBuilder; -use MonsieurBiz\SyliusSearchPlugin\Helper\SlugHelper; -use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; -use MonsieurBiz\SyliusSearchPlugin\Repository\ProductAttributeRepositoryInterface; -use MonsieurBiz\SyliusSearchPlugin\Repository\ProductOptionRepositoryInterface; -use MonsieurBiz\SyliusSearchPlugin\Search\Request\AggregationBuilder; -use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; -use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; -use Sylius\Component\Channel\Context\ChannelContextInterface; -use Sylius\Component\Registry\ServiceRegistryInterface; - -class Product implements RequestInterface -{ - private DocumentableInterface $documentable; - private ProductAttributeRepositoryInterface $productAttributeRepository; - private ProductOptionRepositoryInterface $productOptionRepository; - private ChannelContextInterface $channelContext; - private AggregationBuilder $aggregationBuilder; - private ?RequestConfiguration $configuration; - - public function __construct( - ServiceRegistryInterface $documentableRegistry, - ProductAttributeRepositoryInterface $productAttributeRepository, - ProductOptionRepositoryInterface $productOptionRepository, - ChannelContextInterface $channelContext, - AggregationBuilder $aggregationBuilder - ) { - $this->documentable = $documentableRegistry->get('search.documentable.monsieurbiz_product'); - $this->productAttributeRepository = $productAttributeRepository; - $this->productOptionRepository = $productOptionRepository; - $this->channelContext = $channelContext; - $this->aggregationBuilder = $aggregationBuilder; - } - - public function getType(): string - { - return RequestInterface::TAXON_TYPE; - } - - public function getDocumentable(): DocumentableInterface - { - return $this->documentable; - } - - public function getQuery(): Query - { - $qb = new QueryBuilder(); - $searchQuery = $qb->query()->nested() - ->setPath('product_taxons') - ->setQuery( - $qb->query()->nested() - ->setPath('product_taxons.taxon') - ->setQuery( - $qb->query()->term(['product_taxons.taxon.code' => ['value' => $this->configuration->getTaxon()->getCode()]]) - ) - ) - ; - - if ($this->configuration->getTaxon()->isRoot()) { - $searchQuery = $qb->query()->bool(); - } - - $boolQuery = $qb->query()->bool() - ->addFilter($qb->query()->term(['enabled' => ['value' => true]])) - ->addFilter( - $qb->query()->nested() - ->setPath('channels') - ->setQuery( - $qb->query()->term(['channels.code' => ['value' => $this->channelContext->getChannel()->getCode()]]) - ) - ) - ->addMust($searchQuery) - ; - - $query = Query::create($boolQuery); - - $postFilter = new Query\BoolQuery(); - foreach ($this->getFilters() as $filter) { - $postFilter->addMust($filter); - } - $query->setPostFilter($postFilter); - $this->addAggregations($query); - - // Manage sorting - $sorts = $this->configuration->getSorting() ?: ['position' => 'desc']; - foreach ($sorts as $field => $order) { - $sort = $this->getSort($field, $order); - if (0 !== \count($sort)) { - $query->addSort($sort); - } - } - - dump(json_encode($query->toArray(), 1)); - - return $query; - } - - public function supports(string $type, string $documentableCode): bool - { - return RequestInterface::TAXON_TYPE === $type && $documentableCode === $this->getDocumentable()->getIndexCode(); - } - - public function setConfiguration(RequestConfiguration $configuration): void - { - $this->configuration = $configuration; - } - - private function getFilters(): array - { - $filters = []; - - $qb = new \Elastica\QueryBuilder(); - - foreach ($this->configuration->getAppliedFilters('taxon') as $field => $values) { - $mainTaxonQuery = $qb->query() - ->bool() - ; - foreach ($values as $value) { - $mainTaxonQuery->addShould( - $qb->query() - ->term() - ->setTerm(sprintf('%s.code', $field), SlugHelper::toLabel($value)) - ); - } - $filters['main_taxons'] = $qb->query() - ->nested() - ->setPath($field) - ->setQuery( - $mainTaxonQuery - ) - ; - } - - $taxonsSelected = $this->configuration->getAppliedFilters('taxons'); - if (0 !== \count($taxonsSelected)) { - $taxonQuery = $qb->query() - ->bool() - ; - foreach ($this->configuration->getAppliedFilters('taxons') as $value) { - $taxonQuery->addShould( - $qb->query() - ->term() - ->setTerm('product_taxons.taxon.code', SlugHelper::toLabel($value)) - ); - } - - $filters['taxons'] = $qb->query() - ->nested() - ->setPath('product_taxons') - ->setQuery( - $qb->query()->nested() - ->setPath('product_taxons.taxon') - ->setQuery($taxonQuery) - ) - ; - } - - $priceValue = $this->configuration->getAppliedFilters('price'); - if (0 !== \count($priceValue)) { - $qb = new \Elastica\QueryBuilder(); - - // channel filter - $channelPriceFilter = $qb->query() - ->term(['prices.channel_code' => $this->channelContext->getChannel()->getCode()]) - ; - $conditions = []; - if (\array_key_exists('min', $priceValue)) { - $conditions['gte'] = $priceValue['min'] * 100; - } - if (\array_key_exists('max', $priceValue)) { - $conditions['lte'] = $priceValue['max'] * 100; - } - $priceQuery = $qb->query() - ->range('prices.price', $conditions) - ; - $filters['price'] = $qb->query() - ->nested() - ->setPath('prices') - ->setQuery( - $qb->query()->bool() - ->addMust($channelPriceFilter) - ->addMust($priceQuery) - ) - ; - } - - foreach ($this->configuration->getAppliedFilters('attributes') as $field => $values) { - $attributeValueQuery = new Query\BoolQuery(); - - foreach ($values as $value) { - $termQuery = new Query\Terms(sprintf('attributes.%s.value.keyword', $field), [SlugHelper::toLabel($value)]); - $attributeValueQuery->addShould($termQuery); // todo configure the "and" or "or" - } - - $attributeQuery = new Query\Nested(); - $attributeQuery->setPath(sprintf('attributes.%s', $field))->setQuery($attributeValueQuery); - - $filters['attributes.' . $field] = $attributeQuery; - } - - foreach ($this->configuration->getAppliedFilters('options') as $field => $values) { - $attributeValueQuery = new Query\BoolQuery(); - - foreach ($values as $value) { - $termQuery = new Query\Terms(sprintf('variants.options.%s.value.keyword', $field), [SlugHelper::toLabel($value)]); - $attributeValueQuery->addShould($termQuery); // todo configure the "and" or "or" - } - - $attributeQuery = new Query\Nested(); - $attributeQuery->setPath(sprintf('variants.options.%s', $field))->setQuery($attributeValueQuery); - - $filters['options.' . $field] = $attributeQuery; - } - -// dump($filters);die; - - return $filters; - } - - private function addAggregations(Query $query): void - { - $newAggs = $this->aggregationBuilder->buildAggregations( - [ - $this->productAttributeRepository->findIsSearchableOrFilterable(), - $this->productOptionRepository->findIsSearchableOrFilterable(), - ['taxons' => $this->configuration->getTaxon()], - 'price', - ], - $this->getFilters() - ); - - foreach ($newAggs as $aggregation) { - $query->addAggregation($aggregation); - } - } - - // todo find solution to get more extendable - private function getSort(string $field, string $order) - { - $fieldName = $field; - if ('name' == $field) { - $fieldName = $field . '.keyword'; - } - - switch ($field) { - case 'name': - case 'created_at': - return $this->buildSort($fieldName, $order); - case 'price': - return self::buildSort( - 'prices.price', - $order, - 'prices', - 'prices.channel_code', $this->channelContext->getChannel()->getCode() - ); - case 'position': - default: - $qb = new QueryBuilder(); - $filter = $qb->query()->nested() - ->setPath('product_taxons.taxon') - ->setQuery( - $qb->query()->term(['product_taxons.taxon.code' => ['value' => $this->configuration->getTaxon()->getCode()]]) - ) - ; - - return $this->buildSort('product_taxons.position', 'asc', 'product_taxons', null, $filter); - } - } - - /** - * @param string|Query\AbstractQuery|null $sortFilterValue - */ - private function buildSort( - string $field, - string $order, - ?string $nestedPath = null, - ?string $sortFilterField = null, - $sortFilterValue = null - ): array { - $sort = [$field => ['order' => $order]]; - if (null !== $nestedPath) { - $sort[$field]['nested']['path'] = $nestedPath; - if ($sortFilterValue instanceof Query\AbstractQuery) { - $filter = $sortFilterValue->toArray(); - } else { - $filter = [ - 'term' => [ - $sortFilterField => $sortFilterValue, - ], - ]; - } - $sort[$field]['nested']['filter'] = $filter; - } - - return $sort; - } -} From d8b4a635b618e22a0793f37c9d100c7239ba14b9 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 20 Dec 2021 08:18:59 +0100 Subject: [PATCH 079/142] fix: make the configuraiton directory argument of yaml provider easier to override --- .../AutowireMappingProviderParameterPass.php | 41 +++++++++++++++++++ src/MonsieurBizSyliusSearchPlugin.php | 2 + src/Resources/config/services.yaml | 5 ++- 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 src/DependencyInjection/AutowireMappingProviderParameterPass.php diff --git a/src/DependencyInjection/AutowireMappingProviderParameterPass.php b/src/DependencyInjection/AutowireMappingProviderParameterPass.php new file mode 100644 index 00000000..efbe6c60 --- /dev/null +++ b/src/DependencyInjection/AutowireMappingProviderParameterPass.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\DependencyInjection; + +use JoliCode\Elastically\Mapping\YamlProvider; +use MonsieurBiz\SyliusSearchPlugin\Mapping\YamlWithLocaleProvider; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException; + +class AutowireMappingProviderParameterPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container): void + { + if (!$container->hasDefinition(YamlProvider::class) || !$container->hasDefinition(YamlWithLocaleProvider::class)) { + return; + } + + $yamlMappingProvider = $container->getDefinition(YamlProvider::class); + $decoratedYamlMappingProvider = $container->getDefinition(YamlWithLocaleProvider::class); + try { + $decoratedYamlMappingProvider->setArgument( + '$configurationDirectory', + $yamlMappingProvider->getArgument('$configurationDirectory') + ); + } catch (OutOfBoundsException $exception) { + // yaml provider service has no configuration directory argument + } + } +} diff --git a/src/MonsieurBizSyliusSearchPlugin.php b/src/MonsieurBizSyliusSearchPlugin.php index e5282258..620259ac 100644 --- a/src/MonsieurBizSyliusSearchPlugin.php +++ b/src/MonsieurBizSyliusSearchPlugin.php @@ -14,6 +14,7 @@ namespace MonsieurBiz\SyliusSearchPlugin; use MonsieurBiz\SyliusSearchPlugin\DependencyInjection\AutomapperConfigurationRegistryPass; +use MonsieurBiz\SyliusSearchPlugin\DependencyInjection\AutowireMappingProviderParameterPass; use MonsieurBiz\SyliusSearchPlugin\DependencyInjection\DocumentableRegistryPass; use MonsieurBiz\SyliusSearchPlugin\DependencyInjection\RegisterSearchRequestPass; use Sylius\Bundle\CoreBundle\Application\SyliusPluginTrait; @@ -46,5 +47,6 @@ public function build(ContainerBuilder $container): void $container->addCompilerPass(new DocumentableRegistryPass()); $container->addCompilerPass(new RegisterSearchRequestPass()); $container->addCompilerPass(new AutomapperConfigurationRegistryPass()); + $container->addCompilerPass(new AutowireMappingProviderParameterPass()); } } diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index 31f7e203..801888d0 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -14,7 +14,6 @@ services: $productVariantResolver: '@sylius.product_variant_resolver.default' $documentableRegistry: '@monsieurbiz.search.registry.documentable' $searchRequestsRegistry: '@monsieurbiz.search.registry.search_request' - $configurationDirectory: '@=service("file_locator").locate("@MonsieurBizSyliusSearchPlugin/Resources/config/elasticsearch")' MonsieurBiz\SyliusSearchPlugin\: resource: '../../*' @@ -40,7 +39,9 @@ services: $attributeRepository: '@sylius.repository.product_attribute' # Define our mapping provider - JoliCode\Elastically\Mapping\YamlProvider: ~ + JoliCode\Elastically\Mapping\YamlProvider: + arguments: + $configurationDirectory: '@=service("file_locator").locate("@MonsieurBizSyliusSearchPlugin/Resources/config/elasticsearch")' MonsieurBiz\SyliusSearchPlugin\Mapping\YamlWithLocaleProvider: decorates: JoliCode\Elastically\Mapping\YamlProvider arguments: From 695788cc2ef69a1dfa44f25b40fd9c678536757c Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Thu, 23 Dec 2021 15:35:07 +0100 Subject: [PATCH 080/142] refactor: add filter builder classes --- src/Resources/config/services.yaml | 10 ++ src/Search/Request/ProductRequest/Search.php | 4 +- src/Search/Request/ProductRequest/Taxon.php | 4 +- .../QueryFilter/Product/SearchTermFilter.php | 4 +- src/Search/Response.php | 117 +++--------------- .../FilterBuilders/FilterBuilderInterface.php | 33 +++++ .../Product/AttributeFilterBuilder.php | 74 +++++++++++ .../Product/MainTaxonFilterBuilder.php | 79 ++++++++++++ .../Product/PriceFilterBuilder.php | 55 ++++++++ .../Product/TaxonsFilterBuilder.php | 65 ++++++++++ src/Search/ResponseFactory.php | 38 ++++++ src/Search/Search.php | 14 ++- 12 files changed, 390 insertions(+), 107 deletions(-) create mode 100644 src/Search/Response/FilterBuilders/FilterBuilderInterface.php create mode 100644 src/Search/Response/FilterBuilders/Product/AttributeFilterBuilder.php create mode 100644 src/Search/Response/FilterBuilders/Product/MainTaxonFilterBuilder.php create mode 100644 src/Search/Response/FilterBuilders/Product/PriceFilterBuilder.php create mode 100644 src/Search/Response/FilterBuilders/Product/TaxonsFilterBuilder.php create mode 100644 src/Search/ResponseFactory.php diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index 801888d0..75fd6327 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -142,3 +142,13 @@ services: $queryFilterRegistry: '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\ProductTaxonRegistry' $postFilterRegistry: '@MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\ProductTaxonRegistry' $sorterRegistry: '@MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\ProductSorterRegistry' + + # Define the filter builders + MonsieurBiz\SyliusSearchPlugin\Search\ResponseFactory: + arguments: + $filterBuilders: { + product_main_taxon: '@MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders\Product\MainTaxonFilterBuilder', + product_taxons: '@MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders\Product\TaxonsFilterBuilder', + product_price: '@MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders\Product\PriceFilterBuilder', + product_attribute: '@MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders\Product\AttributeFilterBuilder' + } diff --git a/src/Search/Request/ProductRequest/Search.php b/src/Search/Request/ProductRequest/Search.php index 847e87f6..e84c34c2 100644 --- a/src/Search/Request/ProductRequest/Search.php +++ b/src/Search/Request/ProductRequest/Search.php @@ -112,10 +112,10 @@ private function addAggregations(Query $query, Query\BoolQuery $postFilter): voi { $aggregations = $this->aggregationBuilder->buildAggregations( [ - $this->productAttributeRepository->findIsSearchableOrFilterable(), - $this->productOptionRepository->findIsSearchableOrFilterable(), 'main_taxon', 'price', + $this->productAttributeRepository->findIsSearchableOrFilterable(), + $this->productOptionRepository->findIsSearchableOrFilterable(), ], $postFilter->hasParam('must') ? $postFilter->getParam('must') : [] ); diff --git a/src/Search/Request/ProductRequest/Taxon.php b/src/Search/Request/ProductRequest/Taxon.php index 96518a9d..51e0c5af 100644 --- a/src/Search/Request/ProductRequest/Taxon.php +++ b/src/Search/Request/ProductRequest/Taxon.php @@ -110,10 +110,10 @@ private function addAggregations(Query $query, Query\BoolQuery $postFilter): voi { $aggregations = $this->aggregationBuilder->buildAggregations( [ - $this->productAttributeRepository->findIsSearchableOrFilterable(), - $this->productOptionRepository->findIsSearchableOrFilterable(), ['taxons' => $this->configuration->getTaxon()], 'price', + $this->productAttributeRepository->findIsSearchableOrFilterable(), + $this->productOptionRepository->findIsSearchableOrFilterable(), ], $postFilter->hasParam('must') ? $postFilter->getParam('must') : [] ); diff --git a/src/Search/Request/QueryFilter/Product/SearchTermFilter.php b/src/Search/Request/QueryFilter/Product/SearchTermFilter.php index 781512f3..fc6acc41 100644 --- a/src/Search/Request/QueryFilter/Product/SearchTermFilter.php +++ b/src/Search/Request/QueryFilter/Product/SearchTermFilter.php @@ -61,7 +61,7 @@ public function apply(BoolQuery $boolQuery, RequestConfiguration $requestConfigu $boolQuery->addMust($searchQuery); } - protected function addAttributesQueries(BoolQuery $searchQuery, RequestConfiguration $requestConfiguration): void + private function addAttributesQueries(BoolQuery $searchQuery, RequestConfiguration $requestConfiguration): void { $qb = new QueryBuilder(); foreach ($this->productAttributeRepository->findIsSearchableOrFilterable() as $productAttribute) { @@ -83,7 +83,7 @@ protected function addAttributesQueries(BoolQuery $searchQuery, RequestConfigura } } - protected function addOptionsQueries(BoolQuery $searchQuery, RequestConfiguration $requestConfiguration): void + private function addOptionsQueries(BoolQuery $searchQuery, RequestConfiguration $requestConfiguration): void { $qb = new QueryBuilder(); foreach ($this->productOptionRepository->findIsSearchableOrFilterable() as $productOption) { diff --git a/src/Search/Response.php b/src/Search/Response.php index ace639eb..6e39e98c 100644 --- a/src/Search/Response.php +++ b/src/Search/Response.php @@ -15,8 +15,6 @@ use Elastica\ResultSet; use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; -use MonsieurBiz\SyliusSearchPlugin\Search\Filter\Filter; -use MonsieurBiz\SyliusSearchPlugin\Search\Filter\RangeFilter; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use Pagerfanta\Adapter\AdapterInterface; use Pagerfanta\Pagerfanta; @@ -28,12 +26,18 @@ class Response implements ResponseInterface private DocumentableInterface $documentable; private ?Pagerfanta $paginator = null; private array $filters = []; - - public function __construct(RequestConfiguration $requestConfiguration, AdapterInterface $adapter, DocumentableInterface $documentable) - { + private iterable $filterBuilders; + + public function __construct( + RequestConfiguration $requestConfiguration, + AdapterInterface $adapter, + DocumentableInterface $documentable, + iterable $filterBuilders + ) { $this->requestConfiguration = $requestConfiguration; $this->adapter = $adapter; $this->documentable = $documentable; + $this->filterBuilders = $filterBuilders; $this->buildFilters(); } @@ -78,102 +82,21 @@ private function buildFilters(): void return; } - // todo main taxon - $taxonAggregation = $aggregations['main_taxon']['main_taxon'] ?? null; - if ($taxonAggregation && $taxonAggregation['doc_count'] > 0) { - $filter = new Filter($this->requestConfiguration, 'main_taxon', 'monsieurbiz_searchplugin.filters.taxon_filter', $taxonAggregation['doc_count'], 'taxon'); - - // Get main taxon code in aggregation - $taxonCodeBuckets = $taxonAggregation['codes']['buckets'] ?? []; - foreach ($taxonCodeBuckets as $taxonCodeBucket) { - if (0 === $taxonCodeBucket['doc_count']) { - continue; - } - $taxonCode = $taxonCodeBucket['key']; - $taxonName = null; - - // Get main taxon level in aggregation - $taxonLevelBuckets = $taxonCodeBucket['levels']['buckets'] ?? []; - foreach ($taxonLevelBuckets as $taxonLevelBucket) { - // Get main taxon name in aggregation - $taxonNameBuckets = $taxonLevelBucket['names']['buckets'] ?? []; - foreach ($taxonNameBuckets as $taxonNameBucket) { - $taxonName = $taxonNameBucket['key']; - $filter->addValue($taxonName ?? $taxonCode, $taxonCodeBucket['doc_count'], $taxonCode); - break 2; - } + array_map(function($aggregationCode, $aggregationData): void { + foreach ($this->filterBuilders as $filterBuilder) { + if (null !== $filter = $filterBuilder->build($this->getDocumentable(), $this->requestConfiguration, $aggregationCode, $aggregationData)) { + $this->filters[$filterBuilder->getPosition()] = $filter; } } + }, array_keys($aggregations), $aggregations); - // Put taxon filter in first if contains value - if (0 !== \count($filter->getValues())) { - $this->filters[] = $filter; - } - } - - // todo taxons - $taxonAggregation = $aggregations['taxons']['taxons']['taxons']['taxons'] ?? null; - if ($taxonAggregation && $taxonAggregation['doc_count'] > 0) { - $filter = new Filter($this->requestConfiguration, 'taxons', 'monsieurbiz_searchplugin.filters.taxon_filter', $taxonAggregation['doc_count']); - - // Get main taxon code in aggregation - $taxonCodeBuckets = $taxonAggregation['codes']['buckets'] ?? []; - foreach ($taxonCodeBuckets as $taxonCodeBucket) { - if (0 === $taxonCodeBucket['doc_count']) { - continue; - } - $taxonCode = $taxonCodeBucket['key']; - $taxonName = null; - $taxonNameBuckets = $taxonCodeBucket['names']['buckets'] ?? []; - foreach ($taxonNameBuckets as $taxonNameBucket) { - $taxonName = $taxonNameBucket['key']; - $filter->addValue($taxonName ?? $taxonCode, $taxonCodeBucket['doc_count'], $taxonCode); - } - } - - // Put taxon filter in first if contains value - if (0 !== \count($filter->getValues())) { - $this->filters[] = $filter; - } - } - // todo price - $priceAggregation = $aggregations['prices']['prices']['prices'] ?? null; - if ($priceAggregation && $priceAggregation['doc_count'] > 0) { - $this->filters[] = new RangeFilter( - $this->requestConfiguration, - 'price', - 'monsieurbiz_searchplugin.filters.price_filter', - 'monsieurbiz_searchplugin.filters.price_min', - 'monsieurbiz_searchplugin.filters.price_max', - (int) floor(($priceAggregation['prices_stats']['min'] ?? 0) / 100), - (int) ceil(($priceAggregation['prices_stats']['max'] ?? 0) / 100) - ); - } - - // Retrieve filters in aggregations - foreach (['attributes', 'options'] as $aggregationType) { - $attributeAggregations = $aggregations[$aggregationType] ?? []; - $attributeAggregations = $attributeAggregations[$aggregationType] ?? $attributeAggregations; - unset($attributeAggregations['doc_count']); - foreach ($attributeAggregations as $attributeCode => $attributeAggregation) { - if (isset($attributeAggregation[$attributeCode])) { - $attributeAggregation = $attributeAggregation[$attributeCode]; - } - $attributeNameBuckets = $attributeAggregation['names']['buckets'] ?? []; - foreach ($attributeNameBuckets as $attributeNameBucket) { - $attributeValueBuckets = $attributeNameBucket['values']['buckets'] ?? []; - $filter = new Filter($this->requestConfiguration, $attributeCode, $attributeNameBucket['key'], $attributeNameBucket['doc_count'], $aggregationType); - foreach ($attributeValueBuckets as $attributeValueBucket) { - if (0 === $attributeValueBucket['doc_count']) { - continue; - } - if (isset($attributeValueBucket['key']) && isset($attributeValueBucket['doc_count'])) { - $filter->addValue($attributeValueBucket['key'], $attributeValueBucket['doc_count']); - } - } - $this->filters[] = $filter; - } + $result = []; + ksort($this->filters); + foreach ($this->filters as $filters) { + foreach ($filters as $filter) { + $result[] = $filter; } } + $this->filters = $result; } } diff --git a/src/Search/Response/FilterBuilders/FilterBuilderInterface.php b/src/Search/Response/FilterBuilders/FilterBuilderInterface.php new file mode 100644 index 00000000..be5e08fe --- /dev/null +++ b/src/Search/Response/FilterBuilders/FilterBuilderInterface.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders; + +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; +use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterInterface; + +interface FilterBuilderInterface +{ + /** + * @return FilterInterface[]|null + */ + public function build( + DocumentableInterface $documentable, + RequestConfiguration $requestConfiguration, + string $aggregationCode, + array $aggregationData + ): ?array; + + public function getPosition(): int; +} diff --git a/src/Search/Response/FilterBuilders/Product/AttributeFilterBuilder.php b/src/Search/Response/FilterBuilders/Product/AttributeFilterBuilder.php new file mode 100644 index 00000000..493a2c1b --- /dev/null +++ b/src/Search/Response/FilterBuilders/Product/AttributeFilterBuilder.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders\Product; + +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Filter\Filter; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; +use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders\FilterBuilderInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterInterface; + +class AttributeFilterBuilder implements FilterBuilderInterface +{ + public function build( + DocumentableInterface $documentable, + RequestConfiguration $requestConfiguration, + string $aggregationCode, + array $aggregationData + ): ?array { + if ('monsieurbiz_product' !== $documentable->getIndexCode() || !\in_array($aggregationCode, ['attributes', 'options'], true)) { + return null; + } + + $attributeAggregations = $aggregationData[$aggregationCode] ?? []; + $attributeAggregations = $attributeAggregations[$aggregationCode] ?? $attributeAggregations; + unset($attributeAggregations['doc_count']); + $filters = []; + foreach ($attributeAggregations as $attributeCode => $attributeAggregation) { + if (isset($attributeAggregation[$attributeCode])) { + $attributeAggregation = $attributeAggregation[$attributeCode]; + } + $attributeNameBuckets = $attributeAggregation['names']['buckets'] ?? []; + foreach ($attributeNameBuckets as $attributeNameBucket) { + $attributeValueBuckets = $attributeNameBucket['values']['buckets'] ?? []; + $filter = new Filter( + $requestConfiguration, + $attributeCode, + $attributeNameBucket['key'], + $attributeNameBucket['doc_count'], + $aggregationCode + ); + foreach ($attributeValueBuckets as $attributeValueBucket) { + if (0 === $attributeValueBucket['doc_count']) { + continue; + } + if (isset($attributeValueBucket['key']) && isset($attributeValueBucket['doc_count'])) { + $filter->addValue($attributeValueBucket['key'], $attributeValueBucket['doc_count']); + } + } + + if (0 !== \count($filter->getValues())) { + $filters[] = $filter; + } + } + } + + return 0 !== \count($filters) ? $filters : null; + } + + public function getPosition(): int + { + return 20; + } +} diff --git a/src/Search/Response/FilterBuilders/Product/MainTaxonFilterBuilder.php b/src/Search/Response/FilterBuilders/Product/MainTaxonFilterBuilder.php new file mode 100644 index 00000000..adc19025 --- /dev/null +++ b/src/Search/Response/FilterBuilders/Product/MainTaxonFilterBuilder.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders\Product; + +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Filter\Filter; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; +use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders\FilterBuilderInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterInterface; + +class MainTaxonFilterBuilder implements FilterBuilderInterface +{ + public function build( + DocumentableInterface $documentable, + RequestConfiguration $requestConfiguration, + string $aggregationCode, + array $aggregationData + ): ?array { + if ('monsieurbiz_product' !== $documentable->getIndexCode() || 'main_taxon' !== $aggregationCode) { + return null; + } + + $taxonAggregation = $aggregationData['main_taxon'] ?? null; + if ($taxonAggregation && $taxonAggregation['doc_count'] > 0) { + $filter = new Filter( + $requestConfiguration, + 'main_taxon', + 'monsieurbiz_searchplugin.filters.taxon_filter', + $taxonAggregation['doc_count'], + 'taxon' + ); + + // Get main taxon code in aggregation + $taxonCodeBuckets = $taxonAggregation['codes']['buckets'] ?? []; + foreach ($taxonCodeBuckets as $taxonCodeBucket) { + if (0 === $taxonCodeBucket['doc_count']) { + continue; + } + $taxonCode = $taxonCodeBucket['key']; + $taxonName = null; + + // Get main taxon level in aggregation + $taxonLevelBuckets = $taxonCodeBucket['levels']['buckets'] ?? []; + foreach ($taxonLevelBuckets as $taxonLevelBucket) { + // Get main taxon name in aggregation + $taxonNameBuckets = $taxonLevelBucket['names']['buckets'] ?? []; + foreach ($taxonNameBuckets as $taxonNameBucket) { + $taxonName = $taxonNameBucket['key']; + $filter->addValue($taxonName ?? $taxonCode, $taxonCodeBucket['doc_count'], $taxonCode); + break 2; + } + } + } + + // Put taxon filter in first if contains value + if (0 !== \count($filter->getValues())) { + return [$filter]; + } + } + + return null; + } + + public function getPosition(): int + { + return 1; + } +} diff --git a/src/Search/Response/FilterBuilders/Product/PriceFilterBuilder.php b/src/Search/Response/FilterBuilders/Product/PriceFilterBuilder.php new file mode 100644 index 00000000..62b298c4 --- /dev/null +++ b/src/Search/Response/FilterBuilders/Product/PriceFilterBuilder.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders\Product; + +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Filter\RangeFilter; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; +use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders\FilterBuilderInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterInterface; + +class PriceFilterBuilder implements FilterBuilderInterface +{ + public function build( + DocumentableInterface $documentable, + RequestConfiguration $requestConfiguration, + string $aggregationCode, + array $aggregationData + ): ?array { + if ('monsieurbiz_product' !== $documentable->getIndexCode() || 'prices' !== $aggregationCode) { + return null; + } + + $filter = null; + $priceAggregation = $aggregationData['prices']['prices'] ?? null; + if ($priceAggregation && $priceAggregation['doc_count'] > 0) { + $filter = new RangeFilter( + $requestConfiguration, + 'price', + 'monsieurbiz_searchplugin.filters.price_filter', + 'monsieurbiz_searchplugin.filters.price_min', + 'monsieurbiz_searchplugin.filters.price_max', + (int) floor(($priceAggregation['prices_stats']['min'] ?? 0) / 100), + (int) ceil(($priceAggregation['prices_stats']['max'] ?? 0) / 100) + ); + } + + return [$filter]; + } + + public function getPosition(): int + { + return 10; + } +} diff --git a/src/Search/Response/FilterBuilders/Product/TaxonsFilterBuilder.php b/src/Search/Response/FilterBuilders/Product/TaxonsFilterBuilder.php new file mode 100644 index 00000000..328c5dfe --- /dev/null +++ b/src/Search/Response/FilterBuilders/Product/TaxonsFilterBuilder.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders\Product; + +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Filter\Filter; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; +use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterInterface; + +class TaxonsFilterBuilder +{ + public function build( + DocumentableInterface $documentable, + RequestConfiguration $requestConfiguration, + string $aggregationCode, + array $aggregationData + ): ?array { + if ('monsieurbiz_product' !== $documentable->getIndexCode() || 'taxons' !== $aggregationCode) { + return null; + } + + $taxonAggregation = $aggregationData['taxons']['taxons']['taxons'] ?? null; + if ($taxonAggregation && $taxonAggregation['doc_count'] > 0) { + $filter = new Filter($requestConfiguration, 'taxons', 'monsieurbiz_searchplugin.filters.taxon_filter', $taxonAggregation['doc_count']); + + // Get main taxon code in aggregation + $taxonCodeBuckets = $taxonAggregation['codes']['buckets'] ?? []; + foreach ($taxonCodeBuckets as $taxonCodeBucket) { + if (0 === $taxonCodeBucket['doc_count']) { + continue; + } + $taxonCode = $taxonCodeBucket['key']; + $taxonName = null; + $taxonNameBuckets = $taxonCodeBucket['names']['buckets'] ?? []; + foreach ($taxonNameBuckets as $taxonNameBucket) { + $taxonName = $taxonNameBucket['key']; + $filter->addValue($taxonName ?? $taxonCode, $taxonCodeBucket['doc_count'], $taxonCode); + } + } + + // Put taxon filter in first if contains value + if (0 !== \count($filter->getValues())) { + return [$filter]; + } + } + + return null; + } + + public function getPosition(): int + { + return 2; + } +} diff --git a/src/Search/ResponseFactory.php b/src/Search/ResponseFactory.php new file mode 100644 index 00000000..64b7f21d --- /dev/null +++ b/src/Search/ResponseFactory.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search; + +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; +use Pagerfanta\Adapter\AdapterInterface; + +class ResponseFactory +{ + private iterable $filterBuilders; + + public function __construct(iterable $filterBuilders) + { + $this->filterBuilders = $filterBuilders; + } + + public function build(RequestConfiguration $requestConfiguration, AdapterInterface $adapter, DocumentableInterface $documentable): ResponseInterface + { + return new Response( + $requestConfiguration, + $adapter, + $documentable, + $this->filterBuilders + ); + } +} diff --git a/src/Search/Search.php b/src/Search/Search.php index 99ee2d55..3943dc58 100644 --- a/src/Search/Search.php +++ b/src/Search/Search.php @@ -24,12 +24,18 @@ class Search implements SearchInterface private LocaleContextInterface $localeContext; private RequestHandler $requestHandler; private ClientFactory $clientFactory; - - public function __construct(ClientFactory $clientFactory, LocaleContextInterface $localeContext, RequestHandler $requestHandler) - { + private ResponseFactory $responseFactory; + + public function __construct( + ClientFactory $clientFactory, + LocaleContextInterface $localeContext, + RequestHandler $requestHandler, + ResponseFactory $responseFactory + ) { $this->localeContext = $localeContext; $this->requestHandler = $requestHandler; $this->clientFactory = $clientFactory; + $this->responseFactory = $responseFactory; } /** @@ -42,7 +48,7 @@ public function search(RequestConfiguration $requestConfiguration): ResponseInte $indexName = $this->clientFactory->getIndexName($request->getDocumentable(), $this->localeContext->getLocaleCode()); $client = $this->clientFactory->getClient($request->getDocumentable(), $this->localeContext->getLocaleCode()); - return new Response( + return $this->responseFactory->build( $requestConfiguration, new ElasticaAdapter($client->getIndex($indexName), $request->getQuery()), $request->getDocumentable() From 41eb3d512e8edf1c2a3f788dd62fe90ebbd5e2bf Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Thu, 23 Dec 2021 15:38:42 +0100 Subject: [PATCH 081/142] fix: phpcs errors on aggregation classes --- src/Search/Request/Aggregation/MainTaxonAggregation.php | 2 +- src/Search/Request/Aggregation/TaxonsAggregation.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Search/Request/Aggregation/MainTaxonAggregation.php b/src/Search/Request/Aggregation/MainTaxonAggregation.php index 0db45723..85b76f23 100644 --- a/src/Search/Request/Aggregation/MainTaxonAggregation.php +++ b/src/Search/Request/Aggregation/MainTaxonAggregation.php @@ -55,7 +55,7 @@ public function build($aggregation, array $filters) ; } - protected function isSupported($aggregation): bool + private function isSupported($aggregation): bool { return 'main_taxon' === $aggregation; } diff --git a/src/Search/Request/Aggregation/TaxonsAggregation.php b/src/Search/Request/Aggregation/TaxonsAggregation.php index 1a0d8929..2f40325f 100644 --- a/src/Search/Request/Aggregation/TaxonsAggregation.php +++ b/src/Search/Request/Aggregation/TaxonsAggregation.php @@ -62,7 +62,7 @@ public function build($aggregation, array $filters) ; } - protected function isSupported($aggregation): bool + private function isSupported($aggregation): bool { return \array_key_exists('taxons', $aggregation); } From bf6ae7c4af24bccc12bc970edb0370d857bdcebf Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Thu, 23 Dec 2021 16:17:47 +0100 Subject: [PATCH 082/142] refactor: return all query parameters in the applied filter method --- src/Search/Filter/Filter.php | 3 +-- src/Search/Filter/RangeFilter.php | 3 +-- src/Search/Request/RequestConfiguration.php | 15 ++++++--------- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/Search/Filter/Filter.php b/src/Search/Filter/Filter.php index a47fee8e..324527f4 100644 --- a/src/Search/Filter/Filter.php +++ b/src/Search/Filter/Filter.php @@ -122,8 +122,7 @@ public function getAppliedValues(): array protected function getCurrentValues(): array { $appliedFilters = $this->requestConfiguration->getAppliedFilters(); - $appliedFilters = $appliedFilters[$this->getType()] ?? $appliedFilters[$this->getCode()]; - return $appliedFilters[$this->getCode()] ?? $appliedFilters; + return $appliedFilters[$this->getType()] ?? $appliedFilters[$this->getCode()] ?? $appliedFilters; } } diff --git a/src/Search/Filter/RangeFilter.php b/src/Search/Filter/RangeFilter.php index 75179d84..129edb80 100644 --- a/src/Search/Filter/RangeFilter.php +++ b/src/Search/Filter/RangeFilter.php @@ -126,8 +126,7 @@ public function getValueType($valueLabel): string protected function getCurrentValues(): array { $appliedFilters = $this->requestConfiguration->getAppliedFilters(); - $appliedFilters = $appliedFilters[$this->getType()] ?? $appliedFilters[$this->getCode()]; - return $appliedFilters[$this->getCode()] ?? $appliedFilters; + return $appliedFilters[$this->getType()] ?? $appliedFilters[$this->getCode()] ?? $appliedFilters; } } diff --git a/src/Search/Request/RequestConfiguration.php b/src/Search/Request/RequestConfiguration.php index de106b0a..30a81061 100644 --- a/src/Search/Request/RequestConfiguration.php +++ b/src/Search/Request/RequestConfiguration.php @@ -54,15 +54,12 @@ public function getQueryText(): string public function getAppliedFilters($type = null): array { - $appliedFilters = [ - 'taxon' => $this->request->get('taxon', []), - 'price' => array_filter($this->request->get('price', [])), - 'attributes' => $this->request->get('attributes', []), - 'options' => $this->request->get('options', []), - 'taxons' => $this->request->get('taxons', []), - ]; - - return null !== $type ? ($appliedFilters[$type] ?? []) : $appliedFilters; + $requestQuery = $this->request->query->all(); + $requestQuery = array_map(function($query): ?array { + return array_filter($query); + }, $requestQuery); + + return null !== $type ? ($requestQuery[$type] ?? []) : $requestQuery; } public function getSorting(): array From 6d268ac23ecc28e6b986edf53b5ca535171e77af Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Thu, 6 Jan 2022 17:13:21 +0100 Subject: [PATCH 083/142] doc: add installation documentation --- README.md | 117 ++++++++++++++++++++++++++++++++------ recipes/2.0/manifest.json | 3 + 2 files changed, 104 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index aa737950..d6e0eb4a 100644 --- a/README.md +++ b/README.md @@ -17,29 +17,114 @@ A search plugin for Sylius using [Elastically](https://github.com/jolicode/elastically) and [Jane](https://github.com/janephp/janephp). -TODO +## Installation -## Jane +Require the plugin : +``` +composer require monsieurbiz/sylius-search-plugin="2.0.x-dev@dev" +``` + +If you are using Symfony Flex, the recipe will automatically do some actions. + +
+For the installation without flex, follow these additional steps +

+ +Change your `config/bundles.php` file to add this line for the plugin declaration: +```php + ['all' => true], + Jane\Bundle\AutoMapperBundle\JaneAutoMapperBundle::class => ['all' => true], +]; +``` + +Create the config file in `config/packages/monsieurbiz_sylius_search_plugin.yaml`: + +```yaml +imports: + - { resource: "@MonsieurBizSyliusSearchPlugin/Resources/config/config.yaml" } +``` + +Create the route config file in `config/routes/monsieurbiz_sylius_search_plugin.yaml`: + +```yaml +monsieurbiz_search_plugin: + resource: "@MonsieurBizSyliusSearchPlugin/Resources/config/routing.yaml" +``` + +Copy the override templates: + +```shell +cp -Rv vendor/monsieurbiz/sylius-search-plugin/src/Resources/templates/* templates/ +``` + +Finally configure plugin in your .env file by adding these lines at the end : + +``` +###> MonsieurBizSearchPlugin ### +MONSIEURBIZ_SEARCHPLUGIN_MESSENGER_TRANSPORT_DSN=doctrine://default +MONSIEURBIZ_SEARCHPLUGIN_ES_HOST=${ELASTICSEARCH_HOST:-localhost} +MONSIEURBIZ_SEARCHPLUGIN_ES_PORT=${ELASTICSEARCH_PORT:-9200} +###< MonsieurBizSearchPlugin ### +``` + +

+
+ +1. Install Elasticsearch 💪. See [Infrastructure](#infrastructure) below. + +2. Your `ProductAttribute` and `ProductOption` entities need to implement the `MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface` interface and use the `MonsieurBiz\SyliusSearchPlugin\Model\Product\SearchableTrait` trait. Example with the `ProductAttribute`: + +```diff +namespace App\Entity\Product; + +use Doctrine\ORM\Mapping as ORM; ++use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; ++use MonsieurBiz\SyliusSearchPlugin\Model\Product\SearchableTrait; +use Sylius\Component\Attribute\Model\AttributeTranslationInterface; +use Sylius\Component\Product\Model\ProductAttribute as BaseProductAttribute; + +/** + * @ORM\Entity + * @ORM\Table(name="sylius_product_attribute") + */ +-class ProductAttribute extends BaseProductAttribute ++class ProductAttribute extends BaseProductAttribute implements SearchableInterface +{ ++ use SearchableTrait; + + protected function createTranslation(): AttributeTranslationInterface + { + return new ProductAttributeTranslation(); +``` + +3. You need to run a diff of your doctrine's migrations: `console doctrine:migrations:diff`. Don't forget to run it! (`console doctrine:migrations:migrate`) + +4. Run the populate command. + +## Infrastructure + +The plugin was developed for Elasticsearch 7.16.x versions. You need to have analysis-icu and analysis-phonetic elasticsearch plugin installed. + +## Other information + +### Jane We are using [Jane](https://github.com/janephp/janephp) to create a DTO (Data-transfer object). -Generated classes are on `src/MonsieurBizSearchPlugin/generated` folder. -Jane configuration and JSON Schema are on `src/MonsieurBizSearchPlugin/Resources/config/jane` folder. +Generated classes are on `generated` folder. +Jane configuration and JSON Schema are on `src/Resources/config/jane` folder. To rebuild generated class during plugin development, we are using : ```bash -symfony php vendor/bin/jane generate --config-file=src/Resources/config/jane/dto-config.php +symfony php vendor/bin/jane generate --config-file=src/Resources/config/jane/jane-configuration.php ``` -## Elastically - -The [Elastically](https://github.com/jolicode/elastically) Client is configured in `src/MonsieurBizSearchPlugin/Resources/config/services.yaml` file. -You can customize it if you want in `config/services.yaml`. -Analyzers and YAML mappings are on `src/MonsieurBizSearchPlugin/Resources/config/elasticsearch/mappings` folder. - -You can also find YAML used by plugin to perform the search on Elasticsearch : -- `src/MonsieurBizSearchPlugin/Resources/config/elasticsearch/queries/search.yaml` -- `src/MonsieurBizSearchPlugin/Resources/config/elasticsearch/queries/instant.yaml` -- `src/MonsieurBizSearchPlugin/Resources/config/elasticsearch/queries/taxon.yaml` +### Elastically -These queries can be customized in another folder if you change the plugin config. +The [Elastically](https://github.com/jolicode/elastically) Client is configured in `src/Resources/config/services.yaml` file. +You can customize it in your `.env` file or if you want in `config/services.yaml`. +Analyzers and YAML mappings are on `src/Resources/config/elasticsearch` folder. diff --git a/recipes/2.0/manifest.json b/recipes/2.0/manifest.json index 084337a5..d987d23e 100644 --- a/recipes/2.0/manifest.json +++ b/recipes/2.0/manifest.json @@ -7,6 +7,9 @@ "copy-from-recipe": { "config/": "%CONFIG_DIR%/" }, + "copy-from-package": { + "src/Resources/templates/": "templates/" + }, "env": { "MONSIEURBIZ_SEARCHPLUGIN_ES_HOST": "${ELASTICSEARCH_HOST:-localhost}", "MONSIEURBIZ_SEARCHPLUGIN_ES_PORT": "${ELASTICSEARCH_PORT:-9200}", From 7dced2f1b93758a5dfb8c1fa4ddcd9f57e3e7b53 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Thu, 6 Jan 2022 22:20:33 +0100 Subject: [PATCH 084/142] fix: default settings value --- .../DocumentableRegistryPass.php | 13 +++++++++++++ src/Form/Type/Settings/LimitsSearchType.php | 4 ++-- src/Form/Type/Settings/SettingsSearchType.php | 1 + src/MonsieurBizSyliusSearchPlugin.php | 3 ++- src/Resources/config/monsieurbiz/settings.yaml | 4 +--- src/Resources/translations/messages.en.yml | 4 ++++ src/Resources/translations/messages.fr.yml | 4 ++++ 7 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/DependencyInjection/DocumentableRegistryPass.php b/src/DependencyInjection/DocumentableRegistryPass.php index f0171188..63f7eb99 100644 --- a/src/DependencyInjection/DocumentableRegistryPass.php +++ b/src/DependencyInjection/DocumentableRegistryPass.php @@ -32,6 +32,11 @@ public function process(ContainerBuilder $container): void if (!\is_array($documentables)) { return; } + $searchSettings = []; + if ($container->hasParameter('monsieurbiz.settings.config.plugins')) { + $searchSettings = $container->getParameter('monsieurbiz.settings.config.plugins'); + } + foreach ($documentables as $indexCode => $documentableConfiguration) { $documentableServiceId = 'search.documentable.' . $indexCode; $documentableDefinition = (new Definition(Documentable::class)) // TODO - move into config @@ -51,6 +56,14 @@ public function process(ContainerBuilder $container): void // Add documentable into registry $registry->addMethodCall('register', [$documentableServiceId, new Reference($documentableServiceId)]); + + // Add the default settings value of documentable + $searchSettings['monsieurbiz.search']['default_values'] = [ + 'instant_search_enabled__' . $indexCode => $documentableConfiguration['instant_search_enabled'], + 'limits__' . $indexCode => $documentableConfiguration['limits'], + ]; } + + $container->setParameter('monsieurbiz.settings.config.plugins', $searchSettings); } } diff --git a/src/Form/Type/Settings/LimitsSearchType.php b/src/Form/Type/Settings/LimitsSearchType.php index 921ab619..08e3006f 100644 --- a/src/Form/Type/Settings/LimitsSearchType.php +++ b/src/Form/Type/Settings/LimitsSearchType.php @@ -33,7 +33,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void CollectionType::class, [ 'entry_type' => NumberType::class, - 'label' => $documentable->getIndexCode() . ' search', + 'label' => 'monsieurbiz_searchplugin.admin.setting_form.limit_search_' . $documentable->getIndexCode(), 'required' => true, 'allow_add' => true, 'allow_delete' => true, @@ -45,7 +45,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void CollectionType::class, [ 'entry_type' => NumberType::class, - 'label' => $documentable->getIndexCode() . ' instant_search', + 'label' => 'monsieurbiz_searchplugin.admin.setting_form.limit_instant_search_' . $documentable->getIndexCode(), 'required' => true, 'allow_add' => true, 'allow_delete' => true, diff --git a/src/Form/Type/Settings/SettingsSearchType.php b/src/Form/Type/Settings/SettingsSearchType.php index 2f46184d..e78fbf6b 100644 --- a/src/Form/Type/Settings/SettingsSearchType.php +++ b/src/Form/Type/Settings/SettingsSearchType.php @@ -38,6 +38,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void CheckboxType::class, [ 'required' => false, + 'label' => 'monsieurbiz_searchplugin.admin.setting_form.instant_search_enabled_' . $documentable->getIndexCode(), ] ); $subOptions = $options; diff --git a/src/MonsieurBizSyliusSearchPlugin.php b/src/MonsieurBizSyliusSearchPlugin.php index 620259ac..d012ee47 100644 --- a/src/MonsieurBizSyliusSearchPlugin.php +++ b/src/MonsieurBizSyliusSearchPlugin.php @@ -18,6 +18,7 @@ use MonsieurBiz\SyliusSearchPlugin\DependencyInjection\DocumentableRegistryPass; use MonsieurBiz\SyliusSearchPlugin\DependencyInjection\RegisterSearchRequestPass; use Sylius\Bundle\CoreBundle\Application\SyliusPluginTrait; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; use Symfony\Component\HttpKernel\Bundle\Bundle; @@ -44,7 +45,7 @@ public function getContainerExtension(): ?ExtensionInterface public function build(ContainerBuilder $container): void { parent::build($container); - $container->addCompilerPass(new DocumentableRegistryPass()); + $container->addCompilerPass(new DocumentableRegistryPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 50); // Run the compiler pass before \MonsieurBiz\SyliusSettingsPlugin\DependencyInjection\InstantiateSettingsPass $container->addCompilerPass(new RegisterSearchRequestPass()); $container->addCompilerPass(new AutomapperConfigurationRegistryPass()); $container->addCompilerPass(new AutowireMappingProviderParameterPass()); diff --git a/src/Resources/config/monsieurbiz/settings.yaml b/src/Resources/config/monsieurbiz/settings.yaml index 5b7971e3..d8397f61 100644 --- a/src/Resources/config/monsieurbiz/settings.yaml +++ b/src/Resources/config/monsieurbiz/settings.yaml @@ -9,6 +9,4 @@ monsieurbiz_sylius_settings: use_locales: false classes: form: MonsieurBiz\SyliusSearchPlugin\Form\Type\Settings\SettingsSearchType - default_values: - instant_search_enabled__monsieurbiz_product: '%monsieurbiz.search.config.documents.monsieurbiz_product.instant_search_enabled%' - limits__monsieurbiz_product: '%monsieurbiz.search.config.documents.monsieurbiz_product.limits%' + default_values: # default values are defined in MonsieurBiz\SyliusSearchPlugin\DependencyInjection\DocumentableRegistryPass diff --git a/src/Resources/translations/messages.en.yml b/src/Resources/translations/messages.en.yml index ba419b58..603ffdc9 100644 --- a/src/Resources/translations/messages.en.yml +++ b/src/Resources/translations/messages.en.yml @@ -8,6 +8,10 @@ monsieurbiz_searchplugin: form: title: Search filterable: Filterable + setting_form: + instant_search_enabled_monsieurbiz_product: 'Product: Instant search enabled' + limit_search_monsieurbiz_product: 'Product: Available limits for search' + limit_instant_search_monsieurbiz_product: 'Product: Available limits for instant search' form: query: 'Search' query_placeholder: 'Search…' diff --git a/src/Resources/translations/messages.fr.yml b/src/Resources/translations/messages.fr.yml index 72b5aeb4..45c7ce65 100644 --- a/src/Resources/translations/messages.fr.yml +++ b/src/Resources/translations/messages.fr.yml @@ -8,6 +8,10 @@ monsieurbiz_searchplugin: form: title: Recherche filterable: Filtrable + setting_form: + instant_search_enabled_monsieurbiz_product: 'Produit: Activer la recherche instantanée' + limit_search_monsieurbiz_product: 'Produit: Limites disponibles pour la recherche' + limit_instant_search_monsieurbiz_product: 'Produit: Limites disponibles pour la recherche instantanée' form: query: 'Recherche' query_placeholder: 'Rechercher…' From b8a59490200fed4df02a7bee41e2135625f3f612 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Thu, 6 Jan 2022 22:21:59 +0100 Subject: [PATCH 085/142] fix: remove unused class --- .../Response/FilterBuilders/Product/AttributeFilterBuilder.php | 1 - .../Response/FilterBuilders/Product/MainTaxonFilterBuilder.php | 1 - .../Response/FilterBuilders/Product/PriceFilterBuilder.php | 1 - .../Response/FilterBuilders/Product/TaxonsFilterBuilder.php | 1 - 4 files changed, 4 deletions(-) diff --git a/src/Search/Response/FilterBuilders/Product/AttributeFilterBuilder.php b/src/Search/Response/FilterBuilders/Product/AttributeFilterBuilder.php index 493a2c1b..0da49fea 100644 --- a/src/Search/Response/FilterBuilders/Product/AttributeFilterBuilder.php +++ b/src/Search/Response/FilterBuilders/Product/AttributeFilterBuilder.php @@ -17,7 +17,6 @@ use MonsieurBiz\SyliusSearchPlugin\Search\Filter\Filter; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders\FilterBuilderInterface; -use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterInterface; class AttributeFilterBuilder implements FilterBuilderInterface { diff --git a/src/Search/Response/FilterBuilders/Product/MainTaxonFilterBuilder.php b/src/Search/Response/FilterBuilders/Product/MainTaxonFilterBuilder.php index adc19025..e1808289 100644 --- a/src/Search/Response/FilterBuilders/Product/MainTaxonFilterBuilder.php +++ b/src/Search/Response/FilterBuilders/Product/MainTaxonFilterBuilder.php @@ -17,7 +17,6 @@ use MonsieurBiz\SyliusSearchPlugin\Search\Filter\Filter; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders\FilterBuilderInterface; -use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterInterface; class MainTaxonFilterBuilder implements FilterBuilderInterface { diff --git a/src/Search/Response/FilterBuilders/Product/PriceFilterBuilder.php b/src/Search/Response/FilterBuilders/Product/PriceFilterBuilder.php index 62b298c4..75665786 100644 --- a/src/Search/Response/FilterBuilders/Product/PriceFilterBuilder.php +++ b/src/Search/Response/FilterBuilders/Product/PriceFilterBuilder.php @@ -17,7 +17,6 @@ use MonsieurBiz\SyliusSearchPlugin\Search\Filter\RangeFilter; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders\FilterBuilderInterface; -use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterInterface; class PriceFilterBuilder implements FilterBuilderInterface { diff --git a/src/Search/Response/FilterBuilders/Product/TaxonsFilterBuilder.php b/src/Search/Response/FilterBuilders/Product/TaxonsFilterBuilder.php index 328c5dfe..7f6d64f6 100644 --- a/src/Search/Response/FilterBuilders/Product/TaxonsFilterBuilder.php +++ b/src/Search/Response/FilterBuilders/Product/TaxonsFilterBuilder.php @@ -16,7 +16,6 @@ use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Filter\Filter; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; -use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterInterface; class TaxonsFilterBuilder { From 5e477b3922447d10a03d7b7239943a2d745e607c Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Thu, 6 Jan 2022 22:37:07 +0100 Subject: [PATCH 086/142] fix: add missing translate in product attribute and option form --- src/Resources/translations/messages.en.yml | 2 ++ src/Resources/translations/messages.fr.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/Resources/translations/messages.en.yml b/src/Resources/translations/messages.en.yml index 603ffdc9..0fb2a51c 100644 --- a/src/Resources/translations/messages.en.yml +++ b/src/Resources/translations/messages.en.yml @@ -4,6 +4,8 @@ monsieurbiz_searchplugin: form: title: Search filterable: Filterable + searchable: Searchable + search_weight: Search Weight test product_option: form: title: Search diff --git a/src/Resources/translations/messages.fr.yml b/src/Resources/translations/messages.fr.yml index 45c7ce65..829fe97e 100644 --- a/src/Resources/translations/messages.fr.yml +++ b/src/Resources/translations/messages.fr.yml @@ -4,6 +4,8 @@ monsieurbiz_searchplugin: form: title: Recherche filterable: Filtrable + searchable: Recherchable + search_weight: Pondération dans la recherche product_option: form: title: Recherche From 7f54d77a3be48584ae98d898285f3c2c970bd8ca Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 10 Jan 2022 16:10:10 +0100 Subject: [PATCH 087/142] fix(filter): return the current values of current filter --- src/Search/Filter/Filter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Search/Filter/Filter.php b/src/Search/Filter/Filter.php index 324527f4..caf12aac 100644 --- a/src/Search/Filter/Filter.php +++ b/src/Search/Filter/Filter.php @@ -123,6 +123,6 @@ protected function getCurrentValues(): array { $appliedFilters = $this->requestConfiguration->getAppliedFilters(); - return $appliedFilters[$this->getType()] ?? $appliedFilters[$this->getCode()] ?? $appliedFilters; + return $appliedFilters[$this->getType()][$this->getCode()] ?? $appliedFilters[$this->getCode()] ?? $appliedFilters; } } From 2ac64ada17f5009bd19d0537413912c977455049 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Wed, 12 Jan 2022 13:56:58 +0100 Subject: [PATCH 088/142] refactor: add tagged iterator for product attribute readers and a reader by type --- .../ProductAttributeValueConfiguration.php | 33 ++++++++++++++----- .../CheckboxReader.php | 22 +++++++++++++ .../DateReader.php | 5 +++ .../DateTimeReader.php | 5 +++ .../DefaultReader.php | 4 ++- .../IntegerReader.php | 22 +++++++++++++ .../PercentReader.php | 22 +++++++++++++ .../ReaderInterface.php | 2 ++ .../SelectReader.php | 5 +++ .../TextReader.php | 22 +++++++++++++ .../TextareaReader.php | 22 +++++++++++++ src/Resources/config/services.yaml | 10 +++--- 12 files changed, 159 insertions(+), 15 deletions(-) create mode 100644 src/AutoMapper/ProductAttributeValueReader/CheckboxReader.php create mode 100644 src/AutoMapper/ProductAttributeValueReader/IntegerReader.php create mode 100644 src/AutoMapper/ProductAttributeValueReader/PercentReader.php create mode 100644 src/AutoMapper/ProductAttributeValueReader/TextReader.php create mode 100644 src/AutoMapper/ProductAttributeValueReader/TextareaReader.php diff --git a/src/AutoMapper/ProductAttributeValueConfiguration.php b/src/AutoMapper/ProductAttributeValueConfiguration.php index 75d7abe9..33f5c75f 100644 --- a/src/AutoMapper/ProductAttributeValueConfiguration.php +++ b/src/AutoMapper/ProductAttributeValueConfiguration.php @@ -16,35 +16,50 @@ use Jane\Bundle\AutoMapperBundle\Configuration\MapperConfigurationInterface; use Jane\Component\AutoMapper\MapperGeneratorMetadataInterface; use MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader\ReaderInterface; +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerAwareTrait; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use RuntimeException; use Sylius\Component\Product\Model\ProductAttributeValueInterface; -final class ProductAttributeValueConfiguration implements MapperConfigurationInterface +final class ProductAttributeValueConfiguration implements MapperConfigurationInterface, LoggerAwareInterface { + use LoggerAwareTrait; + private Configuration $configuration; /** * @var ReaderInterface[] */ private array $productAttributeValueReaders; - public function __construct(Configuration $configuration, array $productAttributeValueReaders = []) + /** + * @var LoggerInterface + */ + protected $logger; + + public function __construct(Configuration $configuration, iterable $productAttributeValueReaders) { + $this->logger = new NullLogger(); $this->configuration = $configuration; - $this->productAttributeValueReaders = $productAttributeValueReaders; + $this->productAttributeValueReaders = $productAttributeValueReaders instanceof \Traversable + ? iterator_to_array($productAttributeValueReaders) + : $productAttributeValueReaders; } public function process(MapperGeneratorMetadataInterface $metadata): void { - if (0 === \count($this->productAttributeValueReaders) || !\array_key_exists('default', $this->productAttributeValueReaders)) { - throw new RuntimeException('Please define the "default" product attribute value reader'); + if (0 === \count($this->productAttributeValueReaders)) { + throw new RuntimeException('Undefined product attribute value reader'); } $metadata->forMember('value', function(ProductAttributeValueInterface $productAttributeValue) { - /** @var ReaderInterface $reader */ - $reader = $this->productAttributeValueReaders['default']; - if (\array_key_exists($productAttributeValue->getType(), $this->productAttributeValueReaders)) { - $reader = $this->productAttributeValueReaders[$productAttributeValue->getType()]; + if (!\array_key_exists($productAttributeValue->getType(), $this->productAttributeValueReaders)) { + $this->logger->alert(sprintf('Missing product attribute value reader for "%s" type', $productAttributeValue->getType())); + + return null; } + $reader = $this->productAttributeValueReaders[$productAttributeValue->getType()]; return $reader->getValue($productAttributeValue); }); diff --git a/src/AutoMapper/ProductAttributeValueReader/CheckboxReader.php b/src/AutoMapper/ProductAttributeValueReader/CheckboxReader.php new file mode 100644 index 00000000..b96d5542 --- /dev/null +++ b/src/AutoMapper/ProductAttributeValueReader/CheckboxReader.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader; + +class CheckboxReader extends DefaultReader +{ + public static function getReaderCode(): string + { + return 'checkbox'; + } +} diff --git a/src/AutoMapper/ProductAttributeValueReader/DateReader.php b/src/AutoMapper/ProductAttributeValueReader/DateReader.php index 180e09df..23566874 100644 --- a/src/AutoMapper/ProductAttributeValueReader/DateReader.php +++ b/src/AutoMapper/ProductAttributeValueReader/DateReader.php @@ -16,4 +16,9 @@ class DateReader extends DateTimeReader implements ReaderInterface { protected string $defaultFormat = 'Y-m-d'; + + public static function getReaderCode(): string + { + return 'date'; + } } diff --git a/src/AutoMapper/ProductAttributeValueReader/DateTimeReader.php b/src/AutoMapper/ProductAttributeValueReader/DateTimeReader.php index 7e2c9656..564b3654 100644 --- a/src/AutoMapper/ProductAttributeValueReader/DateTimeReader.php +++ b/src/AutoMapper/ProductAttributeValueReader/DateTimeReader.php @@ -32,4 +32,9 @@ public function getValue(ProductAttributeValueInterface $productAttribute) return $productAttributeValue; } + + public static function getReaderCode(): string + { + return 'datetime'; + } } diff --git a/src/AutoMapper/ProductAttributeValueReader/DefaultReader.php b/src/AutoMapper/ProductAttributeValueReader/DefaultReader.php index 5423edab..514bb4b0 100644 --- a/src/AutoMapper/ProductAttributeValueReader/DefaultReader.php +++ b/src/AutoMapper/ProductAttributeValueReader/DefaultReader.php @@ -15,10 +15,12 @@ use Sylius\Component\Product\Model\ProductAttributeValueInterface; -class DefaultReader implements ReaderInterface +abstract class DefaultReader implements ReaderInterface { public function getValue(ProductAttributeValueInterface $productAttribute) { return (string) $productAttribute->getValue(); } + + abstract public static function getReaderCode(): string; } diff --git a/src/AutoMapper/ProductAttributeValueReader/IntegerReader.php b/src/AutoMapper/ProductAttributeValueReader/IntegerReader.php new file mode 100644 index 00000000..7c1df8e1 --- /dev/null +++ b/src/AutoMapper/ProductAttributeValueReader/IntegerReader.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader; + +class IntegerReader extends DefaultReader +{ + public static function getReaderCode(): string + { + return 'integer'; + } +} diff --git a/src/AutoMapper/ProductAttributeValueReader/PercentReader.php b/src/AutoMapper/ProductAttributeValueReader/PercentReader.php new file mode 100644 index 00000000..40e284a8 --- /dev/null +++ b/src/AutoMapper/ProductAttributeValueReader/PercentReader.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader; + +class PercentReader extends DefaultReader +{ + public static function getReaderCode(): string + { + return 'percent'; + } +} diff --git a/src/AutoMapper/ProductAttributeValueReader/ReaderInterface.php b/src/AutoMapper/ProductAttributeValueReader/ReaderInterface.php index 74fdb651..6cb41df7 100644 --- a/src/AutoMapper/ProductAttributeValueReader/ReaderInterface.php +++ b/src/AutoMapper/ProductAttributeValueReader/ReaderInterface.php @@ -17,6 +17,8 @@ interface ReaderInterface { + public static function getReaderCode(): string; + /** * @return string|array */ diff --git a/src/AutoMapper/ProductAttributeValueReader/SelectReader.php b/src/AutoMapper/ProductAttributeValueReader/SelectReader.php index 66deb61f..340897e0 100644 --- a/src/AutoMapper/ProductAttributeValueReader/SelectReader.php +++ b/src/AutoMapper/ProductAttributeValueReader/SelectReader.php @@ -49,4 +49,9 @@ public function getValue(ProductAttributeValueInterface $productAttribute) return $result; } + + public static function getReaderCode(): string + { + return 'select'; + } } diff --git a/src/AutoMapper/ProductAttributeValueReader/TextReader.php b/src/AutoMapper/ProductAttributeValueReader/TextReader.php new file mode 100644 index 00000000..0c73c7c7 --- /dev/null +++ b/src/AutoMapper/ProductAttributeValueReader/TextReader.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader; + +class TextReader extends DefaultReader +{ + public static function getReaderCode(): string + { + return 'text'; + } +} diff --git a/src/AutoMapper/ProductAttributeValueReader/TextareaReader.php b/src/AutoMapper/ProductAttributeValueReader/TextareaReader.php new file mode 100644 index 00000000..ffc51f76 --- /dev/null +++ b/src/AutoMapper/ProductAttributeValueReader/TextareaReader.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader; + +class TextareaReader extends DefaultReader +{ + public static function getReaderCode(): string + { + return 'textarea'; + } +} diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index 75fd6327..8d823ca7 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -15,6 +15,10 @@ services: $documentableRegistry: '@monsieurbiz.search.registry.documentable' $searchRequestsRegistry: '@monsieurbiz.search.registry.search_request' + _instanceof: + MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader\ReaderInterface: + tags: [ 'monsieurbiz.search.automapper.product_attribute_value_reader' ] + MonsieurBiz\SyliusSearchPlugin\: resource: '../../*' exclude: '../../{Entity,Migrations,Tests,Kernel.php}' @@ -63,11 +67,7 @@ services: # Define product attribute value readers MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueConfiguration: arguments: - $productAttributeValueReaders: { - default: '@MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader\DefaultReader', - select: '@MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader\SelectReader', - date: '@MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader\DateReader' - } + $productAttributeValueReaders: !tagged_iterator { tag: 'monsieurbiz.search.automapper.product_attribute_value_reader', default_index_method: 'getReaderCode' } # Define aggregation builders MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation\MainTaxonAggregation: From cd91097a15ae6020affada8bf9f529b32d578b88 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Wed, 12 Jan 2022 13:58:21 +0100 Subject: [PATCH 089/142] fix: return null value if price aggregation has no document --- .../Product/PriceFilterBuilder.php | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Search/Response/FilterBuilders/Product/PriceFilterBuilder.php b/src/Search/Response/FilterBuilders/Product/PriceFilterBuilder.php index 75665786..5ff60f39 100644 --- a/src/Search/Response/FilterBuilders/Product/PriceFilterBuilder.php +++ b/src/Search/Response/FilterBuilders/Product/PriceFilterBuilder.php @@ -33,18 +33,20 @@ public function build( $filter = null; $priceAggregation = $aggregationData['prices']['prices'] ?? null; if ($priceAggregation && $priceAggregation['doc_count'] > 0) { - $filter = new RangeFilter( - $requestConfiguration, - 'price', - 'monsieurbiz_searchplugin.filters.price_filter', - 'monsieurbiz_searchplugin.filters.price_min', - 'monsieurbiz_searchplugin.filters.price_max', - (int) floor(($priceAggregation['prices_stats']['min'] ?? 0) / 100), - (int) ceil(($priceAggregation['prices_stats']['max'] ?? 0) / 100) - ); + $filter = [ + new RangeFilter( + $requestConfiguration, + 'price', + 'monsieurbiz_searchplugin.filters.price_filter', + 'monsieurbiz_searchplugin.filters.price_min', + 'monsieurbiz_searchplugin.filters.price_max', + (int) floor(($priceAggregation['prices_stats']['min'] ?? 0) / 100), + (int) ceil(($priceAggregation['prices_stats']['max'] ?? 0) / 100) + ), + ]; } - return [$filter]; + return $filter; } public function getPosition(): int From cf3dec87f78bc948ad6111bc73f61d415f18f7a8 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Wed, 12 Jan 2022 14:27:52 +0100 Subject: [PATCH 090/142] doc: add documentation about product attribute value readers --- doc/ProductAttributeValueReader.md | 47 ++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 doc/ProductAttributeValueReader.md diff --git a/doc/ProductAttributeValueReader.md b/doc/ProductAttributeValueReader.md new file mode 100644 index 00000000..1aaddf7b --- /dev/null +++ b/doc/ProductAttributeValueReader.md @@ -0,0 +1,47 @@ +# Product attribute value reader + +## What is it? + +A product attribute value reader is used to transform the value of an attribute into an indexable value for the elasticsearch document. + +We have defined a reader for the native Sylius types: + +- checkbox +- date +- datetime +- integer +- percent +- select +- textarea +- text + +## Add or replace a reader + +You have added a new attribute type, and you want to index its value. +Or you want to change an existing reader. + +In your `service.yaml`, you can add or replace a product attribute value reader : + +**Create a Product Attribute Value Reader class** that implements the `\MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader\ReaderInterface` interface, and define the two methods: + +- `getReaderCode`: this code matches the reader with the attribute type +- `getValue`: return the indexable value + +```php +use \MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader\ReaderInterface; + +class MyCustomReader implements ReaderInterface +{ + // ... +} +``` + +And add the `monsieurbiz.search.automapper.product_attribute_value_reader` tag on your custom reader : + +```yaml +App\...\MyCustomReader: + tags: [ 'monsieurbiz.search.automapper.product_attribute_value_reader' ] + +``` + +To **replace** an existing product attribute value reader, your `getReaderCode` returns the attribute type code of the existing reader. From 5368d6410fdb8c93045ba7d8badc01159a1c9d84 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Thu, 13 Jan 2022 08:40:39 +0100 Subject: [PATCH 091/142] fix: hide the autocomplete result when focus out a form element --- assets/js/app.js | 19 +- src/Resources/public/js/monsieurbiz-search.js | 182 +----------------- src/Resources/views/Search/_form.html.twig | 2 +- 3 files changed, 16 insertions(+), 187 deletions(-) diff --git a/assets/js/app.js b/assets/js/app.js index 8503ff7d..1570bf44 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -30,12 +30,21 @@ global.MonsieurBizInstantSearch = class { }, keyUpTimeOut); }); - // Hide results when user leave the search field - document.querySelector(searchInputSelector).addEventListener('focusout', function (e) { - var resultElement = e.currentTarget.closest(resultClosestSelector).querySelector(resultFindSelector); - setTimeout(function () { + // Hide results when user leave the autocomplete form + const searchForm = document.querySelector(searchInputSelector).closest(resultClosestSelector); + searchForm.addEventListener('focusout', function (e) { + if (e.relatedTarget === null || !searchForm.contains(e.relatedTarget)) { + const resultElement = searchForm.querySelector(resultFindSelector); resultElement.style.display = 'none'; - }, 100); // Add timeout to keep the click on the result + } + }); + + document.querySelector(searchInputSelector).addEventListener('focus', function (e) { + var query = e.currentTarget.value; + if (query !== '') { + const resultElement = searchForm.querySelector(resultFindSelector); + resultElement.style.display = 'block'; + } }); } } diff --git a/src/Resources/public/js/monsieurbiz-search.js b/src/Resources/public/js/monsieurbiz-search.js index 7ac4f884..bbd5fdb6 100644 --- a/src/Resources/public/js/monsieurbiz-search.js +++ b/src/Resources/public/js/monsieurbiz-search.js @@ -1,181 +1 @@ -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); -/******/ } -/******/ }; -/******/ -/******/ // define __esModule on exports -/******/ __webpack_require__.r = function(exports) { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ -/******/ // create a fake namespace object -/******/ // mode & 1: value is a module id, require it -/******/ // mode & 2: merge all properties of value into the ns -/******/ // mode & 4: return value when already ns object -/******/ // mode & 8|1: behave like require -/******/ __webpack_require__.t = function(value, mode) { -/******/ if(mode & 1) value = __webpack_require__(value); -/******/ if(mode & 8) return value; -/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; -/******/ var ns = Object.create(null); -/******/ __webpack_require__.r(ns); -/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); -/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); -/******/ return ns; -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = "/public/"; -/******/ -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = "./assets/js/app.js"); -/******/ }) -/************************************************************************/ -/******/ ({ - -/***/ "./assets/js/app.js": -/*!**************************!*\ - !*** ./assets/js/app.js ***! - \**************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -/* WEBPACK VAR INJECTION */(function(global) {function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -global.MonsieurBizInstantSearch = /*#__PURE__*/function () { - "use strict"; - - function _class(instantUrl, searchInputSelector, resultClosestSelector, resultFindSelector, keyUpTimeOut, minQueryLength) { - _classCallCheck(this, _class); - - // Init a timeout variable to be used below - var instantSearchTimeout = null; - document.querySelector(searchInputSelector).addEventListener('keyup', function (e) { - clearTimeout(instantSearchTimeout); - var query = e.currentTarget.value; - var resultElement = e.currentTarget.closest(resultClosestSelector).querySelector(resultFindSelector); - instantSearchTimeout = setTimeout(function () { - if (query.length >= minQueryLength) { - var httpRequest = new XMLHttpRequest(); - - httpRequest.onload = function () { - if (this.status === 200) { - resultElement.innerHTML = this.responseText; - resultElement.style.display = 'block'; - } - }; - - httpRequest.open("POST", instantUrl); - httpRequest.setRequestHeader("X-Requested-With", "XMLHttpRequest"); - httpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); - httpRequest.send(new URLSearchParams({ - query: query - }).toString()); - } - }, keyUpTimeOut); - }); // Hide results when user leave the search field - - document.querySelector(searchInputSelector).addEventListener('focusout', function (e) { - var resultElement = e.currentTarget.closest(resultClosestSelector).querySelector(resultFindSelector); - setTimeout(function () { - resultElement.style.display = 'none'; - }, 100); // Add timeout to keep the click on the result - }); - } - - return _class; -}(); - -document.addEventListener("DOMContentLoaded", function () { - new MonsieurBizInstantSearch(monsieurbizSearchPlugin.instantUrl, monsieurbizSearchPlugin.searchInputSelector, monsieurbizSearchPlugin.resultClosestSelector, monsieurbizSearchPlugin.resultFindSelector, monsieurbizSearchPlugin.keyUpTimeOut, monsieurbizSearchPlugin.minQueryLength); -}); -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../node_modules/webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js"))) - -/***/ }), - -/***/ "./node_modules/webpack/buildin/global.js": -/*!***********************************!*\ - !*** (webpack)/buildin/global.js ***! - \***********************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -var g; - -// This works in non-strict mode -g = (function() { - return this; -})(); - -try { - // This works if eval is allowed (see CSP) - g = g || new Function("return this")(); -} catch (e) { - // This works if the window reference is available - if (typeof window === "object") g = window; -} - -// g can still be undefined, but nothing to do about it... -// We return undefined, instead of nothing here, so it's -// easier to handle this case. if(!global) { ...} - -module.exports = g; - - -/***/ }) - -/******/ }); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vYXNzZXRzL2pzL2FwcC5qcyIsIndlYnBhY2s6Ly8vKHdlYnBhY2spL2J1aWxkaW4vZ2xvYmFsLmpzIl0sIm5hbWVzIjpbImdsb2JhbCIsIk1vbnNpZXVyQml6SW5zdGFudFNlYXJjaCIsImluc3RhbnRVcmwiLCJzZWFyY2hJbnB1dFNlbGVjdG9yIiwicmVzdWx0Q2xvc2VzdFNlbGVjdG9yIiwicmVzdWx0RmluZFNlbGVjdG9yIiwia2V5VXBUaW1lT3V0IiwibWluUXVlcnlMZW5ndGgiLCJpbnN0YW50U2VhcmNoVGltZW91dCIsImRvY3VtZW50IiwicXVlcnlTZWxlY3RvciIsImFkZEV2ZW50TGlzdGVuZXIiLCJlIiwiY2xlYXJUaW1lb3V0IiwicXVlcnkiLCJjdXJyZW50VGFyZ2V0IiwidmFsdWUiLCJyZXN1bHRFbGVtZW50IiwiY2xvc2VzdCIsInNldFRpbWVvdXQiLCJsZW5ndGgiLCJodHRwUmVxdWVzdCIsIlhNTEh0dHBSZXF1ZXN0Iiwib25sb2FkIiwic3RhdHVzIiwiaW5uZXJIVE1MIiwicmVzcG9uc2VUZXh0Iiwic3R5bGUiLCJkaXNwbGF5Iiwib3BlbiIsInNldFJlcXVlc3RIZWFkZXIiLCJzZW5kIiwiVVJMU2VhcmNoUGFyYW1zIiwidG9TdHJpbmciLCJtb25zaWV1cmJpelNlYXJjaFBsdWdpbiJdLCJtYXBwaW5ncyI6IjtRQUFBO1FBQ0E7O1FBRUE7UUFDQTs7UUFFQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTs7UUFFQTtRQUNBOztRQUVBO1FBQ0E7O1FBRUE7UUFDQTtRQUNBOzs7UUFHQTtRQUNBOztRQUVBO1FBQ0E7O1FBRUE7UUFDQTtRQUNBO1FBQ0EsMENBQTBDLGdDQUFnQztRQUMxRTtRQUNBOztRQUVBO1FBQ0E7UUFDQTtRQUNBLHdEQUF3RCxrQkFBa0I7UUFDMUU7UUFDQSxpREFBaUQsY0FBYztRQUMvRDs7UUFFQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0EseUNBQXlDLGlDQUFpQztRQUMxRSxnSEFBZ0gsbUJBQW1CLEVBQUU7UUFDckk7UUFDQTs7UUFFQTtRQUNBO1FBQ0E7UUFDQSwyQkFBMkIsMEJBQTBCLEVBQUU7UUFDdkQsaUNBQWlDLGVBQWU7UUFDaEQ7UUFDQTtRQUNBOztRQUVBO1FBQ0Esc0RBQXNELCtEQUErRDs7UUFFckg7UUFDQTs7O1FBR0E7UUFDQTs7Ozs7Ozs7Ozs7Ozs7QUNsRkFBLE1BQU0sQ0FBQ0Msd0JBQVA7QUFBQTs7QUFDSSxrQkFDSUMsVUFESixFQUVJQyxtQkFGSixFQUdJQyxxQkFISixFQUlJQyxrQkFKSixFQUtJQyxZQUxKLEVBTUlDLGNBTkosRUFPRTtBQUFBOztBQUNFO0FBQ0EsUUFBSUMsb0JBQW9CLEdBQUcsSUFBM0I7QUFDQUMsWUFBUSxDQUFDQyxhQUFULENBQXVCUCxtQkFBdkIsRUFBNENRLGdCQUE1QyxDQUE2RCxPQUE3RCxFQUFzRSxVQUFVQyxDQUFWLEVBQWE7QUFDL0VDLGtCQUFZLENBQUNMLG9CQUFELENBQVo7QUFDQSxVQUFJTSxLQUFLLEdBQUdGLENBQUMsQ0FBQ0csYUFBRixDQUFnQkMsS0FBNUI7QUFDQSxVQUFJQyxhQUFhLEdBQUdMLENBQUMsQ0FBQ0csYUFBRixDQUFnQkcsT0FBaEIsQ0FBd0JkLHFCQUF4QixFQUErQ00sYUFBL0MsQ0FBNkRMLGtCQUE3RCxDQUFwQjtBQUNBRywwQkFBb0IsR0FBR1csVUFBVSxDQUFDLFlBQVk7QUFDMUMsWUFBSUwsS0FBSyxDQUFDTSxNQUFOLElBQWdCYixjQUFwQixFQUFvQztBQUNoQyxjQUFJYyxXQUFXLEdBQUcsSUFBSUMsY0FBSixFQUFsQjs7QUFDQUQscUJBQVcsQ0FBQ0UsTUFBWixHQUFxQixZQUFXO0FBQzVCLGdCQUFJLEtBQUtDLE1BQUwsS0FBZ0IsR0FBcEIsRUFBeUI7QUFDckJQLDJCQUFhLENBQUNRLFNBQWQsR0FBMEIsS0FBS0MsWUFBL0I7QUFDQVQsMkJBQWEsQ0FBQ1UsS0FBZCxDQUFvQkMsT0FBcEIsR0FBOEIsT0FBOUI7QUFDSDtBQUNKLFdBTEQ7O0FBTUFQLHFCQUFXLENBQUNRLElBQVosQ0FBaUIsTUFBakIsRUFBeUIzQixVQUF6QjtBQUNBbUIscUJBQVcsQ0FBQ1MsZ0JBQVosQ0FBNkIsa0JBQTdCLEVBQWlELGdCQUFqRDtBQUNBVCxxQkFBVyxDQUFDUyxnQkFBWixDQUE2QixjQUE3QixFQUE2QyxtQ0FBN0M7QUFDQVQscUJBQVcsQ0FBQ1UsSUFBWixDQUFpQixJQUFJQyxlQUFKLENBQW9CO0FBQUNsQixpQkFBSyxFQUFFQTtBQUFSLFdBQXBCLEVBQW9DbUIsUUFBcEMsRUFBakI7QUFDSDtBQUNKLE9BZGdDLEVBYzlCM0IsWUFkOEIsQ0FBakM7QUFlSCxLQW5CRCxFQUhGLENBd0JFOztBQUNBRyxZQUFRLENBQUNDLGFBQVQsQ0FBdUJQLG1CQUF2QixFQUE0Q1EsZ0JBQTVDLENBQTZELFVBQTdELEVBQXlFLFVBQVVDLENBQVYsRUFBYTtBQUNsRixVQUFJSyxhQUFhLEdBQUdMLENBQUMsQ0FBQ0csYUFBRixDQUFnQkcsT0FBaEIsQ0FBd0JkLHFCQUF4QixFQUErQ00sYUFBL0MsQ0FBNkRMLGtCQUE3RCxDQUFwQjtBQUNBYyxnQkFBVSxDQUFDLFlBQVk7QUFDbkJGLHFCQUFhLENBQUNVLEtBQWQsQ0FBb0JDLE9BQXBCLEdBQThCLE1BQTlCO0FBQ0gsT0FGUyxFQUVQLEdBRk8sQ0FBVixDQUZrRixDQUl6RTtBQUNaLEtBTEQ7QUFNSDs7QUF2Q0w7QUFBQTs7QUEwQ0FuQixRQUFRLENBQUNFLGdCQUFULENBQTBCLGtCQUExQixFQUE4QyxZQUFXO0FBQ3JELE1BQUlWLHdCQUFKLENBQ0lpQyx1QkFBdUIsQ0FBQ2hDLFVBRDVCLEVBRUlnQyx1QkFBdUIsQ0FBQy9CLG1CQUY1QixFQUdJK0IsdUJBQXVCLENBQUM5QixxQkFINUIsRUFJSThCLHVCQUF1QixDQUFDN0Isa0JBSjVCLEVBS0k2Qix1QkFBdUIsQ0FBQzVCLFlBTDVCLEVBTUk0Qix1QkFBdUIsQ0FBQzNCLGNBTjVCO0FBUUgsQ0FURCxFOzs7Ozs7Ozs7Ozs7QUMxQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsQ0FBQzs7QUFFRDtBQUNBO0FBQ0E7QUFDQSxDQUFDO0FBQ0Q7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSw0Q0FBNEM7O0FBRTVDIiwiZmlsZSI6ImpzL21vbnNpZXVyYml6LXNlYXJjaC5qcyIsInNvdXJjZXNDb250ZW50IjpbIiBcdC8vIFRoZSBtb2R1bGUgY2FjaGVcbiBcdHZhciBpbnN0YWxsZWRNb2R1bGVzID0ge307XG5cbiBcdC8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG4gXHRmdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cbiBcdFx0Ly8gQ2hlY2sgaWYgbW9kdWxlIGlzIGluIGNhY2hlXG4gXHRcdGlmKGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdKSB7XG4gXHRcdFx0cmV0dXJuIGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdLmV4cG9ydHM7XG4gXHRcdH1cbiBcdFx0Ly8gQ3JlYXRlIGEgbmV3IG1vZHVsZSAoYW5kIHB1dCBpdCBpbnRvIHRoZSBjYWNoZSlcbiBcdFx0dmFyIG1vZHVsZSA9IGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdID0ge1xuIFx0XHRcdGk6IG1vZHVsZUlkLFxuIFx0XHRcdGw6IGZhbHNlLFxuIFx0XHRcdGV4cG9ydHM6IHt9XG4gXHRcdH07XG5cbiBcdFx0Ly8gRXhlY3V0ZSB0aGUgbW9kdWxlIGZ1bmN0aW9uXG4gXHRcdG1vZHVsZXNbbW9kdWxlSWRdLmNhbGwobW9kdWxlLmV4cG9ydHMsIG1vZHVsZSwgbW9kdWxlLmV4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pO1xuXG4gXHRcdC8vIEZsYWcgdGhlIG1vZHVsZSBhcyBsb2FkZWRcbiBcdFx0bW9kdWxlLmwgPSB0cnVlO1xuXG4gXHRcdC8vIFJldHVybiB0aGUgZXhwb3J0cyBvZiB0aGUgbW9kdWxlXG4gXHRcdHJldHVybiBtb2R1bGUuZXhwb3J0cztcbiBcdH1cblxuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZXMgb2JqZWN0IChfX3dlYnBhY2tfbW9kdWxlc19fKVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5tID0gbW9kdWxlcztcblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGUgY2FjaGVcbiBcdF9fd2VicGFja19yZXF1aXJlX18uYyA9IGluc3RhbGxlZE1vZHVsZXM7XG5cbiBcdC8vIGRlZmluZSBnZXR0ZXIgZnVuY3Rpb24gZm9yIGhhcm1vbnkgZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kID0gZnVuY3Rpb24oZXhwb3J0cywgbmFtZSwgZ2V0dGVyKSB7XG4gXHRcdGlmKCFfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZXhwb3J0cywgbmFtZSkpIHtcbiBcdFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgbmFtZSwgeyBlbnVtZXJhYmxlOiB0cnVlLCBnZXQ6IGdldHRlciB9KTtcbiBcdFx0fVxuIFx0fTtcblxuIFx0Ly8gZGVmaW5lIF9fZXNNb2R1bGUgb24gZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5yID0gZnVuY3Rpb24oZXhwb3J0cykge1xuIFx0XHRpZih0eXBlb2YgU3ltYm9sICE9PSAndW5kZWZpbmVkJyAmJiBTeW1ib2wudG9TdHJpbmdUYWcpIHtcbiBcdFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgU3ltYm9sLnRvU3RyaW5nVGFnLCB7IHZhbHVlOiAnTW9kdWxlJyB9KTtcbiBcdFx0fVxuIFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7IHZhbHVlOiB0cnVlIH0pO1xuIFx0fTtcblxuIFx0Ly8gY3JlYXRlIGEgZmFrZSBuYW1lc3BhY2Ugb2JqZWN0XG4gXHQvLyBtb2RlICYgMTogdmFsdWUgaXMgYSBtb2R1bGUgaWQsIHJlcXVpcmUgaXRcbiBcdC8vIG1vZGUgJiAyOiBtZXJnZSBhbGwgcHJvcGVydGllcyBvZiB2YWx1ZSBpbnRvIHRoZSBuc1xuIFx0Ly8gbW9kZSAmIDQ6IHJldHVybiB2YWx1ZSB3aGVuIGFscmVhZHkgbnMgb2JqZWN0XG4gXHQvLyBtb2RlICYgOHwxOiBiZWhhdmUgbGlrZSByZXF1aXJlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnQgPSBmdW5jdGlvbih2YWx1ZSwgbW9kZSkge1xuIFx0XHRpZihtb2RlICYgMSkgdmFsdWUgPSBfX3dlYnBhY2tfcmVxdWlyZV9fKHZhbHVlKTtcbiBcdFx0aWYobW9kZSAmIDgpIHJldHVybiB2YWx1ZTtcbiBcdFx0aWYoKG1vZGUgJiA0KSAmJiB0eXBlb2YgdmFsdWUgPT09ICdvYmplY3QnICYmIHZhbHVlICYmIHZhbHVlLl9fZXNNb2R1bGUpIHJldHVybiB2YWx1ZTtcbiBcdFx0dmFyIG5zID0gT2JqZWN0LmNyZWF0ZShudWxsKTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5yKG5zKTtcbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KG5zLCAnZGVmYXVsdCcsIHsgZW51bWVyYWJsZTogdHJ1ZSwgdmFsdWU6IHZhbHVlIH0pO1xuIFx0XHRpZihtb2RlICYgMiAmJiB0eXBlb2YgdmFsdWUgIT0gJ3N0cmluZycpIGZvcih2YXIga2V5IGluIHZhbHVlKSBfX3dlYnBhY2tfcmVxdWlyZV9fLmQobnMsIGtleSwgZnVuY3Rpb24oa2V5KSB7IHJldHVybiB2YWx1ZVtrZXldOyB9LmJpbmQobnVsbCwga2V5KSk7XG4gXHRcdHJldHVybiBucztcbiBcdH07XG5cbiBcdC8vIGdldERlZmF1bHRFeHBvcnQgZnVuY3Rpb24gZm9yIGNvbXBhdGliaWxpdHkgd2l0aCBub24taGFybW9ueSBtb2R1bGVzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm4gPSBmdW5jdGlvbihtb2R1bGUpIHtcbiBcdFx0dmFyIGdldHRlciA9IG1vZHVsZSAmJiBtb2R1bGUuX19lc01vZHVsZSA/XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0RGVmYXVsdCgpIHsgcmV0dXJuIG1vZHVsZVsnZGVmYXVsdCddOyB9IDpcbiBcdFx0XHRmdW5jdGlvbiBnZXRNb2R1bGVFeHBvcnRzKCkgeyByZXR1cm4gbW9kdWxlOyB9O1xuIFx0XHRfX3dlYnBhY2tfcmVxdWlyZV9fLmQoZ2V0dGVyLCAnYScsIGdldHRlcik7XG4gXHRcdHJldHVybiBnZXR0ZXI7XG4gXHR9O1xuXG4gXHQvLyBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGxcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubyA9IGZ1bmN0aW9uKG9iamVjdCwgcHJvcGVydHkpIHsgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmplY3QsIHByb3BlcnR5KTsgfTtcblxuIFx0Ly8gX193ZWJwYWNrX3B1YmxpY19wYXRoX19cbiBcdF9fd2VicGFja19yZXF1aXJlX18ucCA9IFwiL3B1YmxpYy9cIjtcblxuXG4gXHQvLyBMb2FkIGVudHJ5IG1vZHVsZSBhbmQgcmV0dXJuIGV4cG9ydHNcbiBcdHJldHVybiBfX3dlYnBhY2tfcmVxdWlyZV9fKF9fd2VicGFja19yZXF1aXJlX18ucyA9IFwiLi9hc3NldHMvanMvYXBwLmpzXCIpO1xuIiwiZ2xvYmFsLk1vbnNpZXVyQml6SW5zdGFudFNlYXJjaCA9IGNsYXNzIHtcbiAgICBjb25zdHJ1Y3RvcihcbiAgICAgICAgaW5zdGFudFVybCxcbiAgICAgICAgc2VhcmNoSW5wdXRTZWxlY3RvcixcbiAgICAgICAgcmVzdWx0Q2xvc2VzdFNlbGVjdG9yLFxuICAgICAgICByZXN1bHRGaW5kU2VsZWN0b3IsXG4gICAgICAgIGtleVVwVGltZU91dCxcbiAgICAgICAgbWluUXVlcnlMZW5ndGhcbiAgICApIHtcbiAgICAgICAgLy8gSW5pdCBhIHRpbWVvdXQgdmFyaWFibGUgdG8gYmUgdXNlZCBiZWxvd1xuICAgICAgICB2YXIgaW5zdGFudFNlYXJjaFRpbWVvdXQgPSBudWxsO1xuICAgICAgICBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKHNlYXJjaElucHV0U2VsZWN0b3IpLmFkZEV2ZW50TGlzdGVuZXIoJ2tleXVwJywgZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgICAgIGNsZWFyVGltZW91dChpbnN0YW50U2VhcmNoVGltZW91dCk7XG4gICAgICAgICAgICB2YXIgcXVlcnkgPSBlLmN1cnJlbnRUYXJnZXQudmFsdWU7XG4gICAgICAgICAgICB2YXIgcmVzdWx0RWxlbWVudCA9IGUuY3VycmVudFRhcmdldC5jbG9zZXN0KHJlc3VsdENsb3Nlc3RTZWxlY3RvcikucXVlcnlTZWxlY3RvcihyZXN1bHRGaW5kU2VsZWN0b3IpO1xuICAgICAgICAgICAgaW5zdGFudFNlYXJjaFRpbWVvdXQgPSBzZXRUaW1lb3V0KGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBpZiAocXVlcnkubGVuZ3RoID49IG1pblF1ZXJ5TGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgIHZhciBodHRwUmVxdWVzdCA9IG5ldyBYTUxIdHRwUmVxdWVzdCgpO1xuICAgICAgICAgICAgICAgICAgICBodHRwUmVxdWVzdC5vbmxvYWQgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLnN0YXR1cyA9PT0gMjAwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0RWxlbWVudC5pbm5lckhUTUwgPSB0aGlzLnJlc3BvbnNlVGV4dDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXN1bHRFbGVtZW50LnN0eWxlLmRpc3BsYXkgPSAnYmxvY2snO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgICAgICBodHRwUmVxdWVzdC5vcGVuKFwiUE9TVFwiLCBpbnN0YW50VXJsKTtcbiAgICAgICAgICAgICAgICAgICAgaHR0cFJlcXVlc3Quc2V0UmVxdWVzdEhlYWRlcihcIlgtUmVxdWVzdGVkLVdpdGhcIiwgXCJYTUxIdHRwUmVxdWVzdFwiKTtcbiAgICAgICAgICAgICAgICAgICAgaHR0cFJlcXVlc3Quc2V0UmVxdWVzdEhlYWRlcihcIkNvbnRlbnQtVHlwZVwiLCBcImFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZFwiKTtcbiAgICAgICAgICAgICAgICAgICAgaHR0cFJlcXVlc3Quc2VuZChuZXcgVVJMU2VhcmNoUGFyYW1zKHtxdWVyeTogcXVlcnl9KS50b1N0cmluZygpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LCBrZXlVcFRpbWVPdXQpO1xuICAgICAgICB9KTtcblxuICAgICAgICAvLyBIaWRlIHJlc3VsdHMgd2hlbiB1c2VyIGxlYXZlIHRoZSBzZWFyY2ggZmllbGRcbiAgICAgICAgZG9jdW1lbnQucXVlcnlTZWxlY3RvcihzZWFyY2hJbnB1dFNlbGVjdG9yKS5hZGRFdmVudExpc3RlbmVyKCdmb2N1c291dCcsIGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICAgICB2YXIgcmVzdWx0RWxlbWVudCA9IGUuY3VycmVudFRhcmdldC5jbG9zZXN0KHJlc3VsdENsb3Nlc3RTZWxlY3RvcikucXVlcnlTZWxlY3RvcihyZXN1bHRGaW5kU2VsZWN0b3IpO1xuICAgICAgICAgICAgc2V0VGltZW91dChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgcmVzdWx0RWxlbWVudC5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xuICAgICAgICAgICAgfSwgMTAwKTsgLy8gQWRkIHRpbWVvdXQgdG8ga2VlcCB0aGUgY2xpY2sgb24gdGhlIHJlc3VsdFxuICAgICAgICB9KTtcbiAgICB9XG59XG5cbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoXCJET01Db250ZW50TG9hZGVkXCIsIGZ1bmN0aW9uKCkge1xuICAgIG5ldyBNb25zaWV1ckJpekluc3RhbnRTZWFyY2goXG4gICAgICAgIG1vbnNpZXVyYml6U2VhcmNoUGx1Z2luLmluc3RhbnRVcmwsXG4gICAgICAgIG1vbnNpZXVyYml6U2VhcmNoUGx1Z2luLnNlYXJjaElucHV0U2VsZWN0b3IsXG4gICAgICAgIG1vbnNpZXVyYml6U2VhcmNoUGx1Z2luLnJlc3VsdENsb3Nlc3RTZWxlY3RvcixcbiAgICAgICAgbW9uc2lldXJiaXpTZWFyY2hQbHVnaW4ucmVzdWx0RmluZFNlbGVjdG9yLFxuICAgICAgICBtb25zaWV1cmJpelNlYXJjaFBsdWdpbi5rZXlVcFRpbWVPdXQsXG4gICAgICAgIG1vbnNpZXVyYml6U2VhcmNoUGx1Z2luLm1pblF1ZXJ5TGVuZ3RoXG4gICAgKTtcbn0pO1xuIiwidmFyIGc7XG5cbi8vIFRoaXMgd29ya3MgaW4gbm9uLXN0cmljdCBtb2RlXG5nID0gKGZ1bmN0aW9uKCkge1xuXHRyZXR1cm4gdGhpcztcbn0pKCk7XG5cbnRyeSB7XG5cdC8vIFRoaXMgd29ya3MgaWYgZXZhbCBpcyBhbGxvd2VkIChzZWUgQ1NQKVxuXHRnID0gZyB8fCBuZXcgRnVuY3Rpb24oXCJyZXR1cm4gdGhpc1wiKSgpO1xufSBjYXRjaCAoZSkge1xuXHQvLyBUaGlzIHdvcmtzIGlmIHRoZSB3aW5kb3cgcmVmZXJlbmNlIGlzIGF2YWlsYWJsZVxuXHRpZiAodHlwZW9mIHdpbmRvdyA9PT0gXCJvYmplY3RcIikgZyA9IHdpbmRvdztcbn1cblxuLy8gZyBjYW4gc3RpbGwgYmUgdW5kZWZpbmVkLCBidXQgbm90aGluZyB0byBkbyBhYm91dCBpdC4uLlxuLy8gV2UgcmV0dXJuIHVuZGVmaW5lZCwgaW5zdGVhZCBvZiBub3RoaW5nIGhlcmUsIHNvIGl0J3Ncbi8vIGVhc2llciB0byBoYW5kbGUgdGhpcyBjYXNlLiBpZighZ2xvYmFsKSB7IC4uLn1cblxubW9kdWxlLmV4cG9ydHMgPSBnO1xuIl0sInNvdXJjZVJvb3QiOiIifQ== \ No newline at end of file +!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/public/",n(n.s="ng4s")}({ng4s:function(e,t,n){(function(e){e.MonsieurBizInstantSearch=function(){"use strict";return function e(t,n,r,o,u,i){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e);var c=null;document.querySelector(n).addEventListener("keyup",(function(e){clearTimeout(c);var n=e.currentTarget.value,a=e.currentTarget.closest(r).querySelector(o);c=setTimeout((function(){if(n.length>=i){var e=new XMLHttpRequest;e.onload=function(){200===this.status&&(a.innerHTML=this.responseText,a.style.display="block")},e.open("POST",t),e.setRequestHeader("X-Requested-With","XMLHttpRequest"),e.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),e.send(new URLSearchParams({query:n}).toString())}}),u)}));var a=document.querySelector(n).closest(r);a.addEventListener("focusout",(function(e){null!==e.relatedTarget&&a.contains(e.relatedTarget)||(a.querySelector(o).style.display="none")})),document.querySelector(n).addEventListener("focus",(function(e){""!==e.currentTarget.value&&(a.querySelector(o).style.display="block")}))}}(),document.addEventListener("DOMContentLoaded",(function(){new MonsieurBizInstantSearch(monsieurbizSearchPlugin.instantUrl,monsieurbizSearchPlugin.searchInputSelector,monsieurbizSearchPlugin.resultClosestSelector,monsieurbizSearchPlugin.resultFindSelector,monsieurbizSearchPlugin.keyUpTimeOut,monsieurbizSearchPlugin.minQueryLength)}))}).call(this,n("yLpj"))},yLpj:function(e,t){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(e){"object"==typeof window&&(n=window)}e.exports=n}}); \ No newline at end of file diff --git a/src/Resources/views/Search/_form.html.twig b/src/Resources/views/Search/_form.html.twig index 732a45bb..3d5e5754 100644 --- a/src/Resources/views/Search/_form.html.twig +++ b/src/Resources/views/Search/_form.html.twig @@ -1,6 +1,6 @@ {% form_theme form '@SyliusShop/Form/theme.html.twig' %}
- {{ form_start(form, {'action': path('monsieurbiz_search_post'), 'method': 'POST', 'attr': {'class': 'ui search item category autocomplete-search'}}) }} + {{ form_start(form, {'action': path('monsieurbiz_search_post'), 'method': 'POST', 'attr': {'class': 'ui search item category autocomplete-search', 'tabindex': '-1'}}) }} {{ form_errors(form) }} {{ form_row(form.query, {'value': query, 'label': false}) }} {{ form_row(form.submit, {'attr': {'class': 'ui primary button'}}) }} From 4b14b03a8ecea2b74339ce9b5b15b109dfac5496 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Thu, 13 Jan 2022 08:41:57 +0100 Subject: [PATCH 092/142] feat(autocomplete): add an autocomplete ES field on the product name and use it in query --- .../elasticsearch/monsieurbiz_product_mapping.yaml | 4 ++++ .../Request/QueryFilter/Product/SearchTermFilter.php | 9 +++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml b/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml index ed6318b0..fb768ede 100644 --- a/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml +++ b/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml @@ -16,6 +16,10 @@ mappings: fields: keyword: type: keyword + autocomplete: + type: text + analyzer: search_autocomplete + search_analyzer: standard created_at: type: date description: diff --git a/src/Search/Request/QueryFilter/Product/SearchTermFilter.php b/src/Search/Request/QueryFilter/Product/SearchTermFilter.php index fc6acc41..eb0672ad 100644 --- a/src/Search/Request/QueryFilter/Product/SearchTermFilter.php +++ b/src/Search/Request/QueryFilter/Product/SearchTermFilter.php @@ -20,6 +20,7 @@ use MonsieurBiz\SyliusSearchPlugin\Repository\ProductOptionRepositoryInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\QueryFilterInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; final class SearchTermFilter implements QueryFilterInterface { @@ -41,10 +42,14 @@ public function apply(BoolQuery $boolQuery, RequestConfiguration $requestConfigu $searchCode = $qb->query()->term(['code' => $requestConfiguration->getQueryText()]); $nameAndDescriptionQuery = $qb->query()->multi_match(); - $nameAndDescriptionQuery->setFields([ + $fieldsToSearch = [ 'name^5', // todo configuration 'description', // move to should ? score impact but not include in result - ]); + ]; + if (RequestInterface::INSTANT_TYPE === $requestConfiguration->getType()) { + $fieldsToSearch[] = 'name.autocomplete'; + } + $nameAndDescriptionQuery->setFields($fieldsToSearch); $nameAndDescriptionQuery->setQuery($requestConfiguration->getQueryText()); $nameAndDescriptionQuery->setType(MultiMatch::TYPE_MOST_FIELDS); $nameAndDescriptionQuery->setFuzziness(MultiMatch::FUZZINESS_AUTO); From d667159409af1c83c2783377fd9d602e78b52f51 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Thu, 13 Jan 2022 10:01:40 +0100 Subject: [PATCH 093/142] fix(typo): fix block name and tag name --- src/Resources/config/services.yaml | 12 ++++++------ src/Resources/config/sylius/ui.yaml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index 8d823ca7..34b1e8ae 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -71,22 +71,22 @@ services: # Define aggregation builders MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation\MainTaxonAggregation: - tags: { name: 'monsieurbiz.sarch.aggregation_builder' } + tags: { name: 'monsieurbiz.search.aggregation_builder' } MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation\TaxonsAggregation: - tags: { name: 'monsieurbiz.sarch.aggregation_builder' } + tags: { name: 'monsieurbiz.search.aggregation_builder' } MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation\PriceAggregation: - tags: { name: 'monsieurbiz.sarch.aggregation_builder' } + tags: { name: 'monsieurbiz.search.aggregation_builder' } MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation\ProductAttributesAggregation: - tags: { name: 'monsieurbiz.sarch.aggregation_builder' } + tags: { name: 'monsieurbiz.search.aggregation_builder' } MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation\ProductOptionsAggregation: - tags: { name: 'monsieurbiz.sarch.aggregation_builder' } + tags: { name: 'monsieurbiz.search.aggregation_builder' } MonsieurBiz\SyliusSearchPlugin\Search\Request\AggregationBuilder: - arguments: [ !tagged_iterator { tag: 'monsieurbiz.sarch.aggregation_builder' } ] + arguments: [ !tagged_iterator { tag: 'monsieurbiz.search.aggregation_builder' } ] # Define query filters MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\ProductSearchRegistry: diff --git a/src/Resources/config/sylius/ui.yaml b/src/Resources/config/sylius/ui.yaml index 2841374a..7642e9a0 100644 --- a/src/Resources/config/sylius/ui.yaml +++ b/src/Resources/config/sylius/ui.yaml @@ -2,7 +2,7 @@ sylius_ui: events: sylius.shop.layout.javascripts: blocks: - monsieurbiz_toolbox_javascripts: + monsieurbiz_search_javascripts: template: "@MonsieurBizSyliusSearchPlugin/_scripts.html.twig" sylius.shop.layout.header.content: blocks: From 6bb2a1dea3f23262de388f1e28794fe6c21089aa Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Thu, 13 Jan 2022 15:30:28 +0100 Subject: [PATCH 094/142] feat(mapping): add parameter for the product attribute and option value analyzer --- ...ppendProductAttributeMappingSubscriber.php | 86 +++++++++---------- src/Resources/config/services.yaml | 6 ++ 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php b/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php index d6ec3055..19c3ecb0 100644 --- a/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php +++ b/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php @@ -13,6 +13,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\EventSubscriber; +use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; use MonsieurBiz\SyliusSearchPlugin\Event\MappingProviderEvent; use MonsieurBiz\SyliusSearchPlugin\Repository\ProductAttributeRepositoryInterface; use MonsieurBiz\SyliusSearchPlugin\Repository\ProductOptionRepositoryInterface; @@ -22,13 +23,16 @@ class AppendProductAttributeMappingSubscriber implements EventSubscriberInterfac { private ProductAttributeRepositoryInterface $productAttributeRepository; private ProductOptionRepositoryInterface $productOptionRepository; + private string $fieldAnalyzer; public function __construct( ProductAttributeRepositoryInterface $productAttributeRepository, - ProductOptionRepositoryInterface $productOptionRepository + ProductOptionRepositoryInterface $productOptionRepository, + string $fieldAnalyzer ) { $this->productAttributeRepository = $productAttributeRepository; $this->productOptionRepository = $productOptionRepository; + $this->fieldAnalyzer = $fieldAnalyzer; } public static function getSubscribedEvents() @@ -48,62 +52,52 @@ public function omMappingProvider(MappingProviderEvent $event): void return; } $mappings = $mapping->offsetGet('mappings'); + $attributesMapping = []; foreach ($this->productAttributeRepository->findIsSearchableOrFilterable() as $productAttribute) { - if (!\array_key_exists('attributes', $mappings['properties'])) { - $mappings['properties']['attributes'] = [ - 'type' => 'nested', - 'properties' => [], - ]; - } - $mappings['properties']['attributes']['properties'][$productAttribute->getCode()] = [ + $attributesMapping[$productAttribute->getCode()] = $this->getProductAttributeOrOptionProperties($productAttribute); + } + if (0 < \count($attributesMapping)) { + $mappings['properties']['attributes'] = [ 'type' => 'nested', - 'properties' => [ - 'code' => ['type' => 'keyword'], - 'name' => ['type' => 'keyword'], - 'value' => ['type' => 'text'], - ], + 'properties' => $attributesMapping, ]; - - if ($productAttribute->isFilterable()) { - $mappings['properties']['attributes']['properties'][$productAttribute->getCode()]['properties']['value']['fields'] = [ - 'keyword' => ['type' => 'keyword'], - ]; - } - - if ($productAttribute->isSearchable()) { - // TODO replace to a configurable value - $mappings['properties']['attributes']['properties'][$productAttribute->getCode()]['properties']['value']['analyzer'] = 'search_standard'; - } } + $optionsMapping = []; foreach ($this->productOptionRepository->findIsSearchableOrFilterable() as $productOption) { - if (!\array_key_exists('options', $mappings['properties']['variants']['properties'])) { - $mappings['properties']['variants']['properties']['options'] = [ - 'type' => 'nested', - 'properties' => [], - ]; - } - $mappings['properties']['variants']['properties']['options']['properties'][$productOption->getCode()] = [ + $optionsMapping[$productOption->getCode()] = $this->getProductAttributeOrOptionProperties($productOption); + } + if (0 < \count($optionsMapping)) { + $mappings['properties']['variants']['properties']['options'] = [ 'type' => 'nested', - 'properties' => [ - 'code' => ['type' => 'keyword'], - 'name' => ['type' => 'keyword'], - 'value' => ['type' => 'text'], - ], + 'properties' => $optionsMapping, ]; + } + + $mapping->offsetSet('mappings', $mappings); + } - if ($productOption->isFilterable()) { - $mappings['properties']['variants']['properties']['options']['properties'][$productOption->getCode()]['properties']['value']['fields'] = [ - 'keyword' => ['type' => 'keyword'], - ]; - } + private function getProductAttributeOrOptionProperties(SearchableInterface $productAttributeOrOption): array + { + $properties = [ + 'type' => 'nested', + 'properties' => [ + 'code' => ['type' => 'keyword'], + 'name' => ['type' => 'keyword'], + 'value' => ['type' => 'text'], + ], + ]; - if ($productOption->isSearchable()) { - // TODO replace to a configurable value - $mappings['properties']['variants']['properties']['options']['properties'][$productOption->getCode()]['properties']['value']['analyzer'] = 'search_standard'; - } + if ($productAttributeOrOption->isFilterable()) { + $properties['properties']['value']['fields'] = [ + 'keyword' => ['type' => 'keyword'], + ]; } - $mapping->offsetSet('mappings', $mappings); + if ($productAttributeOrOption->isSearchable()) { + $properties['properties']['value']['analyzer'] = $this->fieldAnalyzer; + } + + return $properties; } } diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index 34b1e8ae..ea7a19d2 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -1,6 +1,7 @@ parameters: monsieurbiz.search.model.documentable.interface: MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface monsieurbiz.search.request.interface: MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface + monsieurbiz.search.product_attribute_analyzer: 'search_standard' services: @@ -69,6 +70,11 @@ services: arguments: $productAttributeValueReaders: !tagged_iterator { tag: 'monsieurbiz.search.automapper.product_attribute_value_reader', default_index_method: 'getReaderCode' } + # + MonsieurBiz\SyliusSearchPlugin\EventSubscriber\AppendProductAttributeMappingSubscriber: + arguments: + $fieldAnalyzer: '%monsieurbiz.search.product_attribute_analyzer%' + # Define aggregation builders MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation\MainTaxonAggregation: tags: { name: 'monsieurbiz.search.aggregation_builder' } From 6ceff8bf20a2fac07203a06da7340a697580ef68 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Thu, 13 Jan 2022 17:32:14 +0100 Subject: [PATCH 095/142] feat(query): can replace the fields used in search in the service definition --- src/Resources/config/services.yaml | 28 ++++++++++++- .../QueryFilter/Product/SearchTermFilter.php | 39 ++++++++++--------- 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index ea7a19d2..481a6f90 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -95,10 +95,34 @@ services: arguments: [ !tagged_iterator { tag: 'monsieurbiz.search.aggregation_builder' } ] # Define query filters + monsieurbiz.search.request.query_filter.product_search.search_term_filter: + class: MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\SearchTermFilter + arguments: + $fieldsToSearch: + - 'name^5' + - 'description' + + monsieurbiz.search.request.query_filter.product_instant_search.search_term_filter: + class: MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\SearchTermFilter + arguments: + $fieldsToSearch: + - 'name^5' + - 'description' + - 'name.autocomplete' + MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\ProductSearchRegistry: arguments: - [ - '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\SearchTermFilter', + '@monsieurbiz.search.request.query_filter.product_search.search_term_filter', + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\ChannelFilter', + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\EnabledFilter' + ] + + monsieurbiz.search.request.query_filter.product_instant_search_registry: + class: MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\ProductSearchRegistry + arguments: + - [ + '@monsieurbiz.search.request.query_filter.product_instant_search.search_term_filter', '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\ChannelFilter', '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\EnabledFilter' ] @@ -141,7 +165,7 @@ services: MonsieurBiz\SyliusSearchPlugin\Search\Request\ProductRequest\InstantSearch: arguments: - $queryFilterRegistry: '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\ProductSearchRegistry' + $queryFilterRegistry: '@monsieurbiz.search.request.query_filter.product_instant_search_registry' MonsieurBiz\SyliusSearchPlugin\Search\Request\ProductRequest\Taxon: arguments: diff --git a/src/Search/Request/QueryFilter/Product/SearchTermFilter.php b/src/Search/Request/QueryFilter/Product/SearchTermFilter.php index eb0672ad..e11a2752 100644 --- a/src/Search/Request/QueryFilter/Product/SearchTermFilter.php +++ b/src/Search/Request/QueryFilter/Product/SearchTermFilter.php @@ -20,19 +20,21 @@ use MonsieurBiz\SyliusSearchPlugin\Repository\ProductOptionRepositoryInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\QueryFilterInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; -use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; final class SearchTermFilter implements QueryFilterInterface { private ProductAttributeRepositoryInterface $productAttributeRepository; private ProductOptionRepositoryInterface $productOptionRepository; + private array $fieldsToSearch; public function __construct( ProductAttributeRepositoryInterface $productAttributeRepository, - ProductOptionRepositoryInterface $productOptionRepository + ProductOptionRepositoryInterface $productOptionRepository, + array $fieldsToSearch ) { $this->productAttributeRepository = $productAttributeRepository; $this->productOptionRepository = $productOptionRepository; + $this->fieldsToSearch = $fieldsToSearch; } public function apply(BoolQuery $boolQuery, RequestConfiguration $requestConfiguration): void @@ -41,24 +43,9 @@ public function apply(BoolQuery $boolQuery, RequestConfiguration $requestConfigu $searchCode = $qb->query()->term(['code' => $requestConfiguration->getQueryText()]); - $nameAndDescriptionQuery = $qb->query()->multi_match(); - $fieldsToSearch = [ - 'name^5', // todo configuration - 'description', // move to should ? score impact but not include in result - ]; - if (RequestInterface::INSTANT_TYPE === $requestConfiguration->getType()) { - $fieldsToSearch[] = 'name.autocomplete'; - } - $nameAndDescriptionQuery->setFields($fieldsToSearch); - $nameAndDescriptionQuery->setQuery($requestConfiguration->getQueryText()); - $nameAndDescriptionQuery->setType(MultiMatch::TYPE_MOST_FIELDS); - $nameAndDescriptionQuery->setFuzziness(MultiMatch::FUZZINESS_AUTO); - $searchQuery = $qb->query()->bool(); - $searchQuery - ->addShould($searchCode) - ->addShould($nameAndDescriptionQuery) - ; + $searchQuery->addShould($searchCode); + $this->addFieldsToSearchCondition($searchQuery, $requestConfiguration); $this->addAttributesQueries($searchQuery, $requestConfiguration); $this->addOptionsQueries($searchQuery, $requestConfiguration); @@ -66,6 +53,20 @@ public function apply(BoolQuery $boolQuery, RequestConfiguration $requestConfigu $boolQuery->addMust($searchQuery); } + private function addFieldsToSearchCondition(BoolQuery $searchQuery, RequestConfiguration $requestConfiguration): void + { + if (0 === \count($this->fieldsToSearch)) { + return; + } + $qb = new QueryBuilder(); + $nameAndDescriptionQuery = $qb->query()->multi_match(); + $nameAndDescriptionQuery->setFields($this->fieldsToSearch); + $nameAndDescriptionQuery->setQuery($requestConfiguration->getQueryText()); + $nameAndDescriptionQuery->setType(MultiMatch::TYPE_MOST_FIELDS); + $nameAndDescriptionQuery->setFuzziness(MultiMatch::FUZZINESS_AUTO); + $searchQuery->addShould($nameAndDescriptionQuery); + } + private function addAttributesQueries(BoolQuery $searchQuery, RequestConfiguration $requestConfiguration): void { $qb = new QueryBuilder(); From b6d0b38fb8878801ae15c24a33409467aa273d6a Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Fri, 14 Jan 2022 08:47:39 +0100 Subject: [PATCH 096/142] feat(populate): add cheapest product variant resolver --- src/AutoMapper/ProductMapperConfiguration.php | 23 ++++++--- .../CheapestProductVariantResolver.php | 51 +++++++++++++++++++ src/Resources/config/services.yaml | 2 +- 3 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 src/Resolver/CheapestProductVariantResolver.php diff --git a/src/AutoMapper/ProductMapperConfiguration.php b/src/AutoMapper/ProductMapperConfiguration.php index 1f00f5a8..ec804be8 100644 --- a/src/AutoMapper/ProductMapperConfiguration.php +++ b/src/AutoMapper/ProductMapperConfiguration.php @@ -21,24 +21,28 @@ use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\ProductInterface; use Sylius\Component\Core\Model\ProductTaxonInterface; -use Sylius\Component\Core\Model\ProductVariantInterface; use Sylius\Component\Product\Resolver\ProductVariantResolverInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; final class ProductMapperConfiguration implements MapperConfigurationInterface { private Configuration $configuration; private AutoMapperInterface $autoMapper; private ProductVariantResolverInterface $productVariantResolver; + private RequestStack $requestStack; public function __construct( Configuration $configuration, AutoMapperInterface $autoMapper, - ProductVariantResolverInterface $productVariantResolver + ProductVariantResolverInterface $productVariantResolver, + RequestStack $requestStack ) { $this->configuration = $configuration; $this->autoMapper = $autoMapper; // todo change the resolver from the configuration $this->productVariantResolver = $productVariantResolver; + $this->requestStack = $requestStack; } public function process(MapperGeneratorMetadataInterface $metadata): void @@ -132,10 +136,17 @@ public function process(MapperGeneratorMetadataInterface $metadata): void $metadata->forMember('prices', function(ProductInterface $product): array { $prices = []; - /** @var ProductVariantInterface $variant */ - $variant = $this->productVariantResolver->getVariant($product); - foreach ($variant->getChannelPricings() as $channelPricing) { - $prices[] = $this->autoMapper->map($channelPricing, $this->configuration->getTargetClass('pricing')); + foreach ($product->getChannels() as $channel) { + $request = new Request(['_channel_code' => $channel->getCode()]); + $this->requestStack->push($request); + if (null === ($variant = $this->productVariantResolver->getVariant($product))) { + continue; + } + $this->requestStack->pop(); + $prices[] = $this->autoMapper->map( + $variant->getChannelPricingForChannel($channel), + $this->configuration->getTargetClass('pricing') + ); } return $prices; diff --git a/src/Resolver/CheapestProductVariantResolver.php b/src/Resolver/CheapestProductVariantResolver.php new file mode 100644 index 00000000..aee69c1e --- /dev/null +++ b/src/Resolver/CheapestProductVariantResolver.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Resolver; + +use Sylius\Component\Channel\Context\ChannelContextInterface; +use Sylius\Component\Product\Model\ProductInterface; +use Sylius\Component\Product\Model\ProductVariantInterface; +use Sylius\Component\Product\Resolver\ProductVariantResolverInterface; + +class CheapestProductVariantResolver implements ProductVariantResolverInterface +{ + private ChannelContextInterface $channelContext; + + public function __construct(ChannelContextInterface $channelContext) + { + $this->channelContext = $channelContext; + } + + public function getVariant(ProductInterface $subject): ?ProductVariantInterface + { + if ($subject->getEnabledVariants()->isEmpty()) { + return null; + } + + $cheapestVariant = null; + $cheapestPrice = null; + $variants = $subject->getEnabledVariants(); + foreach ($variants as $variant) { + if (null === ($channelPrice = $variant->getChannelPricingForChannel($this->channelContext->getChannel()))) { + continue; + } + if (null === $cheapestPrice || $channelPrice->getPrice() < $cheapestPrice) { + $cheapestPrice = $channelPrice->getPrice(); + $cheapestVariant = $variant; + } + } + + return $cheapestVariant; + } +} diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index 481a6f90..cdb0b4c5 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -12,7 +12,7 @@ services: bind: Sylius\Bundle\ResourceBundle\Controller\ParametersParserInterface: '@sylius.resource_controller.parameters_parser' $localeProvider: '@sylius.translation_locale_provider' - $productVariantResolver: '@sylius.product_variant_resolver.default' + $productVariantResolver: '@MonsieurBiz\SyliusSearchPlugin\Resolver\CheapestProductVariantResolver' $documentableRegistry: '@monsieurbiz.search.registry.documentable' $searchRequestsRegistry: '@monsieurbiz.search.registry.search_request' From d54b516e39de2ca98583ce86232aae9f5f76b193 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Fri, 14 Jan 2022 16:20:47 +0100 Subject: [PATCH 097/142] feat(search): add an optionnal "is in stock" filter in product requests --- src/AutoMapper/VariantMapperConfiguration.php | 12 ++++- src/Resources/config/services.yaml | 11 +++-- .../QueryFilter/Product/IsInStockFilter.php | 45 +++++++++++++++++++ 3 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 src/Search/Request/QueryFilter/Product/IsInStockFilter.php diff --git a/src/AutoMapper/VariantMapperConfiguration.php b/src/AutoMapper/VariantMapperConfiguration.php index 7fa2dc08..ad786739 100644 --- a/src/AutoMapper/VariantMapperConfiguration.php +++ b/src/AutoMapper/VariantMapperConfiguration.php @@ -15,8 +15,10 @@ use Jane\Bundle\AutoMapperBundle\Configuration\MapperConfigurationInterface; use Jane\Component\AutoMapper\MapperGeneratorMetadataInterface; -use Sylius\Component\Core\Model\ProductVariantInterface; +use Jane\Component\AutoMapper\MapperMetadata; use Sylius\Component\Inventory\Checker\AvailabilityCheckerInterface; +use Sylius\Component\Inventory\Model\StockableInterface; +use Sylius\Component\Product\Model\ProductVariantInterface; final class VariantMapperConfiguration implements MapperConfigurationInterface { @@ -31,7 +33,15 @@ public function __construct(Configuration $configuration, AvailabilityCheckerInt public function process(MapperGeneratorMetadataInterface $metadata): void { + if (!$metadata instanceof MapperMetadata) { + return; + } + $metadata->forMember('is_in_stock', function(ProductVariantInterface $productVariant): bool { + if (!$productVariant instanceof StockableInterface) { + return true; + } + return $this->availabilityChecker->isStockAvailable($productVariant); }); diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index cdb0b4c5..08d4b6b9 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -2,6 +2,7 @@ parameters: monsieurbiz.search.model.documentable.interface: MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface monsieurbiz.search.request.interface: MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface monsieurbiz.search.product_attribute_analyzer: 'search_standard' + monsieurbiz.search.product.enable_stock_filter: false services: @@ -15,6 +16,7 @@ services: $productVariantResolver: '@MonsieurBiz\SyliusSearchPlugin\Resolver\CheapestProductVariantResolver' $documentableRegistry: '@monsieurbiz.search.registry.documentable' $searchRequestsRegistry: '@monsieurbiz.search.registry.search_request' + $enableStockFilter: '%monsieurbiz.search.product.enable_stock_filter%' _instanceof: MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader\ReaderInterface: @@ -115,7 +117,8 @@ services: - [ '@monsieurbiz.search.request.query_filter.product_search.search_term_filter', '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\ChannelFilter', - '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\EnabledFilter' + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\EnabledFilter', + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\IsInStockFilter' ] monsieurbiz.search.request.query_filter.product_instant_search_registry: @@ -124,7 +127,8 @@ services: - [ '@monsieurbiz.search.request.query_filter.product_instant_search.search_term_filter', '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\ChannelFilter', - '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\EnabledFilter' + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\EnabledFilter', + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\IsInStockFilter' ] MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\ProductTaxonRegistry: @@ -132,7 +136,8 @@ services: - [ '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\ChannelFilter', '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\EnabledFilter', - '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\TaxonFilter' + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\TaxonFilter', + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product\IsInStockFilter' ] # Define post filters diff --git a/src/Search/Request/QueryFilter/Product/IsInStockFilter.php b/src/Search/Request/QueryFilter/Product/IsInStockFilter.php new file mode 100644 index 00000000..df85d925 --- /dev/null +++ b/src/Search/Request/QueryFilter/Product/IsInStockFilter.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product; + +use Elastica\Query\BoolQuery; +use Elastica\QueryBuilder; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\QueryFilterInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; + +class IsInStockFilter implements QueryFilterInterface +{ + private bool $enableStockFilter; + + public function __construct(bool $enableStockFilter) + { + $this->enableStockFilter = $enableStockFilter; + } + + public function apply(BoolQuery $boolQuery, RequestConfiguration $requestConfiguration): void + { + if (!$this->enableStockFilter) { + return; + } + + $qb = new QueryBuilder(); + $boolQuery->addFilter( + $qb->query()->nested() + ->setPath('variants') + ->setQuery( + $qb->query()->term(['variants.is_in_stock' => ['value' => true]]) + ) + ); + } +} From aa21955d7dc3795607dee922f713f170f3e0ef2b Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Fri, 14 Jan 2022 16:22:49 +0100 Subject: [PATCH 098/142] fix(filter): allow a filter on same "position" - like attribute and option filters --- src/Search/Response.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Search/Response.php b/src/Search/Response.php index 6e39e98c..4c8e092c 100644 --- a/src/Search/Response.php +++ b/src/Search/Response.php @@ -85,7 +85,7 @@ private function buildFilters(): void array_map(function($aggregationCode, $aggregationData): void { foreach ($this->filterBuilders as $filterBuilder) { if (null !== $filter = $filterBuilder->build($this->getDocumentable(), $this->requestConfiguration, $aggregationCode, $aggregationData)) { - $this->filters[$filterBuilder->getPosition()] = $filter; + $this->filters[$filterBuilder->getPosition()][] = $filter; } } }, array_keys($aggregations), $aggregations); @@ -97,6 +97,6 @@ private function buildFilters(): void $result[] = $filter; } } - $this->filters = $result; + $this->filters = array_merge(...$result); } } From 08c869f99de1116c94be9553547f4747ebc51cc2 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 17 Jan 2022 14:05:17 +0100 Subject: [PATCH 099/142] fix(filter): fix product option facet --- src/AutoMapper/ProductMapperConfiguration.php | 48 +++++++++++- src/AutoMapper/VariantMapperConfiguration.php | 14 ---- ...ppendProductAttributeMappingSubscriber.php | 43 +++++++++-- src/Model/Product/VariantDTO.php | 10 +++ .../monsieurbiz_product_mapping.yaml | 4 +- src/Resources/config/services.yaml | 3 +- .../Aggregation/ProductOptionAggregation.php | 34 +++++++-- .../Aggregation/ProductOptionsAggregation.php | 5 +- .../PostFilter/Product/OptionsPostFilter.php | 19 ++++- .../QueryFilter/Product/SearchTermFilter.php | 4 +- .../Product/AttributeFilterBuilder.php | 2 +- .../Product/OptionFilterBuilder.php | 73 +++++++++++++++++++ 12 files changed, 222 insertions(+), 37 deletions(-) create mode 100644 src/Search/Response/FilterBuilders/Product/OptionFilterBuilder.php diff --git a/src/AutoMapper/ProductMapperConfiguration.php b/src/AutoMapper/ProductMapperConfiguration.php index ec804be8..b2007fea 100644 --- a/src/AutoMapper/ProductMapperConfiguration.php +++ b/src/AutoMapper/ProductMapperConfiguration.php @@ -21,6 +21,9 @@ use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\ProductInterface; use Sylius\Component\Core\Model\ProductTaxonInterface; +use Sylius\Component\Inventory\Checker\AvailabilityCheckerInterface; +use Sylius\Component\Inventory\Model\StockableInterface; +use Sylius\Component\Product\Model\ProductVariantInterface; use Sylius\Component\Product\Resolver\ProductVariantResolverInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; @@ -31,18 +34,21 @@ final class ProductMapperConfiguration implements MapperConfigurationInterface private AutoMapperInterface $autoMapper; private ProductVariantResolverInterface $productVariantResolver; private RequestStack $requestStack; + private AvailabilityCheckerInterface $availabilityChecker; public function __construct( Configuration $configuration, AutoMapperInterface $autoMapper, ProductVariantResolverInterface $productVariantResolver, - RequestStack $requestStack + RequestStack $requestStack, + AvailabilityCheckerInterface $availabilityChecker ) { $this->configuration = $configuration; $this->autoMapper = $autoMapper; // todo change the resolver from the configuration $this->productVariantResolver = $productVariantResolver; $this->requestStack = $requestStack; + $this->availabilityChecker = $availabilityChecker; } public function process(MapperGeneratorMetadataInterface $metadata): void @@ -124,6 +130,37 @@ public function process(MapperGeneratorMetadataInterface $metadata): void return $attributes; }); + $metadata->forMember('options', function(ProductInterface $product): array { + $options = []; + $currentLocale = $product->getTranslation()->getLocale(); + foreach ($product->getVariants() as $variant) { + foreach ($variant->getOptionValues() as $optionValue) { + if (!isset($options[$optionValue->getOptionCode()])) { + $options[$optionValue->getOptionCode()] = [ + 'name' => $optionValue->getOption()->getTranslation($currentLocale)->getName(), + 'values' => [], + ]; + } + $isEnabled = ($options[$optionValue->getOptionCode()]['values'][$optionValue->getCode()]['enabled'] ?? false) + || $variant->isEnabled(); + // A variant option is considered to be in stock if the current option is enabled and is in stock + $isInStock = ($options[$optionValue->getOptionCode()]['values'][$optionValue->getCode()]['is_in_stock'] ?? false) + || ($variant->isEnabled() && $this->isProductVariantInStock($variant)); + $options[$optionValue->getOptionCode()]['values'][$optionValue->getCode()] = [ + 'value' => $optionValue->getTranslation($currentLocale)->getValue(), + 'enabled' => $isEnabled, + 'is_in_stock' => $isInStock, + ]; + } + } + + foreach ($options as $optionCode => $optionValues) { + $options[$optionCode]['values'] = array_values($optionValues['values']); + } + + return $options; + }); + $metadata->forMember('variants', function(ProductInterface $product): array { $variants = []; $productVariantDTOClass = $this->configuration->getTargetClass('product_variant'); @@ -162,4 +199,13 @@ public function getTarget(): string { return $this->configuration->getTargetClass('product'); } + + private function isProductVariantInStock(ProductVariantInterface $productVariant): bool + { + if (!$productVariant instanceof StockableInterface) { + return true; + } + + return $this->availabilityChecker->isStockAvailable($productVariant); + } } diff --git a/src/AutoMapper/VariantMapperConfiguration.php b/src/AutoMapper/VariantMapperConfiguration.php index ad786739..e0bf7001 100644 --- a/src/AutoMapper/VariantMapperConfiguration.php +++ b/src/AutoMapper/VariantMapperConfiguration.php @@ -44,20 +44,6 @@ public function process(MapperGeneratorMetadataInterface $metadata): void return $this->availabilityChecker->isStockAvailable($productVariant); }); - - $metadata->forMember('options', function(ProductVariantInterface $productVariant): array { - $currentLocale = $productVariant->getTranslation()->getLocale(); - $options = []; - foreach ($productVariant->getOptionValues() as $optionValue) { - $options[$optionValue->getOptionCode()] = [ - 'name' => $optionValue->getName(), - 'code' => $optionValue->getCode(), - 'value' => $optionValue->getTranslation($currentLocale)->getValue(), - ]; - } - - return $options; - }); } public function getSource(): string diff --git a/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php b/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php index 19c3ecb0..29a27a63 100644 --- a/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php +++ b/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php @@ -54,7 +54,7 @@ public function omMappingProvider(MappingProviderEvent $event): void $mappings = $mapping->offsetGet('mappings'); $attributesMapping = []; foreach ($this->productAttributeRepository->findIsSearchableOrFilterable() as $productAttribute) { - $attributesMapping[$productAttribute->getCode()] = $this->getProductAttributeOrOptionProperties($productAttribute); + $attributesMapping[$productAttribute->getCode()] = $this->getProductAttributeProperties($productAttribute); } if (0 < \count($attributesMapping)) { $mappings['properties']['attributes'] = [ @@ -65,10 +65,10 @@ public function omMappingProvider(MappingProviderEvent $event): void $optionsMapping = []; foreach ($this->productOptionRepository->findIsSearchableOrFilterable() as $productOption) { - $optionsMapping[$productOption->getCode()] = $this->getProductAttributeOrOptionProperties($productOption); + $optionsMapping[$productOption->getCode()] = $this->getProductOptionProperties($productOption); } if (0 < \count($optionsMapping)) { - $mappings['properties']['variants']['properties']['options'] = [ + $mappings['properties']['options'] = [ 'type' => 'nested', 'properties' => $optionsMapping, ]; @@ -77,7 +77,7 @@ public function omMappingProvider(MappingProviderEvent $event): void $mapping->offsetSet('mappings', $mappings); } - private function getProductAttributeOrOptionProperties(SearchableInterface $productAttributeOrOption): array + private function getProductAttributeProperties(SearchableInterface $productAttribute): array { $properties = [ 'type' => 'nested', @@ -88,16 +88,47 @@ private function getProductAttributeOrOptionProperties(SearchableInterface $prod ], ]; - if ($productAttributeOrOption->isFilterable()) { + if ($productAttribute->isFilterable()) { $properties['properties']['value']['fields'] = [ 'keyword' => ['type' => 'keyword'], ]; } - if ($productAttributeOrOption->isSearchable()) { + if ($productAttribute->isSearchable()) { $properties['properties']['value']['analyzer'] = $this->fieldAnalyzer; } return $properties; } + + private function getProductOptionProperties(SearchableInterface $productOption): array + { + $properties = [ + 'type' => 'nested', + 'properties' => [ + 'code' => ['type' => 'keyword'], + 'name' => ['type' => 'keyword'], + 'values' => [ + 'type' => 'nested', + 'properties' => [ + 'value' => ['type' => 'text'], + 'enabled' => ['type' => 'boolean'], + 'is_in_stock' => ['type' => 'boolean'], + ], + ], + ], + ]; + + if ($productOption->isFilterable()) { + $properties['properties']['values']['properties']['value']['fields'] = [ + 'keyword' => ['type' => 'keyword'], + ]; + } + + if ($productOption->isSearchable()) { + $properties['properties']['values']['properties']['value']['analyzer'] = $this->fieldAnalyzer; + } + + return $properties; + } } diff --git a/src/Model/Product/VariantDTO.php b/src/Model/Product/VariantDTO.php index 6bf3976c..1ad3a4f6 100644 --- a/src/Model/Product/VariantDTO.php +++ b/src/Model/Product/VariantDTO.php @@ -17,6 +17,16 @@ final class VariantDTO extends Eater { + public function getCode(): ?string + { + return $this->getData('code'); + } + + public function setCode(string $code): void + { + $this->setData('code', $code); + } + /** * @return bool */ diff --git a/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml b/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml index fb768ede..0162f285 100644 --- a/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml +++ b/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml @@ -2,6 +2,7 @@ mappings: dynamic: false properties: # attributes mapping is managed dynamically + # options mapping is managed dynamically code: type: keyword enabled: @@ -70,8 +71,9 @@ mappings: variants: type: nested properties: + code: + type: keyword enabled: type: boolean is_in_stock: type: boolean - # variant options mapping is managed dynamically diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index 08d4b6b9..157f7690 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -185,5 +185,6 @@ services: product_main_taxon: '@MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders\Product\MainTaxonFilterBuilder', product_taxons: '@MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders\Product\TaxonsFilterBuilder', product_price: '@MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders\Product\PriceFilterBuilder', - product_attribute: '@MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders\Product\AttributeFilterBuilder' + product_attribute: '@MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders\Product\AttributeFilterBuilder', + product_option: '@MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders\Product\OptionFilterBuilder' } diff --git a/src/Search/Request/Aggregation/ProductOptionAggregation.php b/src/Search/Request/Aggregation/ProductOptionAggregation.php index cd2bdcec..bef367fa 100644 --- a/src/Search/Request/Aggregation/ProductOptionAggregation.php +++ b/src/Search/Request/Aggregation/ProductOptionAggregation.php @@ -13,12 +13,20 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; +use Elastica\Query\AbstractQuery; use Elastica\QueryBuilder; use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; use Sylius\Component\Product\Model\ProductOptionInterface; final class ProductOptionAggregation implements AggregationBuilderInterface { + private bool $enableStockFilter; + + public function __construct(bool $enableStockFilter) + { + $this->enableStockFilter = $enableStockFilter; + } + public function build($aggregation, array $filters) { /** @var ProductOptionInterface&SearchableInterface $aggregation */ @@ -28,10 +36,10 @@ public function build($aggregation, array $filters) $qb = new QueryBuilder(); - $filters = array_filter($filters, function($filter) use ($aggregation): bool { + $filters = array_filter($filters, function(AbstractQuery $filter) use ($aggregation): bool { return !$filter->hasParam('path') || ( false !== strpos($filter->getParam('path'), 'options.') - && 'variants.options.' . $aggregation->getCode() !== $filter->getParam('path') + && 'options.' . $aggregation->getCode() . '.values' !== $filter->getParam('path') ); }); @@ -41,17 +49,31 @@ public function build($aggregation, array $filters) } $qb = new QueryBuilder(); + $optionBoolConditions = $qb->query()->bool() + ->addMust($qb->query()->term([sprintf('options.%s.values.enabled', $aggregation->getCode()) => ['value' => true]])) + ; + if ($this->enableStockFilter) { + $optionBoolConditions->addMust($qb->query()->term([sprintf('options.%s.values.is_in_stock', $aggregation->getCode()) => ['value' => true]])); + } + $valuesAggregation = $qb->aggregation()->filter('values', $optionBoolConditions) + ->addAggregation( + $qb->aggregation()->terms('values') + ->setField(sprintf('options.%s.values.value.keyword', $aggregation->getCode())) + ) + ; return $qb->aggregation()->filter($aggregation->getCode()) ->setFilter($filterQuery) ->addAggregation( - $qb->aggregation()->nested($aggregation->getCode(), sprintf('variants.options.%s', $aggregation->getCode())) + $qb->aggregation()->nested($aggregation->getCode(), sprintf('options.%s', $aggregation->getCode())) ->addAggregation( $qb->aggregation()->terms('names') - ->setField(sprintf('variants.options.%s.name', $aggregation->getCode())) + ->setField(sprintf('options.%s.name', $aggregation->getCode())) ->addAggregation( - $qb->aggregation()->terms('values') - ->setField(sprintf('variants.options.%s.value.keyword', $aggregation->getCode())) + $qb->aggregation()->nested('values', sprintf('options.%s.values', $aggregation->getCode())) + ->addAggregation( + $valuesAggregation + ) ) ) ) diff --git a/src/Search/Request/Aggregation/ProductOptionsAggregation.php b/src/Search/Request/Aggregation/ProductOptionsAggregation.php index 47bba51a..c4206f07 100644 --- a/src/Search/Request/Aggregation/ProductOptionsAggregation.php +++ b/src/Search/Request/Aggregation/ProductOptionsAggregation.php @@ -14,6 +14,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; use Elastica\Query\AbstractQuery; +use Elastica\QueryBuilder; use Sylius\Component\Product\Model\ProductOptionInterface; final class ProductOptionsAggregation implements AggregationBuilderInterface @@ -31,7 +32,7 @@ public function build($aggregation, array $filters) return null; } - $qb = new \Elastica\QueryBuilder(); + $qb = new QueryBuilder(); $currentFilters = array_filter($filters, function(AbstractQuery $filter): bool { return !$filter->hasParam('path') || false === strpos($filter->getParam('path'), 'options.'); }); @@ -40,7 +41,7 @@ public function build($aggregation, array $filters) $filterQuery->addMust($filter); } - $optionsAggregation = $qb->aggregation()->nested('options', 'variants.options'); + $optionsAggregation = $qb->aggregation()->nested('options', 'options'); foreach ($aggregation as $subAggregation) { $subAggregationObject = $this->productOptionAggregationBuilder->build($subAggregation, $filters); if (null === $subAggregationObject) { diff --git a/src/Search/Request/PostFilter/Product/OptionsPostFilter.php b/src/Search/Request/PostFilter/Product/OptionsPostFilter.php index d346d109..cbc13f2c 100644 --- a/src/Search/Request/PostFilter/Product/OptionsPostFilter.php +++ b/src/Search/Request/PostFilter/Product/OptionsPostFilter.php @@ -21,19 +21,32 @@ final class OptionsPostFilter implements PostFilterInterface { + private bool $enableStockFilter; + + public function __construct(bool $enableStockFilter) + { + $this->enableStockFilter = $enableStockFilter; + } + public function apply(BoolQuery $boolQuery, RequestConfiguration $requestConfiguration): void { $qb = new QueryBuilder(); foreach ($requestConfiguration->getAppliedFilters('options') as $field => $values) { $optionValueQuery = $qb->query()->bool(); - foreach ($values as $value) { - $termQuery = $qb->query()->term([sprintf('variants.options.%s.value.keyword', $field) => SlugHelper::toLabel($value)]); + $termQuery = $qb->query()->term([sprintf('options.%s.values.value.keyword', $field) => SlugHelper::toLabel($value)]); $optionValueQuery->addShould($termQuery); // todo configure the "and" or "or" } $optionQuery = $qb->query()->nested(); - $optionQuery->setPath(sprintf('variants.options.%s', $field))->setQuery($optionValueQuery); + $condition = $qb->query()->bool() + ->addMust($qb->query()->term([sprintf('options.%s.values.enabled', $field) => true])) + ; + if ($this->enableStockFilter) { + $condition->addMust($qb->query()->term([sprintf('options.%s.values.is_in_stock', $field) => true])); + } + $condition->addMust($optionValueQuery); + $optionQuery->setPath(sprintf('options.%s.values', $field))->setQuery($condition); $boolQuery->addMust($optionQuery); } diff --git a/src/Search/Request/QueryFilter/Product/SearchTermFilter.php b/src/Search/Request/QueryFilter/Product/SearchTermFilter.php index e11a2752..ab13328a 100644 --- a/src/Search/Request/QueryFilter/Product/SearchTermFilter.php +++ b/src/Search/Request/QueryFilter/Product/SearchTermFilter.php @@ -99,13 +99,13 @@ private function addOptionsQueries(BoolQuery $searchQuery, RequestConfiguration $attributeValueQuery = $qb->query()->multi_match(); $attributeValueQuery->setFields([ - sprintf('variants.options.%s.value^%d', $productOption->getCode(), $productOption->getSearchWeight()), + sprintf('options.%s.values.value^%d', $productOption->getCode(), $productOption->getSearchWeight()), ]); $attributeValueQuery->setQuery($requestConfiguration->getQueryText()); $attributeValueQuery->setFuzziness(MultiMatch::FUZZINESS_AUTO); $attributeQuery = $qb->query()->nested(); - $attributeQuery->setPath(sprintf('variants.options.%s', $productOption->getCode()))->setQuery($attributeValueQuery); + $attributeQuery->setPath(sprintf('options.%s.values', $productOption->getCode()))->setQuery($attributeValueQuery); $searchQuery->addShould($attributeQuery); } diff --git a/src/Search/Response/FilterBuilders/Product/AttributeFilterBuilder.php b/src/Search/Response/FilterBuilders/Product/AttributeFilterBuilder.php index 0da49fea..f97183c4 100644 --- a/src/Search/Response/FilterBuilders/Product/AttributeFilterBuilder.php +++ b/src/Search/Response/FilterBuilders/Product/AttributeFilterBuilder.php @@ -26,7 +26,7 @@ public function build( string $aggregationCode, array $aggregationData ): ?array { - if ('monsieurbiz_product' !== $documentable->getIndexCode() || !\in_array($aggregationCode, ['attributes', 'options'], true)) { + if ('monsieurbiz_product' !== $documentable->getIndexCode() || 'attributes' !== $aggregationCode) { return null; } diff --git a/src/Search/Response/FilterBuilders/Product/OptionFilterBuilder.php b/src/Search/Response/FilterBuilders/Product/OptionFilterBuilder.php new file mode 100644 index 00000000..12fa47a3 --- /dev/null +++ b/src/Search/Response/FilterBuilders/Product/OptionFilterBuilder.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders\Product; + +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Filter\Filter; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; +use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterBuilders\FilterBuilderInterface; + +class OptionFilterBuilder implements FilterBuilderInterface +{ + public function build( + DocumentableInterface $documentable, + RequestConfiguration $requestConfiguration, + string $aggregationCode, + array $aggregationData + ): ?array { + if ('monsieurbiz_product' !== $documentable->getIndexCode() || 'options' !== $aggregationCode) { + return null; + } + + $attributeAggregations = $aggregationData[$aggregationCode] ?? []; + $attributeAggregations = $attributeAggregations[$aggregationCode] ?? $attributeAggregations; + unset($attributeAggregations['doc_count']); + $filters = []; + foreach ($attributeAggregations as $attributeCode => $attributeAggregation) { + if (isset($attributeAggregation[$attributeCode])) { + $attributeAggregation = $attributeAggregation[$attributeCode]; + } + $attributeNameBuckets = $attributeAggregation['names']['buckets'] ?? []; + foreach ($attributeNameBuckets as $attributeNameBucket) { + $attributeValueBuckets = $attributeNameBucket['values']['values']['values']['buckets'] ?? []; + $filter = new Filter( + $requestConfiguration, + $attributeCode, + $attributeNameBucket['key'], + $attributeNameBucket['doc_count'], + $aggregationCode + ); + foreach ($attributeValueBuckets as $attributeValueBucket) { + if (0 === $attributeValueBucket['doc_count']) { + continue; + } + if (isset($attributeValueBucket['key']) && isset($attributeValueBucket['doc_count'])) { + $filter->addValue($attributeValueBucket['key'], $attributeValueBucket['doc_count']); + } + } + + if (0 !== \count($filter->getValues())) { + $filters[] = $filter; + } + } + } + + return 0 !== \count($filters) ? $filters : null; + } + + public function getPosition(): int + { + return 20; + } +} From 0c414fc8dba8cf98d7d1c35f19e13f111395a43e Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 17 Jan 2022 14:05:49 +0100 Subject: [PATCH 100/142] fix: allow a non array in query parameters --- src/Search/Request/RequestConfiguration.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Search/Request/RequestConfiguration.php b/src/Search/Request/RequestConfiguration.php index 30a81061..57c886df 100644 --- a/src/Search/Request/RequestConfiguration.php +++ b/src/Search/Request/RequestConfiguration.php @@ -52,11 +52,11 @@ public function getQueryText(): string return $this->request->get('query', ''); } - public function getAppliedFilters($type = null): array + public function getAppliedFilters(string $type = null): array { $requestQuery = $this->request->query->all(); - $requestQuery = array_map(function($query): ?array { - return array_filter($query); + $requestQuery = array_map(function($query) { + return \is_array($query) ? array_filter($query) : $query; }, $requestQuery); return null !== $type ? ($requestQuery[$type] ?? []) : $requestQuery; From 26cd2768d6cd225f8d31e524a9bfb7fa94b79fac Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 17 Jan 2022 15:23:39 +0100 Subject: [PATCH 101/142] feat(query): add the "is in stock" weight function --- src/Resources/config/services.yaml | 17 ++++++ .../FunctionScore/FunctionScoreInterface.php | 22 ++++++++ .../FunctionScoreRegistryInterface.php | 20 +++++++ .../Product/InStockWeightFunction.php | 54 +++++++++++++++++++ .../ProductFunctionScoreRegistry.php | 28 ++++++++++ .../Request/ProductRequest/InstantSearch.php | 21 +++++++- src/Search/Request/ProductRequest/Search.php | 17 +++++- src/Search/Request/ProductRequest/Taxon.php | 17 +++++- .../Sorting/Product/PositionSorter.php | 6 +-- 9 files changed, 192 insertions(+), 10 deletions(-) create mode 100644 src/Search/Request/FunctionScore/FunctionScoreInterface.php create mode 100644 src/Search/Request/FunctionScore/FunctionScoreRegistryInterface.php create mode 100644 src/Search/Request/FunctionScore/Product/InStockWeightFunction.php create mode 100644 src/Search/Request/FunctionScore/ProductFunctionScoreRegistry.php diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index 157f7690..045101df 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -3,6 +3,11 @@ parameters: monsieurbiz.search.request.interface: MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface monsieurbiz.search.product_attribute_analyzer: 'search_standard' monsieurbiz.search.product.enable_stock_filter: false + monsieurbiz.search.product.is_in_stock_scoring_boost: 0 # The value is used to multiply the document score (0 to disable the scoring boost) + monsieurbiz.search.product.apply_is_in_stock_scoring_boost_on: + - !php/const MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface::SEARCH_TYPE + - !php/const MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface::TAXON_TYPE + - !php/const MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface::INSTANT_TYPE services: @@ -161,6 +166,18 @@ services: '@MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\Product\CreatedAtSorter', ] + # Functions score + MonsieurBiz\SyliusSearchPlugin\Search\Request\FunctionScore\Product\InStockWeightFunction: + arguments: + $inStockWeight: '%monsieurbiz.search.product.is_in_stock_scoring_boost%' + $applyOnRequestTypes: '%monsieurbiz.search.product.apply_is_in_stock_scoring_boost_on%' + + MonsieurBiz\SyliusSearchPlugin\Search\Request\FunctionScore\ProductFunctionScoreRegistry: + arguments: + - [ + '@MonsieurBiz\SyliusSearchPlugin\Search\Request\FunctionScore\Product\InStockWeightFunction' + ] + # Define the product queries MonsieurBiz\SyliusSearchPlugin\Search\Request\ProductRequest\Search: arguments: diff --git a/src/Search/Request/FunctionScore/FunctionScoreInterface.php b/src/Search/Request/FunctionScore/FunctionScoreInterface.php new file mode 100644 index 00000000..67b5e772 --- /dev/null +++ b/src/Search/Request/FunctionScore/FunctionScoreInterface.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\FunctionScore; + +use Elastica\Query\FunctionScore; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; + +interface FunctionScoreInterface +{ + public function addFunctionScore(FunctionScore $functionScore, RequestConfiguration $requestConfiguration): void; +} diff --git a/src/Search/Request/FunctionScore/FunctionScoreRegistryInterface.php b/src/Search/Request/FunctionScore/FunctionScoreRegistryInterface.php new file mode 100644 index 00000000..7338cfa2 --- /dev/null +++ b/src/Search/Request/FunctionScore/FunctionScoreRegistryInterface.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\FunctionScore; + +use Sylius\Component\Registry\ServiceRegistryInterface; + +interface FunctionScoreRegistryInterface extends ServiceRegistryInterface +{ +} diff --git a/src/Search/Request/FunctionScore/Product/InStockWeightFunction.php b/src/Search/Request/FunctionScore/Product/InStockWeightFunction.php new file mode 100644 index 00000000..1839ad83 --- /dev/null +++ b/src/Search/Request/FunctionScore/Product/InStockWeightFunction.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\FunctionScore\Product; + +use Elastica\Query\FunctionScore; +use Elastica\QueryBuilder; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\FunctionScore\FunctionScoreInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; + +class InStockWeightFunction implements FunctionScoreInterface +{ + private bool $enableStockFilter; + private int $inStockWeight; + private array $applyOnRequestTypes; + + public function __construct( + bool $enableStockFilter, + int $inStockWeight, + array $applyOnRequestTypes + ) { + $this->enableStockFilter = $enableStockFilter; + $this->inStockWeight = $inStockWeight; + $this->applyOnRequestTypes = $applyOnRequestTypes; + } + + public function addFunctionScore(FunctionScore $functionScore, RequestConfiguration $requestConfiguration): void + { + if ( + $this->enableStockFilter + || 1 > $this->inStockWeight + || !\in_array($requestConfiguration->getType(), $this->applyOnRequestTypes, true) + ) { + return; + } + + $qb = new QueryBuilder(); + $functionScore->addWeightFunction( + $this->inStockWeight, + $qb->query()->nested()->setPath('variants') + ->setQuery($qb->query()->term(['variants.is_in_stock' => true])) + ); + } +} diff --git a/src/Search/Request/FunctionScore/ProductFunctionScoreRegistry.php b/src/Search/Request/FunctionScore/ProductFunctionScoreRegistry.php new file mode 100644 index 00000000..bafb2eff --- /dev/null +++ b/src/Search/Request/FunctionScore/ProductFunctionScoreRegistry.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\FunctionScore; + +use Sylius\Component\Registry\ServiceRegistry; + +final class ProductFunctionScoreRegistry extends ServiceRegistry implements FunctionScoreRegistryInterface +{ + public function __construct(array $functionsScore = []) + { + parent::__construct(FunctionScoreInterface::class, 'monsieurbiz.search'); + + foreach ($functionsScore as $functionScore) { + $this->register(\get_class($functionScore), $functionScore); + } + } +} diff --git a/src/Search/Request/ProductRequest/InstantSearch.php b/src/Search/Request/ProductRequest/InstantSearch.php index eb7be112..d4fdac8b 100644 --- a/src/Search/Request/ProductRequest/InstantSearch.php +++ b/src/Search/Request/ProductRequest/InstantSearch.php @@ -16,6 +16,7 @@ use Elastica\Query; use Elastica\QueryBuilder; use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\FunctionScore\FunctionScoreRegistryInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\QueryFilterRegistryInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; @@ -27,13 +28,16 @@ final class InstantSearch implements RequestInterface private DocumentableInterface $documentable; private ?RequestConfiguration $configuration; private QueryFilterRegistryInterface $queryFilterRegistry; + private FunctionScoreRegistryInterface $functionScoreRegistry; public function __construct( ServiceRegistryInterface $documentableRegistry, - QueryFilterRegistryInterface $queryFilterRegistry + QueryFilterRegistryInterface $queryFilterRegistry, + FunctionScoreRegistryInterface $functionScoreRegistry ) { $this->documentable = $documentableRegistry->get('search.documentable.monsieurbiz_product'); $this->queryFilterRegistry = $queryFilterRegistry; + $this->functionScoreRegistry = $functionScoreRegistry; } public function getType(): string @@ -58,7 +62,20 @@ public function getQuery(): Query $queryFilter->apply($boolQuery, $this->configuration); } - return Query::create($boolQuery); + $query = Query::create($boolQuery); + + $functionScore = $qb->query()->function_score() + ->setQuery($query->getQuery()) + ->setBoostMode(Query\FunctionScore::BOOST_MODE_MULTIPLY) + ->setScoreMode(Query\FunctionScore::SCORE_MODE_MULTIPLY) + ; + foreach ($this->functionScoreRegistry->all() as $functionScoreClass) { + $functionScoreClass->addFunctionScore($functionScore, $this->configuration); + } + + $query->setQuery($functionScore); + + return $query; } public function supports(string $type, string $documentableCode): bool diff --git a/src/Search/Request/ProductRequest/Search.php b/src/Search/Request/ProductRequest/Search.php index e84c34c2..919b1063 100644 --- a/src/Search/Request/ProductRequest/Search.php +++ b/src/Search/Request/ProductRequest/Search.php @@ -19,6 +19,7 @@ use MonsieurBiz\SyliusSearchPlugin\Repository\ProductAttributeRepositoryInterface; use MonsieurBiz\SyliusSearchPlugin\Repository\ProductOptionRepositoryInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\AggregationBuilder; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\FunctionScore\FunctionScoreRegistryInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\PostFilterRegistryInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\QueryFilterRegistryInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; @@ -37,6 +38,7 @@ final class Search implements RequestInterface private QueryFilterRegistryInterface $queryFilterRegistry; private PostFilterRegistryInterface $postFilterRegistry; private SorterRegistryInterface $sorterRegistry; + private FunctionScoreRegistryInterface $functionScoreRegistry; public function __construct( ServiceRegistryInterface $documentableRegistry, @@ -45,7 +47,8 @@ public function __construct( AggregationBuilder $aggregationBuilder, QueryFilterRegistryInterface $queryFilterRegistry, PostFilterRegistryInterface $postFilterRegistry, - SorterRegistryInterface $sorterRegistry + SorterRegistryInterface $sorterRegistry, + FunctionScoreRegistryInterface $functionScoreRegistry ) { //TODO check if exist, return a dummy documentable if not $this->documentable = $documentableRegistry->get('search.documentable.monsieurbiz_product'); @@ -55,6 +58,7 @@ public function __construct( $this->queryFilterRegistry = $queryFilterRegistry; $this->postFilterRegistry = $postFilterRegistry; $this->sorterRegistry = $sorterRegistry; + $this->functionScoreRegistry = $functionScoreRegistry; } public function getType(): string @@ -98,7 +102,16 @@ public function getQuery(): Query $sorter->apply($query, $this->configuration); } - dump(json_encode($query->toArray(), 1)); + $functionScore = $qb->query()->function_score() + ->setQuery($query->getQuery()) + ->setBoostMode(Query\FunctionScore::BOOST_MODE_MULTIPLY) + ->setScoreMode(Query\FunctionScore::SCORE_MODE_MULTIPLY) + ; + foreach ($this->functionScoreRegistry->all() as $functionScoreClass) { + $functionScoreClass->addFunctionScore($functionScore, $this->configuration); + } + + $query->setQuery($functionScore); return $query; } diff --git a/src/Search/Request/ProductRequest/Taxon.php b/src/Search/Request/ProductRequest/Taxon.php index 51e0c5af..b12301ad 100644 --- a/src/Search/Request/ProductRequest/Taxon.php +++ b/src/Search/Request/ProductRequest/Taxon.php @@ -19,6 +19,7 @@ use MonsieurBiz\SyliusSearchPlugin\Repository\ProductAttributeRepositoryInterface; use MonsieurBiz\SyliusSearchPlugin\Repository\ProductOptionRepositoryInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\AggregationBuilder; +use MonsieurBiz\SyliusSearchPlugin\Search\Request\FunctionScore\FunctionScoreRegistryInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\PostFilter\PostFilterRegistryInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\QueryFilterRegistryInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; @@ -38,6 +39,7 @@ final class Taxon implements RequestInterface private QueryFilterRegistryInterface $queryFilterRegistry; private PostFilterRegistryInterface $postFilterRegistry; private SorterRegistryInterface $sorterRegistry; + private FunctionScoreRegistryInterface $functionScoreRegistry; public function __construct( ServiceRegistryInterface $documentableRegistry, @@ -47,7 +49,8 @@ public function __construct( AggregationBuilder $aggregationBuilder, QueryFilterRegistryInterface $queryFilterRegistry, PostFilterRegistryInterface $postFilterRegistry, - SorterRegistryInterface $sorterRegistry + SorterRegistryInterface $sorterRegistry, + FunctionScoreRegistryInterface $functionScoreRegistry ) { $this->documentable = $documentableRegistry->get('search.documentable.monsieurbiz_product'); $this->productAttributeRepository = $productAttributeRepository; @@ -57,6 +60,7 @@ public function __construct( $this->queryFilterRegistry = $queryFilterRegistry; $this->postFilterRegistry = $postFilterRegistry; $this->sorterRegistry = $sorterRegistry; + $this->functionScoreRegistry = $functionScoreRegistry; } public function getType(): string @@ -91,7 +95,16 @@ public function getQuery(): Query $sorter->apply($query, $this->configuration); } - dump(json_encode($query->toArray(), 1)); + $functionScore = $qb->query()->function_score() + ->setQuery($query->getQuery()) + ->setBoostMode(Query\FunctionScore::BOOST_MODE_MULTIPLY) + ->setScoreMode(Query\FunctionScore::SCORE_MODE_MULTIPLY) + ; + foreach ($this->functionScoreRegistry->all() as $functionScoreClass) { + $functionScoreClass->addFunctionScore($functionScore, $this->configuration); + } + + $query->setQuery($functionScore); return $query; } diff --git a/src/Search/Request/Sorting/Product/PositionSorter.php b/src/Search/Request/Sorting/Product/PositionSorter.php index 32f3efa7..8b81c41d 100644 --- a/src/Search/Request/Sorting/Product/PositionSorter.php +++ b/src/Search/Request/Sorting/Product/PositionSorter.php @@ -31,7 +31,7 @@ public function apply(Query $query, RequestConfiguration $requestConfiguration): return; } - $sort = $this->buildSort('_score', 'desc'); + $query->addSort($this->buildSort('_score', 'desc')); if (RequestInterface::TAXON_TYPE == $requestConfiguration->getType()) { $qb = new QueryBuilder(); $filter = $qb->query()->nested() @@ -40,9 +40,7 @@ public function apply(Query $query, RequestConfiguration $requestConfiguration): $qb->query()->term(['product_taxons.taxon.code' => ['value' => $requestConfiguration->getTaxon()->getCode()]]) ) ; - $sort = $this->buildSort('product_taxons.position', 'asc', 'product_taxons', null, $filter); + $query->addSort($this->buildSort('product_taxons.position', 'asc', 'product_taxons', null, $filter)); } - - $query->addSort($sort); } } From 09d6d2b2a19c710283b002385e99e7497cd01f90 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 17 Jan 2022 15:47:31 +0100 Subject: [PATCH 102/142] refactor: throw an exception if taxon doesnt exist and remove todo --- src/AutoMapper/ProductMapperConfiguration.php | 1 - .../ObjectNotInstanceOfClassException.php | 24 +++++++++++++++++++ src/Search/Request/ProductRequest/Search.php | 1 - src/Search/Request/RequestConfiguration.php | 13 ++++++++-- 4 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 src/Exception/ObjectNotInstanceOfClassException.php diff --git a/src/AutoMapper/ProductMapperConfiguration.php b/src/AutoMapper/ProductMapperConfiguration.php index b2007fea..39fa4b00 100644 --- a/src/AutoMapper/ProductMapperConfiguration.php +++ b/src/AutoMapper/ProductMapperConfiguration.php @@ -45,7 +45,6 @@ public function __construct( ) { $this->configuration = $configuration; $this->autoMapper = $autoMapper; - // todo change the resolver from the configuration $this->productVariantResolver = $productVariantResolver; $this->requestStack = $requestStack; $this->availabilityChecker = $availabilityChecker; diff --git a/src/Exception/ObjectNotInstanceOfClassException.php b/src/Exception/ObjectNotInstanceOfClassException.php new file mode 100644 index 00000000..9790aa2f --- /dev/null +++ b/src/Exception/ObjectNotInstanceOfClassException.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Exception; + +use InvalidArgumentException; + +class ObjectNotInstanceOfClassException extends InvalidArgumentException +{ + public static function fromClassName(string $className): self + { + return new self(sprintf('Object is not instance of class "%s"', $className)); + } +} diff --git a/src/Search/Request/ProductRequest/Search.php b/src/Search/Request/ProductRequest/Search.php index 919b1063..1215ba49 100644 --- a/src/Search/Request/ProductRequest/Search.php +++ b/src/Search/Request/ProductRequest/Search.php @@ -50,7 +50,6 @@ public function __construct( SorterRegistryInterface $sorterRegistry, FunctionScoreRegistryInterface $functionScoreRegistry ) { - //TODO check if exist, return a dummy documentable if not $this->documentable = $documentableRegistry->get('search.documentable.monsieurbiz_product'); $this->productAttributeRepository = $productAttributeRepository; $this->productOptionRepository = $productOptionRepository; diff --git a/src/Search/Request/RequestConfiguration.php b/src/Search/Request/RequestConfiguration.php index 57c886df..69727783 100644 --- a/src/Search/Request/RequestConfiguration.php +++ b/src/Search/Request/RequestConfiguration.php @@ -13,11 +13,13 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request; +use MonsieurBiz\SyliusSearchPlugin\Exception\ObjectNotInstanceOfClassException; use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; use MonsieurBiz\SyliusSettingsPlugin\Settings\SettingsInterface; use Sylius\Bundle\ResourceBundle\Controller\Parameters; use Sylius\Component\Channel\Context\ChannelContextInterface; use Sylius\Component\Core\Model\TaxonInterface; +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; use Symfony\Component\HttpFoundation\Request; final class RequestConfiguration @@ -29,7 +31,7 @@ final class RequestConfiguration private DocumentableInterface $documentable; private SettingsInterface $searchSettings; private ChannelContextInterface $channelContext; - private ?Parameters $parameters; + private Parameters $parameters; public function __construct( Request $request, @@ -107,7 +109,14 @@ public function getDocumentType(): string public function getTaxon(): TaxonInterface { - // todo throw exception if not exist + if (!$this->parameters->has('taxon')) { + throw new ParameterNotFoundException('taxon'); + } + $taxon = $this->parameters->get('taxon'); + if (!$taxon instanceof TaxonInterface) { + throw ObjectNotInstanceOfClassException::fromClassName(TaxonInterface::class); + } + return $this->parameters->get('taxon'); } } From 349fc4f01b12ca8dfe3ea15a9789fc851a0e4876 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 17 Jan 2022 17:22:23 +0100 Subject: [PATCH 103/142] refactor: allow to define the documentable class in the documentable definition --- src/DependencyInjection/Configuration.php | 2 ++ .../DocumentableRegistryPass.php | 19 +++++++++++++++++-- src/Resources/config/monsieurbiz_search.yaml | 1 + 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index c0b1917d..4c46d94a 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -15,6 +15,7 @@ use MonsieurBiz\SyliusSearchPlugin\Mapping\YamlWithLocaleProvider; use MonsieurBiz\SyliusSearchPlugin\Model\Datasource\RepositoryDatasource; +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\Documentable; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; @@ -34,6 +35,7 @@ public function getConfigTreeBuilder(): TreeBuilder ->defaultValue([]) ->arrayPrototype() ->children() + ->scalarNode('document_class')->defaultValue(Documentable::class)->end() ->scalarNode('instant_search_enabled')->defaultValue(false)->end() ->scalarNode('source')->isRequired()->cannotBeEmpty()->end() ->scalarNode('target')->isRequired()->cannotBeEmpty()->end() diff --git a/src/DependencyInjection/DocumentableRegistryPass.php b/src/DependencyInjection/DocumentableRegistryPass.php index 63f7eb99..3edabdbd 100644 --- a/src/DependencyInjection/DocumentableRegistryPass.php +++ b/src/DependencyInjection/DocumentableRegistryPass.php @@ -13,7 +13,8 @@ namespace MonsieurBiz\SyliusSearchPlugin\DependencyInjection; -use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\Documentable; +use InvalidArgumentException; +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; @@ -39,7 +40,9 @@ public function process(ContainerBuilder $container): void foreach ($documentables as $indexCode => $documentableConfiguration) { $documentableServiceId = 'search.documentable.' . $indexCode; - $documentableDefinition = (new Definition(Documentable::class)) // TODO - move into config + $documentableClass = $documentableConfiguration['document_class']; + $this->validateDocumentableResource($documentableClass); + $documentableDefinition = (new Definition($documentableClass)) ->setAutowired(true) ->setArguments([ '$indexCode' => $indexCode, @@ -66,4 +69,16 @@ public function process(ContainerBuilder $container): void $container->setParameter('monsieurbiz.settings.config.plugins', $searchSettings); } + + /** + * @throws InvalidArgumentException + */ + private function validateDocumentableResource(string $class): void + { + $interfaces = (array) (class_implements($class) ?? []); + + if (!\in_array(DocumentableInterface::class, $interfaces, true)) { + throw new InvalidArgumentException(sprintf('Class "%s" must implement "%s" to be registered as a Documentable.', $class, DocumentableInterface::class)); + } + } } diff --git a/src/Resources/config/monsieurbiz_search.yaml b/src/Resources/config/monsieurbiz_search.yaml index 47efa2f8..398c49a1 100644 --- a/src/Resources/config/monsieurbiz_search.yaml +++ b/src/Resources/config/monsieurbiz_search.yaml @@ -1,6 +1,7 @@ monsieurbiz_sylius_search: documents: monsieurbiz_product: + #document_class: '…' # by default MonsieurBiz\SyliusSearchPlugin\Model\Documentable\Documentable instant_search_enabled: true # by default false limits: search: [9, 18, 27] From 83abedf444be5749ff7e547930ca7ba2e1e608d9 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 17 Jan 2022 17:22:52 +0100 Subject: [PATCH 104/142] fix(filter): prevent empty values in main taxon filter --- src/Search/Request/PostFilter/Product/MainTaxonPostFilter.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Search/Request/PostFilter/Product/MainTaxonPostFilter.php b/src/Search/Request/PostFilter/Product/MainTaxonPostFilter.php index 2af93bbc..152e3c12 100644 --- a/src/Search/Request/PostFilter/Product/MainTaxonPostFilter.php +++ b/src/Search/Request/PostFilter/Product/MainTaxonPostFilter.php @@ -28,6 +28,7 @@ public function apply(BoolQuery $boolQuery, RequestConfiguration $requestConfigu $mainTaxonQuery = $qb->query() ->bool() ; + $values = array_filter($values); foreach ($values as $value) { $mainTaxonQuery->addShould( $qb->query() From b4457f941fc1f9e9612d73f5445ca93c058e817c Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Mon, 17 Jan 2022 17:24:21 +0100 Subject: [PATCH 105/142] fix(filter): return current values of current filter --- src/Search/Filter/Filter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Search/Filter/Filter.php b/src/Search/Filter/Filter.php index caf12aac..b4319fee 100644 --- a/src/Search/Filter/Filter.php +++ b/src/Search/Filter/Filter.php @@ -123,6 +123,6 @@ protected function getCurrentValues(): array { $appliedFilters = $this->requestConfiguration->getAppliedFilters(); - return $appliedFilters[$this->getType()][$this->getCode()] ?? $appliedFilters[$this->getCode()] ?? $appliedFilters; + return $appliedFilters[$this->getType()][$this->getCode()] ?? $appliedFilters[$this->getCode()] ?? []; } } From 30d3a044183b7346cc47676ed1dc4104c6f96551 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Tue, 18 Jan 2022 08:40:29 +0100 Subject: [PATCH 106/142] feat(messenger): change the queue name --- src/Resources/config/messenger.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Resources/config/messenger.yaml b/src/Resources/config/messenger.yaml index 37bcdb6b..b82225f4 100644 --- a/src/Resources/config/messenger.yaml +++ b/src/Resources/config/messenger.yaml @@ -1,7 +1,10 @@ framework: messenger: transports: - async_search: '%env(MONSIEURBIZ_SEARCHPLUGIN_MESSENGER_TRANSPORT_DSN)%' + async_search: + dsn: '%env(MONSIEURBIZ_SEARCHPLUGIN_MESSENGER_TRANSPORT_DSN)%' + options: + queue_name: 'monsieurbiz_search' routing: MonsieurBiz\SyliusSearchPlugin\Message\ProductReindexFromTaxon: async_search From 0af73cc4fd9e5d5a77ac58508703ba9f54ca1d6b Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Thu, 27 Jan 2022 15:38:57 +0100 Subject: [PATCH 107/142] fix(test): add parse-tags option in the lint:yaml --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e416f418..a9d5facc 100644 --- a/Makefile +++ b/Makefile @@ -87,7 +87,7 @@ test.container: ## Lint the symfony container ${CONSOLE} lint:container test.yaml: ## Lint the symfony Yaml files - ${CONSOLE} lint:yaml ../../recipes ../../src/Resources/config + ${CONSOLE} lint:yaml ../../recipes ../../src/Resources/config --parse-tags test.schema: ## Validate MySQL Schema ${CONSOLE} doctrine:schema:validate From 506febca677c8b39770ac0e729fc9a1cf1761351 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Thu, 27 Jan 2022 17:53:32 +0100 Subject: [PATCH 108/142] fix(test): fix phpstan errors --- src/AutoMapper/Configuration.php | 4 +-- .../ProductAttributeValueConfiguration.php | 7 ++++ src/AutoMapper/ProductMapperConfiguration.php | 24 ++++++++++--- src/Command/SearchCommand.php | 11 +++--- src/Controller/SearchController.php | 11 ++++-- .../AutomapperConfigurationRegistryPass.php | 2 +- src/Event/MappingProviderEvent.php | 18 ++++++++-- ...ppendProductAttributeMappingSubscriber.php | 1 + .../ReindexProductEventSubscriber.php | 35 ++++++++++++------- src/Index/Indexer.php | 16 +++------ src/Mapping/YamlWithLocaleProvider.php | 7 ++-- .../ProductReindexFromIdsHandler.php | 5 ++- .../ProductReindexFromTaxonHandler.php | 9 ++++- .../ProductToDeleteFromIdsHandler.php | 5 ++- src/Model/Datasource/RepositoryDatasource.php | 8 ++--- .../Product/ProductDTONormalizer.php | 16 ++++----- .../CheapestProductVariantResolver.php | 10 ++++-- src/Search/Filter/FilterValue.php | 14 ++------ src/Search/Filter/RangeFilter.php | 2 +- .../AggregationBuilderInterface.php | 4 +-- .../Aggregation/MainTaxonAggregation.php | 7 +++- .../Request/Aggregation/PriceAggregation.php | 11 ++++-- .../ProductAttributeAggregation.php | 12 ++++--- .../ProductAttributesAggregation.php | 11 ++++-- .../Aggregation/ProductOptionAggregation.php | 7 +++- .../Aggregation/ProductOptionsAggregation.php | 6 +++- .../Request/Aggregation/TaxonsAggregation.php | 15 +++++--- src/Search/Request/AggregationBuilder.php | 5 +-- .../Product/MainTaxonPostFilter.php | 2 +- .../Request/ProductRequest/InstantSearch.php | 8 +++-- src/Search/Request/ProductRequest/Search.php | 8 +++-- src/Search/Request/ProductRequest/Taxon.php | 12 +++++-- src/Search/Request/RequestConfiguration.php | 4 +-- .../Request/Sorting/SorterBuilderTrait.php | 3 ++ src/Search/Response.php | 4 +++ src/Search/ResponseInterface.php | 11 +++++- src/Twig/Extension/RenderSearchForm.php | 15 ++++---- 37 files changed, 238 insertions(+), 112 deletions(-) diff --git a/src/AutoMapper/Configuration.php b/src/AutoMapper/Configuration.php index a9f3286b..4519881c 100644 --- a/src/AutoMapper/Configuration.php +++ b/src/AutoMapper/Configuration.php @@ -25,7 +25,7 @@ public function addSourceClass(string $identifier, string $className): void $this->sourceClasses[$identifier] = $className; } - public function getSourceClass($identifier): string + public function getSourceClass(string $identifier): string { if (!\array_key_exists($identifier, $this->sourceClasses)) { throw new RuntimeException('Unknown source class for: ' . $identifier); @@ -39,7 +39,7 @@ public function addTargetClass(string $identifier, string $className): void $this->targetClasses[$identifier] = $className; } - public function getTargetClass($identifier): string + public function getTargetClass(string $identifier): string { if (!\array_key_exists($identifier, $this->targetClasses)) { throw new RuntimeException('Unknown target class for: ' . $identifier); diff --git a/src/AutoMapper/ProductAttributeValueConfiguration.php b/src/AutoMapper/ProductAttributeValueConfiguration.php index 33f5c75f..be987b95 100644 --- a/src/AutoMapper/ProductAttributeValueConfiguration.php +++ b/src/AutoMapper/ProductAttributeValueConfiguration.php @@ -15,6 +15,7 @@ use Jane\Bundle\AutoMapperBundle\Configuration\MapperConfigurationInterface; use Jane\Component\AutoMapper\MapperGeneratorMetadataInterface; +use Jane\Component\AutoMapper\MapperMetadata; use MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader\ReaderInterface; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareTrait; @@ -49,11 +50,17 @@ public function __construct(Configuration $configuration, iterable $productAttri public function process(MapperGeneratorMetadataInterface $metadata): void { + if (!$metadata instanceof MapperMetadata) { + return; + } if (0 === \count($this->productAttributeValueReaders)) { throw new RuntimeException('Undefined product attribute value reader'); } $metadata->forMember('value', function(ProductAttributeValueInterface $productAttributeValue) { + if (null === $productAttributeValue->getType()) { + return null; + } if (!\array_key_exists($productAttributeValue->getType(), $this->productAttributeValueReaders)) { $this->logger->alert(sprintf('Missing product attribute value reader for "%s" type', $productAttributeValue->getType())); diff --git a/src/AutoMapper/ProductMapperConfiguration.php b/src/AutoMapper/ProductMapperConfiguration.php index 39fa4b00..079610ca 100644 --- a/src/AutoMapper/ProductMapperConfiguration.php +++ b/src/AutoMapper/ProductMapperConfiguration.php @@ -13,6 +13,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\AutoMapper; +use DateTimeInterface; use Jane\Bundle\AutoMapperBundle\Configuration\MapperConfigurationInterface; use Jane\Component\AutoMapper\AutoMapperInterface; use Jane\Component\AutoMapper\MapperGeneratorMetadataInterface; @@ -21,6 +22,7 @@ use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\ProductInterface; use Sylius\Component\Core\Model\ProductTaxonInterface; +use Sylius\Component\Core\Model\ProductVariantInterface as ModelProductVariantInterface; use Sylius\Component\Inventory\Checker\AvailabilityCheckerInterface; use Sylius\Component\Inventory\Model\StockableInterface; use Sylius\Component\Product\Model\ProductVariantInterface; @@ -80,7 +82,7 @@ public function process(MapperGeneratorMetadataInterface $metadata): void return $product->getDescription(); }); - $metadata->forMember('created_at', function(ProductInterface $product) { + $metadata->forMember('created_at', function(ProductInterface $product): ?DateTimeInterface { return $product->getCreatedAt(); }); @@ -95,7 +97,9 @@ public function process(MapperGeneratorMetadataInterface $metadata): void }); $metadata->forMember('mainTaxon', function(ProductInterface $product) { - return $product->getMainTaxon() ? $this->autoMapper->map($product->getMainTaxon(), $this->configuration->getTargetClass('taxon')) : null; + return null !== $product->getMainTaxon() + ? $this->autoMapper->map($product->getMainTaxon(), $this->configuration->getTargetClass('taxon')) + : null; }); $metadata->forMember('product_taxons', function(ProductInterface $product): array { @@ -114,6 +118,9 @@ public function process(MapperGeneratorMetadataInterface $metadata): void $metadata->forMember('attributes', function(ProductInterface $product): array { $attributes = []; $currentLocale = $product->getTranslation()->getLocale(); + if (null === $currentLocale) { + return $attributes; + } $productAttributeDTOClass = $this->configuration->getTargetClass('product_attribute'); foreach ($product->getAttributesByLocale($currentLocale, $currentLocale) as $attributeValue) { if (null === $attributeValue->getName() || null === $attributeValue->getValue()) { @@ -134,6 +141,9 @@ public function process(MapperGeneratorMetadataInterface $metadata): void $currentLocale = $product->getTranslation()->getLocale(); foreach ($product->getVariants() as $variant) { foreach ($variant->getOptionValues() as $optionValue) { + if (null === $optionValue->getOption()) { + continue; + } if (!isset($options[$optionValue->getOptionCode()])) { $options[$optionValue->getOptionCode()] = [ 'name' => $optionValue->getOption()->getTranslation($currentLocale)->getName(), @@ -173,14 +183,20 @@ public function process(MapperGeneratorMetadataInterface $metadata): void $metadata->forMember('prices', function(ProductInterface $product): array { $prices = []; foreach ($product->getChannels() as $channel) { + /** @var ChannelInterface $channel */ $request = new Request(['_channel_code' => $channel->getCode()]); $this->requestStack->push($request); - if (null === ($variant = $this->productVariantResolver->getVariant($product))) { + if ( + null === ($variant = $this->productVariantResolver->getVariant($product)) + || !$variant instanceof ModelProductVariantInterface + || null === ($channelPricing = $variant->getChannelPricingForChannel($channel)) + ) { + $this->requestStack->pop(); continue; } $this->requestStack->pop(); $prices[] = $this->autoMapper->map( - $variant->getChannelPricingForChannel($channel), + $channelPricing, $this->configuration->getTargetClass('pricing') ); } diff --git a/src/Command/SearchCommand.php b/src/Command/SearchCommand.php index bef80573..068c42cb 100644 --- a/src/Command/SearchCommand.php +++ b/src/Command/SearchCommand.php @@ -14,7 +14,8 @@ namespace MonsieurBiz\SyliusSearchPlugin\Command; use Elastica\Exception\Connection\HttpException; -use Jacquesbh\Eater\EaterInterface; +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; +use MonsieurBiz\SyliusSearchPlugin\Model\Product\ProductDTO; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Search; @@ -69,10 +70,12 @@ protected function execute(InputInterface $input, OutputInterface $output) $query = $input->getArgument('query'); $request = new Request(['query' => $query, '_channel_code' => $input->getOption('channel')]); $this->requestStack->push($request); + /** @var DocumentableInterface $documentable */ + $documentable = $this->documentableRegistry->get('search.documentable.monsieurbiz_product'); $requestConfiguration = new RequestConfiguration( $request, RequestInterface::SEARCH_TYPE, - $this->documentableRegistry->get('search.documentable.monsieurbiz_product'), + $documentable, $this->searchSettings, $this->channelContext ); @@ -89,9 +92,9 @@ protected function execute(InputInterface $input, OutputInterface $output) $io->section('Nb results: ' . $result->count()); $documents = []; foreach ($result->getIterator() as $resultItem) { - /** @var EaterInterface $productDTO */ + /** @var ProductDTO $productDTO */ $productDTO = $resultItem->getModel(); - $documents[] = [$resultItem->getScore(), $productDTO->getId()]; + $documents[] = [$resultItem->getScore(), $productDTO->getData('id')]; } $io->table(['Score', 'Document ID'], $documents); diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php index b73a4b64..5617116f 100644 --- a/src/Controller/SearchController.php +++ b/src/Controller/SearchController.php @@ -62,10 +62,12 @@ public function __construct( // TODO add an optional parameter $documentType (nullable => get the default document type) public function searchAction(Request $request, string $query): Response { + /** @var DocumentableInterface $documentable */ + $documentable = $this->documentableRegistry->get('search.documentable.monsieurbiz_product'); $requestConfiguration = new RequestConfiguration( $request, RequestInterface::SEARCH_TYPE, - $this->documentableRegistry->get('search.documentable.monsieurbiz_product'), + $documentable, $this->searchSettings, $this->channelContext ); @@ -85,7 +87,8 @@ public function searchAction(Request $request, string $query): Response */ public function postAction(Request $request): RedirectResponse { - $query = $request->request->get('monsieurbiz_searchplugin_search')['query'] ?? ''; + $query = (array) $request->request->get('monsieurbiz_searchplugin_search') ?? []; + $query = $query['query'] ?? ''; return $this->redirect( $this->generateUrl( @@ -128,10 +131,12 @@ public function instantAction(Request $request): Response public function taxonAction(Request $request): Response { + /** @var DocumentableInterface $documentable */ + $documentable = $this->documentableRegistry->get('search.documentable.monsieurbiz_product'); $requestConfiguration = new RequestConfiguration( $request, RequestInterface::TAXON_TYPE, - $this->documentableRegistry->get('search.documentable.monsieurbiz_product'), + $documentable, $this->searchSettings, $this->channelContext, new Parameters($this->parametersParser->parseRequestValues($request->attributes->get('_sylius', []), $request)) diff --git a/src/DependencyInjection/AutomapperConfigurationRegistryPass.php b/src/DependencyInjection/AutomapperConfigurationRegistryPass.php index 7aed6b7f..9f4baf95 100644 --- a/src/DependencyInjection/AutomapperConfigurationRegistryPass.php +++ b/src/DependencyInjection/AutomapperConfigurationRegistryPass.php @@ -21,7 +21,7 @@ final class AutomapperConfigurationRegistryPass implements CompilerPassInterface public function process(ContainerBuilder $container): void { $automapperConfig = $container->getDefinition(\MonsieurBiz\SyliusSearchPlugin\AutoMapper\Configuration::class); - $automapperClasses = $container->getParameter('monsieurbiz.search.config.automapper_classes'); + $automapperClasses = (array) $container->getParameter('monsieurbiz.search.config.automapper_classes'); foreach ($automapperClasses['sources'] as $identifier => $sourceClass) { $automapperConfig->addMethodCall('addSourceClass', [$identifier, $sourceClass]); } diff --git a/src/Event/MappingProviderEvent.php b/src/Event/MappingProviderEvent.php index 8e73fd26..94dfd25f 100644 --- a/src/Event/MappingProviderEvent.php +++ b/src/Event/MappingProviderEvent.php @@ -13,6 +13,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Event; +use ArrayObject; use Symfony\Contracts\EventDispatcher\Event; class MappingProviderEvent extends Event @@ -20,9 +21,17 @@ class MappingProviderEvent extends Event public const EVENT_NAME = 'monsieurbiz.search.mapping.provider'; private string $indexCode; - private ?\ArrayObject $mapping; - public function __construct(string $indexCode, ?\ArrayObject $mapping) + /** + * @var ArrayObject|null + */ + private ?ArrayObject $mapping; + + /** + * @param string $indexCode + * @param ArrayObject|null $mapping + */ + public function __construct(string $indexCode, ?ArrayObject $mapping) { $this->indexCode = $indexCode; $this->mapping = $mapping; @@ -33,7 +42,10 @@ public function getIndexCode(): string return $this->indexCode; } - public function getMapping(): ?\ArrayObject + /** + * @return ArrayObject|null + */ + public function getMapping(): ?ArrayObject { return $this->mapping; } diff --git a/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php b/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php index 29a27a63..a4ab969c 100644 --- a/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php +++ b/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php @@ -51,6 +51,7 @@ public function omMappingProvider(MappingProviderEvent $event): void if (null === $mapping || !$mapping->offsetExists('mappings')) { return; } + /** @var array $mappings */ $mappings = $mapping->offsetGet('mappings'); $attributesMapping = []; foreach ($this->productAttributeRepository->findIsSearchableOrFilterable() as $productAttribute) { diff --git a/src/EventSubscriber/ReindexProductEventSubscriber.php b/src/EventSubscriber/ReindexProductEventSubscriber.php index 1fbaae15..7f4f2555 100644 --- a/src/EventSubscriber/ReindexProductEventSubscriber.php +++ b/src/EventSubscriber/ReindexProductEventSubscriber.php @@ -15,7 +15,6 @@ use Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface; use Doctrine\ORM\Event\OnFlushEventArgs; -use Doctrine\ORM\Event\PostFlushEventArgs; use Doctrine\ORM\Events; use MonsieurBiz\SyliusSearchPlugin\Message\ProductReindexFromIds; use MonsieurBiz\SyliusSearchPlugin\Message\ProductReindexFromTaxon; @@ -28,6 +27,7 @@ use Sylius\Component\Core\Model\ProductTaxonInterface; use Sylius\Component\Core\Model\ProductTranslationInterface; use Sylius\Component\Product\Model\ProductAttributeValueInterface; +use Sylius\Component\Product\Model\ProductInterface as ModelProductInterface; use Sylius\Component\Product\Model\ProductVariantInterface; use Sylius\Component\Product\Model\ProductVariantTranslationInterface; use Symfony\Component\Messenger\MessageBusInterface; @@ -37,7 +37,7 @@ class ReindexProductEventSubscriber implements EventSubscriberInterface, LoggerA use LoggerAwareTrait; /** - * @var ProductInterface[] + * @var ModelProductInterface[] */ private array $productsToReindex = []; private array $productsToBeDelete = []; @@ -63,7 +63,7 @@ public function onFlush(OnFlushEventArgs $eventArgs): void $collections = array_merge($unitOfWork->getScheduledCollectionUpdates(), $unitOfWork->getScheduledCollectionDeletions()); foreach ($collections as $collection) { - if ($collection->getOwner() instanceof ProductInterface) { + if (method_exists($collection, 'getOwner') && $collection->getOwner() instanceof ProductInterface) { $this->productsToReindex[] = $collection->getOwner(); } } @@ -89,7 +89,7 @@ public function onFlush(OnFlushEventArgs $eventArgs): void // todo reindex all data when: change/create/remove attribute/option, add/remove channel, add/remove locale } - public function postFlush(PostFlushEventArgs $eventArgs): void + public function postFlush(): void { $productReindexFormIdsMessage = new ProductReindexFromIds(); @@ -102,7 +102,6 @@ public function postFlush(PostFlushEventArgs $eventArgs): void $this->productsToReindex = []; if (0 !== \count($productReindexFormIdsMessage->getProductIds())) { - $this->logger->info('Schedule reindex for: ' . implode(', ', $productReindexFormIdsMessage->getProductIds()), ['monsieurbiz.search']); $this->messageBus->dispatch($productReindexFormIdsMessage); } } @@ -114,7 +113,7 @@ private function onFlushEntities(array $entities, string $type = 'insertionsOrUp $this->productsToBeDelete[] = $entity; continue; } - if ($entity instanceof ProductTaxonInterface) { + if ($entity instanceof ProductTaxonInterface && null !== $entity->getTaxon()) { $this->messageBus->dispatch(new ProductReindexFromTaxon($entity->getTaxon()->getId())); continue; } @@ -125,10 +124,8 @@ private function onFlushEntities(array $entities, string $type = 'insertionsOrUp } } - private function getProduct($entity): ?\Sylius\Component\Product\Model\ProductInterface + private function getProduct(object $entity): ?ModelProductInterface { - $this->logger->info(\get_class($entity) . ': ' . implode(', ', class_implements($entity) ?? []), ['monsieurbiz.search']); - switch (true) { case $entity instanceof ProductInterface: return $entity; @@ -137,15 +134,27 @@ private function getProduct($entity): ?\Sylius\Component\Product\Model\ProductIn case $entity instanceof ProductTaxonInterface: return $entity->getProduct(); case $entity instanceof ProductTranslationInterface && $entity->getTranslatable() instanceof ProductInterface: - return $entity->getTranslatable(); + /** @var ProductInterface $product */ + $product = $entity->getTranslatable(); + + return $product; case $entity instanceof ProductAttributeValueInterface: return $entity->getProduct(); case $entity instanceof ProductImageInterface && $entity->getOwner() instanceof ProductInterface: - return $entity->getOwner(); + /** @var ProductInterface $product */ + $product = $entity->getOwner(); + + return $product; case $entity instanceof ChannelPricingInterface && $entity->getProductVariant() instanceof ProductVariantInterface: - return $entity->getProductVariant()->getProduct(); + /** @var ProductVariantInterface $productVariant */ + $productVariant = $entity->getProductVariant(); + + return $productVariant->getProduct(); case $entity instanceof ProductVariantTranslationInterface && $entity->getTranslatable() instanceof ProductVariantInterface: - return $entity->getTranslatable()->getProduct(); + /** @var ProductVariantInterface $productVariant */ + $productVariant = $entity->getTranslatable(); + + return $productVariant->getProduct(); } return null; diff --git a/src/Index/Indexer.php b/src/Index/Indexer.php index 18a08bb8..35623135 100644 --- a/src/Index/Indexer.php +++ b/src/Index/Indexer.php @@ -18,17 +18,13 @@ use Jane\Component\AutoMapper\AutoMapperInterface; use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; use MonsieurBiz\SyliusSearchPlugin\Search\ClientFactory; -use Psr\Log\LoggerAwareInterface; -use Psr\Log\LoggerAwareTrait; use Sylius\Component\Locale\Model\LocaleInterface; use Sylius\Component\Registry\ServiceRegistryInterface; use Sylius\Component\Resource\Model\TranslatableInterface; use Sylius\Component\Resource\Repository\RepositoryInterface; -final class Indexer implements LoggerAwareInterface +final class Indexer { - use LoggerAwareTrait; - private ServiceRegistryInterface $documentableRegistry; private RepositoryInterface $localeRepository; private array $locales = []; @@ -55,7 +51,7 @@ public function __construct( */ public function getLocales(): array { - if (empty($this->locales)) { + if (0 === \count($this->locales)) { $locales = $this->localeRepository->findAll(); $this->locales = array_filter(array_map( function(LocaleInterface $locale): string { @@ -91,7 +87,6 @@ public function indexByDocuments(DocumentableInterface $documentable, array $doc } $indexer->flush(); - $this->logger->info('flush', ['monsieurbiz.search']); return; } @@ -101,8 +96,8 @@ public function indexByDocuments(DocumentableInterface $documentable, array $doc $document->setCurrentLocale($locale); } $dto = $this->autoMapper->map($document, $documentable->getTargetClass()); + /* @phpstan-ignore-next-line */ $indexer->scheduleIndex($indexName, new Document((string) $document->getId(), $dto)); - $this->logger->info('index - ' . $locale . ': ' . $document->getId(), ['monsieurbiz.search']); } } @@ -118,7 +113,6 @@ public function deleteByDocuments(DocumentableInterface $documentable, array $do } $indexer->flush(); - $this->logger->info('flush', ['monsieurbiz.search']); return; } @@ -129,7 +123,6 @@ public function deleteByDocuments(DocumentableInterface $documentable, array $do $document->setCurrentLocale($locale); } $indexer->scheduleDelete($indexName, (string) $document->getId()); - $this->logger->info('delete - ' . $locale . ': ' . $document->getId(), ['monsieurbiz.search']); } } @@ -146,7 +139,7 @@ private function indexDocumentable(DocumentableInterface $documentable, ?string $indexBuilder = $this->clientFactory->getIndexBuilder($documentable, $locale); $newIndex = $indexBuilder->createIndex($indexName, [ 'index_code' => $documentable->getIndexCode(), - 'locale' => $locale ? strtolower($locale) : null, + 'locale' => null !== $locale ? strtolower($locale) : null, ]); $indexer = $this->clientFactory->getIndexer($documentable, $locale); @@ -155,6 +148,7 @@ private function indexDocumentable(DocumentableInterface $documentable, ?string $item->setCurrentLocale($locale); } $dto = $this->autoMapper->map($item, $documentable->getTargetClass()); + /* @phpstan-ignore-next-line */ $indexer->scheduleIndex($newIndex, new Document((string) $item->getId(), $dto)); } $indexer->flush(); diff --git a/src/Mapping/YamlWithLocaleProvider.php b/src/Mapping/YamlWithLocaleProvider.php index 6a72a920..ce23bbc5 100644 --- a/src/Mapping/YamlWithLocaleProvider.php +++ b/src/Mapping/YamlWithLocaleProvider.php @@ -13,6 +13,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Mapping; +use ArrayObject; use JoliCode\Elastically\Mapping\MappingProviderInterface; use JoliCode\Elastically\Mapping\YamlProvider; use MonsieurBiz\SyliusSearchPlugin\Event\MappingProviderEvent; @@ -45,14 +46,14 @@ public function __construct( public function provideMapping(string $indexName, array $context = []): ?array { - $mapping = $this->decorated->provideMapping($context['index_code'] ?? $indexName, $context); + $mapping = $this->decorated->provideMapping($context['index_code'] ?? $indexName, $context) ?? []; $locale = $context['locale'] ?? null; - if (null !== $mapping && null !== $locale) { + if (null !== $locale) { $mapping = $this->appendLocaleAnalyzers($mapping, $locale); } - $mappingProviderEvent = new MappingProviderEvent($context['index_code'] ?? $indexName, new \ArrayObject($mapping)); + $mappingProviderEvent = new MappingProviderEvent($context['index_code'] ?? $indexName, new ArrayObject($mapping)); $this->eventDispatcher->dispatch( $mappingProviderEvent, MappingProviderEvent::EVENT_NAME diff --git a/src/MessageHandler/ProductReindexFromIdsHandler.php b/src/MessageHandler/ProductReindexFromIdsHandler.php index 9309a428..d1145a63 100644 --- a/src/MessageHandler/ProductReindexFromIdsHandler.php +++ b/src/MessageHandler/ProductReindexFromIdsHandler.php @@ -15,6 +15,7 @@ use MonsieurBiz\SyliusSearchPlugin\Index\Indexer; use MonsieurBiz\SyliusSearchPlugin\Message\ProductReindexFromIds; +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; use Sylius\Component\Core\Repository\ProductRepositoryInterface; use Sylius\Component\Registry\ServiceRegistryInterface; use Symfony\Component\Messenger\Handler\MessageHandlerInterface; @@ -37,10 +38,12 @@ public function __construct( public function __invoke(ProductReindexFromIds $message): void { + /** @var DocumentableInterface $documentable */ + $documentable = $this->documentableRegistry->get('search.documentable.monsieurbiz_product'); $products = $this->productRepository->findBy(['id' => $message->getProductIds()]); $this->indexer->indexByDocuments( - $this->documentableRegistry->get('search.documentable.monsieurbiz_product'), + $documentable, $products ); } diff --git a/src/MessageHandler/ProductReindexFromTaxonHandler.php b/src/MessageHandler/ProductReindexFromTaxonHandler.php index b33a66c0..f5d56230 100644 --- a/src/MessageHandler/ProductReindexFromTaxonHandler.php +++ b/src/MessageHandler/ProductReindexFromTaxonHandler.php @@ -13,8 +13,10 @@ namespace MonsieurBiz\SyliusSearchPlugin\MessageHandler; +use Doctrine\ORM\EntityRepository; use MonsieurBiz\SyliusSearchPlugin\Index\Indexer; use MonsieurBiz\SyliusSearchPlugin\Message\ProductReindexFromTaxon; +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; use Sylius\Component\Core\Repository\ProductRepositoryInterface; use Sylius\Component\Registry\ServiceRegistryInterface; use Symfony\Component\Messenger\Handler\MessageHandlerInterface; @@ -37,6 +39,11 @@ public function __construct( public function __invoke(ProductReindexFromTaxon $message): void { + /** @var DocumentableInterface $documentable */ + $documentable = $this->documentableRegistry->get('search.documentable.monsieurbiz_product'); + if (!$this->productRepository instanceof EntityRepository) { + return; + } $products = $this->productRepository->createQueryBuilder('o') ->innerJoin('o.productTaxons', 'productTaxon') ->andWhere('productTaxon.taxon = :taxonId') @@ -44,7 +51,7 @@ public function __invoke(ProductReindexFromTaxon $message): void ; $this->indexer->indexByDocuments( - $this->documentableRegistry->get('search.documentable.monsieurbiz_product'), + $documentable, $products ); } diff --git a/src/MessageHandler/ProductToDeleteFromIdsHandler.php b/src/MessageHandler/ProductToDeleteFromIdsHandler.php index bc86af5f..861e49ad 100644 --- a/src/MessageHandler/ProductToDeleteFromIdsHandler.php +++ b/src/MessageHandler/ProductToDeleteFromIdsHandler.php @@ -15,6 +15,7 @@ use MonsieurBiz\SyliusSearchPlugin\Index\Indexer; use MonsieurBiz\SyliusSearchPlugin\Message\ProductToDeleteFromIds; +use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; use Sylius\Component\Core\Repository\ProductRepositoryInterface; use Sylius\Component\Registry\ServiceRegistryInterface; use Symfony\Component\Messenger\Handler\MessageHandlerInterface; @@ -37,10 +38,12 @@ public function __construct( public function __invoke(ProductToDeleteFromIds $message): void { + /** @var DocumentableInterface $documentable */ + $documentable = $this->documentableRegistry->get('search.documentable.monsieurbiz_product'); $products = $this->productRepository->findBy(['id' => $message->getProductIds()]); $this->indexer->deleteByDocuments( - $this->documentableRegistry->get('search.documentable.monsieurbiz_product'), + $documentable, $products ); } diff --git a/src/Model/Datasource/RepositoryDatasource.php b/src/Model/Datasource/RepositoryDatasource.php index 7f547434..2128f41e 100644 --- a/src/Model/Datasource/RepositoryDatasource.php +++ b/src/Model/Datasource/RepositoryDatasource.php @@ -14,6 +14,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Model\Datasource; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\EntityRepository; use Pagerfanta\Pagerfanta; use Sylius\Component\Resource\Repository\RepositoryInterface; @@ -28,10 +29,9 @@ public function __construct(EntityManagerInterface $entityManager) public function getItems(string $sourceClass): iterable { + /** @phpstan-ignore-next-line */ $repository = $this->entityManager->getRepository($sourceClass); - if ($repository instanceof RepositoryInterface) { - /** @var Pagerfanta $paginator */ - $paginator = $repository->createPaginator(); + if ($repository instanceof RepositoryInterface && ($paginator = $repository->createPaginator()) instanceof Pagerfanta) { $page = 1; while ($paginator->hasNextPage()) { $paginator->setCurrentPage($page); @@ -46,6 +46,6 @@ public function getItems(string $sourceClass): iterable return null; } - return $repository->createQueryBuilder('o')->getQuery()->toIterable(); + return $repository instanceof EntityRepository ? $repository->createQueryBuilder('o')->getQuery()->toIterable() : null; } } diff --git a/src/Normalizer/Product/ProductDTONormalizer.php b/src/Normalizer/Product/ProductDTONormalizer.php index b8b600d8..19bbd70a 100644 --- a/src/Normalizer/Product/ProductDTONormalizer.php +++ b/src/Normalizer/Product/ProductDTONormalizer.php @@ -64,7 +64,7 @@ public function denormalize($data, string $type, string $format = null, array $c if (\array_key_exists('main_taxon', $data)) { $taxonDTOClass = $this->automapperConfiguration->getTargetClass('taxon'); - $object->setMainTaxon($this->denormalizer->denormalize($data['main_taxon'], $taxonDTOClass, 'json', $context)); + $object->setData('main_taxon', $this->denormalizer->denormalize($data['main_taxon'], $taxonDTOClass, 'json', $context)); unset($data['main_taxon']); } @@ -74,7 +74,7 @@ public function denormalize($data, string $type, string $format = null, array $c foreach ($data['product_taxons'] as $value) { $values[] = $this->denormalizer->denormalize($value, $productTaxonDTOClass, 'json', $context); } - $object->setProductTaxons($values); + $object->setData('product_taxons', $values); unset($data['product_taxons']); } @@ -84,7 +84,7 @@ public function denormalize($data, string $type, string $format = null, array $c foreach ($data['images'] as $value) { $values[] = $this->denormalizer->denormalize($value, $imageDTOClass, 'json', $context); } - $object->setImages($values); + $object->setData('images', $values); unset($data['product_taxons']); } @@ -94,7 +94,7 @@ public function denormalize($data, string $type, string $format = null, array $c foreach ($data['channels'] as $value) { $values[] = $this->denormalizer->denormalize($value, $channelDTOClass, 'json', $context); } - $object->setChannels($values); + $object->setData('channels', $values); unset($data['channels']); } @@ -104,7 +104,7 @@ public function denormalize($data, string $type, string $format = null, array $c foreach ($data['attributes'] as $key => $value) { $values[$key] = $this->denormalizer->denormalize($value, $productAttributeDTOClass, 'json', $context); } - $object->setAttributes($values); + $object->setData('attributes', $values); unset($data['channels']); } @@ -114,19 +114,19 @@ public function denormalize($data, string $type, string $format = null, array $c foreach ($data['prices'] as $key => $value) { $values[$key] = $this->denormalizer->denormalize($value, $pricingDTOClass, 'json', $context); } - $object->setPrices($values); + $object->setData('prices', $values); unset($data['channels']); } return $object; } - public function supportsDenormalization($data, string $type, string $format = null) + public function supportsDenormalization($data, string $type, string $format = null): bool { return $this->automapperConfiguration->getTargetClass('product') === $type; } - public function supportsNormalization($data, string $format = null) + public function supportsNormalization($data, string $format = null): bool { return false; } diff --git a/src/Resolver/CheapestProductVariantResolver.php b/src/Resolver/CheapestProductVariantResolver.php index aee69c1e..66c07a6d 100644 --- a/src/Resolver/CheapestProductVariantResolver.php +++ b/src/Resolver/CheapestProductVariantResolver.php @@ -14,6 +14,8 @@ namespace MonsieurBiz\SyliusSearchPlugin\Resolver; use Sylius\Component\Channel\Context\ChannelContextInterface; +use Sylius\Component\Core\Model\ChannelInterface; +use Sylius\Component\Core\Model\ProductVariantInterface as ModelProductVariantInterface; use Sylius\Component\Product\Model\ProductInterface; use Sylius\Component\Product\Model\ProductVariantInterface; use Sylius\Component\Product\Resolver\ProductVariantResolverInterface; @@ -29,7 +31,8 @@ public function __construct(ChannelContextInterface $channelContext) public function getVariant(ProductInterface $subject): ?ProductVariantInterface { - if ($subject->getEnabledVariants()->isEmpty()) { + $channel = $this->channelContext->getChannel(); + if ($subject->getEnabledVariants()->isEmpty() || !$channel instanceof ChannelInterface) { return null; } @@ -37,7 +40,10 @@ public function getVariant(ProductInterface $subject): ?ProductVariantInterface $cheapestPrice = null; $variants = $subject->getEnabledVariants(); foreach ($variants as $variant) { - if (null === ($channelPrice = $variant->getChannelPricingForChannel($this->channelContext->getChannel()))) { + if (!$variant instanceof ModelProductVariantInterface) { + continue; + } + if (null === ($channelPrice = $variant->getChannelPricingForChannel($channel))) { continue; } if (null === $cheapestPrice || $channelPrice->getPrice() < $cheapestPrice) { diff --git a/src/Search/Filter/FilterValue.php b/src/Search/Filter/FilterValue.php index aefbb8fe..59b2e169 100644 --- a/src/Search/Filter/FilterValue.php +++ b/src/Search/Filter/FilterValue.php @@ -17,16 +17,8 @@ class FilterValue { - /** - * @var string - */ - private $label; - - /** - * @var int - */ - private $count; - + private string $label; + private int $count; private string $value; private bool $isApplied; @@ -73,7 +65,7 @@ public function getValue(): string return $this->value; } - public function setValue($value): void + public function setValue(string $value): void { $this->value = $value; } diff --git a/src/Search/Filter/RangeFilter.php b/src/Search/Filter/RangeFilter.php index 129edb80..80f7c6ff 100644 --- a/src/Search/Filter/RangeFilter.php +++ b/src/Search/Filter/RangeFilter.php @@ -114,7 +114,7 @@ public function getDefaultValue(string $type): int return $this->max; } - public function getValueType($valueLabel): string + public function getValueType(string $valueLabel): string { if ($valueLabel == $this->minLabel) { return 'min'; diff --git a/src/Search/Request/Aggregation/AggregationBuilderInterface.php b/src/Search/Request/Aggregation/AggregationBuilderInterface.php index 99753b6e..943f786c 100644 --- a/src/Search/Request/Aggregation/AggregationBuilderInterface.php +++ b/src/Search/Request/Aggregation/AggregationBuilderInterface.php @@ -18,9 +18,9 @@ interface AggregationBuilderInterface { /** - * @param string|array $aggregation + * @param string|array|object $aggregation * - * @return AbstractAggregation|bool|null + * @return AbstractAggregation|false|null */ public function build($aggregation, array $filters); } diff --git a/src/Search/Request/Aggregation/MainTaxonAggregation.php b/src/Search/Request/Aggregation/MainTaxonAggregation.php index 85b76f23..294e11fd 100644 --- a/src/Search/Request/Aggregation/MainTaxonAggregation.php +++ b/src/Search/Request/Aggregation/MainTaxonAggregation.php @@ -13,6 +13,8 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; +use Elastica\QueryBuilder; + final class MainTaxonAggregation implements AggregationBuilderInterface { public function build($aggregation, array $filters) @@ -21,7 +23,7 @@ public function build($aggregation, array $filters) return null; } - $qb = new \Elastica\QueryBuilder(); + $qb = new QueryBuilder(); $filters = array_filter($filters, function($filter): bool { return !$filter->hasParam('path') || 'main_taxon' !== $filter->getParam('path'); }); @@ -55,6 +57,9 @@ public function build($aggregation, array $filters) ; } + /** + * @param string|array|object $aggregation + */ private function isSupported($aggregation): bool { return 'main_taxon' === $aggregation; diff --git a/src/Search/Request/Aggregation/PriceAggregation.php b/src/Search/Request/Aggregation/PriceAggregation.php index 96eaa393..cb9be0d5 100644 --- a/src/Search/Request/Aggregation/PriceAggregation.php +++ b/src/Search/Request/Aggregation/PriceAggregation.php @@ -13,6 +13,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; +use Elastica\QueryBuilder; use Sylius\Component\Channel\Context\ChannelContextInterface; final class PriceAggregation implements AggregationBuilderInterface @@ -30,7 +31,8 @@ public function build($aggregation, array $filters) return null; } - $qb = new \Elastica\QueryBuilder(); + $qb = new QueryBuilder(); + $channelCode = $this->channelContext->getChannel()->getCode() ?? ''; $filters = array_filter($filters, function($filter): bool { return !$filter->hasParam('path') || 'prices' !== $filter->getParam('path'); @@ -52,7 +54,7 @@ public function build($aggregation, array $filters) ->filter('prices') ->setFilter( $qb->query()->term() - ->setTerm('prices.channel_code', $this->channelContext->getChannel()->getCode()) + ->setTerm('prices.channel_code', $channelCode) ) ->addAggregation( $qb->aggregation() @@ -64,8 +66,11 @@ public function build($aggregation, array $filters) ; } + /** + * @param string|array|object $aggregation + */ private function isSupported($aggregation): bool { - return 'price' === $aggregation; + return 'price' === $aggregation && null !== $this->channelContext->getChannel()->getCode(); } } diff --git a/src/Search/Request/Aggregation/ProductAttributeAggregation.php b/src/Search/Request/Aggregation/ProductAttributeAggregation.php index 2ce5d885..213cb35f 100644 --- a/src/Search/Request/Aggregation/ProductAttributeAggregation.php +++ b/src/Search/Request/Aggregation/ProductAttributeAggregation.php @@ -13,6 +13,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; +use Elastica\QueryBuilder; use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; use Sylius\Component\Product\Model\ProductAttributeInterface; @@ -25,7 +26,7 @@ public function build($aggregation, array $filters) return null; } - $qb = new \Elastica\QueryBuilder(); + $qb = new QueryBuilder(); $filters = array_filter($filters, function($filter) use ($aggregation): bool { return !$filter->hasParam('path') || ( false !== strpos($filter->getParam('path'), 'attributes.') @@ -38,11 +39,11 @@ public function build($aggregation, array $filters) $filterQuery->addMust($filter); } - $qb = new \Elastica\QueryBuilder(); - + /** @phpstan-ignore-next-line */ return $qb->aggregation()->filter($aggregation->getCode()) ->setFilter($filterQuery) ->addAggregation( + /** @phpstan-ignore-next-line */ $qb->aggregation()->nested($aggregation->getCode(), sprintf('attributes.%s', $aggregation->getCode())) ->addAggregation( $qb->aggregation()->terms('names') @@ -56,8 +57,11 @@ public function build($aggregation, array $filters) ; } + /** + * @param string|array|object $aggregation + */ private function isSupport($aggregation): bool { - return $aggregation instanceof ProductAttributeInterface; + return $aggregation instanceof ProductAttributeInterface && null !== $aggregation->getCode(); } } diff --git a/src/Search/Request/Aggregation/ProductAttributesAggregation.php b/src/Search/Request/Aggregation/ProductAttributesAggregation.php index ba166cb8..1af4d7d7 100644 --- a/src/Search/Request/Aggregation/ProductAttributesAggregation.php +++ b/src/Search/Request/Aggregation/ProductAttributesAggregation.php @@ -14,10 +14,13 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; use Elastica\Query\AbstractQuery; +use Elastica\QueryBuilder; use Sylius\Component\Product\Model\ProductAttributeInterface; final class ProductAttributesAggregation implements AggregationBuilderInterface { + private ProductAttributeAggregation $productAttributeAggregationBuilder; + public function __construct() { $this->productAttributeAggregationBuilder = new ProductAttributeAggregation(); @@ -29,7 +32,7 @@ public function build($aggregation, array $filters) return null; } - $qb = new \Elastica\QueryBuilder(); + $qb = new QueryBuilder(); $currentFilters = array_filter($filters, function(AbstractQuery $filter): bool { return !$filter->hasParam('path') || false === strpos($filter->getParam('path'), 'attributes.'); @@ -41,9 +44,10 @@ public function build($aggregation, array $filters) } $attributesAggregation = $qb->aggregation()->nested('attributes', 'attributes'); + /** @phpstan-ignore-next-line */ foreach ($aggregation as $subAggregation) { $subAggregationObject = $this->productAttributeAggregationBuilder->build($subAggregation, $filters); - if (null === $subAggregationObject) { + if (null === $subAggregationObject || false === $subAggregationObject) { continue; } $attributesAggregation->addAggregation($subAggregationObject); @@ -59,6 +63,9 @@ public function build($aggregation, array $filters) ; } + /** + * @param string|array|object $aggregation + */ private function isSupport($aggregation): bool { if (!\is_array($aggregation)) { diff --git a/src/Search/Request/Aggregation/ProductOptionAggregation.php b/src/Search/Request/Aggregation/ProductOptionAggregation.php index bef367fa..4d1dc2ae 100644 --- a/src/Search/Request/Aggregation/ProductOptionAggregation.php +++ b/src/Search/Request/Aggregation/ProductOptionAggregation.php @@ -62,9 +62,11 @@ public function build($aggregation, array $filters) ) ; + /** @phpstan-ignore-next-line */ return $qb->aggregation()->filter($aggregation->getCode()) ->setFilter($filterQuery) ->addAggregation( + /** @phpstan-ignore-next-line */ $qb->aggregation()->nested($aggregation->getCode(), sprintf('options.%s', $aggregation->getCode())) ->addAggregation( $qb->aggregation()->terms('names') @@ -80,8 +82,11 @@ public function build($aggregation, array $filters) ; } + /** + * @param string|array|object $aggregation + */ private function isSupport($aggregation): bool { - return $aggregation instanceof ProductOptionInterface; + return $aggregation instanceof ProductOptionInterface && null !== $aggregation->getCode(); } } diff --git a/src/Search/Request/Aggregation/ProductOptionsAggregation.php b/src/Search/Request/Aggregation/ProductOptionsAggregation.php index c4206f07..f6f621e9 100644 --- a/src/Search/Request/Aggregation/ProductOptionsAggregation.php +++ b/src/Search/Request/Aggregation/ProductOptionsAggregation.php @@ -42,9 +42,10 @@ public function build($aggregation, array $filters) } $optionsAggregation = $qb->aggregation()->nested('options', 'options'); + /** @phpstan-ignore-next-line */ foreach ($aggregation as $subAggregation) { $subAggregationObject = $this->productOptionAggregationBuilder->build($subAggregation, $filters); - if (null === $subAggregationObject) { + if (null === $subAggregationObject || false === $subAggregationObject) { continue; } $optionsAggregation->addAggregation($subAggregationObject); @@ -60,6 +61,9 @@ public function build($aggregation, array $filters) ; } + /** + * @param string|array|object $aggregation + */ private function isSupport($aggregation): bool { if (!\is_array($aggregation)) { diff --git a/src/Search/Request/Aggregation/TaxonsAggregation.php b/src/Search/Request/Aggregation/TaxonsAggregation.php index 2f40325f..23046c69 100644 --- a/src/Search/Request/Aggregation/TaxonsAggregation.php +++ b/src/Search/Request/Aggregation/TaxonsAggregation.php @@ -13,6 +13,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation; +use Elastica\QueryBuilder; use Sylius\Component\Core\Model\TaxonInterface; final class TaxonsAggregation implements AggregationBuilderInterface @@ -23,10 +24,10 @@ public function build($aggregation, array $filters) return null; } /** @var TaxonInterface $currentTaxon */ - $currentTaxon = $aggregation['taxons']; - $qb = new \Elastica\QueryBuilder(); + $currentTaxon = $aggregation['taxons']; /** @phpstan-ignore-line */ + $qb = new QueryBuilder(); - $filters = array_filter($filters, function($filter) { + $filters = array_filter($filters, function($filter): bool { return !$filter->hasParam('path') || 'product_taxons' !== $filter->getParam('path'); }); @@ -34,6 +35,7 @@ public function build($aggregation, array $filters) foreach ($filters as $filter) { $filterQuery->addMust($filter); } + $taxonLevel = $currentTaxon->getLevel() ?? 0; return $qb->aggregation() ->filter('taxons') @@ -46,7 +48,7 @@ public function build($aggregation, array $filters) ->nested('taxons', 'product_taxons.taxon') ->addAggregation( $qb->aggregation() - ->filter('taxons', $qb->query()->term(['product_taxons.taxon.level' => ['value' => $currentTaxon->getLevel() + 1]])) + ->filter('taxons', $qb->query()->term(['product_taxons.taxon.level' => ['value' => $taxonLevel + 1]])) ->addAggregation( $qb->aggregation() ->terms('codes') @@ -62,8 +64,11 @@ public function build($aggregation, array $filters) ; } + /** + * @param string|array|object $aggregation + */ private function isSupported($aggregation): bool { - return \array_key_exists('taxons', $aggregation); + return \is_array($aggregation) && \array_key_exists('taxons', $aggregation); } } diff --git a/src/Search/Request/AggregationBuilder.php b/src/Search/Request/AggregationBuilder.php index f74090b3..47f56431 100644 --- a/src/Search/Request/AggregationBuilder.php +++ b/src/Search/Request/AggregationBuilder.php @@ -15,6 +15,7 @@ use Elastica\Aggregation\AbstractAggregation; use MonsieurBiz\SyliusSearchPlugin\Search\Request\Aggregation\AggregationBuilderInterface; +use RuntimeException; class AggregationBuilder { @@ -46,7 +47,7 @@ public function buildAggregations(array $aggregations, array $filters): array /** * @param string|array $aggregation * - * @return AbstractAggregation|bool|null + * @return AbstractAggregation|bool */ private function buildAggregation($aggregation, array $filters) { @@ -57,6 +58,6 @@ private function buildAggregation($aggregation, array $filters) } } - throw new \RuntimeException('Aggregation can be build'); // it's throw an exception if we have not filtreable attribute + throw new RuntimeException('Aggregation can be build'); // it's throw an exception if we have not filtreable attribute } } diff --git a/src/Search/Request/PostFilter/Product/MainTaxonPostFilter.php b/src/Search/Request/PostFilter/Product/MainTaxonPostFilter.php index 152e3c12..b6c30ff3 100644 --- a/src/Search/Request/PostFilter/Product/MainTaxonPostFilter.php +++ b/src/Search/Request/PostFilter/Product/MainTaxonPostFilter.php @@ -28,7 +28,7 @@ public function apply(BoolQuery $boolQuery, RequestConfiguration $requestConfigu $mainTaxonQuery = $qb->query() ->bool() ; - $values = array_filter($values); + $values = array_filter($values) ?? []; foreach ($values as $value) { $mainTaxonQuery->addShould( $qb->query() diff --git a/src/Search/Request/ProductRequest/InstantSearch.php b/src/Search/Request/ProductRequest/InstantSearch.php index d4fdac8b..adb87968 100644 --- a/src/Search/Request/ProductRequest/InstantSearch.php +++ b/src/Search/Request/ProductRequest/InstantSearch.php @@ -35,7 +35,9 @@ public function __construct( QueryFilterRegistryInterface $queryFilterRegistry, FunctionScoreRegistryInterface $functionScoreRegistry ) { - $this->documentable = $documentableRegistry->get('search.documentable.monsieurbiz_product'); + /** @var DocumentableInterface $documentable */ + $documentable = $documentableRegistry->get('search.documentable.monsieurbiz_product'); + $this->documentable = $documentable; $this->queryFilterRegistry = $queryFilterRegistry; $this->functionScoreRegistry = $functionScoreRegistry; } @@ -64,8 +66,10 @@ public function getQuery(): Query $query = Query::create($boolQuery); + /** @var Query\AbstractQuery $queryObject */ + $queryObject = $query->getQuery(); $functionScore = $qb->query()->function_score() - ->setQuery($query->getQuery()) + ->setQuery($queryObject) ->setBoostMode(Query\FunctionScore::BOOST_MODE_MULTIPLY) ->setScoreMode(Query\FunctionScore::SCORE_MODE_MULTIPLY) ; diff --git a/src/Search/Request/ProductRequest/Search.php b/src/Search/Request/ProductRequest/Search.php index 1215ba49..283066e7 100644 --- a/src/Search/Request/ProductRequest/Search.php +++ b/src/Search/Request/ProductRequest/Search.php @@ -50,7 +50,9 @@ public function __construct( SorterRegistryInterface $sorterRegistry, FunctionScoreRegistryInterface $functionScoreRegistry ) { - $this->documentable = $documentableRegistry->get('search.documentable.monsieurbiz_product'); + /** @var DocumentableInterface $documentable */ + $documentable = $documentableRegistry->get('search.documentable.monsieurbiz_product'); + $this->documentable = $documentable; $this->productAttributeRepository = $productAttributeRepository; $this->productOptionRepository = $productOptionRepository; $this->aggregationBuilder = $aggregationBuilder; @@ -101,8 +103,10 @@ public function getQuery(): Query $sorter->apply($query, $this->configuration); } + /** @var Query\AbstractQuery $queryObject */ + $queryObject = $query->getQuery(); $functionScore = $qb->query()->function_score() - ->setQuery($query->getQuery()) + ->setQuery($queryObject) ->setBoostMode(Query\FunctionScore::BOOST_MODE_MULTIPLY) ->setScoreMode(Query\FunctionScore::SCORE_MODE_MULTIPLY) ; diff --git a/src/Search/Request/ProductRequest/Taxon.php b/src/Search/Request/ProductRequest/Taxon.php index b12301ad..bf65e83a 100644 --- a/src/Search/Request/ProductRequest/Taxon.php +++ b/src/Search/Request/ProductRequest/Taxon.php @@ -25,6 +25,7 @@ use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\SorterRegistryInterface; +use RuntimeException; use Sylius\Component\Channel\Context\ChannelContextInterface; use Sylius\Component\Registry\ServiceRegistryInterface; @@ -52,7 +53,9 @@ public function __construct( SorterRegistryInterface $sorterRegistry, FunctionScoreRegistryInterface $functionScoreRegistry ) { - $this->documentable = $documentableRegistry->get('search.documentable.monsieurbiz_product'); + /** @var DocumentableInterface $documentable */ + $documentable = $documentableRegistry->get('search.documentable.monsieurbiz_product'); + $this->documentable = $documentable; $this->productAttributeRepository = $productAttributeRepository; $this->productOptionRepository = $productOptionRepository; $this->channelContext = $channelContext; @@ -95,8 +98,10 @@ public function getQuery(): Query $sorter->apply($query, $this->configuration); } + /** @var Query\AbstractQuery $queryObject */ + $queryObject = $query->getQuery(); $functionScore = $qb->query()->function_score() - ->setQuery($query->getQuery()) + ->setQuery($queryObject) ->setBoostMode(Query\FunctionScore::BOOST_MODE_MULTIPLY) ->setScoreMode(Query\FunctionScore::SCORE_MODE_MULTIPLY) ; @@ -121,6 +126,9 @@ public function setConfiguration(RequestConfiguration $configuration): void private function addAggregations(Query $query, Query\BoolQuery $postFilter): void { + if (null === $this->configuration) { + throw new RuntimeException('Missing request configuration'); + } $aggregations = $this->aggregationBuilder->buildAggregations( [ ['taxons' => $this->configuration->getTaxon()], diff --git a/src/Search/Request/RequestConfiguration.php b/src/Search/Request/RequestConfiguration.php index 69727783..2fd33a27 100644 --- a/src/Search/Request/RequestConfiguration.php +++ b/src/Search/Request/RequestConfiguration.php @@ -76,14 +76,14 @@ public function getPage(): int public function getLimit(): int { - $limit = (int) $this->request->get('limit'); + $limit = (int) $this->request->get('limit', self::FALLBACK_LIMIT); $availableLimits = $this->getAvailableLimits(); if (!\in_array($limit, $availableLimits, true)) { $limit = reset($availableLimits); } - return $limit ?: self::FALLBACK_LIMIT; + return $limit; } public function getAvailableLimits(): array diff --git a/src/Search/Request/Sorting/SorterBuilderTrait.php b/src/Search/Request/Sorting/SorterBuilderTrait.php index 798d41ff..fc1d32f6 100644 --- a/src/Search/Request/Sorting/SorterBuilderTrait.php +++ b/src/Search/Request/Sorting/SorterBuilderTrait.php @@ -17,6 +17,9 @@ trait SorterBuilderTrait { + /** + * @param string|AbstractQuery|null $sortFilterValue + */ protected function buildSort( string $field, string $order, diff --git a/src/Search/Response.php b/src/Search/Response.php index 4c8e092c..b05011c8 100644 --- a/src/Search/Response.php +++ b/src/Search/Response.php @@ -14,6 +14,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search; use Elastica\ResultSet; +use JoliCode\Elastically\Result; use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration; use Pagerfanta\Adapter\AdapterInterface; @@ -24,6 +25,9 @@ class Response implements ResponseInterface private RequestConfiguration $requestConfiguration; private AdapterInterface $adapter; private DocumentableInterface $documentable; + /** + * @var Pagerfanta|null + */ private ?Pagerfanta $paginator = null; private array $filters = []; private iterable $filterBuilders; diff --git a/src/Search/ResponseInterface.php b/src/Search/ResponseInterface.php index 18d479b1..46cd3998 100644 --- a/src/Search/ResponseInterface.php +++ b/src/Search/ResponseInterface.php @@ -13,17 +13,26 @@ namespace MonsieurBiz\SyliusSearchPlugin\Search; +use Countable; +use IteratorAggregate; +use JoliCode\Elastically\Result; use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; use MonsieurBiz\SyliusSearchPlugin\Search\Response\FilterInterface; use Pagerfanta\Pagerfanta; -interface ResponseInterface extends \IteratorAggregate, \Countable +/** + * @extends IteratorAggregate + */ +interface ResponseInterface extends IteratorAggregate, Countable { /** * @return FilterInterface[] */ public function getFilters(): array; + /** + * @return Pagerfanta + */ public function getPaginator(): Pagerfanta; public function getDocumentable(): DocumentableInterface; diff --git a/src/Twig/Extension/RenderSearchForm.php b/src/Twig/Extension/RenderSearchForm.php index f6fa0e20..17ceb787 100644 --- a/src/Twig/Extension/RenderSearchForm.php +++ b/src/Twig/Extension/RenderSearchForm.php @@ -23,14 +23,11 @@ class RenderSearchForm extends AbstractExtension { - /** @var FormFactoryInterface */ - private $formFactory; + private FormFactoryInterface $formFactory; - /** @var Environment */ - private $templatingEngine; + private Environment $templatingEngine; - /** @var RequestStack */ - private $requestStack; + private RequestStack $requestStack; public function __construct( FormFactoryInterface $formFactory, @@ -49,13 +46,15 @@ public function getFunctions() ]; } - public function createForm($template = null) + public function createForm(?string $template = null): Markup { + $request = $this->requestStack->getCurrentRequest(); $template = $template ?? '@MonsieurBizSyliusSearchPlugin/Search/_form.html.twig'; + $query = null !== $request ? $request->get('query', '') : ''; return new Markup($this->templatingEngine->render($template, [ 'form' => $this->formFactory->create(SearchType::class)->createView(), - 'query' => urldecode($this->requestStack->getCurrentRequest()->get('query') ?? ''), + 'query' => urldecode($query), ]), 'UTF-8'); } } From 7c5103c27368d67f448d778f0b8543fae564247f Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 10:55:53 +0100 Subject: [PATCH 109/142] Add doctrine dbal conflict --- composer.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/composer.json b/composer.json index 95b1d773..f4ad780c 100644 --- a/composer.json +++ b/composer.json @@ -48,6 +48,9 @@ "vimeo/psalm": "4.7.1", "jane-php/json-schema": "^7.1" }, + "conflict": { + "doctrine/dbal": "^3" + }, "prefer-stable": true, "autoload": { "psr-4": { From 0113e5500ea7ac99c34738114adc4e5516e3fb55 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Wed, 3 Nov 2021 18:04:48 +0100 Subject: [PATCH 110/142] Add copy from template to 1.0 recipe --- recipes/1.0/manifest.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/recipes/1.0/manifest.json b/recipes/1.0/manifest.json index 3166429d..5bd94dec 100644 --- a/recipes/1.0/manifest.json +++ b/recipes/1.0/manifest.json @@ -7,6 +7,9 @@ "copy-from-recipe": { "config/": "%CONFIG_DIR%/" }, + "copy-from-package": { + "src/Resources/templates/": "templates/" + }, "env": { "MONSIEURBIZ_SEARCHPLUGIN_ES_HOST": "localhost", "MONSIEURBIZ_SEARCHPLUGIN_ES_PORT": "9200" From fec96a0a31cb82a4a9101718adb810804877fbf5 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 11:03:06 +0100 Subject: [PATCH 111/142] Remove all in tests/Application --- tests/Application/.babelrc | 1 - tests/Application/.env | 40 --------- tests/Application/.env.test | 1 - tests/Application/.eslintrc.js | 1 - tests/Application/.gitignore | 49 ----------- tests/Application/.php-version | 1 - tests/Application/bin/console | 38 --------- tests/Application/composer.json | 5 -- tests/Application/config/bootstrap.php | 32 ------- tests/Application/config/bundles.php | 76 ----------------- tests/Application/config/jwt | 1 - .../Application/config/packages/_sylius.yaml | 34 -------- .../config/packages/dev/_sylius.yaml | 2 - .../config/packages/dev/framework.yaml | 3 - .../config/packages/dev/jms_serializer.yaml | 12 --- .../config/packages/dev/monolog.yaml | 9 -- .../config/packages/dev/nelmio_alice.yaml | 9 -- .../config/packages/dev/routing.yaml | 3 - .../config/packages/dev/swiftmailer.yaml | 2 - .../config/packages/dev/web_profiler.yaml | 3 - .../Application/config/packages/doctrine.yaml | 25 ------ .../config/packages/doctrine_migrations.yaml | 6 -- .../Application/config/packages/fos_rest.yaml | 1 - .../config/packages/framework.yaml | 1 - .../config/packages/jms_serializer.yaml | 1 - .../packages/lexik_jwt_authentication.yaml | 1 - .../config/packages/liip_imagine.yaml | 1 - .../packages/monsieurbiz_settings_plugin.yaml | 2 - .../monsieurbiz_sylius_search_plugin.yaml | 2 - .../Application/config/packages/routing.yaml | 1 - .../Application/config/packages/security.yaml | 1 - .../packages/stof_doctrine_extensions.yaml | 1 - .../config/packages/swiftmailer.yaml | 1 - tests/Application/config/packages/test | 1 - tests/Application/config/packages/test_cached | 1 - .../config/packages/translation.yaml | 1 - tests/Application/config/packages/twig.yaml | 1 - .../config/packages/validator.yaml | 1 - tests/Application/config/routes.yaml | 1 - tests/Application/config/routes/dev | 1 - .../config/routes/liip_imagine.yaml | 1 - .../routes/monsieurbiz_settings_plugin.yaml | 3 - .../monsieurbiz_sylius_search_plugin.yaml | 1 - .../config/routes/sylius_admin.yaml | 1 - .../Application/config/routes/sylius_api.yaml | 1 - .../config/routes/sylius_shop.yaml | 1 - tests/Application/config/routes/test | 1 - tests/Application/config/routes/test_cached | 1 - tests/Application/config/secrets | 1 - tests/Application/config/services.yaml | 7 -- tests/Application/config/services_test.yaml | 1 - .../config/services_test_cached.yaml | 1 - tests/Application/docker-compose.yaml | 52 ------------ .../docker/elasticsearch/Dockerfile | 7 -- tests/Application/gulpfile.babel.js | 63 -------------- tests/Application/package.json | 58 ------------- tests/Application/php.ini | 1 - tests/Application/public/.htaccess | 1 - tests/Application/public/favicon.ico | 1 - tests/Application/public/index.php | 38 --------- .../Application/public/media/image/.gitignore | 0 tests/Application/public/robots.txt | 4 - .../src/Entity/Product/Product.php | 31 ------- .../src/Entity/Product/ProductAttribute.php | 35 -------- .../src/Entity/Product/ProductOption.php | 28 ------ tests/Application/src/Kernel.php | 85 ------------------- .../src/Migrations/Version20211012092353.php | 42 --------- tests/Application/templates/.gitkeep | 0 .../templates/bundles/SyliusAdminBundle | 1 - tests/Application/translations/.gitignore | 0 tests/Application/webpack.config.js | 1 - 71 files changed, 841 deletions(-) delete mode 120000 tests/Application/.babelrc delete mode 100644 tests/Application/.env delete mode 120000 tests/Application/.env.test delete mode 120000 tests/Application/.eslintrc.js delete mode 100644 tests/Application/.gitignore delete mode 120000 tests/Application/.php-version delete mode 100755 tests/Application/bin/console delete mode 100644 tests/Application/composer.json delete mode 100644 tests/Application/config/bootstrap.php delete mode 100644 tests/Application/config/bundles.php delete mode 120000 tests/Application/config/jwt delete mode 100644 tests/Application/config/packages/_sylius.yaml delete mode 100644 tests/Application/config/packages/dev/_sylius.yaml delete mode 100644 tests/Application/config/packages/dev/framework.yaml delete mode 100644 tests/Application/config/packages/dev/jms_serializer.yaml delete mode 100644 tests/Application/config/packages/dev/monolog.yaml delete mode 100644 tests/Application/config/packages/dev/nelmio_alice.yaml delete mode 100644 tests/Application/config/packages/dev/routing.yaml delete mode 100644 tests/Application/config/packages/dev/swiftmailer.yaml delete mode 100644 tests/Application/config/packages/dev/web_profiler.yaml delete mode 100644 tests/Application/config/packages/doctrine.yaml delete mode 100644 tests/Application/config/packages/doctrine_migrations.yaml delete mode 120000 tests/Application/config/packages/fos_rest.yaml delete mode 120000 tests/Application/config/packages/framework.yaml delete mode 120000 tests/Application/config/packages/jms_serializer.yaml delete mode 120000 tests/Application/config/packages/lexik_jwt_authentication.yaml delete mode 120000 tests/Application/config/packages/liip_imagine.yaml delete mode 100644 tests/Application/config/packages/monsieurbiz_settings_plugin.yaml delete mode 100644 tests/Application/config/packages/monsieurbiz_sylius_search_plugin.yaml delete mode 120000 tests/Application/config/packages/routing.yaml delete mode 120000 tests/Application/config/packages/security.yaml delete mode 120000 tests/Application/config/packages/stof_doctrine_extensions.yaml delete mode 120000 tests/Application/config/packages/swiftmailer.yaml delete mode 120000 tests/Application/config/packages/test delete mode 120000 tests/Application/config/packages/test_cached delete mode 120000 tests/Application/config/packages/translation.yaml delete mode 120000 tests/Application/config/packages/twig.yaml delete mode 120000 tests/Application/config/packages/validator.yaml delete mode 120000 tests/Application/config/routes.yaml delete mode 120000 tests/Application/config/routes/dev delete mode 120000 tests/Application/config/routes/liip_imagine.yaml delete mode 100644 tests/Application/config/routes/monsieurbiz_settings_plugin.yaml delete mode 120000 tests/Application/config/routes/monsieurbiz_sylius_search_plugin.yaml delete mode 120000 tests/Application/config/routes/sylius_admin.yaml delete mode 120000 tests/Application/config/routes/sylius_api.yaml delete mode 120000 tests/Application/config/routes/sylius_shop.yaml delete mode 120000 tests/Application/config/routes/test delete mode 120000 tests/Application/config/routes/test_cached delete mode 120000 tests/Application/config/secrets delete mode 100644 tests/Application/config/services.yaml delete mode 120000 tests/Application/config/services_test.yaml delete mode 120000 tests/Application/config/services_test_cached.yaml delete mode 100644 tests/Application/docker-compose.yaml delete mode 100644 tests/Application/docker/elasticsearch/Dockerfile delete mode 100644 tests/Application/gulpfile.babel.js delete mode 100644 tests/Application/package.json delete mode 100644 tests/Application/php.ini delete mode 120000 tests/Application/public/.htaccess delete mode 120000 tests/Application/public/favicon.ico delete mode 100644 tests/Application/public/index.php delete mode 100644 tests/Application/public/media/image/.gitignore delete mode 100644 tests/Application/public/robots.txt delete mode 100644 tests/Application/src/Entity/Product/Product.php delete mode 100644 tests/Application/src/Entity/Product/ProductAttribute.php delete mode 100644 tests/Application/src/Entity/Product/ProductOption.php delete mode 100644 tests/Application/src/Kernel.php delete mode 100644 tests/Application/src/Migrations/Version20211012092353.php delete mode 100644 tests/Application/templates/.gitkeep delete mode 120000 tests/Application/templates/bundles/SyliusAdminBundle delete mode 100644 tests/Application/translations/.gitignore delete mode 120000 tests/Application/webpack.config.js diff --git a/tests/Application/.babelrc b/tests/Application/.babelrc deleted file mode 120000 index b63c4432..00000000 --- a/tests/Application/.babelrc +++ /dev/null @@ -1 +0,0 @@ -../../vendor/sylius/sylius/.babelrc \ No newline at end of file diff --git a/tests/Application/.env b/tests/Application/.env deleted file mode 100644 index ccf16d2a..00000000 --- a/tests/Application/.env +++ /dev/null @@ -1,40 +0,0 @@ -# This file is a "template" of which env vars needs to be defined in your configuration or in an .env file -# Set variables here that may be different on each deployment target of the app, e.g. development, staging, production. -# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration - -COMPOSE_PROJECT_NAME=search - -###> symfony/framework-bundle ### -APP_ENV=dev -APP_DEBUG=1 -APP_SECRET=EDITME -###< symfony/framework-bundle ### - -###> doctrine/doctrine-bundle ### -# Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url -# For a sqlite database, use: "sqlite:///%kernel.project_dir%/var/data.db" -# Set "serverVersion" to your server version to avoid edge-case exceptions and extra database calls -# If you use Symfony binary this URL is overridden by it -DATABASE_URL=mysql://root@127.0.0.1/sylius -###< doctrine/doctrine-bundle ### - -###> symfony/swiftmailer-bundle ### -# For Gmail as a transport, use: "gmail://username:password@localhost" -# For a generic SMTP server, use: "smtp://localhost:25?encryption=&auth_mode=" -# Delivery is disabled by default via "null://localhost" -MAILER_URL=smtp://localhost:1025 -###< symfony/swiftmailer-bundle ### - -###> lexik/jwt-authentication-bundle ### -JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private-test.pem -JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public-test.pem -JWT_PASSPHRASE=ALL_THAT_IS_GOLD_DOES_NOT_GLITTER_NOT_ALL_THOSE_WHO_WANDER_ARE_LOST -###< lexik/jwt-authentication-bundle ### - -###> Docker ### -KIBANA_PORT=15601 -###< Docker ### - -MONSIEURBIZ_SEARCHPLUGIN_MESSENGER_TRANSPORT_DSN=doctrine://default -MONSIEURBIZ_SEARCHPLUGIN_ES_HOST=${ELASTICSEARCH_HOST:-localhost} -MONSIEURBIZ_SEARCHPLUGIN_ES_PORT=${ELASTICSEARCH_PORT:-9200} diff --git a/tests/Application/.env.test b/tests/Application/.env.test deleted file mode 120000 index c2049e90..00000000 --- a/tests/Application/.env.test +++ /dev/null @@ -1 +0,0 @@ -../../vendor/sylius/sylius/.env.test \ No newline at end of file diff --git a/tests/Application/.eslintrc.js b/tests/Application/.eslintrc.js deleted file mode 120000 index 1fc7cb69..00000000 --- a/tests/Application/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -../../vendor/sylius/sylius/.eslintrc.js \ No newline at end of file diff --git a/tests/Application/.gitignore b/tests/Application/.gitignore deleted file mode 100644 index ba3c6967..00000000 --- a/tests/Application/.gitignore +++ /dev/null @@ -1,49 +0,0 @@ -/public/assets -/public/css -/public/js -/public/media -/public/build -!/public/media/image/.gitkeep - -/app/config/parameters.yml - -/bin/symfony_requirements - -/docs/.doctrees - -/composer.lock -/node_modules - -/etc/build/* -!/etc/build/.gitignore - -/behat.yml -/phpspec.yml - -# Symfony CLI https://symfony.com/doc/current/setup/symfony_server.html#different-php-settings-per-project -/.php-version -/php.ini - -###> symfony/framework-bundle ### -/.env.*.local -/.env.local -/.env.local.php -/public/bundles -/var/ -/vendor/ -###< symfony/framework-bundle ### - -###> friendsofphp/php-cs-fixer ### -/.php_cs -/.php_cs.cache -###< friendsofphp/php-cs-fixer ### - -###> phpunit/phpunit ### -/phpunit.xml -/.phpunit.result.cache -###< phpunit/phpunit ### - -###> lexik/jwt-authentication-bundle ### -/config/jwt/*.pem -!/config/jwt/*-test.pem -###< lexik/jwt-authentication-bundle ### diff --git a/tests/Application/.php-version b/tests/Application/.php-version deleted file mode 120000 index fffb853b..00000000 --- a/tests/Application/.php-version +++ /dev/null @@ -1 +0,0 @@ -../../.php-version \ No newline at end of file diff --git a/tests/Application/bin/console b/tests/Application/bin/console deleted file mode 100755 index b2c34817..00000000 --- a/tests/Application/bin/console +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env php -getParameterOption(['--env', '-e'], null, true)) { - putenv('APP_ENV=' . $_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env); -} - -if ($input->hasParameterOption('--no-debug', true)) { - putenv('APP_DEBUG=' . $_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0'); -} - -require dirname(__DIR__) . '/config/bootstrap.php'; - -if ($_SERVER['APP_DEBUG']) { - umask(0000); - - if (class_exists(Debug::class)) { - Debug::enable(); - } -} - -$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); -$application = new Application($kernel); -$application->run($input); diff --git a/tests/Application/composer.json b/tests/Application/composer.json deleted file mode 100644 index 386df3c4..00000000 --- a/tests/Application/composer.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "monsieurbiz/sylius-search-plugin-test-application", - "description": "", - "license": "MIT" -} diff --git a/tests/Application/config/bootstrap.php b/tests/Application/config/bootstrap.php deleted file mode 100644 index 40b42fb2..00000000 --- a/tests/Application/config/bootstrap.php +++ /dev/null @@ -1,32 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -use Symfony\Component\Dotenv\Dotenv; - -require dirname(__DIR__) . '../../../vendor/autoload.php'; - -// Load cached env vars if the .env.local.php file exists -// Run "composer dump-env prod" to create it (requires symfony/flex >=1.2) -if (is_array($env = @include dirname(__DIR__) . '/.env.local.php')) { - $_SERVER += $env; - $_ENV += $env; -} elseif (!class_exists(Dotenv::class)) { - throw new RuntimeException('Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.'); -} else { - // load all the .env files - (new Dotenv(true))->loadEnv(dirname(__DIR__) . '/.env'); -} - -$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: '1.0'; -$_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV']; -$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0'; diff --git a/tests/Application/config/bundles.php b/tests/Application/config/bundles.php deleted file mode 100644 index 3fa47d8a..00000000 --- a/tests/Application/config/bundles.php +++ /dev/null @@ -1,76 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -return [ - Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], - Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], - Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], - Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['all' => true], - Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], - Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], - Sylius\Bundle\OrderBundle\SyliusOrderBundle::class => ['all' => true], - Sylius\Bundle\MoneyBundle\SyliusMoneyBundle::class => ['all' => true], - Sylius\Bundle\CurrencyBundle\SyliusCurrencyBundle::class => ['all' => true], - Sylius\Bundle\LocaleBundle\SyliusLocaleBundle::class => ['all' => true], - Sylius\Bundle\ProductBundle\SyliusProductBundle::class => ['all' => true], - Sylius\Bundle\ChannelBundle\SyliusChannelBundle::class => ['all' => true], - Sylius\Bundle\AttributeBundle\SyliusAttributeBundle::class => ['all' => true], - Sylius\Bundle\TaxationBundle\SyliusTaxationBundle::class => ['all' => true], - Sylius\Bundle\ShippingBundle\SyliusShippingBundle::class => ['all' => true], - Sylius\Bundle\PaymentBundle\SyliusPaymentBundle::class => ['all' => true], - Sylius\Bundle\MailerBundle\SyliusMailerBundle::class => ['all' => true], - Sylius\Bundle\PromotionBundle\SyliusPromotionBundle::class => ['all' => true], - Sylius\Bundle\AddressingBundle\SyliusAddressingBundle::class => ['all' => true], - Sylius\Bundle\InventoryBundle\SyliusInventoryBundle::class => ['all' => true], - Sylius\Bundle\TaxonomyBundle\SyliusTaxonomyBundle::class => ['all' => true], - Sylius\Bundle\UserBundle\SyliusUserBundle::class => ['all' => true], - Sylius\Bundle\CustomerBundle\SyliusCustomerBundle::class => ['all' => true], - Sylius\Bundle\UiBundle\SyliusUiBundle::class => ['all' => true], - Sylius\Bundle\ReviewBundle\SyliusReviewBundle::class => ['all' => true], - Sylius\Bundle\CoreBundle\SyliusCoreBundle::class => ['all' => true], - Sylius\Bundle\ResourceBundle\SyliusResourceBundle::class => ['all' => true], - Sylius\Bundle\GridBundle\SyliusGridBundle::class => ['all' => true], - winzou\Bundle\StateMachineBundle\winzouStateMachineBundle::class => ['all' => true], - Sonata\BlockBundle\SonataBlockBundle::class => ['all' => true], - Bazinga\Bundle\HateoasBundle\BazingaHateoasBundle::class => ['all' => true], - JMS\SerializerBundle\JMSSerializerBundle::class => ['all' => true], - FOS\RestBundle\FOSRestBundle::class => ['all' => true], - Knp\Bundle\GaufretteBundle\KnpGaufretteBundle::class => ['all' => true], - Knp\Bundle\MenuBundle\KnpMenuBundle::class => ['all' => true], - Liip\ImagineBundle\LiipImagineBundle::class => ['all' => true], - Payum\Bundle\PayumBundle\PayumBundle::class => ['all' => true], - Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle::class => ['all' => true], - BabDev\PagerfantaBundle\BabDevPagerfantaBundle::class => ['all' => true], - Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], - Sylius\Bundle\FixturesBundle\SyliusFixturesBundle::class => ['all' => true], - Sylius\Bundle\PayumBundle\SyliusPayumBundle::class => ['all' => true], - Sylius\Bundle\ThemeBundle\SyliusThemeBundle::class => ['all' => true], - Sylius\Bundle\AdminBundle\SyliusAdminBundle::class => ['all' => true], - Sylius\Bundle\ShopBundle\SyliusShopBundle::class => ['all' => true], - ApiPlatform\Core\Bridge\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true], - Sylius\Bundle\ApiBundle\SyliusApiBundle::class => ['all' => true], - Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true], - Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true], - Fidry\AliceDataFixtures\Bridge\Symfony\FidryAliceDataFixturesBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true], - Nelmio\Alice\Bridge\Symfony\NelmioAliceBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true], - FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true, 'test_cached' => true], - Sylius\Behat\Application\SyliusTestPlugin\SyliusTestPlugin::class => ['test' => true, 'test_cached' => true], - Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true], - SyliusLabs\DoctrineMigrationsExtraBundle\SyliusLabsDoctrineMigrationsExtraBundle::class => ['all' => true], - Sonata\Doctrine\Bridge\Symfony\SonataDoctrineSymfonyBundle::class => ['all' => true], - Sonata\Twig\Bridge\Symfony\SonataTwigSymfonyBundle::class => ['all' => true], - SyliusLabs\Polyfill\Symfony\Security\Bundle\SyliusLabsPolyfillSymfonySecurityBundle::class => ['all' => true], - MonsieurBiz\SyliusSearchPlugin\MonsieurBizSyliusSearchPlugin::class => ['all' => true], - Jane\Bundle\AutoMapperBundle\JaneAutoMapperBundle::class => ['all' => true], - MonsieurBiz\SyliusSettingsPlugin\MonsieurBizSyliusSettingsPlugin::class => ['all' => true], -]; diff --git a/tests/Application/config/jwt b/tests/Application/config/jwt deleted file mode 120000 index efa47433..00000000 --- a/tests/Application/config/jwt +++ /dev/null @@ -1 +0,0 @@ -../../../vendor/sylius/sylius/config/jwt \ No newline at end of file diff --git a/tests/Application/config/packages/_sylius.yaml b/tests/Application/config/packages/_sylius.yaml deleted file mode 100644 index 00221693..00000000 --- a/tests/Application/config/packages/_sylius.yaml +++ /dev/null @@ -1,34 +0,0 @@ -imports: - - { resource: "@SyliusCoreBundle/Resources/config/app/config.yml" } - - - { resource: "@SyliusAdminBundle/Resources/config/app/config.yml" } - - - { resource: "@SyliusShopBundle/Resources/config/app/config.yml" } - - - { resource: "@SyliusApiBundle/Resources/config/app/config.yaml" } - -parameters: - sylius_core.public_dir: '%kernel.project_dir%/public' - -sylius_api: - enabled: false - -sylius_shop: - product_grid: - include_all_descendants: true - -sylius_attribute: - resources: - product: - attribute: - classes: - model: App\Entity\Product\ProductAttribute - -sylius_product: - resources: - product: - classes: - model: App\Entity\Product\Product - product_option: - classes: - model: App\Entity\Product\ProductOption diff --git a/tests/Application/config/packages/dev/_sylius.yaml b/tests/Application/config/packages/dev/_sylius.yaml deleted file mode 100644 index cd01aaf7..00000000 --- a/tests/Application/config/packages/dev/_sylius.yaml +++ /dev/null @@ -1,2 +0,0 @@ -sylius_api: - enabled: true diff --git a/tests/Application/config/packages/dev/framework.yaml b/tests/Application/config/packages/dev/framework.yaml deleted file mode 100644 index 5dd13a03..00000000 --- a/tests/Application/config/packages/dev/framework.yaml +++ /dev/null @@ -1,3 +0,0 @@ -framework: - profiler: { only_exceptions: false } - ide: phpstorm diff --git a/tests/Application/config/packages/dev/jms_serializer.yaml b/tests/Application/config/packages/dev/jms_serializer.yaml deleted file mode 100644 index 2f32a9b1..00000000 --- a/tests/Application/config/packages/dev/jms_serializer.yaml +++ /dev/null @@ -1,12 +0,0 @@ -jms_serializer: - visitors: - json_serialization: - options: - - JSON_PRETTY_PRINT - - JSON_UNESCAPED_SLASHES - - JSON_PRESERVE_ZERO_FRACTION - json_deserialization: - options: - - JSON_PRETTY_PRINT - - JSON_UNESCAPED_SLASHES - - JSON_PRESERVE_ZERO_FRACTION diff --git a/tests/Application/config/packages/dev/monolog.yaml b/tests/Application/config/packages/dev/monolog.yaml deleted file mode 100644 index da2b092d..00000000 --- a/tests/Application/config/packages/dev/monolog.yaml +++ /dev/null @@ -1,9 +0,0 @@ -monolog: - handlers: - main: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.log" - level: debug - firephp: - type: firephp - level: info diff --git a/tests/Application/config/packages/dev/nelmio_alice.yaml b/tests/Application/config/packages/dev/nelmio_alice.yaml deleted file mode 100644 index 4cb9065c..00000000 --- a/tests/Application/config/packages/dev/nelmio_alice.yaml +++ /dev/null @@ -1,9 +0,0 @@ -nelmio_alice: - functions_blacklist: - - 'current' - - 'shuffle' - - 'date' - - 'time' - - 'file' - - 'md5' - - 'sha1' diff --git a/tests/Application/config/packages/dev/routing.yaml b/tests/Application/config/packages/dev/routing.yaml deleted file mode 100644 index 4116679a..00000000 --- a/tests/Application/config/packages/dev/routing.yaml +++ /dev/null @@ -1,3 +0,0 @@ -framework: - router: - strict_requirements: true diff --git a/tests/Application/config/packages/dev/swiftmailer.yaml b/tests/Application/config/packages/dev/swiftmailer.yaml deleted file mode 100644 index f4380780..00000000 --- a/tests/Application/config/packages/dev/swiftmailer.yaml +++ /dev/null @@ -1,2 +0,0 @@ -swiftmailer: - disable_delivery: true diff --git a/tests/Application/config/packages/dev/web_profiler.yaml b/tests/Application/config/packages/dev/web_profiler.yaml deleted file mode 100644 index 1f1cb2bb..00000000 --- a/tests/Application/config/packages/dev/web_profiler.yaml +++ /dev/null @@ -1,3 +0,0 @@ -web_profiler: - toolbar: true - intercept_redirects: false diff --git a/tests/Application/config/packages/doctrine.yaml b/tests/Application/config/packages/doctrine.yaml deleted file mode 100644 index f05546a3..00000000 --- a/tests/Application/config/packages/doctrine.yaml +++ /dev/null @@ -1,25 +0,0 @@ -parameters: - # Adds a fallback DATABASE_URL if the env var is not set. - # This allows you to run cache:warmup even if your - # environment variables are not available yet. - # You should not need to change this value. - env(DATABASE_URL): '' - -doctrine: - dbal: - driver: 'pdo_mysql' - charset: utf8mb4 - - url: '%env(resolve:DATABASE_URL)%' - orm: - auto_generate_proxy_classes: '%kernel.debug%' - entity_managers: - default: - auto_mapping: true - mappings: - App: - is_bundle: false - type: annotation - dir: '%kernel.project_dir%/src/Entity' - prefix: 'App\Entity' - alias: App diff --git a/tests/Application/config/packages/doctrine_migrations.yaml b/tests/Application/config/packages/doctrine_migrations.yaml deleted file mode 100644 index 765b5c5e..00000000 --- a/tests/Application/config/packages/doctrine_migrations.yaml +++ /dev/null @@ -1,6 +0,0 @@ -doctrine_migrations: - storage: - table_storage: - table_name: sylius_migrations - migrations_paths: - 'App\Migrations': "%kernel.project_dir%/src/Migrations" diff --git a/tests/Application/config/packages/fos_rest.yaml b/tests/Application/config/packages/fos_rest.yaml deleted file mode 120000 index 4ec0e792..00000000 --- a/tests/Application/config/packages/fos_rest.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/packages/fos_rest.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/framework.yaml b/tests/Application/config/packages/framework.yaml deleted file mode 120000 index a74ddd00..00000000 --- a/tests/Application/config/packages/framework.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/packages/framework.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/jms_serializer.yaml b/tests/Application/config/packages/jms_serializer.yaml deleted file mode 120000 index 6d2169f2..00000000 --- a/tests/Application/config/packages/jms_serializer.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/packages/jms_serializer.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/lexik_jwt_authentication.yaml b/tests/Application/config/packages/lexik_jwt_authentication.yaml deleted file mode 120000 index bc8765ac..00000000 --- a/tests/Application/config/packages/lexik_jwt_authentication.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/packages/lexik_jwt_authentication.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/liip_imagine.yaml b/tests/Application/config/packages/liip_imagine.yaml deleted file mode 120000 index dd814476..00000000 --- a/tests/Application/config/packages/liip_imagine.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/packages/liip_imagine.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/monsieurbiz_settings_plugin.yaml b/tests/Application/config/packages/monsieurbiz_settings_plugin.yaml deleted file mode 100644 index 02c10948..00000000 --- a/tests/Application/config/packages/monsieurbiz_settings_plugin.yaml +++ /dev/null @@ -1,2 +0,0 @@ -imports: - - { resource: "@MonsieurBizSyliusSettingsPlugin/Resources/config/config.yaml" } diff --git a/tests/Application/config/packages/monsieurbiz_sylius_search_plugin.yaml b/tests/Application/config/packages/monsieurbiz_sylius_search_plugin.yaml deleted file mode 100644 index d39a7f98..00000000 --- a/tests/Application/config/packages/monsieurbiz_sylius_search_plugin.yaml +++ /dev/null @@ -1,2 +0,0 @@ -imports: - - { resource: "@MonsieurBizSyliusSearchPlugin/Resources/config/config.yaml" } diff --git a/tests/Application/config/packages/routing.yaml b/tests/Application/config/packages/routing.yaml deleted file mode 120000 index 94d1a64b..00000000 --- a/tests/Application/config/packages/routing.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/packages/routing.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/security.yaml b/tests/Application/config/packages/security.yaml deleted file mode 120000 index c9ef2491..00000000 --- a/tests/Application/config/packages/security.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/packages/security.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/stof_doctrine_extensions.yaml b/tests/Application/config/packages/stof_doctrine_extensions.yaml deleted file mode 120000 index 88a434a3..00000000 --- a/tests/Application/config/packages/stof_doctrine_extensions.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/packages/stof_doctrine_extensions.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/swiftmailer.yaml b/tests/Application/config/packages/swiftmailer.yaml deleted file mode 120000 index 627c6afc..00000000 --- a/tests/Application/config/packages/swiftmailer.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/packages/swiftmailer.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/test b/tests/Application/config/packages/test deleted file mode 120000 index f7645146..00000000 --- a/tests/Application/config/packages/test +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/packages/test \ No newline at end of file diff --git a/tests/Application/config/packages/test_cached b/tests/Application/config/packages/test_cached deleted file mode 120000 index 66ffd829..00000000 --- a/tests/Application/config/packages/test_cached +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/packages/test_cached \ No newline at end of file diff --git a/tests/Application/config/packages/translation.yaml b/tests/Application/config/packages/translation.yaml deleted file mode 120000 index 759bccbd..00000000 --- a/tests/Application/config/packages/translation.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/packages/translation.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/twig.yaml b/tests/Application/config/packages/twig.yaml deleted file mode 120000 index 305c69ef..00000000 --- a/tests/Application/config/packages/twig.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/packages/twig.yaml \ No newline at end of file diff --git a/tests/Application/config/packages/validator.yaml b/tests/Application/config/packages/validator.yaml deleted file mode 120000 index a381919b..00000000 --- a/tests/Application/config/packages/validator.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/packages/validator.yaml \ No newline at end of file diff --git a/tests/Application/config/routes.yaml b/tests/Application/config/routes.yaml deleted file mode 120000 index 5ce0cf9f..00000000 --- a/tests/Application/config/routes.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../vendor/sylius/sylius/config/routes.yaml \ No newline at end of file diff --git a/tests/Application/config/routes/dev b/tests/Application/config/routes/dev deleted file mode 120000 index 260f2a1d..00000000 --- a/tests/Application/config/routes/dev +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/routes/dev \ No newline at end of file diff --git a/tests/Application/config/routes/liip_imagine.yaml b/tests/Application/config/routes/liip_imagine.yaml deleted file mode 120000 index a0cd29e7..00000000 --- a/tests/Application/config/routes/liip_imagine.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/routes/liip_imagine.yaml \ No newline at end of file diff --git a/tests/Application/config/routes/monsieurbiz_settings_plugin.yaml b/tests/Application/config/routes/monsieurbiz_settings_plugin.yaml deleted file mode 100644 index 3b012295..00000000 --- a/tests/Application/config/routes/monsieurbiz_settings_plugin.yaml +++ /dev/null @@ -1,3 +0,0 @@ -monsieurbiz_sylius_settings_admin: - resource: "@MonsieurBizSyliusSettingsPlugin/Resources/config/routes/admin.yaml" - prefix: /%sylius_admin.path_name% diff --git a/tests/Application/config/routes/monsieurbiz_sylius_search_plugin.yaml b/tests/Application/config/routes/monsieurbiz_sylius_search_plugin.yaml deleted file mode 120000 index 5f50104d..00000000 --- a/tests/Application/config/routes/monsieurbiz_sylius_search_plugin.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../recipes/2.0/config/routes/monsieurbiz_sylius_search_plugin.yaml \ No newline at end of file diff --git a/tests/Application/config/routes/sylius_admin.yaml b/tests/Application/config/routes/sylius_admin.yaml deleted file mode 120000 index fb7ebdfa..00000000 --- a/tests/Application/config/routes/sylius_admin.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/routes/sylius_admin.yaml \ No newline at end of file diff --git a/tests/Application/config/routes/sylius_api.yaml b/tests/Application/config/routes/sylius_api.yaml deleted file mode 120000 index 8e6246c3..00000000 --- a/tests/Application/config/routes/sylius_api.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/routes/sylius_api.yaml \ No newline at end of file diff --git a/tests/Application/config/routes/sylius_shop.yaml b/tests/Application/config/routes/sylius_shop.yaml deleted file mode 120000 index 6ca08a47..00000000 --- a/tests/Application/config/routes/sylius_shop.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/routes/sylius_shop.yaml \ No newline at end of file diff --git a/tests/Application/config/routes/test b/tests/Application/config/routes/test deleted file mode 120000 index b12612e7..00000000 --- a/tests/Application/config/routes/test +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/routes/test \ No newline at end of file diff --git a/tests/Application/config/routes/test_cached b/tests/Application/config/routes/test_cached deleted file mode 120000 index e0707663..00000000 --- a/tests/Application/config/routes/test_cached +++ /dev/null @@ -1 +0,0 @@ -../../../../vendor/sylius/sylius/config/routes/test_cached \ No newline at end of file diff --git a/tests/Application/config/secrets b/tests/Application/config/secrets deleted file mode 120000 index bd4b6402..00000000 --- a/tests/Application/config/secrets +++ /dev/null @@ -1 +0,0 @@ -../../../vendor/sylius/sylius/config/secrets \ No newline at end of file diff --git a/tests/Application/config/services.yaml b/tests/Application/config/services.yaml deleted file mode 100644 index 2d615dc9..00000000 --- a/tests/Application/config/services.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# Put parameters here that don't need to change on each machine where the app is deployed -# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration -parameters: - locale: en_US - - -services: diff --git a/tests/Application/config/services_test.yaml b/tests/Application/config/services_test.yaml deleted file mode 120000 index 177cc6cb..00000000 --- a/tests/Application/config/services_test.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../vendor/sylius/sylius/config/services_test.yaml \ No newline at end of file diff --git a/tests/Application/config/services_test_cached.yaml b/tests/Application/config/services_test_cached.yaml deleted file mode 120000 index 486f379f..00000000 --- a/tests/Application/config/services_test_cached.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../vendor/sylius/sylius/config/services_test_cached.yaml \ No newline at end of file diff --git a/tests/Application/docker-compose.yaml b/tests/Application/docker-compose.yaml deleted file mode 100644 index 3a73d79e..00000000 --- a/tests/Application/docker-compose.yaml +++ /dev/null @@ -1,52 +0,0 @@ -version: '3.8' -services: - database: - image: percona:5.7 - ports: - - 3306 - environment: - MYSQL_ALLOW_EMPTY_PASSWORD: 1 - MYSQL_DATABASE: sylius - volumes: - - database:/var/lib/mysql - mailer: - image: monsieurbiz/mailcatcher - ports: - - 1025 - - 1080 - elasticsearch: - build: - context: ./docker/elasticsearch/ - args: - - ELASTICSEARCH_VERSION=7.16.1 - volumes: - - esdata:/usr/share/elasticsearch/data:rw - environment: - - cluster.name=docker-cluster - - bootstrap.memory_lock=true - - discovery.type=single-node - - "ES_JAVA_OPTS=-Xms512m -Xmx512m" - - "xpack.security.enabled=false" - ulimits: - memlock: - soft: -1 - hard: -1 - ports: - - "9200" - kibana: - image: docker.elastic.co/kibana/kibana:7.16.1 - environment: - - "ELASTICSEARCH_HOSTS=http://elasticsearch:9200" - - "XPACK_GRAPH_ENABLED=false" - - "XPACK_ML_ENABLED=false" - - "XPACK_REPORTING_ENABLED=false" - - "XPACK_SECURITY_ENABLED=false" - - "XPACK_WATCHER_ENABLED=false" - links: - - elasticsearch - ports: - - "${KIBANA_PORT:-5601}:5601" - -volumes: - database: {} - esdata: {} diff --git a/tests/Application/docker/elasticsearch/Dockerfile b/tests/Application/docker/elasticsearch/Dockerfile deleted file mode 100644 index a3d7ebf5..00000000 --- a/tests/Application/docker/elasticsearch/Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -ARG ELASTICSEARCH_VERSION - -FROM docker.elastic.co/elasticsearch/elasticsearch:$ELASTICSEARCH_VERSION - -# Install ES plugins -RUN bin/elasticsearch-plugin install analysis-phonetic && \ - bin/elasticsearch-plugin install analysis-icu diff --git a/tests/Application/gulpfile.babel.js b/tests/Application/gulpfile.babel.js deleted file mode 100644 index 279cf3d1..00000000 --- a/tests/Application/gulpfile.babel.js +++ /dev/null @@ -1,63 +0,0 @@ -import chug from 'gulp-chug'; -import gulp from 'gulp'; -import yargs from 'yargs'; - -const { argv } = yargs - .options({ - rootPath: { - description: ' path to public assets directory', - type: 'string', - requiresArg: true, - required: false, - }, - nodeModulesPath: { - description: ' path to node_modules directory', - type: 'string', - requiresArg: true, - required: false, - }, - }); - -const config = [ - '--rootPath', - argv.rootPath || '../../../../../../../tests/Application/public/assets', - '--nodeModulesPath', - argv.nodeModulesPath || '../../../../../../../tests/Application/node_modules', -]; - -export const buildAdmin = function buildAdmin() { - return gulp.src('../../vendor/sylius/sylius/src/Sylius/Bundle/AdminBundle/gulpfile.babel.js', { read: false }) - .pipe(chug({ args: config, tasks: 'build' })); -}; -buildAdmin.description = 'Build admin assets.'; - -export const watchAdmin = function watchAdmin() { - return gulp.src('../../vendor/sylius/sylius/src/Sylius/Bundle/AdminBundle/gulpfile.babel.js', { read: false }) - .pipe(chug({ args: config, tasks: 'watch' })); -}; -watchAdmin.description = 'Watch admin asset sources and rebuild on changes.'; - -export const buildShop = function buildShop() { - return gulp.src('../../vendor/sylius/sylius/src/Sylius/Bundle/ShopBundle/gulpfile.babel.js', { read: false }) - .pipe(chug({ args: config, tasks: 'build' })); -}; -buildShop.description = 'Build shop assets.'; - -export const watchShop = function watchShop() { - return gulp.src('../../vendor/sylius/sylius/src/Sylius/Bundle/ShopBundle/gulpfile.babel.js', { read: false }) - .pipe(chug({ args: config, tasks: 'watch' })); -}; -watchShop.description = 'Watch shop asset sources and rebuild on changes.'; - -export const build = gulp.parallel(buildAdmin, buildShop); -build.description = 'Build assets.'; - -export const watch = gulp.parallel(watchAdmin, watchShop); -watch.description = 'Watch asset sources and rebuild on changes.'; - -gulp.task('admin', buildAdmin); -gulp.task('admin-watch', watchAdmin); -gulp.task('shop', buildShop); -gulp.task('shop-watch', watchShop); - -export default build; diff --git a/tests/Application/package.json b/tests/Application/package.json deleted file mode 100644 index 2f72522b..00000000 --- a/tests/Application/package.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "dependencies": { - "babel-polyfill": "^6.26.0", - "chart.js": "^2.9.3", - "jquery": "^3.2.0", - "jquery.dirtyforms": "^2.0.0", - "lightbox2": "^2.9.0", - "semantic-ui-css": "^2.2.0", - "slick-carousel": "^1.8.1" - }, - "devDependencies": { - "@symfony/webpack-encore": "^0.28.0", - "babel-core": "^6.26.3", - "babel-plugin-external-helpers": "^6.22.0", - "babel-plugin-module-resolver": "^3.1.1", - "babel-plugin-transform-object-rest-spread": "^6.26.0", - "babel-preset-env": "^1.7.0", - "babel-register": "^6.26.0", - "dedent": "^0.7.0", - "eslint": "^4.19.1", - "eslint-config-airbnb-base": "^12.1.0", - "eslint-import-resolver-babel-module": "^4.0.0", - "eslint-plugin-import": "^2.12.0", - "fast-async": "^6.3.7", - "gulp": "^4.0.0", - "gulp-chug": "^0.5", - "gulp-concat": "^2.6.0", - "gulp-debug": "^2.1.2", - "gulp-if": "^2.0.0", - "gulp-livereload": "^3.8.1", - "gulp-order": "^1.1.1", - "gulp-sass": "^4.0.1", - "gulp-sourcemaps": "^1.6.0", - "gulp-uglifycss": "^1.0.5", - "merge-stream": "^1.0.0", - "rollup": "^0.60.7", - "rollup-plugin-babel": "^3.0.4", - "rollup-plugin-commonjs": "^9.1.3", - "rollup-plugin-inject": "^2.0.0", - "rollup-plugin-node-resolve": "^3.3.0", - "rollup-plugin-uglify": "^4.0.0", - "sass-loader": "^7.0.1", - "upath": "^1.1.0", - "yargs": "^6.4.0" - }, - "scripts": { - "build": "gulp build", - "gulp": "gulp build", - "lint": "yarn lint:js", - "lint:js": "eslint gulpfile.babel.js" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/Sylius/Sylius.git" - }, - "author": "Paweł Jędrzejewski", - "license": "MIT" -} diff --git a/tests/Application/php.ini b/tests/Application/php.ini deleted file mode 100644 index 8519987f..00000000 --- a/tests/Application/php.ini +++ /dev/null @@ -1 +0,0 @@ -date.timezone=Greenwich diff --git a/tests/Application/public/.htaccess b/tests/Application/public/.htaccess deleted file mode 120000 index 59de487f..00000000 --- a/tests/Application/public/.htaccess +++ /dev/null @@ -1 +0,0 @@ -../../../vendor/sylius/sylius/public/.htaccess \ No newline at end of file diff --git a/tests/Application/public/favicon.ico b/tests/Application/public/favicon.ico deleted file mode 120000 index 85df8df0..00000000 --- a/tests/Application/public/favicon.ico +++ /dev/null @@ -1 +0,0 @@ -../../../vendor/sylius/sylius/public/favicon.ico \ No newline at end of file diff --git a/tests/Application/public/index.php b/tests/Application/public/index.php deleted file mode 100644 index d73dcb2e..00000000 --- a/tests/Application/public/index.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -use App\Kernel; -use Symfony\Component\ErrorHandler\Debug; -use Symfony\Component\HttpFoundation\Request; - -require dirname(__DIR__) . '/config/bootstrap.php'; - -if ($_SERVER['APP_DEBUG']) { - umask(0000); - - Debug::enable(); -} - -if ($trustedProxies = $_SERVER['TRUSTED_PROXIES'] ?? $_ENV['TRUSTED_PROXIES'] ?? false) { - Request::setTrustedProxies(explode(',', $trustedProxies), Request::HEADER_X_FORWARDED_ALL ^ Request::HEADER_X_FORWARDED_HOST); -} - -if ($trustedHosts = $_SERVER['TRUSTED_HOSTS'] ?? $_ENV['TRUSTED_HOSTS'] ?? false) { - Request::setTrustedHosts([$trustedHosts]); -} - -$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); -$request = Request::createFromGlobals(); -$response = $kernel->handle($request); -$response->send(); -$kernel->terminate($request, $response); diff --git a/tests/Application/public/media/image/.gitignore b/tests/Application/public/media/image/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/Application/public/robots.txt b/tests/Application/public/robots.txt deleted file mode 100644 index 214e4119..00000000 --- a/tests/Application/public/robots.txt +++ /dev/null @@ -1,4 +0,0 @@ -# www.robotstxt.org/ -# www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449 - -User-agent: * diff --git a/tests/Application/src/Entity/Product/Product.php b/tests/Application/src/Entity/Product/Product.php deleted file mode 100644 index 82f07fa3..00000000 --- a/tests/Application/src/Entity/Product/Product.php +++ /dev/null @@ -1,31 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace App\Entity\Product; - -use Doctrine\ORM\Mapping as ORM; -use Sylius\Component\Core\Model\Product as BaseProduct; -use Sylius\Component\Core\Model\ProductTranslation; -use Sylius\Component\Product\Model\ProductTranslationInterface; - -/** - * @ORM\Entity - * @ORM\Table(name="sylius_product") - */ -class Product extends BaseProduct -{ - protected function createTranslation(): ProductTranslationInterface - { - return new ProductTranslation(); - } -} diff --git a/tests/Application/src/Entity/Product/ProductAttribute.php b/tests/Application/src/Entity/Product/ProductAttribute.php deleted file mode 100644 index c01070cb..00000000 --- a/tests/Application/src/Entity/Product/ProductAttribute.php +++ /dev/null @@ -1,35 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace App\Entity\Product; - -use Doctrine\ORM\Mapping as ORM; -use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; -use MonsieurBiz\SyliusSearchPlugin\Model\Product\SearchableTrait; -use Sylius\Component\Attribute\Model\AttributeTranslationInterface; -use Sylius\Component\Product\Model\ProductAttribute as BaseProductAttribute; -use Sylius\Component\Product\Model\ProductAttributeTranslation; - -/** - * @ORM\Entity - * @ORM\Table(name="sylius_product_attribute") - */ -class ProductAttribute extends BaseProductAttribute implements SearchableInterface -{ - use SearchableTrait; - - protected function createTranslation(): AttributeTranslationInterface - { - return new ProductAttributeTranslation(); - } -} diff --git a/tests/Application/src/Entity/Product/ProductOption.php b/tests/Application/src/Entity/Product/ProductOption.php deleted file mode 100644 index 1c586a91..00000000 --- a/tests/Application/src/Entity/Product/ProductOption.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace App\Entity\Product; - -use Doctrine\ORM\Mapping as ORM; -use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; -use MonsieurBiz\SyliusSearchPlugin\Model\Product\SearchableTrait; -use Sylius\Component\Product\Model\ProductOption as BaseProductOption; - -/** - * @ORM\Entity - * @ORM\Table(name="sylius_product_option") - */ -class ProductOption extends BaseProductOption implements SearchableInterface -{ - use SearchableTrait; -} diff --git a/tests/Application/src/Kernel.php b/tests/Application/src/Kernel.php deleted file mode 100644 index 038732ca..00000000 --- a/tests/Application/src/Kernel.php +++ /dev/null @@ -1,85 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace App; - -use PSS\SymfonyMockerContainer\DependencyInjection\MockerContainer; -use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; -use Symfony\Component\Config\Loader\LoaderInterface; -use Symfony\Component\Config\Resource\FileResource; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\HttpKernel\Kernel as BaseKernel; -use Symfony\Component\Routing\RouteCollectionBuilder; - -final class Kernel extends BaseKernel -{ - use MicroKernelTrait; - - private const CONFIG_EXTS = '.{php,xml,yaml,yml}'; - - public function getCacheDir(): string - { - return $this->getProjectDir() . '/var/cache/' . $this->environment; - } - - public function getLogDir(): string - { - return $this->getProjectDir() . '/var/log'; - } - - public function registerBundles(): iterable - { - /** @psalm-suppress UnresolvableInclude */ - $contents = require $this->getProjectDir() . '/config/bundles.php'; - foreach ($contents as $class => $envs) { - if (isset($envs['all']) || isset($envs[$this->environment])) { - yield new $class(); - } - } - } - - protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void - { - $container->addResource(new FileResource($this->getProjectDir() . '/config/bundles.php')); - $container->setParameter('container.dumper.inline_class_loader', true); - $confDir = $this->getProjectDir() . '/config'; - - $loader->load($confDir . '/{packages}/*' . self::CONFIG_EXTS, 'glob'); - $loader->load($confDir . '/{packages}/' . $this->environment . '/**/*' . self::CONFIG_EXTS, 'glob'); - $loader->load($confDir . '/{services}' . self::CONFIG_EXTS, 'glob'); - $loader->load($confDir . '/{services}_' . $this->environment . self::CONFIG_EXTS, 'glob'); - } - - protected function configureRoutes(RouteCollectionBuilder $routes): void - { - $confDir = $this->getProjectDir() . '/config'; - - $routes->import($confDir . '/{routes}/*' . self::CONFIG_EXTS, '/', 'glob'); - $routes->import($confDir . '/{routes}/' . $this->environment . '/**/*' . self::CONFIG_EXTS, '/', 'glob'); - $routes->import($confDir . '/{routes}' . self::CONFIG_EXTS, '/', 'glob'); - } - - protected function getContainerBaseClass(): string - { - if ($this->isTestEnvironment()) { - return MockerContainer::class; - } - - return parent::getContainerBaseClass(); - } - - private function isTestEnvironment(): bool - { - return 0 === strpos($this->getEnvironment(), 'test'); - } -} diff --git a/tests/Application/src/Migrations/Version20211012092353.php b/tests/Application/src/Migrations/Version20211012092353.php deleted file mode 100644 index efbfcfa4..00000000 --- a/tests/Application/src/Migrations/Version20211012092353.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace App\Migrations; - -use Doctrine\DBAL\Schema\Schema; -use Doctrine\Migrations\AbstractMigration; - -/** - * Auto-generated Migration: Please modify to your needs! - */ -final class Version20211012092353 extends AbstractMigration -{ - public function getDescription(): string - { - return 'Add search columns on product attribute and product option'; - } - - public function up(Schema $schema): void - { - // this up() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE sylius_product_attribute ADD searchable TINYINT(1) DEFAULT \'1\' NOT NULL, ADD filterable TINYINT(1) DEFAULT \'0\' NOT NULL, ADD search_weight SMALLINT UNSIGNED DEFAULT 1 NOT NULL'); - $this->addSql('ALTER TABLE sylius_product_option ADD searchable TINYINT(1) DEFAULT \'1\' NOT NULL, ADD filterable TINYINT(1) DEFAULT \'0\' NOT NULL, ADD search_weight SMALLINT UNSIGNED DEFAULT 1 NOT NULL'); - } - - public function down(Schema $schema): void - { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE sylius_product_attribute DROP searchable, DROP filterable, DROP search_weight'); - $this->addSql('ALTER TABLE sylius_product_option DROP searchable, DROP filterable, DROP search_weight'); - } -} diff --git a/tests/Application/templates/.gitkeep b/tests/Application/templates/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/Application/templates/bundles/SyliusAdminBundle b/tests/Application/templates/bundles/SyliusAdminBundle deleted file mode 120000 index d0ee031e..00000000 --- a/tests/Application/templates/bundles/SyliusAdminBundle +++ /dev/null @@ -1 +0,0 @@ -../../../../src/Resources/templates/bundles/SyliusAdminBundle/ \ No newline at end of file diff --git a/tests/Application/translations/.gitignore b/tests/Application/translations/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/Application/webpack.config.js b/tests/Application/webpack.config.js deleted file mode 120000 index 5603856b..00000000 --- a/tests/Application/webpack.config.js +++ /dev/null @@ -1 +0,0 @@ -../../vendor/sylius/sylius/webpack.config.js \ No newline at end of file From 8b672513fdb7673a4edf10ca14e8d0bb13e1c1cb Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 11:04:46 +0100 Subject: [PATCH 112/142] Update .gitignore --- .gitignore | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 0d68c09e..2d7b6c5c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,18 @@ /vendor/ -/node_modules/ /composer.lock /etc/build/* !/etc/build/.gitignore -/tests/Application/yarn.lock +/tests/Application /behat.yml /phpspec.yml /package-lock.json -/.php_cs.cache /.php-version +/php.ini /.phpunit.result.cache /node_modules -yarn.lock +/yarn.lock From 056ea9b46b4e21faa6c7b9e22ebd4e56c336d8d7 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 11:05:49 +0100 Subject: [PATCH 113/142] Update github actions --- .github/.github/workflows/recipe.yaml | 85 +++++++++++++++++++++++++ .github/.github/workflows/security.yaml | 48 ++++++++++++++ .github/.github/workflows/tests.yaml | 74 +++++++++++++++++++++ .github/workflows/composer.yml | 31 --------- 4 files changed, 207 insertions(+), 31 deletions(-) create mode 100644 .github/.github/workflows/recipe.yaml create mode 100644 .github/.github/workflows/security.yaml create mode 100644 .github/.github/workflows/tests.yaml delete mode 100644 .github/workflows/composer.yml diff --git a/.github/.github/workflows/recipe.yaml b/.github/.github/workflows/recipe.yaml new file mode 100644 index 00000000..ee799eec --- /dev/null +++ b/.github/.github/workflows/recipe.yaml @@ -0,0 +1,85 @@ +name: Flex Recipe + +on: + push: + branches: [ master ] + pull_request: + +jobs: + + recipe: + + runs-on: ubuntu-latest + + env: + SYMFONY_ENDPOINT: http://127.0.0.1/ + + strategy: + fail-fast: false + matrix: + php: ['7.4' ,'8.0'] + sylius: ["~1.8.0", "~1.9.0", "~1.10.0"] + exclude: + - php: 8.0 + sylius: "~1.8.0" + - php: 8.0 + sylius: "~1.9.0" + + steps: + - name: Setup PHP + run: | + sudo update-alternatives --set php /usr/bin/php${{ matrix.php }} + echo "date.timezone=UTC" >> /tmp/timezone.ini + sudo mv /tmp/timezone.ini /etc/php/${{ matrix.php }}/cli/conf.d/timezone.ini + echo ${{ matrix.php }} > .php-version + + - uses: actions/checkout@v2 + with: + path: plugin + + # Run the server at the start so it can download the recipes! + - name: Run standalone symfony flex server + run: | + echo ${{ github.token }} | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin + docker run --rm --name flex -d -v $PWD/plugin/recipes:/var/www/flex/var/repo/private/monsieurbiz/sylius-search-plugin -p 80:80 docker.pkg.github.com/monsieurbiz/docker/symfony-flex-server:latest contrib official + docker ps + + - run: mkdir -p /home/runner/{.composer/cache,.config/composer} + + - uses: actions/cache@v1 + id: cache-composer + with: + path: /home/runner/.composer/cache + key: composer2-php:${{ matrix.php }}-sylius:${{ matrix.sylius }}-${{ github.sha }} + restore-keys: composer2-php:${{ matrix.php }}-sylius:${{ matrix.sylius }}- + + - name: Composer v2 + run: sudo composer self-update --2 + + - name: Composer Github Auth + run: composer config -g github-oauth.github.com ${{ github.token }} + + - name: Create Sylius-Standard project without install + run: | + composer create-project --prefer-dist --no-scripts --no-progress --no-install sylius/sylius-standard sylius "${{ matrix.sylius }}" + + - name: Setup some requirements + working-directory: ./sylius + run: | + composer config repositories.plugin '{"type": "path", "url": "../plugin/"}' + composer config extra.symfony.allow-contrib true + composer config secure-http false + composer config --unset platform.php + + - name: Require plugin without install + working-directory: ./sylius + run: | + composer require --no-install --no-update monsieurbiz/sylius-search-plugin="*@dev" + + - name: Composer install + working-directory: ./sylius + run: | + composer install + + - name: Show flex server logs + run: docker logs --tail 100 flex diff --git a/.github/.github/workflows/security.yaml b/.github/.github/workflows/security.yaml new file mode 100644 index 00000000..1cf94aa4 --- /dev/null +++ b/.github/.github/workflows/security.yaml @@ -0,0 +1,48 @@ +name: Security + +on: + push: + pull_request: + +jobs: + + security: + + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + php: ['7.4', '8.0'] + + steps: + - uses: actions/checkout@v2 + + - name: Setup PHP + run: | + sudo update-alternatives --set php /usr/bin/php${{ matrix.php }} + echo "date.timezone=UTC" | sudo tee /etc/php/${{ matrix.php }}/cli/conf.d/timezone.ini + echo "${{ matrix.php }}" > .php-version + + - uses: actions/cache@v1 + id: cache-composer + with: + path: /home/runner/.composer/cache + key: composer2-php:${{ matrix.php }}-${{ github.sha }} + restore-keys: composer2-php:${{ matrix.php }}- + + - run: mkdir -p /home/runner/{.composer/cache,.config/composer} + if: steps.cache-composer.outputs.cache-hit != 'true' + + - name: Composer v2 + run: sudo composer self-update --2 + + - name: Composer Github Auth + run: composer config -g github-oauth.github.com ${{ github.token }} + + - uses: actions/checkout@v2 + + - name: Install PHP dependencies + run: composer update --prefer-dist + + - uses: symfonycorp/security-checker-action@v2 diff --git a/.github/.github/workflows/tests.yaml b/.github/.github/workflows/tests.yaml new file mode 100644 index 00000000..ebb0b2cd --- /dev/null +++ b/.github/.github/workflows/tests.yaml @@ -0,0 +1,74 @@ +name: Tests + +on: + push: + branches: [ master ] + pull_request: + +jobs: + + php: + + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + php: ['7.4', '8.0'] + + env: + SYMFONY_ARGS: --no-tls + COMPOSER_ARGS: --prefer-dist + DOCKER_INTERACTIVE_ARGS: -t + + steps: + - uses: actions/checkout@v2 + + - name: Setup PHP + run: | + sudo update-alternatives --set php /usr/bin/php${{ matrix.php }} + echo "date.timezone=UTC" | sudo tee /etc/php/${{ matrix.php }}/cli/conf.d/timezone.ini + echo "${{ matrix.php }}" > .php-version + + - name: Install symfony CLI + run: | + curl https://get.symfony.com/cli/installer | bash + echo "${HOME}/.symfony/bin" >> $GITHUB_PATH + + - uses: actions/cache@v1 + id: cache-composer + with: + path: /home/runner/.composer/cache + key: composer2-php:${{ matrix.php }}-${{ github.sha }} + restore-keys: composer2-php:${{ matrix.php }}- + + - run: mkdir -p /home/runner/{.composer/cache,.config/composer} + if: steps.cache-composer.outputs.cache-hit != 'true' + + - name: Composer v2 + run: sudo composer self-update --2 + + - name: Composer Github Auth + run: composer config -g github-oauth.github.com ${{ github.token }} + + - run: make install + + - run: make test.composer + + - run: make test.phpcs + + - run: make test.phpunit + + - run: make test.phpstan + + - run: make test.phpmd + + - run: make test.phpspec + + - run: make test.yaml + + - run: make test.twig + + - run: make test.schema + + - run: make test.container diff --git a/.github/workflows/composer.yml b/.github/workflows/composer.yml deleted file mode 100644 index f6a269e0..00000000 --- a/.github/workflows/composer.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: PHP Composer - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - schedule: - - cron: '0 0 * * *' - -jobs: - build: - - runs-on: ubuntu-latest - - strategy: - matrix: - php: [7.2, 7.3, 7.4, 8.0] - - steps: - - uses: actions/checkout@v2 - - - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - - - name: Validate composer.json and composer.lock - run: composer validate - - - name: Install dependencies - run: composer install --prefer-dist --no-progress --no-suggest From 5751c347f98cf621af619d5a4719c098f344ce30 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 11:07:16 +0100 Subject: [PATCH 114/142] Update composer.json --- composer.json | 88 ++++++++++++++++++++++++--------------------------- 1 file changed, 41 insertions(+), 47 deletions(-) diff --git a/composer.json b/composer.json index f4ad780c..1bce1bb8 100644 --- a/composer.json +++ b/composer.json @@ -1,52 +1,48 @@ { "name": "monsieurbiz/sylius-search-plugin", "type": "sylius-plugin", - "keywords": ["sylius", "sylius-plugin", "elasticsearch"], + "keywords": ["sylius", "sylius-plugin", "monsieurbiz"], "description": "A search plugin using Elasticsearch for Sylius.", "license": "MIT", "require": { - "php": "^7.4 || ^8.0", - "babdev/pagerfanta-bundle": "^2.5", - "jacquesbh/eater": "^2.0", - "jane-php/automapper-bundle": "^7.1", - "jolicode/elastically": "^1.4.0", - "monsieurbiz/sylius-settings-plugin": "^1.0", - "sylius/sylius": "^1.9", - "symfony/messenger": "^4.4 || ^5.2" + "php": "~7.4|~8.0", + "sylius/sylius": ">=1.8 <1.11", + "jolicode/elastically": "^1.0.0" }, "require-dev": { "behat/behat": "^3.6.1", "behat/mink-selenium2-driver": "^1.4", "dmore/behat-chrome-extension": "^1.3", "dmore/chrome-mink-driver": "^2.7", - "friendsofphp/php-cs-fixer": "^3.1", + "doctrine/data-fixtures": "^1.4", + "ergebnis/composer-normalize": "^2.5", "friends-of-behat/mink": "^1.8", "friends-of-behat/mink-browserkit-driver": "^1.4", - "friends-of-behat/mink-debug-extension": "^2.0.0", "friends-of-behat/mink-extension": "^2.4", "friends-of-behat/page-object-extension": "^0.3", - "friends-of-behat/suite-settings-extension": "^1.0", "friends-of-behat/symfony-extension": "^2.1", "friends-of-behat/variadic-extension": "^1.3", - "friendsofsymfony/oauth-server-bundle": ">2.0.0-alpha.0 ^2.0@dev", - "lchrusciel/api-test-case": "^3.0 || ^5.0", - "phpmd/phpmd": "@stable", - "phpspec/phpspec": "^7.0", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "0.12.85", - "phpstan/phpstan-doctrine": "0.12.33", - "phpstan/phpstan-strict-rules": "^0.12.0", - "phpstan/phpstan-webmozart-assert": "0.12.12", - "phpunit/phpunit": "^9.5", - "sensiolabs/security-checker": "^6.0", - "sylius-labs/coding-standard": "^4.0", - "symfony/browser-kit": "^4.4 || ^5.2", - "symfony/debug-bundle": "^4.4 || ^5.2", - "symfony/dotenv": "^4.4 || ^5.2", - "symfony/intl": "^4.4 || ^5.2", - "symfony/web-profiler-bundle": "^4.4 || ^5.2", - "vimeo/psalm": "4.7.1", - "jane-php/json-schema": "^7.1" + "hwi/oauth-bundle": "^1.1", + "lchrusciel/api-test-case": "^5.0", + "matthiasnoback/symfony-config-test": "^4.2", + "matthiasnoback/symfony-dependency-injection-test": "^4.1", + "mikey179/vfsstream": "^1.6", + "mockery/mockery": "^1.4", + "pamil/prophecy-common": "^0.1", + "phpspec/phpspec": "^6.1", + "phpstan/phpstan": "^0.12.57", + "phpstan/phpstan-doctrine": "^0.12.19", + "phpstan/phpstan-webmozart-assert": "^0.12.7", + "phpunit/phpunit": "^8.5", + "psalm/plugin-mockery": "^0.3", + "psr/event-dispatcher": "^1.0", + "sylius-labs/coding-standard": "^3.1", + "symfony/browser-kit": "^4.4", + "symfony/debug-bundle": "^4.4", + "symfony/dotenv": "^4.4", + "symfony/flex": "^1.7", + "symfony/web-profiler-bundle": "^4.4", + "phpmd/phpmd": "@stable" }, "conflict": { "doctrine/dbal": "^3" @@ -54,28 +50,26 @@ "prefer-stable": true, "autoload": { "psr-4": { - "MonsieurBiz\\SyliusSearchPlugin\\": "src/", - "MonsieurBiz\\SyliusSearchPlugin\\Generated\\": "generated/", - "Tests\\MonsieurBiz\\SyliusSearchPlugin\\": "tests/" - } - }, - "autoload-dev": { - "classmap": ["tests/Application/src/Kernel.php"], - "psr-4": { - "App\\": "tests/Application/src/" + "MonsieurBiz\\SyliusSearchPlugin\\": "src/" } }, "scripts": { - "phpcs": "php-cs-fixer fix --using-cache=no", - "jane-generate": "jane generate --config-file=src/Resources/config/jane/jane-configuration.php" - }, - "config": { - "sort-packages": true + "auto-scripts": { + "cache:clear": "symfony-cmd", + "assets:install %PUBLIC_DIR%": "symfony-cmd" + }, + "phpcs": "php-cs-fixer fix --using-cache=false", + "phpstan": "phpstan analyse -c phpstan.neon src/", + "phpmd": "phpmd --exclude Migrations/* src/ ansi phpmd.xml", + "phpunit": "phpunit", + "phpspec": "phpspec run" }, "extra": { + "symfony": { + "require": "^4.4" + }, "branch-alias": { - "dev-master": "1.0-dev", - "dev-2.0.x": "2.0-dev" + "dev-master": "2.0-dev" } } } From dc19832f401eda8636fc26aec2aecb6e698b9138 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Wed, 3 Nov 2021 18:21:39 +0100 Subject: [PATCH 115/142] Update Makefile and some other files --- DEVELOPMENT.md | 15 ++++++++ LICENSE | 21 ---------- LICENSE.txt | 7 ++++ Makefile | 82 +++++++++++++++++++++++++++++----------- TESTING.md | 80 +++++++++++++++------------------------ docker-compose.yaml.dist | 20 ++++++++++ php.ini.dist | 1 + phpmd.xml | 2 +- phpspec.yml.dist | 2 + phpstan.neon | 3 +- phpunit.xml.dist | 47 ++++++++++++++++++++--- tests/Unit/.gitkeep | 0 tests/bootstrap.php | 22 +++++++++++ 13 files changed, 199 insertions(+), 103 deletions(-) create mode 100644 DEVELOPMENT.md delete mode 100644 LICENSE create mode 100644 LICENSE.txt create mode 100644 docker-compose.yaml.dist create mode 100644 php.ini.dist create mode 100644 tests/Unit/.gitkeep create mode 100644 tests/bootstrap.php diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 00000000..9971d62e --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,15 @@ +## Node + +Be sure you have node 14 on your machine. You can use NVM to easily switch versions. + +# Docker + +Be sure you have docker on your machine. + +# Symfony + +Be sure you have the Symfony binary on your machine. + +``` + curl -sS https://get.symfony.com/cli/installer | bash + ``` diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 06c219d8..00000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 Monsieur Biz - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 00000000..1b1809b8 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,7 @@ +Copyright 2021 Monsieur Biz + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile index a9d5facc..f74327d9 100644 --- a/Makefile +++ b/Makefile @@ -1,21 +1,20 @@ .DEFAULT_GOAL := help SHELL=/bin/bash APP_DIR=tests/Application +SYLIUS_VERSION=1.10 SYMFONY=cd ${APP_DIR} && symfony COMPOSER=symfony composer CONSOLE=${SYMFONY} console export COMPOSE_PROJECT_NAME=search +PLUGIN_NAME=sylius-${COMPOSE_PROJECT_NAME}-plugin COMPOSE=docker-compose YARN=yarn -PHPSTAN=symfony php vendor/bin/phpstan -PHPUNIT=symfony php vendor/bin/phpunit -PHPSPEC=symfony php vendor/bin/phpspec ### ### DEVELOPMENT ### ¯¯¯¯¯¯¯¯¯¯¯ -install: platform sylius ## Install the plugin +install: application platform sylius es.reindex ## Install the plugin .PHONY: install up: docker.up server.start ## Up the project (start docker, start symfony server) @@ -23,7 +22,8 @@ stop: server.stop docker.stop ## Stop the project (stop docker, stop symfony ser down: server.stop docker.down ## Down the project (removes docker containers, stop symfony server) reset: docker.down ## Stop docker and remove dependencies - rm -rf ${APP_DIR}/{node_modules} ${APP_DIR}/yarn.lock + rm -rf ${APP_DIR}/node_modules ${APP_DIR}/yarn.lock + rm -rf ${APP_DIR} rm -rf vendor composer.lock .PHONY: reset @@ -32,7 +32,9 @@ dependencies: vendor node_modules ## Setup the dependencies .php-version: .php-version.dist cp .php-version.dist .php-version - (cd ${APP_DIR} && ln -sf ../../.php-version) + +php.ini: php.ini.dist + cp php.ini.dist php.ini vendor: composer.lock ## Install the PHP dependencies using composer ifdef GITHUB_ACTIONS @@ -51,6 +53,7 @@ endif yarn.install: ${APP_DIR}/yarn.lock ${APP_DIR}/yarn.lock: + ln -sf ${APP_DIR}/node_modules node_modules cd ${APP_DIR} && ${YARN} install && ${YARN} build ${YARN} install ${YARN} encore prod @@ -59,23 +62,61 @@ node_modules: ${APP_DIR}/node_modules ## Install the Node dependencies using yar ${APP_DIR}/node_modules: yarn.install +### +### TEST APPLICATION +### ¯¯¯¯¯ + +application: .php-version php.ini ${APP_DIR} setup_application ${APP_DIR}/docker-compose.yaml + +${APP_DIR}: + (${COMPOSER} create-project --prefer-dist --no-scripts --no-progress --no-install sylius/sylius-standard="${SYLIUS_VERSION}" ${APP_DIR}) + +setup_application: + rm -f ${APP_DIR}/yarn.lock + (cd ${APP_DIR} && ${COMPOSER} config repositories.plugin '{"type": "path", "url": "../../"}') + (cd ${APP_DIR} && ${COMPOSER} config extra.symfony.allow-contrib true) + (cd ${APP_DIR} && ${COMPOSER} require --no-scripts --no-progress --no-install --no-update monsieurbiz/${PLUGIN_NAME}="*@dev") + $(MAKE) apply_dist + $(MAKE) ${APP_DIR}/.php-version + $(MAKE) ${APP_DIR}/php.ini + (cd ${APP_DIR} && ${COMPOSER} install) + +${APP_DIR}/docker-compose.yaml: + rm -f ${APP_DIR}/docker-compose.yml + rm -f ${APP_DIR}/docker-compose.yaml + cp docker-compose.yaml.dist ${APP_DIR}/docker-compose.yaml +.PHONY: ${APP_DIR}/docker-compose.yaml + +${APP_DIR}/.php-version: .php-version + (cd ${APP_DIR} && ln -sf ../../.php-version) + +${APP_DIR}/php.ini: php.ini + (cd ${APP_DIR} && ln -sf ../../php.ini) + +apply_dist: + cp -Rv dist/* ${APP_DIR} + cp -Rv dist/.env.local ${APP_DIR}/.env.local + ### ### TESTS ### ¯¯¯¯¯ -test.all: test.composer test.phpstan test.phpunit test.phpspec test.phpcs test.yaml test.schema test.twig ## Run all tests in once +test.all: test.composer test.phpstan test.phpmd test.phpunit test.phpspec test.phpcs test.yaml test.schema test.twig test.container ## Run all tests in once test.composer: ## Validate composer.json ${COMPOSER} validate --strict test.phpstan: ## Run PHPStan - ${PHPSTAN} analyse -c phpstan.neon src/ + ${COMPOSER} phpstan || true + +test.phpmd: ## Run PHPMD + ${COMPOSER} phpmd || true test.phpunit: ## Run PHPUnit - ${PHPUNIT} + ${COMPOSER} phpunit test.phpspec: ## Run PHPSpec - ${PHPSPEC} run + ${COMPOSER} phpspec test.phpcs: ## Run PHP CS Fixer in dry-run ${COMPOSER} run -- phpcs --dry-run -v @@ -87,7 +128,7 @@ test.container: ## Lint the symfony container ${CONSOLE} lint:container test.yaml: ## Lint the symfony Yaml files - ${CONSOLE} lint:yaml ../../recipes ../../src/Resources/config --parse-tags + ${CONSOLE} lint:yaml ../../recipes ../../src/Resources/config test.schema: ## Validate MySQL Schema ${CONSOLE} doctrine:schema:validate @@ -115,12 +156,6 @@ sylius.assets: ## Install all assets with symlinks ${CONSOLE} sylius:install:assets ${CONSOLE} sylius:theme:assets:install --symlink -### -### JANE -### ¯¯¯¯¯¯ -jane.generate: ## Generate Models classes with Jane - ${COMPOSER} run -- jane-generate - ### ### PLATFORM ### ¯¯¯¯¯¯¯¯ @@ -130,11 +165,6 @@ platform: .php-version up ## Setup the platform tools docker.pull: ## Pull the docker images cd ${APP_DIR} && ${COMPOSE} pull -.PHONY: docker.pull - -docker.build: ## Build (and pull) the docker images - cd ${APP_DIR} && ${COMPOSE} build --pull -.PHONY: docker.build docker.up: ## Start the docker containers cd ${APP_DIR} && ${COMPOSE} up -d @@ -148,13 +178,19 @@ docker.down: ## Stop and remove the docker containers cd ${APP_DIR} && ${COMPOSE} down .PHONY: docker.down +docker.logs: ## Logs the docker containers + cd ${APP_DIR} && ${COMPOSE} logs -f +.PHONY: docker.logs + server.start: ## Run the local webserver using Symfony - ${SYMFONY} local:proxy:domain:attach ${COMPOSE_PROJECT_NAME} ${SYMFONY} local:server:start -d server.stop: ## Stop the local webserver ${SYMFONY} local:server:stop +es.reindex: ## Reindex elasticsearch + ${CONSOLE} monsieurbiz:search:populate + ### ### HELP ### ¯¯¯¯ diff --git a/TESTING.md b/TESTING.md index 269aa464..dd1a5fcf 100644 --- a/TESTING.md +++ b/TESTING.md @@ -1,78 +1,58 @@ # Testing -Be sure you've run the `composer install` before reading this file. -With Symfony binary, you should run `symfony composer install` +## Requirements -## Installation +You'll need: -1. To be able to run yarn build correctly, create symlink for `node_modules` : +- PHP 7.4 minimum. +- docker, for the database. +- symfony CLI, to run the local server. +- composer, to install PHP dependencies. +- npm and yarn, to install ui dependencies and build the JS/CSS files. - ```bash - ln -s tests/Application/node_modules node_modules - ``` +## Installation -2. From the plugin root directory, run the following commands: +```bash +make install +``` - ```bash - $ (cd tests/Application && yarn install) - $ (cd tests/Application && yarn build) - $ (cd tests/Application && bin/console assets:install public -e test) - $ (cd tests/Application && bin/console doctrine:database:drop --force -e test --if-exists) - $ (cd tests/Application && bin/console doctrine:database:create -e test) - $ (cd tests/Application && bin/console doctrine:schema:create -e test) - ``` - -To be able to setup the plugin's database, remember to configure your database credentials in `tests/Application/.env` -and `tests/Application/.env.test`. You can also add custom configuration in `tests/Application/.env.test.local`. +This will run a Sylius app (the one in `tests/Application/`) with the plugin +installed and all Sylius' sample data. It uses the symfony binary. ## Usage -### Running plugin tests +### List all available commands - - PHPUnit +```bash +make help +``` - ```bash - $ vendor/bin/phpunit - ``` +### Running minimum plugin tests - - PHPSpec +- PHPUnit ```bash - $ vendor/bin/phpspec run + make test.phpunit ``` - - - PHPStan - - ```bash - $ vendor/bin/phpstan analyse src - ``` - -### Opening Sylius with the plugin - -- Using `test` environment: - ```bash - $ (cd tests/Application && bin/console sylius:fixtures:load -e test) - $ (cd tests/Application && bin/console server:run -d public -e test) - ``` - -- Using `dev` environment: +- PHP CS fixer ```bash - $ (cd tests/Application && bin/console sylius:fixtures:load -e 1.0-dev) - $ (cd tests/Application && bin/console server:run -d public -e 1.0-dev) + make test.phpcs ``` -### Reindex Elasticsearch + > Tip: You can fix your code with `make test.phpcs.fix`! -- Using `test` environment: +- PHPSpec ```bash - $ (cd tests/Application && bin/console monsieurbiz:search:populate -e test) + make test.phpspec ``` - -- Using `dev` environment: + +- PHPStan ```bash - $ (cd tests/Application && bin/console monsieurbiz:search:populate -e 1.0-dev) + make test.phpstan ``` + +> Tip: You can run all tests with `make test.all`! diff --git a/docker-compose.yaml.dist b/docker-compose.yaml.dist new file mode 100644 index 00000000..3135e791 --- /dev/null +++ b/docker-compose.yaml.dist @@ -0,0 +1,20 @@ +version: '3.8' +services: + database: + image: mysql:8.0 + ports: + - 3306 + environment: + MYSQL_ALLOW_EMPTY_PASSWORD: 1 + MYSQL_DATABASE: sylius + volumes: + - database:/var/lib/mysql + + mailer: + image: monsieurbiz/mailcatcher + ports: + - 1025 + - 1080 + +volumes: + database: {} diff --git a/php.ini.dist b/php.ini.dist new file mode 100644 index 00000000..b0fe7fef --- /dev/null +++ b/php.ini.dist @@ -0,0 +1 @@ +memory_limit=-1 diff --git a/phpmd.xml b/phpmd.xml index 39c72fac..d0c34236 100644 --- a/phpmd.xml +++ b/phpmd.xml @@ -44,7 +44,7 @@ - + diff --git a/phpspec.yml.dist b/phpspec.yml.dist index dab4879d..2632afde 100644 --- a/phpspec.yml.dist +++ b/phpspec.yml.dist @@ -2,3 +2,5 @@ suites: main: namespace: MonsieurBiz\SyliusSearchPlugin psr4_prefix: MonsieurBiz\SyliusSearchPlugin + src_path: src + spec_path: tests diff --git a/phpstan.neon b/phpstan.neon index 86babbad..e83fc353 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -11,5 +11,4 @@ parameters: - 'src/DependencyInjection/MonsieurBizSyliusSearchExtension.php' # Test dependencies - - 'tests/Application/app/**.php' - - 'tests/Application/src/**.php' + - 'tests/Application/**/*' diff --git a/phpunit.xml.dist b/phpunit.xml.dist index ab87647a..9700364d 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,20 +1,55 @@ - tests + tests/Unit - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/Unit/.gitkeep b/tests/Unit/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 00000000..33a53433 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +use Symfony\Component\Dotenv\Dotenv; + +require dirname(__DIR__) . '/vendor/autoload.php'; + +if (file_exists(dirname(__DIR__) . '/config/bootstrap.php')) { + require dirname(__DIR__) . '/config/bootstrap.php'; +} elseif (method_exists(Dotenv::class, 'bootEnv')) { + (new Dotenv())->bootEnv(dirname(__DIR__) . '/.env'); +} From 824210d7f2093e4fda037031931c60bd64e013d7 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 11:13:38 +0100 Subject: [PATCH 116/142] Make PHP Stan ignore generated files --- phpstan.neon | 3 +++ 1 file changed, 3 insertions(+) diff --git a/phpstan.neon b/phpstan.neon index e83fc353..708fba1b 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -12,3 +12,6 @@ parameters: # Test dependencies - 'tests/Application/**/*' + + # Generated files + - 'generated/**/*' From 9e929df932cf1fb62bd695ac4a40abb319e22772 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Wed, 3 Nov 2021 19:09:29 +0100 Subject: [PATCH 117/142] Override basic docker infrastructure for search --- dist/docker-compose.override.yaml | 48 ++++++++++++++++++++++++++++ dist/docker/elasticsearch/Dockerfile | 5 +++ 2 files changed, 53 insertions(+) create mode 100644 dist/docker-compose.override.yaml create mode 100644 dist/docker/elasticsearch/Dockerfile diff --git a/dist/docker-compose.override.yaml b/dist/docker-compose.override.yaml new file mode 100644 index 00000000..c52250be --- /dev/null +++ b/dist/docker-compose.override.yaml @@ -0,0 +1,48 @@ +version: '3.8' +services: + elasticsearch: + build: + context: ./docker/elasticsearch/ + args: + USER_UID: ${USER_UID} + volumes: + - esdata:/usr/share/elasticsearch/data:rw + environment: + - node.name=elasticsearch + - cluster.initial_master_nodes=elasticsearch + - cluster.name=docker-cluster + - bootstrap.memory_lock=true + - "ES_JAVA_OPTS=-Xms512m -Xmx512m" + - "xpack.security.enabled=false" + ulimits: + memlock: + soft: -1 + hard: -1 + ports: + - "9200:9200" + - "9300:9300" + + cerebro: + image: lmenezes/cerebro + ports: + - "9000:9000" + links: + - elasticsearch + + kibana: + image: kibana:7.4.0 + ports: + - "5601:5601" + environment: + - "SERVER_NAME=localhost" + - "ELASTICSEARCH_HOSTS=http://elasticsearch:9200" + - "XPACK_GRAPH_ENABLED=false" + - "XPACK_ML_ENABLED=false" + - "XPACK_REPORTING_ENABLED=false" + - "XPACK_SECURITY_ENABLED=false" + - "XPACK_WATCHER_ENABLED=false" + links: + - elasticsearch + +volumes: + esdata: {} diff --git a/dist/docker/elasticsearch/Dockerfile b/dist/docker/elasticsearch/Dockerfile new file mode 100644 index 00000000..8ff7d402 --- /dev/null +++ b/dist/docker/elasticsearch/Dockerfile @@ -0,0 +1,5 @@ +FROM docker.elastic.co/elasticsearch/elasticsearch:7.4.0 + +# Install ES plugins +RUN bin/elasticsearch-plugin install analysis-phonetic +RUN bin/elasticsearch-plugin install analysis-icu From de00a303a05744c4f134120fb4ea689779fc5d6c Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 11:16:48 +0100 Subject: [PATCH 118/142] Correct recipes --- recipes/1.0/manifest.json | 2 +- recipes/2.0/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/recipes/1.0/manifest.json b/recipes/1.0/manifest.json index 5bd94dec..acb378dd 100644 --- a/recipes/1.0/manifest.json +++ b/recipes/1.0/manifest.json @@ -1,6 +1,6 @@ { "bundles": { - "MonsieurBiz\\SyliusSearchPlugin\\MonsieurBizSyliusSearchPlugin::class": [ + "MonsieurBiz\\SyliusSearchPlugin\\MonsieurBizSyliusSearchPlugin": [ "all" ] }, diff --git a/recipes/2.0/manifest.json b/recipes/2.0/manifest.json index d987d23e..769b861f 100644 --- a/recipes/2.0/manifest.json +++ b/recipes/2.0/manifest.json @@ -1,6 +1,6 @@ { "bundles": { - "MonsieurBiz\\SyliusSearchPlugin\\MonsieurBizSyliusSearchPlugin::class": [ + "MonsieurBiz\\SyliusSearchPlugin\\MonsieurBizSyliusSearchPlugin": [ "all" ] }, From 580e2dfa0d247d8cae826ef2afa48f81509b4e3a Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 11:17:44 +0100 Subject: [PATCH 119/142] Correct github actions location --- .github/{.github => }/workflows/recipe.yaml | 0 .github/{.github => }/workflows/security.yaml | 0 .github/{.github => }/workflows/tests.yaml | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename .github/{.github => }/workflows/recipe.yaml (100%) rename .github/{.github => }/workflows/security.yaml (100%) rename .github/{.github => }/workflows/tests.yaml (100%) diff --git a/.github/.github/workflows/recipe.yaml b/.github/workflows/recipe.yaml similarity index 100% rename from .github/.github/workflows/recipe.yaml rename to .github/workflows/recipe.yaml diff --git a/.github/.github/workflows/security.yaml b/.github/workflows/security.yaml similarity index 100% rename from .github/.github/workflows/security.yaml rename to .github/workflows/security.yaml diff --git a/.github/.github/workflows/tests.yaml b/.github/workflows/tests.yaml similarity index 100% rename from .github/.github/workflows/tests.yaml rename to .github/workflows/tests.yaml From f635f21c9b46b99a19718890b21b722e753f1ba5 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 11:27:09 +0100 Subject: [PATCH 120/142] Add dist files --- dist/.env.local | 3 ++ .../monsieurbiz_sylius_search_plugin.yaml | 2 + .../monsieurbiz_sylius_search_plugin.yaml | 2 + dist/docker/elasticsearch/Dockerfile | 2 +- dist/src/Entity/Product/ProductAttribute.php | 35 ++++++++++++++++ dist/src/Entity/Product/ProductOption.php | 28 +++++++++++++ dist/src/Migrations/Version20211012092353.php | 42 +++++++++++++++++++ .../ProductAttribute/_form.html.twig | 30 +++++++++++++ .../ProductOption/_form.html.twig | 20 +++++++++ 9 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 dist/.env.local create mode 100644 dist/config/packages/monsieurbiz_sylius_search_plugin.yaml create mode 100644 dist/config/routes/monsieurbiz_sylius_search_plugin.yaml create mode 100644 dist/src/Entity/Product/ProductAttribute.php create mode 100644 dist/src/Entity/Product/ProductOption.php create mode 100644 dist/src/Migrations/Version20211012092353.php create mode 100644 dist/templates/bundles/SyliusAdminBundle/ProductAttribute/_form.html.twig create mode 100644 dist/templates/bundles/SyliusAdminBundle/ProductOption/_form.html.twig diff --git a/dist/.env.local b/dist/.env.local new file mode 100644 index 00000000..d0f2d4df --- /dev/null +++ b/dist/.env.local @@ -0,0 +1,3 @@ +MONSIEURBIZ_SEARCHPLUGIN_MESSENGER_TRANSPORT_DSN=doctrine://default +MONSIEURBIZ_SEARCHPLUGIN_ES_HOST=${ELASTICSEARCH_HOST:-localhost} +MONSIEURBIZ_SEARCHPLUGIN_ES_PORT=${ELASTICSEARCH_PORT:-9200} diff --git a/dist/config/packages/monsieurbiz_sylius_search_plugin.yaml b/dist/config/packages/monsieurbiz_sylius_search_plugin.yaml new file mode 100644 index 00000000..d39a7f98 --- /dev/null +++ b/dist/config/packages/monsieurbiz_sylius_search_plugin.yaml @@ -0,0 +1,2 @@ +imports: + - { resource: "@MonsieurBizSyliusSearchPlugin/Resources/config/config.yaml" } diff --git a/dist/config/routes/monsieurbiz_sylius_search_plugin.yaml b/dist/config/routes/monsieurbiz_sylius_search_plugin.yaml new file mode 100644 index 00000000..6ad0857e --- /dev/null +++ b/dist/config/routes/monsieurbiz_sylius_search_plugin.yaml @@ -0,0 +1,2 @@ +monsieurbiz_search_plugin: + resource: "@MonsieurBizSyliusSearchPlugin/Resources/config/routing.yaml" diff --git a/dist/docker/elasticsearch/Dockerfile b/dist/docker/elasticsearch/Dockerfile index 8ff7d402..c9843113 100644 --- a/dist/docker/elasticsearch/Dockerfile +++ b/dist/docker/elasticsearch/Dockerfile @@ -1,4 +1,4 @@ -FROM docker.elastic.co/elasticsearch/elasticsearch:7.4.0 +FROM docker.elastic.co/elasticsearch/elasticsearch:7.16.3 # Install ES plugins RUN bin/elasticsearch-plugin install analysis-phonetic diff --git a/dist/src/Entity/Product/ProductAttribute.php b/dist/src/Entity/Product/ProductAttribute.php new file mode 100644 index 00000000..c01070cb --- /dev/null +++ b/dist/src/Entity/Product/ProductAttribute.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace App\Entity\Product; + +use Doctrine\ORM\Mapping as ORM; +use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; +use MonsieurBiz\SyliusSearchPlugin\Model\Product\SearchableTrait; +use Sylius\Component\Attribute\Model\AttributeTranslationInterface; +use Sylius\Component\Product\Model\ProductAttribute as BaseProductAttribute; +use Sylius\Component\Product\Model\ProductAttributeTranslation; + +/** + * @ORM\Entity + * @ORM\Table(name="sylius_product_attribute") + */ +class ProductAttribute extends BaseProductAttribute implements SearchableInterface +{ + use SearchableTrait; + + protected function createTranslation(): AttributeTranslationInterface + { + return new ProductAttributeTranslation(); + } +} diff --git a/dist/src/Entity/Product/ProductOption.php b/dist/src/Entity/Product/ProductOption.php new file mode 100644 index 00000000..1c586a91 --- /dev/null +++ b/dist/src/Entity/Product/ProductOption.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace App\Entity\Product; + +use Doctrine\ORM\Mapping as ORM; +use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; +use MonsieurBiz\SyliusSearchPlugin\Model\Product\SearchableTrait; +use Sylius\Component\Product\Model\ProductOption as BaseProductOption; + +/** + * @ORM\Entity + * @ORM\Table(name="sylius_product_option") + */ +class ProductOption extends BaseProductOption implements SearchableInterface +{ + use SearchableTrait; +} diff --git a/dist/src/Migrations/Version20211012092353.php b/dist/src/Migrations/Version20211012092353.php new file mode 100644 index 00000000..efbfcfa4 --- /dev/null +++ b/dist/src/Migrations/Version20211012092353.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace App\Migrations; + +use Doctrine\DBAL\Schema\Schema; +use Doctrine\Migrations\AbstractMigration; + +/** + * Auto-generated Migration: Please modify to your needs! + */ +final class Version20211012092353 extends AbstractMigration +{ + public function getDescription(): string + { + return 'Add search columns on product attribute and product option'; + } + + public function up(Schema $schema): void + { + // this up() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE sylius_product_attribute ADD searchable TINYINT(1) DEFAULT \'1\' NOT NULL, ADD filterable TINYINT(1) DEFAULT \'0\' NOT NULL, ADD search_weight SMALLINT UNSIGNED DEFAULT 1 NOT NULL'); + $this->addSql('ALTER TABLE sylius_product_option ADD searchable TINYINT(1) DEFAULT \'1\' NOT NULL, ADD filterable TINYINT(1) DEFAULT \'0\' NOT NULL, ADD search_weight SMALLINT UNSIGNED DEFAULT 1 NOT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE sylius_product_attribute DROP searchable, DROP filterable, DROP search_weight'); + $this->addSql('ALTER TABLE sylius_product_option DROP searchable, DROP filterable, DROP search_weight'); + } +} diff --git a/dist/templates/bundles/SyliusAdminBundle/ProductAttribute/_form.html.twig b/dist/templates/bundles/SyliusAdminBundle/ProductAttribute/_form.html.twig new file mode 100644 index 00000000..3130f269 --- /dev/null +++ b/dist/templates/bundles/SyliusAdminBundle/ProductAttribute/_form.html.twig @@ -0,0 +1,30 @@ +{% from '@SyliusAdmin/Macro/translationForm.html.twig' import translationForm %} + +{{ form_errors(form) }} + +
+
+ {{ form_row(form.code) }} + {{ form_row(form.position) }} + {{ form_row(form.type) }} +
+ {{ form_row(form.translatable) }} +
+
+

{{ 'monsieurbiz_searchplugin.admin.product_attribute.form.title'|trans }}

+
+ {{ form_row(form.searchable) }} + {{ form_row(form.filterable) }} + {{ form_row(form.search_weight) }} +
+
+{% if form.configuration is defined %} +
+

{{ 'sylius.ui.configuration'|trans }}

+ {% for field in form.configuration %} + {{ form_row(field) }} + {% endfor %} +
+{% endif %} +{{ translationForm(form.translations) }} + diff --git a/dist/templates/bundles/SyliusAdminBundle/ProductOption/_form.html.twig b/dist/templates/bundles/SyliusAdminBundle/ProductOption/_form.html.twig new file mode 100644 index 00000000..661788ae --- /dev/null +++ b/dist/templates/bundles/SyliusAdminBundle/ProductOption/_form.html.twig @@ -0,0 +1,20 @@ +{% from '@SyliusAdmin/Macro/translationForm.html.twig' import translationForm %} + +
+ {{ form_errors(form) }} +
+ {{ form_row(form.code) }} + {{ form_row(form.position) }} +
+ {{ translationForm(form.translations) }} +
+
+

{{ 'monsieurbiz_searchplugin.admin.product_option.form.title'|trans }}

+
+ {{ form_row(form.searchable) }} + {{ form_row(form.filterable) }} + {{ form_row(form.search_weight) }} +
+
+

{{ 'sylius.ui.values'|trans }}

+{{ form_row(form.values) }} From e2cfe614864ac681bf89c1b287f764903cdf2728 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 11:42:50 +0100 Subject: [PATCH 121/142] Remove some files in src/generated --- src/generated/Model/Document.php | 358 ------------------ .../Normalizer/DocumentNormalizer.php | 240 ------------ 2 files changed, 598 deletions(-) delete mode 100644 src/generated/Model/Document.php delete mode 100644 src/generated/Normalizer/DocumentNormalizer.php diff --git a/src/generated/Model/Document.php b/src/generated/Model/Document.php deleted file mode 100644 index f5c0856f..00000000 --- a/src/generated/Model/Document.php +++ /dev/null @@ -1,358 +0,0 @@ -type; - } - /** - * - * - * @param string|null $type - * - * @return self - */ - public function setType(?string $type) : self - { - $this->type = $type; - return $this; - } - /** - * - * - * @return string|null - */ - public function getCode() : ?string - { - return $this->code; - } - /** - * - * - * @param string|null $code - * - * @return self - */ - public function setCode(?string $code) : self - { - $this->code = $code; - return $this; - } - /** - * - * - * @return int|null - */ - public function getId() : ?int - { - return $this->id; - } - /** - * - * - * @param int|null $id - * - * @return self - */ - public function setId(?int $id) : self - { - $this->id = $id; - return $this; - } - /** - * - * - * @return bool|null - */ - public function getEnabled() : ?bool - { - return $this->enabled; - } - /** - * - * - * @param bool|null $enabled - * - * @return self - */ - public function setEnabled(?bool $enabled) : self - { - $this->enabled = $enabled; - return $this; - } - /** - * - * - * @return bool|null - */ - public function getInStock() : ?bool - { - return $this->inStock; - } - /** - * - * - * @param bool|null $inStock - * - * @return self - */ - public function setInStock(?bool $inStock) : self - { - $this->inStock = $inStock; - return $this; - } - /** - * - * - * @return string|null - */ - public function getSlug() : ?string - { - return $this->slug; - } - /** - * - * - * @param string|null $slug - * - * @return self - */ - public function setSlug(?string $slug) : self - { - $this->slug = $slug; - return $this; - } - /** - * - * - * @return string|null - */ - public function getImage() : ?string - { - return $this->image; - } - /** - * - * - * @param string|null $image - * - * @return self - */ - public function setImage(?string $image) : self - { - $this->image = $image; - return $this; - } - /** - * - * - * @return string[]|null - */ - public function getChannel() : ?array - { - return $this->channel; - } - /** - * - * - * @param string[]|null $channel - * - * @return self - */ - public function setChannel(?array $channel) : self - { - $this->channel = $channel; - return $this; - } - /** - * - * - * @return Taxon|null - */ - public function getMainTaxon() : ?Taxon - { - return $this->mainTaxon; - } - /** - * - * - * @param Taxon|null $mainTaxon - * - * @return self - */ - public function setMainTaxon(?Taxon $mainTaxon) : self - { - $this->mainTaxon = $mainTaxon; - return $this; - } - /** - * - * - * @return Taxon[]|null - */ - public function getTaxon() : ?array - { - return $this->taxon; - } - /** - * - * - * @param Taxon[]|null $taxon - * - * @return self - */ - public function setTaxon(?array $taxon) : self - { - $this->taxon = $taxon; - return $this; - } - /** - * - * - * @return Attributes[]|null - */ - public function getAttributes() : ?array - { - return $this->attributes; - } - /** - * - * - * @param Attributes[]|null $attributes - * - * @return self - */ - public function setAttributes(?array $attributes) : self - { - $this->attributes = $attributes; - return $this; - } - /** - * - * - * @return Price[]|null - */ - public function getPrice() : ?array - { - return $this->price; - } - /** - * - * - * @param Price[]|null $price - * - * @return self - */ - public function setPrice(?array $price) : self - { - $this->price = $price; - return $this; - } - /** - * - * - * @return Price[]|null - */ - public function getOriginalPrice() : ?array - { - return $this->originalPrice; - } - /** - * - * - * @param Price[]|null $originalPrice - * - * @return self - */ - public function setOriginalPrice(?array $originalPrice) : self - { - $this->originalPrice = $originalPrice; - return $this; - } -} diff --git a/src/generated/Normalizer/DocumentNormalizer.php b/src/generated/Normalizer/DocumentNormalizer.php deleted file mode 100644 index 937e0a09..00000000 --- a/src/generated/Normalizer/DocumentNormalizer.php +++ /dev/null @@ -1,240 +0,0 @@ -{'$ref'})) { - return new Reference($data->{'$ref'}, $context['document-origin']); - } - if (isset($data->{'$recursiveRef'})) { - return new Reference($data->{'$recursiveRef'}, $context['document-origin']); - } - $object = new \MonsieurBiz\SyliusSearchPlugin\generated\Model\Document(); - if (property_exists($data, 'type') && $data->{'type'} !== null) { - $object->setType($data->{'type'}); - } - elseif (property_exists($data, 'type') && $data->{'type'} === null) { - $object->setType(null); - } - if (property_exists($data, 'code') && $data->{'code'} !== null) { - $object->setCode($data->{'code'}); - } - elseif (property_exists($data, 'code') && $data->{'code'} === null) { - $object->setCode(null); - } - if (property_exists($data, 'id') && $data->{'id'} !== null) { - $object->setId($data->{'id'}); - } - elseif (property_exists($data, 'id') && $data->{'id'} === null) { - $object->setId(null); - } - if (property_exists($data, 'enabled') && $data->{'enabled'} !== null) { - $object->setEnabled($data->{'enabled'}); - } - elseif (property_exists($data, 'enabled') && $data->{'enabled'} === null) { - $object->setEnabled(null); - } - if (property_exists($data, 'inStock') && $data->{'inStock'} !== null) { - $object->setInStock($data->{'inStock'}); - } - elseif (property_exists($data, 'inStock') && $data->{'inStock'} === null) { - $object->setInStock(null); - } - if (property_exists($data, 'slug') && $data->{'slug'} !== null) { - $object->setSlug($data->{'slug'}); - } - elseif (property_exists($data, 'slug') && $data->{'slug'} === null) { - $object->setSlug(null); - } - if (property_exists($data, 'image') && $data->{'image'} !== null) { - $object->setImage($data->{'image'}); - } - elseif (property_exists($data, 'image') && $data->{'image'} === null) { - $object->setImage(null); - } - if (property_exists($data, 'channel') && $data->{'channel'} !== null) { - $values = array(); - foreach ($data->{'channel'} as $value) { - $values[] = $value; - } - $object->setChannel($values); - } - elseif (property_exists($data, 'channel') && $data->{'channel'} === null) { - $object->setChannel(null); - } - if (property_exists($data, 'main_taxon') && $data->{'main_taxon'} !== null) { - $object->setMainTaxon($this->denormalizer->denormalize($data->{'main_taxon'}, 'MonsieurBiz\\SyliusSearchPlugin\\generated\\Model\\Taxon', 'json', $context)); - } - elseif (property_exists($data, 'main_taxon') && $data->{'main_taxon'} === null) { - $object->setMainTaxon(null); - } - if (property_exists($data, 'taxon') && $data->{'taxon'} !== null) { - $values_1 = array(); - foreach ($data->{'taxon'} as $value_1) { - $values_1[] = $this->denormalizer->denormalize($value_1, 'MonsieurBiz\\SyliusSearchPlugin\\generated\\Model\\Taxon', 'json', $context); - } - $object->setTaxon($values_1); - } - elseif (property_exists($data, 'taxon') && $data->{'taxon'} === null) { - $object->setTaxon(null); - } - if (property_exists($data, 'attributes') && $data->{'attributes'} !== null) { - $values_2 = array(); - foreach ($data->{'attributes'} as $value_2) { - $values_2[] = $this->denormalizer->denormalize($value_2, 'MonsieurBiz\\SyliusSearchPlugin\\generated\\Model\\Attributes', 'json', $context); - } - $object->setAttributes($values_2); - } - elseif (property_exists($data, 'attributes') && $data->{'attributes'} === null) { - $object->setAttributes(null); - } - if (property_exists($data, 'price') && $data->{'price'} !== null) { - $values_3 = array(); - foreach ($data->{'price'} as $value_3) { - $values_3[] = $this->denormalizer->denormalize($value_3, 'MonsieurBiz\\SyliusSearchPlugin\\generated\\Model\\Price', 'json', $context); - } - $object->setPrice($values_3); - } - elseif (property_exists($data, 'price') && $data->{'price'} === null) { - $object->setPrice(null); - } - if (property_exists($data, 'original_price') && $data->{'original_price'} !== null) { - $values_4 = array(); - foreach ($data->{'original_price'} as $value_4) { - $values_4[] = $this->denormalizer->denormalize($value_4, 'MonsieurBiz\\SyliusSearchPlugin\\generated\\Model\\Price', 'json', $context); - } - $object->setOriginalPrice($values_4); - } - elseif (property_exists($data, 'original_price') && $data->{'original_price'} === null) { - $object->setOriginalPrice(null); - } - return $object; - } - public function normalize($object, $format = null, array $context = array()) - { - $data = new \stdClass(); - if (null !== $object->getType()) { - $data->{'type'} = $object->getType(); - } - else { - $data->{'type'} = null; - } - if (null !== $object->getCode()) { - $data->{'code'} = $object->getCode(); - } - else { - $data->{'code'} = null; - } - if (null !== $object->getId()) { - $data->{'id'} = $object->getId(); - } - else { - $data->{'id'} = null; - } - if (null !== $object->getEnabled()) { - $data->{'enabled'} = $object->getEnabled(); - } - else { - $data->{'enabled'} = null; - } - if (null !== $object->getInStock()) { - $data->{'inStock'} = $object->getInStock(); - } - else { - $data->{'inStock'} = null; - } - if (null !== $object->getSlug()) { - $data->{'slug'} = $object->getSlug(); - } - else { - $data->{'slug'} = null; - } - if (null !== $object->getImage()) { - $data->{'image'} = $object->getImage(); - } - else { - $data->{'image'} = null; - } - if (null !== $object->getChannel()) { - $values = array(); - foreach ($object->getChannel() as $value) { - $values[] = $value; - } - $data->{'channel'} = $values; - } - else { - $data->{'channel'} = null; - } - if (null !== $object->getMainTaxon()) { - $data->{'main_taxon'} = $this->normalizer->normalize($object->getMainTaxon(), 'json', $context); - } - else { - $data->{'main_taxon'} = null; - } - if (null !== $object->getTaxon()) { - $values_1 = array(); - foreach ($object->getTaxon() as $value_1) { - $values_1[] = $this->normalizer->normalize($value_1, 'json', $context); - } - $data->{'taxon'} = $values_1; - } - else { - $data->{'taxon'} = null; - } - if (null !== $object->getAttributes()) { - $values_2 = array(); - foreach ($object->getAttributes() as $value_2) { - $values_2[] = $this->normalizer->normalize($value_2, 'json', $context); - } - $data->{'attributes'} = $values_2; - } - else { - $data->{'attributes'} = null; - } - if (null !== $object->getPrice()) { - $values_3 = array(); - foreach ($object->getPrice() as $value_3) { - $values_3[] = $this->normalizer->normalize($value_3, 'json', $context); - } - $data->{'price'} = $values_3; - } - else { - $data->{'price'} = null; - } - if (null !== $object->getOriginalPrice()) { - $values_4 = array(); - foreach ($object->getOriginalPrice() as $value_4) { - $values_4[] = $this->normalizer->normalize($value_4, 'json', $context); - } - $data->{'original_price'} = $values_4; - } - else { - $data->{'original_price'} = null; - } - return $data; - } -} From 4c6aa4c8b6d7e8bcb19ccc07d67883e57b186ec2 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 12:21:22 +0100 Subject: [PATCH 122/142] Ignore symfony.lock --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2d7b6c5c..e184ceaa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /vendor/ /composer.lock +/symfony.lock /etc/build/* !/etc/build/.gitignore From 94e334287b485a670c3b59669e56bf908b086625 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 12:21:34 +0100 Subject: [PATCH 123/142] Update composer.json to fit with v2 --- composer.json | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 1bce1bb8..f32d1e41 100644 --- a/composer.json +++ b/composer.json @@ -6,8 +6,13 @@ "license": "MIT", "require": { "php": "~7.4|~8.0", + "babdev/pagerfanta-bundle": "^2.5", + "jacquesbh/eater": "^2.0", + "jane-php/automapper-bundle": "^7.1", + "jolicode/elastically": "^1.4.0", + "monsieurbiz/sylius-settings-plugin": "^1.0", "sylius/sylius": ">=1.8 <1.11", - "jolicode/elastically": "^1.0.0" + "symfony/messenger": "^4.4 || ^5.2" }, "require-dev": { "behat/behat": "^3.6.1", @@ -50,7 +55,8 @@ "prefer-stable": true, "autoload": { "psr-4": { - "MonsieurBiz\\SyliusSearchPlugin\\": "src/" + "MonsieurBiz\\SyliusSearchPlugin\\": "src/", + "MonsieurBiz\\SyliusSearchPlugin\\Generated\\": "generated/" } }, "scripts": { @@ -59,6 +65,7 @@ "assets:install %PUBLIC_DIR%": "symfony-cmd" }, "phpcs": "php-cs-fixer fix --using-cache=false", + "jane-generate": "jane generate --config-file=src/Resources/config/jane/jane-configuration.php", "phpstan": "phpstan analyse -c phpstan.neon src/", "phpmd": "phpmd --exclude Migrations/* src/ ansi phpmd.xml", "phpunit": "phpunit", From ac7e86194d3fcb36ea7b6b818259ef5cc0ea4639 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 12:21:49 +0100 Subject: [PATCH 124/142] Update docker-compose manage login --- docker-compose.yaml.dist | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yaml.dist b/docker-compose.yaml.dist index 3135e791..955683cc 100644 --- a/docker-compose.yaml.dist +++ b/docker-compose.yaml.dist @@ -2,6 +2,7 @@ version: '3.8' services: database: image: mysql:8.0 + command: --default-authentication-plugin=mysql_native_password ports: - 3306 environment: From 9d45f97a5291881b88cac410b9710182f58dc1db Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 12:22:02 +0100 Subject: [PATCH 125/142] Update Makefile --- Makefile | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/Makefile b/Makefile index f74327d9..5496274e 100644 --- a/Makefile +++ b/Makefile @@ -21,13 +21,14 @@ up: docker.up server.start ## Up the project (start docker, start symfony server stop: server.stop docker.stop ## Stop the project (stop docker, stop symfony server) down: server.stop docker.down ## Down the project (removes docker containers, stop symfony server) -reset: docker.down ## Stop docker and remove dependencies +reset: ## Stop docker and remove dependencies + ${MAKE} docker.down || true rm -rf ${APP_DIR}/node_modules ${APP_DIR}/yarn.lock rm -rf ${APP_DIR} rm -rf vendor composer.lock .PHONY: reset -dependencies: vendor node_modules ## Setup the dependencies +dependencies: composer.lock node_modules ## Setup the dependencies .PHONY: dependencies .php-version: .php-version.dist @@ -36,19 +37,8 @@ dependencies: vendor node_modules ## Setup the dependencies php.ini: php.ini.dist cp php.ini.dist php.ini -vendor: composer.lock ## Install the PHP dependencies using composer -ifdef GITHUB_ACTIONS - ${COMPOSER} install --prefer-dist -else - ${COMPOSER} install --prefer-source -endif - composer.lock: composer.json -ifdef GITHUB_ACTIONS - ${COMPOSER} update --prefer-dist -else - ${COMPOSER} update --prefer-source -endif + ${COMPOSER} install --no-scripts --no-plugins yarn.install: ${APP_DIR}/yarn.lock @@ -75,10 +65,9 @@ setup_application: rm -f ${APP_DIR}/yarn.lock (cd ${APP_DIR} && ${COMPOSER} config repositories.plugin '{"type": "path", "url": "../../"}') (cd ${APP_DIR} && ${COMPOSER} config extra.symfony.allow-contrib true) + (cd ${APP_DIR} && ${COMPOSER} config minimum-stability dev) (cd ${APP_DIR} && ${COMPOSER} require --no-scripts --no-progress --no-install --no-update monsieurbiz/${PLUGIN_NAME}="*@dev") - $(MAKE) apply_dist - $(MAKE) ${APP_DIR}/.php-version - $(MAKE) ${APP_DIR}/php.ini + $(MAKE) apply_dist ${APP_DIR}/.php-version ${APP_DIR}/php.ini (cd ${APP_DIR} && ${COMPOSER} install) ${APP_DIR}/docker-compose.yaml: @@ -94,8 +83,15 @@ ${APP_DIR}/php.ini: php.ini (cd ${APP_DIR} && ln -sf ../../php.ini) apply_dist: - cp -Rv dist/* ${APP_DIR} - cp -Rv dist/.env.local ${APP_DIR}/.env.local + ROOT_DIR=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))); \ + for i in `cd dist && find . -type f`; do \ + FILE_PATH=`echo $$i | sed 's|./||'`; \ + FOLDER_PATH=`dirname $$FILE_PATH`; \ + echo $$FILE_PATH; \ + (cd ${APP_DIR} && rm -f $$FILE_PATH); \ + (cd ${APP_DIR} && mkdir -p $$FOLDER_PATH); \ + (cd ${APP_DIR} && ln -s $$ROOT_DIR/dist/$$FILE_PATH $$FILE_PATH); \ + done ### ### TESTS @@ -107,10 +103,10 @@ test.composer: ## Validate composer.json ${COMPOSER} validate --strict test.phpstan: ## Run PHPStan - ${COMPOSER} phpstan || true + ${COMPOSER} phpstan test.phpmd: ## Run PHPMD - ${COMPOSER} phpmd || true + ${COMPOSER} phpmd test.phpunit: ## Run PHPUnit ${COMPOSER} phpunit From 8010eb47698eaece49456e8b6ea90fad50c80939 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 12:22:11 +0100 Subject: [PATCH 126/142] Correct dist files --- dist/docker-compose.override.yaml | 18 +----------------- dist/src/Entity/Product/ProductAttribute.php | 1 - dist/src/Entity/Product/ProductOption.php | 6 ++++++ 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/dist/docker-compose.override.yaml b/dist/docker-compose.override.yaml index c52250be..9925a26b 100644 --- a/dist/docker-compose.override.yaml +++ b/dist/docker-compose.override.yaml @@ -19,8 +19,7 @@ services: soft: -1 hard: -1 ports: - - "9200:9200" - - "9300:9300" + - 92 cerebro: image: lmenezes/cerebro @@ -29,20 +28,5 @@ services: links: - elasticsearch - kibana: - image: kibana:7.4.0 - ports: - - "5601:5601" - environment: - - "SERVER_NAME=localhost" - - "ELASTICSEARCH_HOSTS=http://elasticsearch:9200" - - "XPACK_GRAPH_ENABLED=false" - - "XPACK_ML_ENABLED=false" - - "XPACK_REPORTING_ENABLED=false" - - "XPACK_SECURITY_ENABLED=false" - - "XPACK_WATCHER_ENABLED=false" - links: - - elasticsearch - volumes: esdata: {} diff --git a/dist/src/Entity/Product/ProductAttribute.php b/dist/src/Entity/Product/ProductAttribute.php index c01070cb..cd9d8879 100644 --- a/dist/src/Entity/Product/ProductAttribute.php +++ b/dist/src/Entity/Product/ProductAttribute.php @@ -18,7 +18,6 @@ use MonsieurBiz\SyliusSearchPlugin\Model\Product\SearchableTrait; use Sylius\Component\Attribute\Model\AttributeTranslationInterface; use Sylius\Component\Product\Model\ProductAttribute as BaseProductAttribute; -use Sylius\Component\Product\Model\ProductAttributeTranslation; /** * @ORM\Entity diff --git a/dist/src/Entity/Product/ProductOption.php b/dist/src/Entity/Product/ProductOption.php index 1c586a91..b7cd397c 100644 --- a/dist/src/Entity/Product/ProductOption.php +++ b/dist/src/Entity/Product/ProductOption.php @@ -17,6 +17,7 @@ use MonsieurBiz\SyliusSearchPlugin\Entity\Product\SearchableInterface; use MonsieurBiz\SyliusSearchPlugin\Model\Product\SearchableTrait; use Sylius\Component\Product\Model\ProductOption as BaseProductOption; +use Sylius\Component\Product\Model\ProductOptionTranslationInterface; /** * @ORM\Entity @@ -25,4 +26,9 @@ class ProductOption extends BaseProductOption implements SearchableInterface { use SearchableTrait; + + protected function createTranslation(): ProductOptionTranslationInterface + { + return new ProductOptionTranslation(); + } } From 171ad4cedb64b4d42445eda5430a45bb43ba50de Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 12:22:28 +0100 Subject: [PATCH 127/142] Remove Logger definition in Automapper --- src/AutoMapper/ProductAttributeValueConfiguration.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/AutoMapper/ProductAttributeValueConfiguration.php b/src/AutoMapper/ProductAttributeValueConfiguration.php index be987b95..2ea92f4d 100644 --- a/src/AutoMapper/ProductAttributeValueConfiguration.php +++ b/src/AutoMapper/ProductAttributeValueConfiguration.php @@ -34,11 +34,6 @@ final class ProductAttributeValueConfiguration implements MapperConfigurationInt */ private array $productAttributeValueReaders; - /** - * @var LoggerInterface - */ - protected $logger; - public function __construct(Configuration $configuration, iterable $productAttributeValueReaders) { $this->logger = new NullLogger(); From adc6cfecf554318a55fc41c67a81aa36d2af0d44 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 12:22:41 +0100 Subject: [PATCH 128/142] Update generated JS file --- src/Resources/public/js/monsieurbiz-search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Resources/public/js/monsieurbiz-search.js b/src/Resources/public/js/monsieurbiz-search.js index bbd5fdb6..f79fd97b 100644 --- a/src/Resources/public/js/monsieurbiz-search.js +++ b/src/Resources/public/js/monsieurbiz-search.js @@ -1 +1 @@ -!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/public/",n(n.s="ng4s")}({ng4s:function(e,t,n){(function(e){e.MonsieurBizInstantSearch=function(){"use strict";return function e(t,n,r,o,u,i){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e);var c=null;document.querySelector(n).addEventListener("keyup",(function(e){clearTimeout(c);var n=e.currentTarget.value,a=e.currentTarget.closest(r).querySelector(o);c=setTimeout((function(){if(n.length>=i){var e=new XMLHttpRequest;e.onload=function(){200===this.status&&(a.innerHTML=this.responseText,a.style.display="block")},e.open("POST",t),e.setRequestHeader("X-Requested-With","XMLHttpRequest"),e.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),e.send(new URLSearchParams({query:n}).toString())}}),u)}));var a=document.querySelector(n).closest(r);a.addEventListener("focusout",(function(e){null!==e.relatedTarget&&a.contains(e.relatedTarget)||(a.querySelector(o).style.display="none")})),document.querySelector(n).addEventListener("focus",(function(e){""!==e.currentTarget.value&&(a.querySelector(o).style.display="block")}))}}(),document.addEventListener("DOMContentLoaded",(function(){new MonsieurBizInstantSearch(monsieurbizSearchPlugin.instantUrl,monsieurbizSearchPlugin.searchInputSelector,monsieurbizSearchPlugin.resultClosestSelector,monsieurbizSearchPlugin.resultFindSelector,monsieurbizSearchPlugin.keyUpTimeOut,monsieurbizSearchPlugin.minQueryLength)}))}).call(this,n("yLpj"))},yLpj:function(e,t){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(e){"object"==typeof window&&(n=window)}e.exports=n}}); \ No newline at end of file +!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/public/",n(n.s="ng4s")}({ng4s:function(e,t,n){(function(e){function t(e,t){for(var n=0;n=i){var e=new XMLHttpRequest;e.onload=function(){200===this.status&&(a.innerHTML=this.responseText,a.style.display="block")},e.open("POST",t),e.setRequestHeader("X-Requested-With","XMLHttpRequest"),e.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),e.send(new URLSearchParams({query:n}).toString())}}),u)}));var a=document.querySelector(n).closest(r);a.addEventListener("focusout",(function(e){null!==e.relatedTarget&&a.contains(e.relatedTarget)||(a.querySelector(o).style.display="none")})),document.querySelector(n).addEventListener("focus",(function(e){""!==e.currentTarget.value&&(a.querySelector(o).style.display="block")}))},n&&t(e.prototype,n),r&&t(e,r),Object.defineProperty(e,"prototype",{writable:!1}),e;var e,n,r}(),document.addEventListener("DOMContentLoaded",(function(){new MonsieurBizInstantSearch(monsieurbizSearchPlugin.instantUrl,monsieurbizSearchPlugin.searchInputSelector,monsieurbizSearchPlugin.resultClosestSelector,monsieurbizSearchPlugin.resultFindSelector,monsieurbizSearchPlugin.keyUpTimeOut,monsieurbizSearchPlugin.minQueryLength)}))}).call(this,n("yLpj"))},yLpj:function(e,t){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(e){"object"==typeof window&&(n=window)}e.exports=n}}); \ No newline at end of file From e25f88217fd958a6263aae1131618f1f493bbed4 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 12:24:21 +0100 Subject: [PATCH 129/142] Update PHPCS fixer rules --- .php-cs-fixer.dist.php | 156 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 146 insertions(+), 10 deletions(-) diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index b0267ce0..a0b260d9 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + declare(strict_types=1); $header = <<<'HEADER' @@ -7,7 +16,7 @@ (c) Monsieur Biz -For the full copyright and license information, please view the LICENSE +For the full copyright and license information, please view the LICENSE.txt file that was distributed with this source code. HEADER; @@ -15,10 +24,7 @@ ->in(__DIR__) ->exclude( [ - 'tests/Application/var', - 'tests/Application/bin', - 'tests/Application/config', - 'generated', + 'tests/Application', ] ) ; @@ -40,11 +46,32 @@ 'array_syntax' => [ 'syntax' => 'short', ], + 'binary_operator_spaces' => true, + 'blank_line_after_opening_tag' => true, + 'blank_line_after_namespace' => true, + 'blank_line_before_statement' => true, + 'braces' => [ + 'allow_single_line_closure' => true, + ], + 'cast_spaces' => true, + 'class_attributes_separation' => true, + 'class_definition' => [ + 'single_item_single_line' => true, + 'multi_line_extends_each_single_line' => true, + ], + 'combine_consecutive_issets' => true, + 'combine_consecutive_unsets' => true, 'comment_to_phpdoc' => true, 'compact_nullable_typehint' => true, 'concat_space' => [ 'spacing' => 'one', ], + 'constant_case' => [ + 'case' => 'lower', + ], + 'declare_equal_normalize' => true, + 'dir_constant' => true, + 'declare_strict_types' => true, 'doctrine_annotation_array_assignment' => [ 'operator' => '=', ], @@ -52,40 +79,105 @@ 'after_array_assignments_equals' => false, 'before_array_assignments_equals' => false, ], + 'elseif' => true, + 'encoding' => true, + 'ereg_to_preg' => true, + 'error_suppression' => true, 'explicit_indirect_variable' => true, + 'full_opening_tag' => true, 'fully_qualified_strict_types' => true, - 'function_declaration' => [ - 'closure_function_spacing' => 'none', - ], + 'function_declaration' => true, + 'function_to_constant' => true, + 'function_typehint_space' => true, + 'general_phpdoc_tag_rename' => true, 'header_comment' => [ 'header' => $header, 'location' => 'after_open', ], + 'include' => true, + 'increment_style' => [ + 'style' => 'pre', + ], + 'indentation_type' => true, + 'is_null' => true, + 'line_ending' => true, + 'list_syntax' => [ + 'syntax' => 'short', + ], 'logical_operators' => true, + 'lowercase_cast' => true, + 'lowercase_keywords' => true, + 'lowercase_static_reference' => true, + 'magic_constant_casing' => true, + 'method_argument_space' => true, + 'modernize_types_casting' => true, 'multiline_comment_opening_closing' => true, 'multiline_whitespace_before_semicolons' => [ 'strategy' => 'new_line_for_chained_calls', ], + 'native_constant_invocation' => true, + 'native_function_casing' => true, + 'new_with_braces' => true, + 'no_alias_functions' => true, 'no_alternative_syntax' => true, + 'no_blank_lines_after_class_opening' => true, + 'no_blank_lines_after_phpdoc' => true, + 'no_break_comment' => true, + 'no_closing_tag' => true, + 'no_empty_comment' => true, + 'no_empty_phpdoc' => true, + 'no_empty_statement' => true, 'no_extra_blank_lines' => [ 'tokens' => [ 'break', + 'case', 'continue', 'curly_brace_block', + 'default', 'extra', 'parenthesis_brace_block', 'return', 'square_brace_block', + 'switch', 'throw', 'use', ], ], + 'no_homoglyph_names' => true, + 'no_leading_import_slash' => true, + 'no_leading_namespace_whitespace' => true, + 'no_mixed_echo_print' => [ + 'use' => 'echo', + ], + 'no_multiline_whitespace_around_double_arrow' => true, + 'no_null_property_initialization' => true, + 'no_php4_constructor' => true, + 'no_singleline_whitespace_before_semicolons' => true, + 'no_short_bool_cast' => true, + 'no_spaces_after_function_name' => true, + 'no_spaces_around_offset' => true, + 'no_spaces_inside_parenthesis' => true, 'no_superfluous_elseif' => true, - 'no_superfluous_phpdoc_tags' => false, + 'no_superfluous_phpdoc_tags' => [ + 'allow_mixed' => true, + ], 'no_unset_cast' => true, + 'no_unneeded_control_parentheses' => true, + 'no_unneeded_curly_braces' => true, + 'no_unneeded_final_method' => true, 'no_unset_on_property' => true, + 'no_unused_imports' => true, 'no_useless_else' => true, 'no_useless_return' => true, + 'no_trailing_comma_in_list_call' => true, + 'no_trailing_comma_in_singleline_array' => true, + 'no_trailing_whitespace' => true, + 'no_trailing_whitespace_in_comment' => true, + 'no_whitespace_before_comma_in_array' => true, + 'no_whitespace_in_blank_line' => true, + 'non_printable_character' => true, + 'normalize_index_brace' => true, + 'object_operator_without_whitespace' => true, 'ordered_imports' => [ 'imports_order' => [ 'class', @@ -94,6 +186,8 @@ ], 'sort_algorithm' => 'alpha', ], + 'php_unit_dedicate_assert' => true, + 'php_unit_fqcn_annotation' => true, 'php_unit_method_casing' => [ 'case' => 'camel_case', ], @@ -107,21 +201,63 @@ 'phpdoc_add_missing_param_annotation' => [ 'only_untyped' => true, ], + 'phpdoc_indent' => true, + 'phpdoc_inline_tag_normalizer' => true, + 'phpdoc_no_access' => true, + 'phpdoc_no_alias_tag' => true, + 'phpdoc_no_empty_return' => true, + 'phpdoc_no_package' => true, + 'phpdoc_no_useless_inheritdoc' => true, 'phpdoc_order' => true, + 'phpdoc_return_self_reference' => true, + 'phpdoc_scalar' => true, + 'phpdoc_separation' => true, 'phpdoc_single_line_var_spacing' => true, + 'phpdoc_tag_type' => true, 'phpdoc_to_comment' => false, + 'phpdoc_trim' => true, 'phpdoc_trim_consecutive_blank_line_separation' => true, + 'phpdoc_types' => true, + 'phpdoc_types_order' => [ + 'null_adjustment' => 'always_last', + 'sort_algorithm' => 'none', + ], 'phpdoc_var_annotation_correct_order' => true, + 'phpdoc_var_without_name' => true, + 'pow_to_exponentiation' => true, + 'protected_to_private' => true, 'return_assignment' => true, + 'return_type_declaration' => true, + 'self_accessor' => true, + 'short_scalar_cast' => true, + 'single_blank_line_at_eof' => true, + 'single_blank_line_before_namespace' => true, + 'single_class_element_per_statement' => true, + 'single_import_per_statement' => true, + 'single_line_after_imports' => true, + 'single_line_comment_style' => true, + 'single_quote' => true, + 'space_after_semicolon' => true, + 'standardize_not_equals' => true, 'strict_param' => true, + 'switch_case_semicolon_to_colon' => true, + 'switch_case_space' => true, + 'ternary_operator_spaces' => true, + 'ternary_to_null_coalescing' => true, + 'trailing_comma_in_multiline' => [ + 'elements' => ['arrays'], + ], + 'trim_array_spaces' => true, + 'unary_operator_spaces' => true, 'visibility_required' => [ 'elements' => [ 'const', - 'method', 'property', + 'method', ], ], 'void_return' => true, + 'whitespace_after_comma_in_array' => true, // alerady in symfony set ]) ->setFinder($finder) ; From 356f66d3d3c6050df4ed6d1781142a852be03dea Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 12:24:36 +0100 Subject: [PATCH 130/142] Fix PHP CS --- dist/src/Entity/Product/ProductAttribute.php | 2 +- dist/src/Entity/Product/ProductOption.php | 2 +- dist/src/Migrations/Version20211012092353.php | 2 +- generated/Model/ChannelDTO.php | 34 +++--- generated/Model/ImageDTO.php | 36 +++--- generated/Model/PricingDTO.php | 104 +++++++----------- generated/Model/ProductAttributeDTO.php | 76 ++++++------- generated/Model/ProductTaxonDTO.php | 58 ++++------ generated/Model/TaxonDTO.php | 100 ++++++----------- generated/Normalizer/ChannelDTONormalizer.php | 33 ++++-- generated/Normalizer/ImageDTONormalizer.php | 46 +++++--- generated/Normalizer/JaneObjectNormalizer.php | 42 +++++-- generated/Normalizer/PricingDTONormalizer.php | 63 +++++++---- .../ProductAttributeDTONormalizer.php | 44 +++++--- .../Normalizer/ProductTaxonDTONormalizer.php | 46 +++++--- generated/Normalizer/TaxonDTONormalizer.php | 33 ++++-- generated/Runtime/Normalizer/CheckArray.php | 19 +++- .../Normalizer/ReferenceNormalizer.php | 20 +++- src/AutoMapper/Configuration.php | 3 +- .../ProductAttributeValueConfiguration.php | 6 +- .../CheckboxReader.php | 2 +- .../DateReader.php | 2 +- .../DateTimeReader.php | 2 +- .../DefaultReader.php | 2 +- .../IntegerReader.php | 2 +- .../PercentReader.php | 2 +- .../ReaderInterface.php | 2 +- .../SelectReader.php | 2 +- .../TextReader.php | 2 +- .../TextareaReader.php | 2 +- src/AutoMapper/ProductMapperConfiguration.php | 41 ++++--- src/AutoMapper/VariantMapperConfiguration.php | 5 +- src/Command/PopulateCommand.php | 3 +- src/Command/SearchCommand.php | 7 +- src/Controller/SearchController.php | 9 +- .../AutomapperConfigurationRegistryPass.php | 2 +- .../AutowireMappingProviderParameterPass.php | 3 +- src/DependencyInjection/Configuration.php | 4 +- .../DocumentableRegistryPass.php | 2 +- .../MonsieurBizSyliusSearchExtension.php | 4 +- .../RegisterSearchRequestPass.php | 2 +- src/Entity/Product/SearchableInterface.php | 2 +- src/Event/MappingProviderEvent.php | 3 +- ...ppendProductAttributeMappingSubscriber.php | 4 +- .../ReindexProductEventSubscriber.php | 8 +- .../ObjectNotInstanceOfClassException.php | 2 +- src/Exception/UnknownRequestTypeException.php | 2 +- .../ProductAttributeTypeExtension.php | 2 +- .../Extension/ProductOptionTypeExtension.php | 2 +- src/Form/Type/SearchType.php | 8 +- src/Form/Type/Settings/LimitsSearchType.php | 2 +- src/Form/Type/Settings/SettingsSearchType.php | 2 +- src/Helper/SlugHelper.php | 2 +- src/Index/Indexer.php | 13 ++- src/Mapping/YamlWithLocaleProvider.php | 7 +- src/Message/ProductReindexFromIds.php | 2 +- src/Message/ProductReindexFromTaxon.php | 2 +- src/Message/ProductToDeleteFromIds.php | 2 +- .../ProductReindexFromIdsHandler.php | 4 +- .../ProductReindexFromTaxonHandler.php | 4 +- .../ProductToDeleteFromIdsHandler.php | 4 +- src/Model/Datasource/DatasourceInterface.php | 2 +- src/Model/Datasource/RepositoryDatasource.php | 2 +- src/Model/Document/Index/Indexer.php | 27 +---- src/Model/Documentable/Documentable.php | 8 +- .../DocumentableDatasourceTrait.php | 2 +- .../Documentable/DocumentableInterface.php | 2 +- .../DocumentableMappingProviderTrait.php | 2 +- .../Documentable/DocumentableProductTrait.php | 46 +------- src/Model/Product/ProductDTO.php | 2 +- src/Model/Product/SearchableTrait.php | 2 +- src/Model/Product/VariantDTO.php | 14 +-- src/MonsieurBizSyliusSearchPlugin.php | 2 +- .../Product/ProductDTONormalizer.php | 3 +- src/Repository/ProductAttributeRepository.php | 2 +- .../ProductAttributeRepositoryInterface.php | 2 +- src/Repository/ProductOptionRepository.php | 2 +- .../ProductOptionRepositoryInterface.php | 2 +- .../CheapestProductVariantResolver.php | 2 +- .../config/jane/jane-configuration.php | 2 +- src/Search/ClientFactory.php | 3 +- src/Search/Filter/Filter.php | 20 +--- src/Search/Filter/FilterValue.php | 17 +-- src/Search/Filter/RangeFilter.php | 11 +- .../AggregationBuilderInterface.php | 2 +- .../Aggregation/MainTaxonAggregation.php | 4 +- .../Request/Aggregation/PriceAggregation.php | 4 +- .../ProductAttributeAggregation.php | 4 +- .../ProductAttributesAggregation.php | 4 +- .../Aggregation/ProductOptionAggregation.php | 4 +- .../Aggregation/ProductOptionsAggregation.php | 4 +- .../Request/Aggregation/TaxonsAggregation.php | 4 +- src/Search/Request/AggregationBuilder.php | 2 +- .../FunctionScore/FunctionScoreInterface.php | 2 +- .../FunctionScoreRegistryInterface.php | 2 +- .../Product/InStockWeightFunction.php | 4 +- .../ProductFunctionScoreRegistry.php | 2 +- .../PostFilter/PostFilterInterface.php | 2 +- .../PostFilterRegistryInterface.php | 2 +- .../Product/AttributesPostFilter.php | 2 +- .../Product/MainTaxonPostFilter.php | 2 +- .../PostFilter/Product/OptionsPostFilter.php | 2 +- .../PostFilter/Product/PricePostFilter.php | 2 +- .../Product/ProductTaxonPostFilter.php | 2 +- .../PostFilter/ProductTaxonRegistry.php | 2 +- .../Request/ProductRequest/InstantSearch.php | 5 +- src/Search/Request/ProductRequest/Search.php | 10 +- src/Search/Request/ProductRequest/Taxon.php | 11 +- .../QueryFilter/Product/ChannelFilter.php | 2 +- .../QueryFilter/Product/EnabledFilter.php | 2 +- .../QueryFilter/Product/IsInStockFilter.php | 2 +- .../QueryFilter/Product/SearchTermFilter.php | 4 +- .../QueryFilter/Product/TaxonFilter.php | 2 +- .../QueryFilter/ProductSearchRegistry.php | 2 +- .../QueryFilter/ProductTaxonRegistry.php | 2 +- .../QueryFilter/QueryFilterInterface.php | 2 +- .../QueryFilterRegistryInterface.php | 2 +- src/Search/Request/RequestConfiguration.php | 9 +- src/Search/Request/RequestHandler.php | 2 +- src/Search/Request/RequestInterface.php | 4 +- .../Sorting/Product/CreatedAtSorter.php | 2 +- .../Request/Sorting/Product/NameSorter.php | 2 +- .../Sorting/Product/PositionSorter.php | 2 +- .../Request/Sorting/Product/PriceSorter.php | 2 +- .../Request/Sorting/ProductSorterRegistry.php | 2 +- .../Request/Sorting/SorterBuilderTrait.php | 2 +- .../Request/Sorting/SorterInterface.php | 2 +- .../Sorting/SorterRegistryInterface.php | 2 +- src/Search/Response.php | 9 +- .../FilterBuilders/FilterBuilderInterface.php | 2 +- .../Product/AttributeFilterBuilder.php | 4 +- .../Product/MainTaxonFilterBuilder.php | 3 +- .../Product/OptionFilterBuilder.php | 4 +- .../Product/PriceFilterBuilder.php | 2 +- .../Product/TaxonsFilterBuilder.php | 2 +- src/Search/Response/FilterInterface.php | 2 +- src/Search/ResponseFactory.php | 2 +- src/Search/ResponseInterface.php | 2 +- src/Search/Search.php | 5 +- src/Search/SearchInterface.php | 2 +- src/Twig/Extension/RenderSearchForm.php | 2 +- 141 files changed, 681 insertions(+), 620 deletions(-) diff --git a/dist/src/Entity/Product/ProductAttribute.php b/dist/src/Entity/Product/ProductAttribute.php index cd9d8879..eaff27e7 100644 --- a/dist/src/Entity/Product/ProductAttribute.php +++ b/dist/src/Entity/Product/ProductAttribute.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/dist/src/Entity/Product/ProductOption.php b/dist/src/Entity/Product/ProductOption.php index b7cd397c..2adc02e8 100644 --- a/dist/src/Entity/Product/ProductOption.php +++ b/dist/src/Entity/Product/ProductOption.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/dist/src/Migrations/Version20211012092353.php b/dist/src/Migrations/Version20211012092353.php index efbfcfa4..60e21270 100644 --- a/dist/src/Migrations/Version20211012092353.php +++ b/dist/src/Migrations/Version20211012092353.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/generated/Model/ChannelDTO.php b/generated/Model/ChannelDTO.php index 9fe20849..dfa0250c 100644 --- a/generated/Model/ChannelDTO.php +++ b/generated/Model/ChannelDTO.php @@ -1,34 +1,34 @@ + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Generated\Model; class ChannelDTO { /** - * - * * @var string */ protected $code; - /** - * - * - * @return string - */ - public function getCode() : string + + public function getCode(): string { return $this->code; } - /** - * - * - * @param string $code - * - * @return self - */ - public function setCode(string $code) : self + + public function setCode(string $code): self { $this->code = $code; + return $this; } -} \ No newline at end of file +} diff --git a/generated/Model/ImageDTO.php b/generated/Model/ImageDTO.php index 59146cf4..e19a2d5c 100644 --- a/generated/Model/ImageDTO.php +++ b/generated/Model/ImageDTO.php @@ -1,34 +1,34 @@ + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Generated\Model; class ImageDTO { /** - * - * - * @var null|string + * @var string|null */ protected $path; - /** - * - * - * @return null|string - */ - public function getPath() : ?string + + public function getPath(): ?string { return $this->path; } - /** - * - * - * @param null|string $path - * - * @return self - */ - public function setPath(?string $path) : self + + public function setPath(?string $path): self { $this->path = $path; + return $this; } -} \ No newline at end of file +} diff --git a/generated/Model/PricingDTO.php b/generated/Model/PricingDTO.php index 63d9c9b1..a79f0d5f 100644 --- a/generated/Model/PricingDTO.php +++ b/generated/Model/PricingDTO.php @@ -1,115 +1,85 @@ + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Generated\Model; class PricingDTO { /** - * - * * @var string */ protected $channelCode; + /** - * - * - * @var null|int + * @var int|null */ protected $price; + /** - * - * - * @var null|int + * @var int|null */ protected $originalPrice; + /** - * - * * @var bool */ protected $priceReduced; - /** - * - * - * @return string - */ - public function getChannelCode() : string + + public function getChannelCode(): string { return $this->channelCode; } - /** - * - * - * @param string $channelCode - * - * @return self - */ - public function setChannelCode(string $channelCode) : self + + public function setChannelCode(string $channelCode): self { $this->channelCode = $channelCode; + return $this; } - /** - * - * - * @return null|int - */ - public function getPrice() : ?int + + public function getPrice(): ?int { return $this->price; } - /** - * - * - * @param null|int $price - * - * @return self - */ - public function setPrice(?int $price) : self + + public function setPrice(?int $price): self { $this->price = $price; + return $this; } - /** - * - * - * @return null|int - */ - public function getOriginalPrice() : ?int + + public function getOriginalPrice(): ?int { return $this->originalPrice; } - /** - * - * - * @param null|int $originalPrice - * - * @return self - */ - public function setOriginalPrice(?int $originalPrice) : self + + public function setOriginalPrice(?int $originalPrice): self { $this->originalPrice = $originalPrice; + return $this; } - /** - * - * - * @return bool - */ - public function getPriceReduced() : bool + + public function getPriceReduced(): bool { return $this->priceReduced; } - /** - * - * - * @param bool $priceReduced - * - * @return self - */ - public function setPriceReduced(bool $priceReduced) : self + + public function setPriceReduced(bool $priceReduced): self { $this->priceReduced = $priceReduced; + return $this; } -} \ No newline at end of file +} diff --git a/generated/Model/ProductAttributeDTO.php b/generated/Model/ProductAttributeDTO.php index d2b9643e..c4225abf 100644 --- a/generated/Model/ProductAttributeDTO.php +++ b/generated/Model/ProductAttributeDTO.php @@ -1,88 +1,74 @@ + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Generated\Model; class ProductAttributeDTO { /** - * - * * @var string */ protected $code; + /** - * - * * @var string */ protected $name; + /** - * - * - * @var null|mixed + * @var mixed|null */ protected $value; - /** - * - * - * @return string - */ - public function getCode() : string + + public function getCode(): string { return $this->code; } - /** - * - * - * @param string $code - * - * @return self - */ - public function setCode(string $code) : self + + public function setCode(string $code): self { $this->code = $code; + return $this; } - /** - * - * - * @return string - */ - public function getName() : string + + public function getName(): string { return $this->name; } - /** - * - * - * @param string $name - * - * @return self - */ - public function setName(string $name) : self + + public function setName(string $name): self { $this->name = $name; + return $this; } + /** - * - * - * @return null|mixed + * @return mixed|null */ public function getValue() { return $this->value; } + /** - * - * - * @param null|mixed $value - * - * @return self + * @param mixed|null $value */ - public function setValue($value) : self + public function setValue($value): self { $this->value = $value; + return $this; } -} \ No newline at end of file +} diff --git a/generated/Model/ProductTaxonDTO.php b/generated/Model/ProductTaxonDTO.php index bcadb042..915a3dd6 100644 --- a/generated/Model/ProductTaxonDTO.php +++ b/generated/Model/ProductTaxonDTO.php @@ -1,61 +1,51 @@ + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Generated\Model; class ProductTaxonDTO { /** - * - * * @var TaxonDTO */ protected $taxon; + /** - * - * - * @var null|int + * @var int|null */ protected $position; - /** - * - * - * @return TaxonDTO - */ - public function getTaxon() : TaxonDTO + + public function getTaxon(): TaxonDTO { return $this->taxon; } - /** - * - * - * @param TaxonDTO $taxon - * - * @return self - */ - public function setTaxon(TaxonDTO $taxon) : self + + public function setTaxon(TaxonDTO $taxon): self { $this->taxon = $taxon; + return $this; } - /** - * - * - * @return null|int - */ - public function getPosition() : ?int + + public function getPosition(): ?int { return $this->position; } - /** - * - * - * @param null|int $position - * - * @return self - */ - public function setPosition(?int $position) : self + + public function setPosition(?int $position): self { $this->position = $position; + return $this; } -} \ No newline at end of file +} diff --git a/generated/Model/TaxonDTO.php b/generated/Model/TaxonDTO.php index 87f86f39..ed788d8a 100644 --- a/generated/Model/TaxonDTO.php +++ b/generated/Model/TaxonDTO.php @@ -1,115 +1,85 @@ + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Generated\Model; class TaxonDTO { /** - * - * * @var string */ protected $name; + /** - * - * * @var string */ protected $code; + /** - * - * * @var int */ protected $position; + /** - * - * * @var int */ protected $level; - /** - * - * - * @return string - */ - public function getName() : string + + public function getName(): string { return $this->name; } - /** - * - * - * @param string $name - * - * @return self - */ - public function setName(string $name) : self + + public function setName(string $name): self { $this->name = $name; + return $this; } - /** - * - * - * @return string - */ - public function getCode() : string + + public function getCode(): string { return $this->code; } - /** - * - * - * @param string $code - * - * @return self - */ - public function setCode(string $code) : self + + public function setCode(string $code): self { $this->code = $code; + return $this; } - /** - * - * - * @return int - */ - public function getPosition() : int + + public function getPosition(): int { return $this->position; } - /** - * - * - * @param int $position - * - * @return self - */ - public function setPosition(int $position) : self + + public function setPosition(int $position): self { $this->position = $position; + return $this; } - /** - * - * - * @return int - */ - public function getLevel() : int + + public function getLevel(): int { return $this->level; } - /** - * - * - * @param int $level - * - * @return self - */ - public function setLevel(int $level) : self + + public function setLevel(int $level): self { $this->level = $level; + return $this; } -} \ No newline at end of file +} diff --git a/generated/Normalizer/ChannelDTONormalizer.php b/generated/Normalizer/ChannelDTONormalizer.php index 135a422b..c5b96b76 100644 --- a/generated/Normalizer/ChannelDTONormalizer.php +++ b/generated/Normalizer/ChannelDTONormalizer.php @@ -1,30 +1,46 @@ + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Generated\Normalizer; use Jane\Component\JsonSchemaRuntime\Reference; use MonsieurBiz\SyliusSearchPlugin\Generated\Runtime\Normalizer\CheckArray; -use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + class ChannelDTONormalizer implements DenormalizerInterface, NormalizerInterface, DenormalizerAwareInterface, NormalizerAwareInterface { + use CheckArray; + use DenormalizerAwareTrait; + use NormalizerAwareTrait; - use CheckArray; + public function supportsDenormalization($data, $type, $format = null) { - return $type === 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ChannelDTO'; + return 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ChannelDTO' === $type; } + public function supportsNormalization($data, $format = null) { return $data instanceof \MonsieurBiz\SyliusSearchPlugin\Generated\Model\ChannelDTO; } - public function denormalize($data, $class, $format = null, array $context = array()) + + public function denormalize($data, $class, $format = null, array $context = []) { if (isset($data['$ref'])) { return new Reference($data['$ref'], $context['document-origin']); @@ -39,14 +55,17 @@ public function denormalize($data, $class, $format = null, array $context = arra if (\array_key_exists('code', $data)) { $object->setCode($data['code']); } + return $object; } - public function normalize($object, $format = null, array $context = array()) + + public function normalize($object, $format = null, array $context = []) { - $data = array(); + $data = []; if (null !== $object->getCode()) { $data['code'] = $object->getCode(); } + return $data; } -} \ No newline at end of file +} diff --git a/generated/Normalizer/ImageDTONormalizer.php b/generated/Normalizer/ImageDTONormalizer.php index 3847cb45..6b2acde5 100644 --- a/generated/Normalizer/ImageDTONormalizer.php +++ b/generated/Normalizer/ImageDTONormalizer.php @@ -1,30 +1,46 @@ + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Generated\Normalizer; use Jane\Component\JsonSchemaRuntime\Reference; use MonsieurBiz\SyliusSearchPlugin\Generated\Runtime\Normalizer\CheckArray; -use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + class ImageDTONormalizer implements DenormalizerInterface, NormalizerInterface, DenormalizerAwareInterface, NormalizerAwareInterface { + use CheckArray; + use DenormalizerAwareTrait; + use NormalizerAwareTrait; - use CheckArray; + public function supportsDenormalization($data, $type, $format = null) { - return $type === 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ImageDTO'; + return 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ImageDTO' === $type; } + public function supportsNormalization($data, $format = null) { return $data instanceof \MonsieurBiz\SyliusSearchPlugin\Generated\Model\ImageDTO; } - public function denormalize($data, $class, $format = null, array $context = array()) + + public function denormalize($data, $class, $format = null, array $context = []) { if (isset($data['$ref'])) { return new Reference($data['$ref'], $context['document-origin']); @@ -36,32 +52,34 @@ public function denormalize($data, $class, $format = null, array $context = arra if (null === $data || false === \is_array($data)) { return $object; } - if (\array_key_exists('path', $data) && $data['path'] !== null) { + if (\array_key_exists('path', $data) && null !== $data['path']) { $value = $data['path']; - if (is_null($data['path'])) { + if (null === $data['path']) { $value = $data['path']; - } elseif (is_string($data['path'])) { + } elseif (\is_string($data['path'])) { $value = $data['path']; } $object->setPath($value); - } - elseif (\array_key_exists('path', $data) && $data['path'] === null) { + } elseif (\array_key_exists('path', $data) && null === $data['path']) { $object->setPath(null); } + return $object; } - public function normalize($object, $format = null, array $context = array()) + + public function normalize($object, $format = null, array $context = []) { - $data = array(); + $data = []; if (null !== $object->getPath()) { $value = $object->getPath(); - if (is_null($object->getPath())) { + if (null === $object->getPath()) { $value = $object->getPath(); - } elseif (is_string($object->getPath())) { + } elseif (\is_string($object->getPath())) { $value = $object->getPath(); } $data['path'] = $value; } + return $data; } -} \ No newline at end of file +} diff --git a/generated/Normalizer/JaneObjectNormalizer.php b/generated/Normalizer/JaneObjectNormalizer.php index c63e8f20..48f7047c 100644 --- a/generated/Normalizer/JaneObjectNormalizer.php +++ b/generated/Normalizer/JaneObjectNormalizer.php @@ -1,5 +1,16 @@ + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Generated\Normalizer; use MonsieurBiz\SyliusSearchPlugin\Generated\Runtime\Normalizer\CheckArray; @@ -9,42 +20,57 @@ use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + class JaneObjectNormalizer implements DenormalizerInterface, NormalizerInterface, DenormalizerAwareInterface, NormalizerAwareInterface { + use CheckArray; + use DenormalizerAwareTrait; + use NormalizerAwareTrait; - use CheckArray; - protected $normalizers = array('MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ImageDTO' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ImageDTONormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ChannelDTO' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ChannelDTONormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductTaxonDTO' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductTaxonDTONormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\TaxonDTO' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\TaxonDTONormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductAttributeDTO' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductAttributeDTONormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\PricingDTO' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\PricingDTONormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\MonsieurBiz\\SyliusSearchPlugin\\Generated\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + + protected $normalizers = ['MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ImageDTO' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ImageDTONormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ChannelDTO' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ChannelDTONormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductTaxonDTO' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductTaxonDTONormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\TaxonDTO' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\TaxonDTONormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductAttributeDTO' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\ProductAttributeDTONormalizer', 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\PricingDTO' => 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Normalizer\\PricingDTONormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\MonsieurBiz\\SyliusSearchPlugin\\Generated\\Runtime\\Normalizer\\ReferenceNormalizer']; + + protected $normalizersCache = []; + public function supportsDenormalization($data, $type, $format = null) { - return array_key_exists($type, $this->normalizers); + return \array_key_exists($type, $this->normalizers); } + public function supportsNormalization($data, $format = null) { - return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + return \is_object($data) && \array_key_exists(\get_class($data), $this->normalizers); } - public function normalize($object, $format = null, array $context = array()) + + public function normalize($object, $format = null, array $context = []) { - $normalizerClass = $this->normalizers[get_class($object)]; + $normalizerClass = $this->normalizers[\get_class($object)]; $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); } - public function denormalize($data, $class, $format = null, array $context = array()) + + public function denormalize($data, $class, $format = null, array $context = []) { $denormalizerClass = $this->normalizers[$class]; $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); } + private function getNormalizer(string $normalizerClass) { return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); } + private function initNormalizer(string $normalizerClass) { $normalizer = new $normalizerClass(); $normalizer->setNormalizer($this->normalizer); $normalizer->setDenormalizer($this->denormalizer); $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; } -} \ No newline at end of file +} diff --git a/generated/Normalizer/PricingDTONormalizer.php b/generated/Normalizer/PricingDTONormalizer.php index e0e1d23b..b556b93b 100644 --- a/generated/Normalizer/PricingDTONormalizer.php +++ b/generated/Normalizer/PricingDTONormalizer.php @@ -1,30 +1,46 @@ + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Generated\Normalizer; use Jane\Component\JsonSchemaRuntime\Reference; use MonsieurBiz\SyliusSearchPlugin\Generated\Runtime\Normalizer\CheckArray; -use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + class PricingDTONormalizer implements DenormalizerInterface, NormalizerInterface, DenormalizerAwareInterface, NormalizerAwareInterface { + use CheckArray; + use DenormalizerAwareTrait; + use NormalizerAwareTrait; - use CheckArray; + public function supportsDenormalization($data, $type, $format = null) { - return $type === 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\PricingDTO'; + return 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\PricingDTO' === $type; } + public function supportsNormalization($data, $format = null) { return $data instanceof \MonsieurBiz\SyliusSearchPlugin\Generated\Model\PricingDTO; } - public function denormalize($data, $class, $format = null, array $context = array()) + + public function denormalize($data, $class, $format = null, array $context = []) { if (isset($data['$ref'])) { return new Reference($data['$ref'], $context['document-origin']); @@ -39,70 +55,71 @@ public function denormalize($data, $class, $format = null, array $context = arra if (\array_key_exists('channel_code', $data)) { $object->setChannelCode($data['channel_code']); } - if (\array_key_exists('price', $data) && $data['price'] !== null) { + if (\array_key_exists('price', $data) && null !== $data['price']) { $value = $data['price']; - if (is_null($data['price'])) { + if (null === $data['price']) { $value = $data['price']; - } elseif (is_int($data['price'])) { + } elseif (\is_int($data['price'])) { $value = $data['price']; } $object->setPrice($value); - } - elseif (\array_key_exists('price', $data) && $data['price'] === null) { + } elseif (\array_key_exists('price', $data) && null === $data['price']) { $object->setPrice(null); } - if (\array_key_exists('original_price', $data) && $data['original_price'] !== null) { + if (\array_key_exists('original_price', $data) && null !== $data['original_price']) { $value_1 = $data['original_price']; - if (is_null($data['original_price'])) { + if (null === $data['original_price']) { $value_1 = $data['original_price']; - } elseif (is_int($data['original_price'])) { + } elseif (\is_int($data['original_price'])) { $value_1 = $data['original_price']; } $object->setOriginalPrice($value_1); - } - elseif (\array_key_exists('original_price', $data) && $data['original_price'] === null) { + } elseif (\array_key_exists('original_price', $data) && null === $data['original_price']) { $object->setOriginalPrice(null); } if (\array_key_exists('price_reduced', $data)) { $value_2 = $data['price_reduced']; - if (is_bool($data['price_reduced'])) { + if (\is_bool($data['price_reduced'])) { $value_2 = $data['price_reduced']; } $object->setPriceReduced($value_2); } + return $object; } - public function normalize($object, $format = null, array $context = array()) + + public function normalize($object, $format = null, array $context = []) { - $data = array(); + $data = []; if (null !== $object->getChannelCode()) { $data['channel_code'] = $object->getChannelCode(); } if (null !== $object->getPrice()) { $value = $object->getPrice(); - if (is_null($object->getPrice())) { + if (null === $object->getPrice()) { $value = $object->getPrice(); - } elseif (is_int($object->getPrice())) { + } elseif (\is_int($object->getPrice())) { $value = $object->getPrice(); } $data['price'] = $value; } if (null !== $object->getOriginalPrice()) { $value_1 = $object->getOriginalPrice(); - if (is_null($object->getOriginalPrice())) { + if (null === $object->getOriginalPrice()) { $value_1 = $object->getOriginalPrice(); - } elseif (is_int($object->getOriginalPrice())) { + } elseif (\is_int($object->getOriginalPrice())) { $value_1 = $object->getOriginalPrice(); } $data['original_price'] = $value_1; } if (null !== $object->getPriceReduced()) { $value_2 = $object->getPriceReduced(); - if (is_bool($object->getPriceReduced())) { + if (\is_bool($object->getPriceReduced())) { $value_2 = $object->getPriceReduced(); } $data['price_reduced'] = $value_2; } + return $data; } -} \ No newline at end of file +} diff --git a/generated/Normalizer/ProductAttributeDTONormalizer.php b/generated/Normalizer/ProductAttributeDTONormalizer.php index ccbc4e1e..7936b408 100644 --- a/generated/Normalizer/ProductAttributeDTONormalizer.php +++ b/generated/Normalizer/ProductAttributeDTONormalizer.php @@ -1,30 +1,46 @@ + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Generated\Normalizer; use Jane\Component\JsonSchemaRuntime\Reference; use MonsieurBiz\SyliusSearchPlugin\Generated\Runtime\Normalizer\CheckArray; -use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + class ProductAttributeDTONormalizer implements DenormalizerInterface, NormalizerInterface, DenormalizerAwareInterface, NormalizerAwareInterface { + use CheckArray; + use DenormalizerAwareTrait; + use NormalizerAwareTrait; - use CheckArray; + public function supportsDenormalization($data, $type, $format = null) { - return $type === 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductAttributeDTO'; + return 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductAttributeDTO' === $type; } + public function supportsNormalization($data, $format = null) { return $data instanceof \MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductAttributeDTO; } - public function denormalize($data, $class, $format = null, array $context = array()) + + public function denormalize($data, $class, $format = null, array $context = []) { if (isset($data['$ref'])) { return new Reference($data['$ref'], $context['document-origin']); @@ -42,23 +58,24 @@ public function denormalize($data, $class, $format = null, array $context = arra if (\array_key_exists('name', $data)) { $object->setName($data['name']); } - if (\array_key_exists('value', $data) && $data['value'] !== null) { + if (\array_key_exists('value', $data) && null !== $data['value']) { $value = $data['value']; - if (is_null($data['value'])) { + if (null === $data['value']) { $value = $data['value']; } elseif (isset($data['value'])) { $value = $data['value']; } $object->setValue($value); - } - elseif (\array_key_exists('value', $data) && $data['value'] === null) { + } elseif (\array_key_exists('value', $data) && null === $data['value']) { $object->setValue(null); } + return $object; } - public function normalize($object, $format = null, array $context = array()) + + public function normalize($object, $format = null, array $context = []) { - $data = array(); + $data = []; if (null !== $object->getCode()) { $data['code'] = $object->getCode(); } @@ -67,13 +84,14 @@ public function normalize($object, $format = null, array $context = array()) } if (null !== $object->getValue()) { $value = $object->getValue(); - if (is_null($object->getValue())) { + if (null === $object->getValue()) { $value = $object->getValue(); - } elseif (!is_null($object->getValue())) { + } elseif (null !== $object->getValue()) { $value = $object->getValue(); } $data['value'] = $value; } + return $data; } -} \ No newline at end of file +} diff --git a/generated/Normalizer/ProductTaxonDTONormalizer.php b/generated/Normalizer/ProductTaxonDTONormalizer.php index 77920aa7..c9831949 100644 --- a/generated/Normalizer/ProductTaxonDTONormalizer.php +++ b/generated/Normalizer/ProductTaxonDTONormalizer.php @@ -1,30 +1,46 @@ + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Generated\Normalizer; use Jane\Component\JsonSchemaRuntime\Reference; use MonsieurBiz\SyliusSearchPlugin\Generated\Runtime\Normalizer\CheckArray; -use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + class ProductTaxonDTONormalizer implements DenormalizerInterface, NormalizerInterface, DenormalizerAwareInterface, NormalizerAwareInterface { + use CheckArray; + use DenormalizerAwareTrait; + use NormalizerAwareTrait; - use CheckArray; + public function supportsDenormalization($data, $type, $format = null) { - return $type === 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductTaxonDTO'; + return 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\ProductTaxonDTO' === $type; } + public function supportsNormalization($data, $format = null) { return $data instanceof \MonsieurBiz\SyliusSearchPlugin\Generated\Model\ProductTaxonDTO; } - public function denormalize($data, $class, $format = null, array $context = array()) + + public function denormalize($data, $class, $format = null, array $context = []) { if (isset($data['$ref'])) { return new Reference($data['$ref'], $context['document-origin']); @@ -39,35 +55,37 @@ public function denormalize($data, $class, $format = null, array $context = arra if (\array_key_exists('taxon', $data)) { $object->setTaxon($this->denormalizer->denormalize($data['taxon'], 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\TaxonDTO', 'json', $context)); } - if (\array_key_exists('position', $data) && $data['position'] !== null) { + if (\array_key_exists('position', $data) && null !== $data['position']) { $value = $data['position']; - if (is_null($data['position'])) { + if (null === $data['position']) { $value = $data['position']; - } elseif (is_int($data['position'])) { + } elseif (\is_int($data['position'])) { $value = $data['position']; } $object->setPosition($value); - } - elseif (\array_key_exists('position', $data) && $data['position'] === null) { + } elseif (\array_key_exists('position', $data) && null === $data['position']) { $object->setPosition(null); } + return $object; } - public function normalize($object, $format = null, array $context = array()) + + public function normalize($object, $format = null, array $context = []) { - $data = array(); + $data = []; if (null !== $object->getTaxon()) { $data['taxon'] = $this->normalizer->normalize($object->getTaxon(), 'json', $context); } if (null !== $object->getPosition()) { $value = $object->getPosition(); - if (is_null($object->getPosition())) { + if (null === $object->getPosition()) { $value = $object->getPosition(); - } elseif (is_int($object->getPosition())) { + } elseif (\is_int($object->getPosition())) { $value = $object->getPosition(); } $data['position'] = $value; } + return $data; } -} \ No newline at end of file +} diff --git a/generated/Normalizer/TaxonDTONormalizer.php b/generated/Normalizer/TaxonDTONormalizer.php index 21bc2440..c7ac195c 100644 --- a/generated/Normalizer/TaxonDTONormalizer.php +++ b/generated/Normalizer/TaxonDTONormalizer.php @@ -1,30 +1,46 @@ + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Generated\Normalizer; use Jane\Component\JsonSchemaRuntime\Reference; use MonsieurBiz\SyliusSearchPlugin\Generated\Runtime\Normalizer\CheckArray; -use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + class TaxonDTONormalizer implements DenormalizerInterface, NormalizerInterface, DenormalizerAwareInterface, NormalizerAwareInterface { + use CheckArray; + use DenormalizerAwareTrait; + use NormalizerAwareTrait; - use CheckArray; + public function supportsDenormalization($data, $type, $format = null) { - return $type === 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\TaxonDTO'; + return 'MonsieurBiz\\SyliusSearchPlugin\\Generated\\Model\\TaxonDTO' === $type; } + public function supportsNormalization($data, $format = null) { return $data instanceof \MonsieurBiz\SyliusSearchPlugin\Generated\Model\TaxonDTO; } - public function denormalize($data, $class, $format = null, array $context = array()) + + public function denormalize($data, $class, $format = null, array $context = []) { if (isset($data['$ref'])) { return new Reference($data['$ref'], $context['document-origin']); @@ -48,11 +64,13 @@ public function denormalize($data, $class, $format = null, array $context = arra if (\array_key_exists('level', $data)) { $object->setLevel($data['level']); } + return $object; } - public function normalize($object, $format = null, array $context = array()) + + public function normalize($object, $format = null, array $context = []) { - $data = array(); + $data = []; if (null !== $object->getName()) { $data['name'] = $object->getName(); } @@ -65,6 +83,7 @@ public function normalize($object, $format = null, array $context = array()) if (null !== $object->getLevel()) { $data['level'] = $object->getLevel(); } + return $data; } -} \ No newline at end of file +} diff --git a/generated/Runtime/Normalizer/CheckArray.php b/generated/Runtime/Normalizer/CheckArray.php index 4e48da3d..db91ddff 100644 --- a/generated/Runtime/Normalizer/CheckArray.php +++ b/generated/Runtime/Normalizer/CheckArray.php @@ -1,13 +1,24 @@ + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Generated\Runtime\Normalizer; trait CheckArray { - public function isOnlyNumericKeys(array $array) : bool + public function isOnlyNumericKeys(array $array): bool { - return count(array_filter($array, function ($key) { + return \count(array_filter($array, function ($key) { return is_numeric($key); - }, ARRAY_FILTER_USE_KEY)) === count($array); + }, \ARRAY_FILTER_USE_KEY)) === \count($array); } -} \ No newline at end of file +} diff --git a/generated/Runtime/Normalizer/ReferenceNormalizer.php b/generated/Runtime/Normalizer/ReferenceNormalizer.php index d892e309..e7d978e6 100644 --- a/generated/Runtime/Normalizer/ReferenceNormalizer.php +++ b/generated/Runtime/Normalizer/ReferenceNormalizer.php @@ -1,25 +1,39 @@ + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + namespace MonsieurBiz\SyliusSearchPlugin\Generated\Runtime\Normalizer; use Jane\Component\JsonSchemaRuntime\Reference; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + class ReferenceNormalizer implements NormalizerInterface { /** - * {@inheritdoc} + * @inheritdoc */ public function normalize($object, $format = null, array $context = []) { $ref = []; $ref['$ref'] = (string) $object->getReferenceUri(); + return $ref; } + /** - * {@inheritdoc} + * @inheritdoc */ public function supportsNormalization($data, $format = null) { return $data instanceof Reference; } -} \ No newline at end of file +} diff --git a/src/AutoMapper/Configuration.php b/src/AutoMapper/Configuration.php index 4519881c..e7ae44cd 100644 --- a/src/AutoMapper/Configuration.php +++ b/src/AutoMapper/Configuration.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -18,6 +18,7 @@ final class Configuration { private array $sourceClasses = []; + private array $targetClasses = []; public function addSourceClass(string $identifier, string $className): void diff --git a/src/AutoMapper/ProductAttributeValueConfiguration.php b/src/AutoMapper/ProductAttributeValueConfiguration.php index 2ea92f4d..5811559b 100644 --- a/src/AutoMapper/ProductAttributeValueConfiguration.php +++ b/src/AutoMapper/ProductAttributeValueConfiguration.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -19,7 +19,6 @@ use MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductAttributeValueReader\ReaderInterface; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareTrait; -use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; use RuntimeException; use Sylius\Component\Product\Model\ProductAttributeValueInterface; @@ -29,6 +28,7 @@ final class ProductAttributeValueConfiguration implements MapperConfigurationInt use LoggerAwareTrait; private Configuration $configuration; + /** * @var ReaderInterface[] */ @@ -52,7 +52,7 @@ public function process(MapperGeneratorMetadataInterface $metadata): void throw new RuntimeException('Undefined product attribute value reader'); } - $metadata->forMember('value', function(ProductAttributeValueInterface $productAttributeValue) { + $metadata->forMember('value', function (ProductAttributeValueInterface $productAttributeValue) { if (null === $productAttributeValue->getType()) { return null; } diff --git a/src/AutoMapper/ProductAttributeValueReader/CheckboxReader.php b/src/AutoMapper/ProductAttributeValueReader/CheckboxReader.php index b96d5542..705799da 100644 --- a/src/AutoMapper/ProductAttributeValueReader/CheckboxReader.php +++ b/src/AutoMapper/ProductAttributeValueReader/CheckboxReader.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/AutoMapper/ProductAttributeValueReader/DateReader.php b/src/AutoMapper/ProductAttributeValueReader/DateReader.php index 23566874..1ebd7c4e 100644 --- a/src/AutoMapper/ProductAttributeValueReader/DateReader.php +++ b/src/AutoMapper/ProductAttributeValueReader/DateReader.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/AutoMapper/ProductAttributeValueReader/DateTimeReader.php b/src/AutoMapper/ProductAttributeValueReader/DateTimeReader.php index 564b3654..bb6e6786 100644 --- a/src/AutoMapper/ProductAttributeValueReader/DateTimeReader.php +++ b/src/AutoMapper/ProductAttributeValueReader/DateTimeReader.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/AutoMapper/ProductAttributeValueReader/DefaultReader.php b/src/AutoMapper/ProductAttributeValueReader/DefaultReader.php index 514bb4b0..77bc57af 100644 --- a/src/AutoMapper/ProductAttributeValueReader/DefaultReader.php +++ b/src/AutoMapper/ProductAttributeValueReader/DefaultReader.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/AutoMapper/ProductAttributeValueReader/IntegerReader.php b/src/AutoMapper/ProductAttributeValueReader/IntegerReader.php index 7c1df8e1..d3fde12c 100644 --- a/src/AutoMapper/ProductAttributeValueReader/IntegerReader.php +++ b/src/AutoMapper/ProductAttributeValueReader/IntegerReader.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/AutoMapper/ProductAttributeValueReader/PercentReader.php b/src/AutoMapper/ProductAttributeValueReader/PercentReader.php index 40e284a8..09dd8e62 100644 --- a/src/AutoMapper/ProductAttributeValueReader/PercentReader.php +++ b/src/AutoMapper/ProductAttributeValueReader/PercentReader.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/AutoMapper/ProductAttributeValueReader/ReaderInterface.php b/src/AutoMapper/ProductAttributeValueReader/ReaderInterface.php index 6cb41df7..dc135260 100644 --- a/src/AutoMapper/ProductAttributeValueReader/ReaderInterface.php +++ b/src/AutoMapper/ProductAttributeValueReader/ReaderInterface.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/AutoMapper/ProductAttributeValueReader/SelectReader.php b/src/AutoMapper/ProductAttributeValueReader/SelectReader.php index 340897e0..1c30340a 100644 --- a/src/AutoMapper/ProductAttributeValueReader/SelectReader.php +++ b/src/AutoMapper/ProductAttributeValueReader/SelectReader.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/AutoMapper/ProductAttributeValueReader/TextReader.php b/src/AutoMapper/ProductAttributeValueReader/TextReader.php index 0c73c7c7..b28c8f04 100644 --- a/src/AutoMapper/ProductAttributeValueReader/TextReader.php +++ b/src/AutoMapper/ProductAttributeValueReader/TextReader.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/AutoMapper/ProductAttributeValueReader/TextareaReader.php b/src/AutoMapper/ProductAttributeValueReader/TextareaReader.php index ffc51f76..8a4cde49 100644 --- a/src/AutoMapper/ProductAttributeValueReader/TextareaReader.php +++ b/src/AutoMapper/ProductAttributeValueReader/TextareaReader.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/AutoMapper/ProductMapperConfiguration.php b/src/AutoMapper/ProductMapperConfiguration.php index 079610ca..97d8ee69 100644 --- a/src/AutoMapper/ProductMapperConfiguration.php +++ b/src/AutoMapper/ProductMapperConfiguration.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -33,9 +33,13 @@ final class ProductMapperConfiguration implements MapperConfigurationInterface { private Configuration $configuration; + private AutoMapperInterface $autoMapper; + private ProductVariantResolverInterface $productVariantResolver; + private RequestStack $requestStack; + private AvailabilityCheckerInterface $availabilityChecker; public function __construct( @@ -58,35 +62,35 @@ public function process(MapperGeneratorMetadataInterface $metadata): void return; } - $metadata->forMember('id', function(ProductInterface $product): int { + $metadata->forMember('id', function (ProductInterface $product): int { return $product->getId(); }); - $metadata->forMember('code', function(ProductInterface $product): ?string { + $metadata->forMember('code', function (ProductInterface $product): ?string { return $product->getCode(); }); - $metadata->forMember('enabled', function(ProductInterface $product): bool { + $metadata->forMember('enabled', function (ProductInterface $product): bool { return $product->isEnabled(); }); - $metadata->forMember('slug', function(ProductInterface $product): ?string { + $metadata->forMember('slug', function (ProductInterface $product): ?string { return $product->getSlug(); }); - $metadata->forMember('name', function(ProductInterface $product): ?string { + $metadata->forMember('name', function (ProductInterface $product): ?string { return $product->getName(); }); - $metadata->forMember('description', function(ProductInterface $product): ?string { + $metadata->forMember('description', function (ProductInterface $product): ?string { return $product->getDescription(); }); - $metadata->forMember('created_at', function(ProductInterface $product): ?DateTimeInterface { + $metadata->forMember('created_at', function (ProductInterface $product): ?DateTimeInterface { return $product->getCreatedAt(); }); - $metadata->forMember('images', function(ProductInterface $product): array { + $metadata->forMember('images', function (ProductInterface $product): array { $images = []; $imageDTOClass = $this->configuration->getTargetClass('image'); foreach ($product->getImages() as $image) { @@ -96,26 +100,26 @@ public function process(MapperGeneratorMetadataInterface $metadata): void return $images; }); - $metadata->forMember('mainTaxon', function(ProductInterface $product) { + $metadata->forMember('mainTaxon', function (ProductInterface $product) { return null !== $product->getMainTaxon() ? $this->autoMapper->map($product->getMainTaxon(), $this->configuration->getTargetClass('taxon')) : null; }); - $metadata->forMember('product_taxons', function(ProductInterface $product): array { - return array_map(function(ProductTaxonInterface $productTaxon) { + $metadata->forMember('product_taxons', function (ProductInterface $product): array { + return array_map(function (ProductTaxonInterface $productTaxon) { // todo add parent taxon in Taxon object with automapper return $this->autoMapper->map($productTaxon, $this->configuration->getTargetClass('product_taxon')); }, $product->getProductTaxons()->toArray()); }); - $metadata->forMember('channels', function(ProductInterface $product): array { - return array_map(function(ChannelInterface $channel) { + $metadata->forMember('channels', function (ProductInterface $product): array { + return array_map(function (ChannelInterface $channel) { return $this->autoMapper->map($channel, $this->configuration->getTargetClass('channel')); }, $product->getChannels()->toArray()); }); - $metadata->forMember('attributes', function(ProductInterface $product): array { + $metadata->forMember('attributes', function (ProductInterface $product): array { $attributes = []; $currentLocale = $product->getTranslation()->getLocale(); if (null === $currentLocale) { @@ -136,7 +140,7 @@ public function process(MapperGeneratorMetadataInterface $metadata): void return $attributes; }); - $metadata->forMember('options', function(ProductInterface $product): array { + $metadata->forMember('options', function (ProductInterface $product): array { $options = []; $currentLocale = $product->getTranslation()->getLocale(); foreach ($product->getVariants() as $variant) { @@ -170,7 +174,7 @@ public function process(MapperGeneratorMetadataInterface $metadata): void return $options; }); - $metadata->forMember('variants', function(ProductInterface $product): array { + $metadata->forMember('variants', function (ProductInterface $product): array { $variants = []; $productVariantDTOClass = $this->configuration->getTargetClass('product_variant'); foreach ($product->getEnabledVariants() as $variant) { @@ -180,7 +184,7 @@ public function process(MapperGeneratorMetadataInterface $metadata): void return $variants; }); - $metadata->forMember('prices', function(ProductInterface $product): array { + $metadata->forMember('prices', function (ProductInterface $product): array { $prices = []; foreach ($product->getChannels() as $channel) { /** @var ChannelInterface $channel */ @@ -192,6 +196,7 @@ public function process(MapperGeneratorMetadataInterface $metadata): void || null === ($channelPricing = $variant->getChannelPricingForChannel($channel)) ) { $this->requestStack->pop(); + continue; } $this->requestStack->pop(); diff --git a/src/AutoMapper/VariantMapperConfiguration.php b/src/AutoMapper/VariantMapperConfiguration.php index e0bf7001..9388a210 100644 --- a/src/AutoMapper/VariantMapperConfiguration.php +++ b/src/AutoMapper/VariantMapperConfiguration.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -23,6 +23,7 @@ final class VariantMapperConfiguration implements MapperConfigurationInterface { private Configuration $configuration; + private AvailabilityCheckerInterface $availabilityChecker; public function __construct(Configuration $configuration, AvailabilityCheckerInterface $availabilityChecker) @@ -37,7 +38,7 @@ public function process(MapperGeneratorMetadataInterface $metadata): void return; } - $metadata->forMember('is_in_stock', function(ProductVariantInterface $productVariant): bool { + $metadata->forMember('is_in_stock', function (ProductVariantInterface $productVariant): bool { if (!$productVariant instanceof StockableInterface) { return true; } diff --git a/src/Command/PopulateCommand.php b/src/Command/PopulateCommand.php index 34c9ea82..b0af5ba2 100644 --- a/src/Command/PopulateCommand.php +++ b/src/Command/PopulateCommand.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -21,6 +21,7 @@ class PopulateCommand extends Command { protected static $defaultName = 'monsieurbiz:search:populate'; + private Indexer $indexer; public function __construct(Indexer $indexer, $name = null) diff --git a/src/Command/SearchCommand.php b/src/Command/SearchCommand.php index 068c42cb..aa026d72 100644 --- a/src/Command/SearchCommand.php +++ b/src/Command/SearchCommand.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -34,10 +34,15 @@ class SearchCommand extends Command { protected static $defaultName = 'monsieurbiz:search:search'; + private Search $search; + private RequestStack $requestStack; + private ChannelContextInterface $channelContext; + private SettingsInterface $searchSettings; + private ServiceRegistryInterface $documentableRegistry; public function __construct( diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php index 5617116f..f0b3d41c 100644 --- a/src/Controller/SearchController.php +++ b/src/Controller/SearchController.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -34,11 +34,17 @@ class SearchController extends AbstractController { private Search $search; + private CurrencyContextInterface $currencyContext; + private LocaleContextInterface $localeContext; + private ChannelContextInterface $channelContext; + private SettingsInterface $searchSettings; + private ServiceRegistryInterface $documentableRegistry; + private ParametersParserInterface $parametersParser; public function __construct( @@ -117,6 +123,7 @@ public function instantAction(Request $request): Response $this->searchSettings, $this->channelContext ); + try { $results[] = $this->search->search($requestConfiguration); } catch (UnknownRequestTypeException $e) { diff --git a/src/DependencyInjection/AutomapperConfigurationRegistryPass.php b/src/DependencyInjection/AutomapperConfigurationRegistryPass.php index 9f4baf95..2de4b20d 100644 --- a/src/DependencyInjection/AutomapperConfigurationRegistryPass.php +++ b/src/DependencyInjection/AutomapperConfigurationRegistryPass.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/DependencyInjection/AutowireMappingProviderParameterPass.php b/src/DependencyInjection/AutowireMappingProviderParameterPass.php index efbe6c60..173d6187 100644 --- a/src/DependencyInjection/AutowireMappingProviderParameterPass.php +++ b/src/DependencyInjection/AutowireMappingProviderParameterPass.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -29,6 +29,7 @@ public function process(ContainerBuilder $container): void $yamlMappingProvider = $container->getDefinition(YamlProvider::class); $decoratedYamlMappingProvider = $container->getDefinition(YamlWithLocaleProvider::class); + try { $decoratedYamlMappingProvider->setArgument( '$configurationDirectory', diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 4c46d94a..20dccdd7 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -22,7 +22,7 @@ final class Configuration implements ConfigurationInterface { /** - * {@inheritdoc} + * @inheritdoc */ public function getConfigTreeBuilder(): TreeBuilder { diff --git a/src/DependencyInjection/DocumentableRegistryPass.php b/src/DependencyInjection/DocumentableRegistryPass.php index 3edabdbd..c97b5933 100644 --- a/src/DependencyInjection/DocumentableRegistryPass.php +++ b/src/DependencyInjection/DocumentableRegistryPass.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php b/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php index fff2bc2d..cc7eaa34 100644 --- a/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php +++ b/src/DependencyInjection/MonsieurBizSyliusSearchExtension.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -42,7 +42,7 @@ public function load(array $configs, ContainerBuilder $container): void } /** - * {@inheritdoc} + * @inheritdoc */ public function getAlias() { diff --git a/src/DependencyInjection/RegisterSearchRequestPass.php b/src/DependencyInjection/RegisterSearchRequestPass.php index c76aab82..fa1f1366 100644 --- a/src/DependencyInjection/RegisterSearchRequestPass.php +++ b/src/DependencyInjection/RegisterSearchRequestPass.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Entity/Product/SearchableInterface.php b/src/Entity/Product/SearchableInterface.php index 06b49f44..56071ad5 100644 --- a/src/Entity/Product/SearchableInterface.php +++ b/src/Entity/Product/SearchableInterface.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Event/MappingProviderEvent.php b/src/Event/MappingProviderEvent.php index 94dfd25f..35b077d1 100644 --- a/src/Event/MappingProviderEvent.php +++ b/src/Event/MappingProviderEvent.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -28,7 +28,6 @@ class MappingProviderEvent extends Event private ?ArrayObject $mapping; /** - * @param string $indexCode * @param ArrayObject|null $mapping */ public function __construct(string $indexCode, ?ArrayObject $mapping) diff --git a/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php b/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php index a4ab969c..12b7f8ef 100644 --- a/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php +++ b/src/EventSubscriber/AppendProductAttributeMappingSubscriber.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -22,7 +22,9 @@ class AppendProductAttributeMappingSubscriber implements EventSubscriberInterface { private ProductAttributeRepositoryInterface $productAttributeRepository; + private ProductOptionRepositoryInterface $productOptionRepository; + private string $fieldAnalyzer; public function __construct( diff --git a/src/EventSubscriber/ReindexProductEventSubscriber.php b/src/EventSubscriber/ReindexProductEventSubscriber.php index 7f4f2555..8f2f9bd3 100644 --- a/src/EventSubscriber/ReindexProductEventSubscriber.php +++ b/src/EventSubscriber/ReindexProductEventSubscriber.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -40,7 +40,9 @@ class ReindexProductEventSubscriber implements EventSubscriberInterface, LoggerA * @var ModelProductInterface[] */ private array $productsToReindex = []; + private array $productsToBeDelete = []; + private MessageBusInterface $messageBus; public function __construct(MessageBusInterface $messageBus) @@ -74,7 +76,7 @@ public function onFlush(OnFlushEventArgs $eventArgs): void if (0 !== \count($this->productsToBeDelete)) { $productToDeleteMessage = new ProductToDeleteFromIds(); - array_map(function(ProductInterface $product) use ($productToDeleteMessage): void { + array_map(function (ProductInterface $product) use ($productToDeleteMessage): void { foreach ($this->productsToReindex as $key => $productsToReindex) { if ($productsToReindex->getId() === $product->getId()) { unset($this->productsToReindex[$key]); @@ -111,10 +113,12 @@ private function onFlushEntities(array $entities, string $type = 'insertionsOrUp foreach ($entities as $entity) { if ($entity instanceof ProductInterface && 'deletions' === $type) { $this->productsToBeDelete[] = $entity; + continue; } if ($entity instanceof ProductTaxonInterface && null !== $entity->getTaxon()) { $this->messageBus->dispatch(new ProductReindexFromTaxon($entity->getTaxon()->getId())); + continue; } $product = $this->getProduct($entity); diff --git a/src/Exception/ObjectNotInstanceOfClassException.php b/src/Exception/ObjectNotInstanceOfClassException.php index 9790aa2f..a288030a 100644 --- a/src/Exception/ObjectNotInstanceOfClassException.php +++ b/src/Exception/ObjectNotInstanceOfClassException.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Exception/UnknownRequestTypeException.php b/src/Exception/UnknownRequestTypeException.php index d9f813b2..5b8c3741 100644 --- a/src/Exception/UnknownRequestTypeException.php +++ b/src/Exception/UnknownRequestTypeException.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Form/Extension/ProductAttributeTypeExtension.php b/src/Form/Extension/ProductAttributeTypeExtension.php index c8de8abd..67bccb4a 100644 --- a/src/Form/Extension/ProductAttributeTypeExtension.php +++ b/src/Form/Extension/ProductAttributeTypeExtension.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Form/Extension/ProductOptionTypeExtension.php b/src/Form/Extension/ProductOptionTypeExtension.php index 7a3bda11..71c2abda 100644 --- a/src/Form/Extension/ProductOptionTypeExtension.php +++ b/src/Form/Extension/ProductOptionTypeExtension.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Form/Type/SearchType.php b/src/Form/Type/SearchType.php index 0f452f1e..883e117b 100644 --- a/src/Form/Type/SearchType.php +++ b/src/Form/Type/SearchType.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -22,10 +22,6 @@ class SearchType extends AbstractType { - /** - * @param FormBuilderInterface $builder - * @param array $options - */ public function buildForm(FormBuilderInterface $builder, array $options): void { $builder @@ -48,7 +44,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void } /** - * {@inheritdoc} + * @inheritdoc */ public function getBlockPrefix() { diff --git a/src/Form/Type/Settings/LimitsSearchType.php b/src/Form/Type/Settings/LimitsSearchType.php index 08e3006f..65d5c4ba 100644 --- a/src/Form/Type/Settings/LimitsSearchType.php +++ b/src/Form/Type/Settings/LimitsSearchType.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Form/Type/Settings/SettingsSearchType.php b/src/Form/Type/Settings/SettingsSearchType.php index e78fbf6b..d58ff765 100644 --- a/src/Form/Type/Settings/SettingsSearchType.php +++ b/src/Form/Type/Settings/SettingsSearchType.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Helper/SlugHelper.php b/src/Helper/SlugHelper.php index 09881656..c9e8fa64 100644 --- a/src/Helper/SlugHelper.php +++ b/src/Helper/SlugHelper.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Index/Indexer.php b/src/Index/Indexer.php index 35623135..04969b2b 100644 --- a/src/Index/Indexer.php +++ b/src/Index/Indexer.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -26,10 +26,15 @@ final class Indexer { private ServiceRegistryInterface $documentableRegistry; + private RepositoryInterface $localeRepository; + private array $locales = []; + private EntityManagerInterface $entityManager; + private AutoMapperInterface $autoMapper; + private ClientFactory $clientFactory; public function __construct( @@ -54,7 +59,7 @@ public function getLocales(): array if (0 === \count($this->locales)) { $locales = $this->localeRepository->findAll(); $this->locales = array_filter(array_map( - function(LocaleInterface $locale): string { + function (LocaleInterface $locale): string { return $locale->getCode() ?? ''; }, $locales @@ -96,7 +101,7 @@ public function indexByDocuments(DocumentableInterface $documentable, array $doc $document->setCurrentLocale($locale); } $dto = $this->autoMapper->map($document, $documentable->getTargetClass()); - /* @phpstan-ignore-next-line */ + // @phpstan-ignore-next-line $indexer->scheduleIndex($indexName, new Document((string) $document->getId(), $dto)); } } @@ -148,7 +153,7 @@ private function indexDocumentable(DocumentableInterface $documentable, ?string $item->setCurrentLocale($locale); } $dto = $this->autoMapper->map($item, $documentable->getTargetClass()); - /* @phpstan-ignore-next-line */ + // @phpstan-ignore-next-line $indexer->scheduleIndex($newIndex, new Document((string) $item->getId(), $dto)); } $indexer->flush(); diff --git a/src/Mapping/YamlWithLocaleProvider.php b/src/Mapping/YamlWithLocaleProvider.php index ce23bbc5..7d51bfde 100644 --- a/src/Mapping/YamlWithLocaleProvider.php +++ b/src/Mapping/YamlWithLocaleProvider.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -25,9 +25,13 @@ class YamlWithLocaleProvider implements MappingProviderInterface { private YamlProvider $decorated; + private string $configurationDirectory; + private Parser $parser; + private ProductAttributeRepositoryInterface $attributeRepository; + private EventDispatcherInterface $eventDispatcher; public function __construct( @@ -66,6 +70,7 @@ private function appendLocaleAnalyzers(array $mapping, string $locale): array { foreach ($this->getLocaleCode($locale) as $localeCode) { $analyzerFilePath = $this->configurationDirectory . \DIRECTORY_SEPARATOR . 'analyzers_' . $localeCode . '.yaml'; + try { $analyzer = $this->parser->parseFile($analyzerFilePath) ?? []; $mapping['settings']['analysis'] = array_merge_recursive($mapping['settings']['analysis'] ?? [], $analyzer); diff --git a/src/Message/ProductReindexFromIds.php b/src/Message/ProductReindexFromIds.php index b15527eb..8627d13d 100644 --- a/src/Message/ProductReindexFromIds.php +++ b/src/Message/ProductReindexFromIds.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Message/ProductReindexFromTaxon.php b/src/Message/ProductReindexFromTaxon.php index ac367484..7b2af22f 100644 --- a/src/Message/ProductReindexFromTaxon.php +++ b/src/Message/ProductReindexFromTaxon.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Message/ProductToDeleteFromIds.php b/src/Message/ProductToDeleteFromIds.php index b46e8967..3f58da2f 100644 --- a/src/Message/ProductToDeleteFromIds.php +++ b/src/Message/ProductToDeleteFromIds.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/MessageHandler/ProductReindexFromIdsHandler.php b/src/MessageHandler/ProductReindexFromIdsHandler.php index d1145a63..7777afc0 100644 --- a/src/MessageHandler/ProductReindexFromIdsHandler.php +++ b/src/MessageHandler/ProductReindexFromIdsHandler.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -23,7 +23,9 @@ class ProductReindexFromIdsHandler implements MessageHandlerInterface { private ProductRepositoryInterface $productRepository; + private Indexer $indexer; + private ServiceRegistryInterface $documentableRegistry; public function __construct( diff --git a/src/MessageHandler/ProductReindexFromTaxonHandler.php b/src/MessageHandler/ProductReindexFromTaxonHandler.php index f5d56230..8f8194da 100644 --- a/src/MessageHandler/ProductReindexFromTaxonHandler.php +++ b/src/MessageHandler/ProductReindexFromTaxonHandler.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -24,7 +24,9 @@ class ProductReindexFromTaxonHandler implements MessageHandlerInterface { private ProductRepositoryInterface $productRepository; + private Indexer $indexer; + private ServiceRegistryInterface $documentableRegistry; public function __construct( diff --git a/src/MessageHandler/ProductToDeleteFromIdsHandler.php b/src/MessageHandler/ProductToDeleteFromIdsHandler.php index 861e49ad..9ed3b90a 100644 --- a/src/MessageHandler/ProductToDeleteFromIdsHandler.php +++ b/src/MessageHandler/ProductToDeleteFromIdsHandler.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -23,7 +23,9 @@ class ProductToDeleteFromIdsHandler implements MessageHandlerInterface { private ProductRepositoryInterface $productRepository; + private Indexer $indexer; + private ServiceRegistryInterface $documentableRegistry; public function __construct( diff --git a/src/Model/Datasource/DatasourceInterface.php b/src/Model/Datasource/DatasourceInterface.php index 16374107..da80d29a 100644 --- a/src/Model/Datasource/DatasourceInterface.php +++ b/src/Model/Datasource/DatasourceInterface.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Model/Datasource/RepositoryDatasource.php b/src/Model/Datasource/RepositoryDatasource.php index 2128f41e..3f7b93b3 100644 --- a/src/Model/Datasource/RepositoryDatasource.php +++ b/src/Model/Datasource/RepositoryDatasource.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Model/Document/Index/Indexer.php b/src/Model/Document/Index/Indexer.php index 64779821..58963949 100644 --- a/src/Model/Document/Index/Indexer.php +++ b/src/Model/Document/Index/Indexer.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -21,8 +21,6 @@ use MonsieurBiz\SyliusSearchPlugin\Model\Document\Result; use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface; use MonsieurBiz\SyliusSearchPlugin\Provider\DocumentRepositoryProvider; -use MonsieurBiz\SyliusSearchPlugin\Provider\SearchQueryProvider; -use Psr\Log\LoggerInterface; use Sylius\Component\Locale\Model\LocaleInterface; use Sylius\Component\Resource\Repository\RepositoryInterface; use Webmozart\Assert\Assert; @@ -42,12 +40,6 @@ class Indexer extends AbstractIndex /** * PopulateCommand constructor. - * - * @param Client $client - * @param DocumentRepositoryProvider $documentRepositoryProvider - * @param RepositoryInterface $localeRepository - * @param SearchQueryProvider $searchQueryProvider - * @param LoggerInterface $logger */ public function __construct( Client $client, @@ -61,15 +53,13 @@ public function __construct( /** * Retrieve all available locales. - * - * @return array */ public function getLocales(): array { if (empty($this->locales)) { $locales = $this->localeRepository->findAll(); $this->locales = array_map( - function(LocaleInterface $locale) { + function (LocaleInterface $locale) { return $locale->getCode(); }, $locales @@ -94,8 +84,6 @@ public function indexAll(): void /** * Index all document for a locale. * - * @param string $locale - * * @throws \Exception */ public function indexAllByLocale(string $locale): void @@ -122,6 +110,7 @@ public function indexAllByLocale(string $locale): void $this->getIndexer()->flush(); $this->getIndexer()->refresh($indexName); + try { $this->getIndexBuilder()->purgeOldIndices($indexName); } catch (ResponseException $exception) { @@ -132,8 +121,6 @@ public function indexAllByLocale(string $locale): void /** * Index a document for all locales. * - * @param DocumentableInterface $subject - * * @throws \Exception */ public function indexOne(DocumentableInterface $subject): void @@ -147,9 +134,6 @@ public function indexOne(DocumentableInterface $subject): void /** * Index a document for one locale. * - * @param Result $document - * @param string $locale - * * @throws MissingParamException */ public function indexOneByLocale(Result $document, string $locale): void @@ -163,8 +147,6 @@ public function indexOneByLocale(Result $document, string $locale): void /** * Remove a document for all locales. * - * @param DocumentableInterface $subject - * * @throws \Exception */ public function removeOne(DocumentableInterface $subject): void @@ -178,9 +160,6 @@ public function removeOne(DocumentableInterface $subject): void /** * Remove a document for all locales. * - * @param Result $document - * @param string $locale - * * @throws MissingParamException */ public function removeOneByLocale(Result $document, string $locale): void diff --git a/src/Model/Documentable/Documentable.php b/src/Model/Documentable/Documentable.php index 064cfc3e..cf2484a3 100644 --- a/src/Model/Documentable/Documentable.php +++ b/src/Model/Documentable/Documentable.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -18,14 +18,20 @@ class Documentable implements DocumentableInterface { use DocumentableDatasourceTrait; + use DocumentableMappingProviderTrait; + private string $indexCode; + private string $sourceClass; + private string $targetClass; + /** * @var array */ private array $templates; + private array $limits; public function __construct( diff --git a/src/Model/Documentable/DocumentableDatasourceTrait.php b/src/Model/Documentable/DocumentableDatasourceTrait.php index f6216231..2a90842c 100644 --- a/src/Model/Documentable/DocumentableDatasourceTrait.php +++ b/src/Model/Documentable/DocumentableDatasourceTrait.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Model/Documentable/DocumentableInterface.php b/src/Model/Documentable/DocumentableInterface.php index e793c8f4..fc17d217 100644 --- a/src/Model/Documentable/DocumentableInterface.php +++ b/src/Model/Documentable/DocumentableInterface.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Model/Documentable/DocumentableMappingProviderTrait.php b/src/Model/Documentable/DocumentableMappingProviderTrait.php index a8d79e2b..55b85d2b 100644 --- a/src/Model/Documentable/DocumentableMappingProviderTrait.php +++ b/src/Model/Documentable/DocumentableMappingProviderTrait.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Model/Documentable/DocumentableProductTrait.php b/src/Model/Documentable/DocumentableProductTrait.php index 9cead518..b73abef9 100644 --- a/src/Model/Documentable/DocumentableProductTrait.php +++ b/src/Model/Documentable/DocumentableProductTrait.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -21,33 +21,22 @@ use Sylius\Component\Core\Model\Image; use Sylius\Component\Core\Model\ProductTaxonInterface; use Sylius\Component\Core\Model\ProductVariant; +use Sylius\Component\Core\Model\ProductVariantInterface; use Sylius\Component\Core\Model\TaxonInterface; use Sylius\Component\Currency\Model\CurrencyInterface; -use Sylius\Component\Core\Model\ProductVariantInterface; trait DocumentableProductTrait { - /** - * @return string - */ public function getDocumentType(): string { return 'product'; } - /** - * @return ResultInterface - */ public function createResult(): ResultInterface { return new Result(); } - /** - * @param string $locale - * - * @return ResultInterface - */ public function convertToDocument(string $locale): ResultInterface { $document = $this->createResult(); @@ -75,11 +64,6 @@ public function convertToDocument(string $locale): ResultInterface return $this->addOptionsInDocument($document, $locale); } - /** - * @param ResultInterface $document - * - * @return ResultInterface - */ protected function addImagesInDocument(ResultInterface $document): ResultInterface { /** @var Image $image */ @@ -90,11 +74,6 @@ protected function addImagesInDocument(ResultInterface $document): ResultInterfa return $document; } - /** - * @param ResultInterface $document - * - * @return ResultInterface - */ protected function addChannelsInDocument(ResultInterface $document): ResultInterface { /** @var Channel $channel */ @@ -105,11 +84,6 @@ protected function addChannelsInDocument(ResultInterface $document): ResultInter return $document; } - /** - * @param ResultInterface $document - * - * @return ResultInterface - */ protected function addPricesInDocument(ResultInterface $document): ResultInterface { /** @var Channel $channel */ @@ -131,12 +105,6 @@ protected function addPricesInDocument(ResultInterface $document): ResultInterfa return $document; } - /** - * @param ResultInterface $document - * @param string $locale - * - * @return ResultInterface - */ protected function addTaxonsInDocument(ResultInterface $document, string $locale): ResultInterface { /** @var TaxonInterface $mainTaxon */ @@ -165,12 +133,6 @@ protected function addTaxonsInDocument(ResultInterface $document, string $locale return $document; } - /** - * @param ResultInterface $document - * @param string $locale - * - * @return ResultInterface - */ protected function addAttributesInDocument(ResultInterface $document, string $locale): ResultInterface { /** @var AttributeValueInterface $attribute */ @@ -191,7 +153,6 @@ protected function addAttributesInDocument(ResultInterface $document, string $lo /** * @param Result $document - * @param string $locale * * @return Result */ @@ -220,8 +181,6 @@ protected function addOptionsInDocument(ResultInterface $document, string $local /** * @param $channel - * - * @return null */ private function getCheapestVariantForChannel($channel) { @@ -248,6 +207,7 @@ private function getProductHasVariantInStock() return true; } } + return false; } } diff --git a/src/Model/Product/ProductDTO.php b/src/Model/Product/ProductDTO.php index e67bc428..a5bad0ef 100644 --- a/src/Model/Product/ProductDTO.php +++ b/src/Model/Product/ProductDTO.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Model/Product/SearchableTrait.php b/src/Model/Product/SearchableTrait.php index cb0dd326..2cd919dd 100644 --- a/src/Model/Product/SearchableTrait.php +++ b/src/Model/Product/SearchableTrait.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Model/Product/VariantDTO.php b/src/Model/Product/VariantDTO.php index 1ad3a4f6..9231f3a0 100644 --- a/src/Model/Product/VariantDTO.php +++ b/src/Model/Product/VariantDTO.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -27,33 +27,21 @@ public function setCode(string $code): void $this->setData('code', $code); } - /** - * @return bool - */ public function isEnabled(): bool { return (bool) $this->getData('enabled'); } - /** - * @param bool $enabled - */ public function setEnabled(bool $enabled): void { $this->setData('enabled', $enabled); } - /** - * @return bool - */ public function isInStock(): bool { return (bool) $this->getData('is_in_stock'); } - /** - * @param bool $isInStock - */ public function setIsInStock(bool $isInStock): void { $this->setData('is_in_stock', $isInStock); diff --git a/src/MonsieurBizSyliusSearchPlugin.php b/src/MonsieurBizSyliusSearchPlugin.php index d012ee47..d72df69e 100644 --- a/src/MonsieurBizSyliusSearchPlugin.php +++ b/src/MonsieurBizSyliusSearchPlugin.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Normalizer/Product/ProductDTONormalizer.php b/src/Normalizer/Product/ProductDTONormalizer.php index 19bbd70a..479379d7 100644 --- a/src/Normalizer/Product/ProductDTONormalizer.php +++ b/src/Normalizer/Product/ProductDTONormalizer.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -31,6 +31,7 @@ final class ProductDTONormalizer extends ObjectNormalizer implements DenormalizerInterface, NormalizerInterface, DenormalizerAwareInterface, NormalizerAwareInterface { use DenormalizerAwareTrait; + use NormalizerAwareTrait; private Configuration $automapperConfiguration; diff --git a/src/Repository/ProductAttributeRepository.php b/src/Repository/ProductAttributeRepository.php index 966eddec..814bbabb 100644 --- a/src/Repository/ProductAttributeRepository.php +++ b/src/Repository/ProductAttributeRepository.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Repository/ProductAttributeRepositoryInterface.php b/src/Repository/ProductAttributeRepositoryInterface.php index 7ce34b8b..79477992 100644 --- a/src/Repository/ProductAttributeRepositoryInterface.php +++ b/src/Repository/ProductAttributeRepositoryInterface.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Repository/ProductOptionRepository.php b/src/Repository/ProductOptionRepository.php index af3b8a27..07aa25e5 100644 --- a/src/Repository/ProductOptionRepository.php +++ b/src/Repository/ProductOptionRepository.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Repository/ProductOptionRepositoryInterface.php b/src/Repository/ProductOptionRepositoryInterface.php index 0d85ef07..62ead329 100644 --- a/src/Repository/ProductOptionRepositoryInterface.php +++ b/src/Repository/ProductOptionRepositoryInterface.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Resolver/CheapestProductVariantResolver.php b/src/Resolver/CheapestProductVariantResolver.php index 66c07a6d..e1c41021 100644 --- a/src/Resolver/CheapestProductVariantResolver.php +++ b/src/Resolver/CheapestProductVariantResolver.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Resources/config/jane/jane-configuration.php b/src/Resources/config/jane/jane-configuration.php index 66b92f59..1f0deb4d 100644 --- a/src/Resources/config/jane/jane-configuration.php +++ b/src/Resources/config/jane/jane-configuration.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/ClientFactory.php b/src/Search/ClientFactory.php index c44ff4dc..ed517b89 100644 --- a/src/Search/ClientFactory.php +++ b/src/Search/ClientFactory.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -23,6 +23,7 @@ class ClientFactory { private array $config; + private SerializerInterface $serializer; public function __construct(SerializerInterface $serializer, array $config = []) diff --git a/src/Search/Filter/Filter.php b/src/Search/Filter/Filter.php index b4319fee..1b365cca 100644 --- a/src/Search/Filter/Filter.php +++ b/src/Search/Filter/Filter.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -40,6 +40,7 @@ class Filter implements FilterInterface private $count; private string $type; + private RequestConfiguration $requestConfiguration; /** @@ -54,17 +55,11 @@ public function __construct(RequestConfiguration $requestConfiguration, string $ $this->requestConfiguration = $requestConfiguration; } - /** - * @return string - */ public function getCode(): string { return $this->code; } - /** - * @return string - */ public function getLabel(): string { return $this->label; @@ -88,25 +83,16 @@ public function addValue(string $label, int $count, ?string $value = null): void ); } - /** - * @return int - */ public function getCount(): int { return $this->count; } - /** - * @return string - */ public function getType(): string { return $this->type; } - /** - * @param string $type - */ public function setType(string $type): void { $this->type = $type; @@ -114,7 +100,7 @@ public function setType(string $type): void public function getAppliedValues(): array { - return array_filter($this->getValues(), function(FilterValue $filterValue): bool { + return array_filter($this->getValues(), function (FilterValue $filterValue): bool { return $filterValue->isApplied(); }); } diff --git a/src/Search/Filter/FilterValue.php b/src/Search/Filter/FilterValue.php index 59b2e169..6ae666b4 100644 --- a/src/Search/Filter/FilterValue.php +++ b/src/Search/Filter/FilterValue.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -18,15 +18,15 @@ class FilterValue { private string $label; + private int $count; + private string $value; + private bool $isApplied; /** * Filter constructor. - * - * @param string $label - * @param int $count */ public function __construct(string $label, int $count, string $value = null, bool $isApplied = false) { @@ -36,25 +36,16 @@ public function __construct(string $label, int $count, string $value = null, boo $this->isApplied = $isApplied; } - /** - * @return string - */ public function getSlug(): string { return SlugHelper::toSlug($this->value); } - /** - * @return string - */ public function getLabel(): string { return $this->label; } - /** - * @return int - */ public function getCount(): int { return $this->count; diff --git a/src/Search/Filter/RangeFilter.php b/src/Search/Filter/RangeFilter.php index 80f7c6ff..59a92fb2 100644 --- a/src/Search/Filter/RangeFilter.php +++ b/src/Search/Filter/RangeFilter.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -44,6 +44,7 @@ class RangeFilter implements FilterInterface private $max; private array $values = []; + private RequestConfiguration $requestConfiguration; /** @@ -62,17 +63,11 @@ public function __construct(RequestConfiguration $requestConfiguration, string $ $this->addValue($maxLabel, 0, (string) $max); } - /** - * @return string - */ public function getCode(): string { return $this->code; } - /** - * @return string - */ public function getLabel(): string { return $this->label; @@ -100,7 +95,7 @@ public function getType(): string public function getAppliedValues(): array { - return array_filter($this->values, function(FilterValue $filterValue): bool { + return array_filter($this->values, function (FilterValue $filterValue): bool { return $filterValue->isApplied(); }); } diff --git a/src/Search/Request/Aggregation/AggregationBuilderInterface.php b/src/Search/Request/Aggregation/AggregationBuilderInterface.php index 943f786c..f4501024 100644 --- a/src/Search/Request/Aggregation/AggregationBuilderInterface.php +++ b/src/Search/Request/Aggregation/AggregationBuilderInterface.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/Aggregation/MainTaxonAggregation.php b/src/Search/Request/Aggregation/MainTaxonAggregation.php index 294e11fd..c8750d97 100644 --- a/src/Search/Request/Aggregation/MainTaxonAggregation.php +++ b/src/Search/Request/Aggregation/MainTaxonAggregation.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -24,7 +24,7 @@ public function build($aggregation, array $filters) } $qb = new QueryBuilder(); - $filters = array_filter($filters, function($filter): bool { + $filters = array_filter($filters, function ($filter): bool { return !$filter->hasParam('path') || 'main_taxon' !== $filter->getParam('path'); }); $filterQuery = $qb->query()->bool(); diff --git a/src/Search/Request/Aggregation/PriceAggregation.php b/src/Search/Request/Aggregation/PriceAggregation.php index cb9be0d5..4df477a9 100644 --- a/src/Search/Request/Aggregation/PriceAggregation.php +++ b/src/Search/Request/Aggregation/PriceAggregation.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -34,7 +34,7 @@ public function build($aggregation, array $filters) $qb = new QueryBuilder(); $channelCode = $this->channelContext->getChannel()->getCode() ?? ''; - $filters = array_filter($filters, function($filter): bool { + $filters = array_filter($filters, function ($filter): bool { return !$filter->hasParam('path') || 'prices' !== $filter->getParam('path'); }); diff --git a/src/Search/Request/Aggregation/ProductAttributeAggregation.php b/src/Search/Request/Aggregation/ProductAttributeAggregation.php index 213cb35f..b18fbed3 100644 --- a/src/Search/Request/Aggregation/ProductAttributeAggregation.php +++ b/src/Search/Request/Aggregation/ProductAttributeAggregation.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -27,7 +27,7 @@ public function build($aggregation, array $filters) } $qb = new QueryBuilder(); - $filters = array_filter($filters, function($filter) use ($aggregation): bool { + $filters = array_filter($filters, function ($filter) use ($aggregation): bool { return !$filter->hasParam('path') || ( false !== strpos($filter->getParam('path'), 'attributes.') && 'attributes.' . $aggregation->getCode() !== $filter->getParam('path') diff --git a/src/Search/Request/Aggregation/ProductAttributesAggregation.php b/src/Search/Request/Aggregation/ProductAttributesAggregation.php index 1af4d7d7..760ef334 100644 --- a/src/Search/Request/Aggregation/ProductAttributesAggregation.php +++ b/src/Search/Request/Aggregation/ProductAttributesAggregation.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -34,7 +34,7 @@ public function build($aggregation, array $filters) $qb = new QueryBuilder(); - $currentFilters = array_filter($filters, function(AbstractQuery $filter): bool { + $currentFilters = array_filter($filters, function (AbstractQuery $filter): bool { return !$filter->hasParam('path') || false === strpos($filter->getParam('path'), 'attributes.'); }); diff --git a/src/Search/Request/Aggregation/ProductOptionAggregation.php b/src/Search/Request/Aggregation/ProductOptionAggregation.php index 4d1dc2ae..9c4f2e85 100644 --- a/src/Search/Request/Aggregation/ProductOptionAggregation.php +++ b/src/Search/Request/Aggregation/ProductOptionAggregation.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -36,7 +36,7 @@ public function build($aggregation, array $filters) $qb = new QueryBuilder(); - $filters = array_filter($filters, function(AbstractQuery $filter) use ($aggregation): bool { + $filters = array_filter($filters, function (AbstractQuery $filter) use ($aggregation): bool { return !$filter->hasParam('path') || ( false !== strpos($filter->getParam('path'), 'options.') && 'options.' . $aggregation->getCode() . '.values' !== $filter->getParam('path') diff --git a/src/Search/Request/Aggregation/ProductOptionsAggregation.php b/src/Search/Request/Aggregation/ProductOptionsAggregation.php index f6f621e9..7c5971e7 100644 --- a/src/Search/Request/Aggregation/ProductOptionsAggregation.php +++ b/src/Search/Request/Aggregation/ProductOptionsAggregation.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -33,7 +33,7 @@ public function build($aggregation, array $filters) } $qb = new QueryBuilder(); - $currentFilters = array_filter($filters, function(AbstractQuery $filter): bool { + $currentFilters = array_filter($filters, function (AbstractQuery $filter): bool { return !$filter->hasParam('path') || false === strpos($filter->getParam('path'), 'options.'); }); $filterQuery = $qb->query()->bool(); diff --git a/src/Search/Request/Aggregation/TaxonsAggregation.php b/src/Search/Request/Aggregation/TaxonsAggregation.php index 23046c69..79acc0b9 100644 --- a/src/Search/Request/Aggregation/TaxonsAggregation.php +++ b/src/Search/Request/Aggregation/TaxonsAggregation.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -27,7 +27,7 @@ public function build($aggregation, array $filters) $currentTaxon = $aggregation['taxons']; /** @phpstan-ignore-line */ $qb = new QueryBuilder(); - $filters = array_filter($filters, function($filter): bool { + $filters = array_filter($filters, function ($filter): bool { return !$filter->hasParam('path') || 'product_taxons' !== $filter->getParam('path'); }); diff --git a/src/Search/Request/AggregationBuilder.php b/src/Search/Request/AggregationBuilder.php index 47f56431..79cafbbb 100644 --- a/src/Search/Request/AggregationBuilder.php +++ b/src/Search/Request/AggregationBuilder.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/FunctionScore/FunctionScoreInterface.php b/src/Search/Request/FunctionScore/FunctionScoreInterface.php index 67b5e772..46ad3a00 100644 --- a/src/Search/Request/FunctionScore/FunctionScoreInterface.php +++ b/src/Search/Request/FunctionScore/FunctionScoreInterface.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/FunctionScore/FunctionScoreRegistryInterface.php b/src/Search/Request/FunctionScore/FunctionScoreRegistryInterface.php index 7338cfa2..8330bc7a 100644 --- a/src/Search/Request/FunctionScore/FunctionScoreRegistryInterface.php +++ b/src/Search/Request/FunctionScore/FunctionScoreRegistryInterface.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/FunctionScore/Product/InStockWeightFunction.php b/src/Search/Request/FunctionScore/Product/InStockWeightFunction.php index 1839ad83..fd7058e9 100644 --- a/src/Search/Request/FunctionScore/Product/InStockWeightFunction.php +++ b/src/Search/Request/FunctionScore/Product/InStockWeightFunction.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -21,7 +21,9 @@ class InStockWeightFunction implements FunctionScoreInterface { private bool $enableStockFilter; + private int $inStockWeight; + private array $applyOnRequestTypes; public function __construct( diff --git a/src/Search/Request/FunctionScore/ProductFunctionScoreRegistry.php b/src/Search/Request/FunctionScore/ProductFunctionScoreRegistry.php index bafb2eff..fc25bc65 100644 --- a/src/Search/Request/FunctionScore/ProductFunctionScoreRegistry.php +++ b/src/Search/Request/FunctionScore/ProductFunctionScoreRegistry.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/PostFilter/PostFilterInterface.php b/src/Search/Request/PostFilter/PostFilterInterface.php index 6b07522a..c3dd4d63 100644 --- a/src/Search/Request/PostFilter/PostFilterInterface.php +++ b/src/Search/Request/PostFilter/PostFilterInterface.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/PostFilter/PostFilterRegistryInterface.php b/src/Search/Request/PostFilter/PostFilterRegistryInterface.php index 5813eb70..74037c55 100644 --- a/src/Search/Request/PostFilter/PostFilterRegistryInterface.php +++ b/src/Search/Request/PostFilter/PostFilterRegistryInterface.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/PostFilter/Product/AttributesPostFilter.php b/src/Search/Request/PostFilter/Product/AttributesPostFilter.php index 5fd94db2..c7209a7a 100644 --- a/src/Search/Request/PostFilter/Product/AttributesPostFilter.php +++ b/src/Search/Request/PostFilter/Product/AttributesPostFilter.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/PostFilter/Product/MainTaxonPostFilter.php b/src/Search/Request/PostFilter/Product/MainTaxonPostFilter.php index b6c30ff3..c8ced4c8 100644 --- a/src/Search/Request/PostFilter/Product/MainTaxonPostFilter.php +++ b/src/Search/Request/PostFilter/Product/MainTaxonPostFilter.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/PostFilter/Product/OptionsPostFilter.php b/src/Search/Request/PostFilter/Product/OptionsPostFilter.php index cbc13f2c..56217bc0 100644 --- a/src/Search/Request/PostFilter/Product/OptionsPostFilter.php +++ b/src/Search/Request/PostFilter/Product/OptionsPostFilter.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/PostFilter/Product/PricePostFilter.php b/src/Search/Request/PostFilter/Product/PricePostFilter.php index cfe686b1..d4b4514c 100644 --- a/src/Search/Request/PostFilter/Product/PricePostFilter.php +++ b/src/Search/Request/PostFilter/Product/PricePostFilter.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/PostFilter/Product/ProductTaxonPostFilter.php b/src/Search/Request/PostFilter/Product/ProductTaxonPostFilter.php index cbc6753f..656c96eb 100644 --- a/src/Search/Request/PostFilter/Product/ProductTaxonPostFilter.php +++ b/src/Search/Request/PostFilter/Product/ProductTaxonPostFilter.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/PostFilter/ProductTaxonRegistry.php b/src/Search/Request/PostFilter/ProductTaxonRegistry.php index a2bd227c..8cdc1b25 100644 --- a/src/Search/Request/PostFilter/ProductTaxonRegistry.php +++ b/src/Search/Request/PostFilter/ProductTaxonRegistry.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/ProductRequest/InstantSearch.php b/src/Search/Request/ProductRequest/InstantSearch.php index adb87968..0cfebcd3 100644 --- a/src/Search/Request/ProductRequest/InstantSearch.php +++ b/src/Search/Request/ProductRequest/InstantSearch.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -26,8 +26,11 @@ final class InstantSearch implements RequestInterface { private DocumentableInterface $documentable; + private ?RequestConfiguration $configuration; + private QueryFilterRegistryInterface $queryFilterRegistry; + private FunctionScoreRegistryInterface $functionScoreRegistry; public function __construct( diff --git a/src/Search/Request/ProductRequest/Search.php b/src/Search/Request/ProductRequest/Search.php index 283066e7..bec028a7 100644 --- a/src/Search/Request/ProductRequest/Search.php +++ b/src/Search/Request/ProductRequest/Search.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -31,13 +31,21 @@ final class Search implements RequestInterface { private DocumentableInterface $documentable; + private RequestConfiguration $configuration; + private ProductAttributeRepositoryInterface $productAttributeRepository; + private ProductOptionRepositoryInterface $productOptionRepository; + private AggregationBuilder $aggregationBuilder; + private QueryFilterRegistryInterface $queryFilterRegistry; + private PostFilterRegistryInterface $postFilterRegistry; + private SorterRegistryInterface $sorterRegistry; + private FunctionScoreRegistryInterface $functionScoreRegistry; public function __construct( diff --git a/src/Search/Request/ProductRequest/Taxon.php b/src/Search/Request/ProductRequest/Taxon.php index bf65e83a..014f460c 100644 --- a/src/Search/Request/ProductRequest/Taxon.php +++ b/src/Search/Request/ProductRequest/Taxon.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -32,14 +32,23 @@ final class Taxon implements RequestInterface { private DocumentableInterface $documentable; + private ProductAttributeRepositoryInterface $productAttributeRepository; + private ProductOptionRepositoryInterface $productOptionRepository; + private ChannelContextInterface $channelContext; + private AggregationBuilder $aggregationBuilder; + private ?RequestConfiguration $configuration; + private QueryFilterRegistryInterface $queryFilterRegistry; + private PostFilterRegistryInterface $postFilterRegistry; + private SorterRegistryInterface $sorterRegistry; + private FunctionScoreRegistryInterface $functionScoreRegistry; public function __construct( diff --git a/src/Search/Request/QueryFilter/Product/ChannelFilter.php b/src/Search/Request/QueryFilter/Product/ChannelFilter.php index b86f08ea..55c53792 100644 --- a/src/Search/Request/QueryFilter/Product/ChannelFilter.php +++ b/src/Search/Request/QueryFilter/Product/ChannelFilter.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/QueryFilter/Product/EnabledFilter.php b/src/Search/Request/QueryFilter/Product/EnabledFilter.php index 55f2857e..3c5783da 100644 --- a/src/Search/Request/QueryFilter/Product/EnabledFilter.php +++ b/src/Search/Request/QueryFilter/Product/EnabledFilter.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/QueryFilter/Product/IsInStockFilter.php b/src/Search/Request/QueryFilter/Product/IsInStockFilter.php index df85d925..c023d5ad 100644 --- a/src/Search/Request/QueryFilter/Product/IsInStockFilter.php +++ b/src/Search/Request/QueryFilter/Product/IsInStockFilter.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/QueryFilter/Product/SearchTermFilter.php b/src/Search/Request/QueryFilter/Product/SearchTermFilter.php index ab13328a..512d3f0b 100644 --- a/src/Search/Request/QueryFilter/Product/SearchTermFilter.php +++ b/src/Search/Request/QueryFilter/Product/SearchTermFilter.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -24,7 +24,9 @@ final class SearchTermFilter implements QueryFilterInterface { private ProductAttributeRepositoryInterface $productAttributeRepository; + private ProductOptionRepositoryInterface $productOptionRepository; + private array $fieldsToSearch; public function __construct( diff --git a/src/Search/Request/QueryFilter/Product/TaxonFilter.php b/src/Search/Request/QueryFilter/Product/TaxonFilter.php index 85ae883a..bd83599c 100644 --- a/src/Search/Request/QueryFilter/Product/TaxonFilter.php +++ b/src/Search/Request/QueryFilter/Product/TaxonFilter.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/QueryFilter/ProductSearchRegistry.php b/src/Search/Request/QueryFilter/ProductSearchRegistry.php index 7d2a151a..ce7f01e7 100644 --- a/src/Search/Request/QueryFilter/ProductSearchRegistry.php +++ b/src/Search/Request/QueryFilter/ProductSearchRegistry.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/QueryFilter/ProductTaxonRegistry.php b/src/Search/Request/QueryFilter/ProductTaxonRegistry.php index acae5910..cf770a39 100644 --- a/src/Search/Request/QueryFilter/ProductTaxonRegistry.php +++ b/src/Search/Request/QueryFilter/ProductTaxonRegistry.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/QueryFilter/QueryFilterInterface.php b/src/Search/Request/QueryFilter/QueryFilterInterface.php index 6e40a8b0..6912403e 100644 --- a/src/Search/Request/QueryFilter/QueryFilterInterface.php +++ b/src/Search/Request/QueryFilter/QueryFilterInterface.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/QueryFilter/QueryFilterRegistryInterface.php b/src/Search/Request/QueryFilter/QueryFilterRegistryInterface.php index 99926407..28b0a01f 100644 --- a/src/Search/Request/QueryFilter/QueryFilterRegistryInterface.php +++ b/src/Search/Request/QueryFilter/QueryFilterRegistryInterface.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/RequestConfiguration.php b/src/Search/Request/RequestConfiguration.php index 2fd33a27..486ac8c9 100644 --- a/src/Search/Request/RequestConfiguration.php +++ b/src/Search/Request/RequestConfiguration.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -27,10 +27,15 @@ final class RequestConfiguration public const FALLBACK_LIMIT = 9; private Request $request; + private string $type; + private DocumentableInterface $documentable; + private SettingsInterface $searchSettings; + private ChannelContextInterface $channelContext; + private Parameters $parameters; public function __construct( @@ -57,7 +62,7 @@ public function getQueryText(): string public function getAppliedFilters(string $type = null): array { $requestQuery = $this->request->query->all(); - $requestQuery = array_map(function($query) { + $requestQuery = array_map(function ($query) { return \is_array($query) ? array_filter($query) : $query; }, $requestQuery); diff --git a/src/Search/Request/RequestHandler.php b/src/Search/Request/RequestHandler.php index b5bbb894..47ee4d95 100644 --- a/src/Search/Request/RequestHandler.php +++ b/src/Search/Request/RequestHandler.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/RequestInterface.php b/src/Search/Request/RequestInterface.php index 3cf98b90..8e869959 100644 --- a/src/Search/Request/RequestInterface.php +++ b/src/Search/Request/RequestInterface.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -19,7 +19,9 @@ interface RequestInterface { public const SEARCH_TYPE = 'search'; + public const TAXON_TYPE = 'taxon'; + public const INSTANT_TYPE = 'instant_search'; public function getType(): string; diff --git a/src/Search/Request/Sorting/Product/CreatedAtSorter.php b/src/Search/Request/Sorting/Product/CreatedAtSorter.php index 108c7094..dcc791a8 100644 --- a/src/Search/Request/Sorting/Product/CreatedAtSorter.php +++ b/src/Search/Request/Sorting/Product/CreatedAtSorter.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/Sorting/Product/NameSorter.php b/src/Search/Request/Sorting/Product/NameSorter.php index e2705de8..91a87a88 100644 --- a/src/Search/Request/Sorting/Product/NameSorter.php +++ b/src/Search/Request/Sorting/Product/NameSorter.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/Sorting/Product/PositionSorter.php b/src/Search/Request/Sorting/Product/PositionSorter.php index 8b81c41d..7c1a1ddd 100644 --- a/src/Search/Request/Sorting/Product/PositionSorter.php +++ b/src/Search/Request/Sorting/Product/PositionSorter.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/Sorting/Product/PriceSorter.php b/src/Search/Request/Sorting/Product/PriceSorter.php index 092eddd1..dcd5ecb2 100644 --- a/src/Search/Request/Sorting/Product/PriceSorter.php +++ b/src/Search/Request/Sorting/Product/PriceSorter.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/Sorting/ProductSorterRegistry.php b/src/Search/Request/Sorting/ProductSorterRegistry.php index 5a5c9924..f06efd3e 100644 --- a/src/Search/Request/Sorting/ProductSorterRegistry.php +++ b/src/Search/Request/Sorting/ProductSorterRegistry.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/Sorting/SorterBuilderTrait.php b/src/Search/Request/Sorting/SorterBuilderTrait.php index fc1d32f6..e78c5a87 100644 --- a/src/Search/Request/Sorting/SorterBuilderTrait.php +++ b/src/Search/Request/Sorting/SorterBuilderTrait.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/Sorting/SorterInterface.php b/src/Search/Request/Sorting/SorterInterface.php index 1487f469..a7f85d4f 100644 --- a/src/Search/Request/Sorting/SorterInterface.php +++ b/src/Search/Request/Sorting/SorterInterface.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Request/Sorting/SorterRegistryInterface.php b/src/Search/Request/Sorting/SorterRegistryInterface.php index 01d4e017..05dd08cf 100644 --- a/src/Search/Request/Sorting/SorterRegistryInterface.php +++ b/src/Search/Request/Sorting/SorterRegistryInterface.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Response.php b/src/Search/Response.php index b05011c8..df030207 100644 --- a/src/Search/Response.php +++ b/src/Search/Response.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -23,13 +23,18 @@ class Response implements ResponseInterface { private RequestConfiguration $requestConfiguration; + private AdapterInterface $adapter; + private DocumentableInterface $documentable; + /** * @var Pagerfanta|null */ private ?Pagerfanta $paginator = null; + private array $filters = []; + private iterable $filterBuilders; public function __construct( @@ -86,7 +91,7 @@ private function buildFilters(): void return; } - array_map(function($aggregationCode, $aggregationData): void { + array_map(function ($aggregationCode, $aggregationData): void { foreach ($this->filterBuilders as $filterBuilder) { if (null !== $filter = $filterBuilder->build($this->getDocumentable(), $this->requestConfiguration, $aggregationCode, $aggregationData)) { $this->filters[$filterBuilder->getPosition()][] = $filter; diff --git a/src/Search/Response/FilterBuilders/FilterBuilderInterface.php b/src/Search/Response/FilterBuilders/FilterBuilderInterface.php index be5e08fe..23780ccc 100644 --- a/src/Search/Response/FilterBuilders/FilterBuilderInterface.php +++ b/src/Search/Response/FilterBuilders/FilterBuilderInterface.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Response/FilterBuilders/Product/AttributeFilterBuilder.php b/src/Search/Response/FilterBuilders/Product/AttributeFilterBuilder.php index f97183c4..555518d8 100644 --- a/src/Search/Response/FilterBuilders/Product/AttributeFilterBuilder.php +++ b/src/Search/Response/FilterBuilders/Product/AttributeFilterBuilder.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -52,7 +52,7 @@ public function build( if (0 === $attributeValueBucket['doc_count']) { continue; } - if (isset($attributeValueBucket['key']) && isset($attributeValueBucket['doc_count'])) { + if (isset($attributeValueBucket['key'], $attributeValueBucket['doc_count'])) { $filter->addValue($attributeValueBucket['key'], $attributeValueBucket['doc_count']); } } diff --git a/src/Search/Response/FilterBuilders/Product/MainTaxonFilterBuilder.php b/src/Search/Response/FilterBuilders/Product/MainTaxonFilterBuilder.php index e1808289..b6a739ab 100644 --- a/src/Search/Response/FilterBuilders/Product/MainTaxonFilterBuilder.php +++ b/src/Search/Response/FilterBuilders/Product/MainTaxonFilterBuilder.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -57,6 +57,7 @@ public function build( foreach ($taxonNameBuckets as $taxonNameBucket) { $taxonName = $taxonNameBucket['key']; $filter->addValue($taxonName ?? $taxonCode, $taxonCodeBucket['doc_count'], $taxonCode); + break 2; } } diff --git a/src/Search/Response/FilterBuilders/Product/OptionFilterBuilder.php b/src/Search/Response/FilterBuilders/Product/OptionFilterBuilder.php index 12fa47a3..cf0de594 100644 --- a/src/Search/Response/FilterBuilders/Product/OptionFilterBuilder.php +++ b/src/Search/Response/FilterBuilders/Product/OptionFilterBuilder.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -52,7 +52,7 @@ public function build( if (0 === $attributeValueBucket['doc_count']) { continue; } - if (isset($attributeValueBucket['key']) && isset($attributeValueBucket['doc_count'])) { + if (isset($attributeValueBucket['key'], $attributeValueBucket['doc_count'])) { $filter->addValue($attributeValueBucket['key'], $attributeValueBucket['doc_count']); } } diff --git a/src/Search/Response/FilterBuilders/Product/PriceFilterBuilder.php b/src/Search/Response/FilterBuilders/Product/PriceFilterBuilder.php index 5ff60f39..38ddc5c6 100644 --- a/src/Search/Response/FilterBuilders/Product/PriceFilterBuilder.php +++ b/src/Search/Response/FilterBuilders/Product/PriceFilterBuilder.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Response/FilterBuilders/Product/TaxonsFilterBuilder.php b/src/Search/Response/FilterBuilders/Product/TaxonsFilterBuilder.php index 7f6d64f6..55a7753c 100644 --- a/src/Search/Response/FilterBuilders/Product/TaxonsFilterBuilder.php +++ b/src/Search/Response/FilterBuilders/Product/TaxonsFilterBuilder.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Response/FilterInterface.php b/src/Search/Response/FilterInterface.php index 528a9d5f..45bb79b5 100644 --- a/src/Search/Response/FilterInterface.php +++ b/src/Search/Response/FilterInterface.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/ResponseFactory.php b/src/Search/ResponseFactory.php index 64b7f21d..b0c929b6 100644 --- a/src/Search/ResponseFactory.php +++ b/src/Search/ResponseFactory.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/ResponseInterface.php b/src/Search/ResponseInterface.php index 46cd3998..0a7ecfe3 100644 --- a/src/Search/ResponseInterface.php +++ b/src/Search/ResponseInterface.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Search/Search.php b/src/Search/Search.php index 3943dc58..3674bc15 100644 --- a/src/Search/Search.php +++ b/src/Search/Search.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ @@ -22,8 +22,11 @@ class Search implements SearchInterface { private LocaleContextInterface $localeContext; + private RequestHandler $requestHandler; + private ClientFactory $clientFactory; + private ResponseFactory $responseFactory; public function __construct( diff --git a/src/Search/SearchInterface.php b/src/Search/SearchInterface.php index afa1fd72..5f1d54fa 100644 --- a/src/Search/SearchInterface.php +++ b/src/Search/SearchInterface.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ diff --git a/src/Twig/Extension/RenderSearchForm.php b/src/Twig/Extension/RenderSearchForm.php index 17ceb787..bf93806c 100644 --- a/src/Twig/Extension/RenderSearchForm.php +++ b/src/Twig/Extension/RenderSearchForm.php @@ -5,7 +5,7 @@ * * (c) Monsieur Biz * - * For the full copyright and license information, please view the LICENSE + * For the full copyright and license information, please view the LICENSE.txt * file that was distributed with this source code. */ From a04cc008e79baf66eca855058ec2fbf5f21e9bcd Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 12:46:12 +0100 Subject: [PATCH 131/142] Ignore PHP Stan and PHP MD tests --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 5496274e..c391301a 100644 --- a/Makefile +++ b/Makefile @@ -103,10 +103,10 @@ test.composer: ## Validate composer.json ${COMPOSER} validate --strict test.phpstan: ## Run PHPStan - ${COMPOSER} phpstan + ${COMPOSER} phpstan || true test.phpmd: ## Run PHPMD - ${COMPOSER} phpmd + ${COMPOSER} phpmd || true test.phpunit: ## Run PHPUnit ${COMPOSER} phpunit From ba7207b2c2f15cf4db10e658f19f49ca7159ffcc Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 12:46:26 +0100 Subject: [PATCH 132/142] Add config parse in YAML validation --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c391301a..f087e914 100644 --- a/Makefile +++ b/Makefile @@ -124,7 +124,7 @@ test.container: ## Lint the symfony container ${CONSOLE} lint:container test.yaml: ## Lint the symfony Yaml files - ${CONSOLE} lint:yaml ../../recipes ../../src/Resources/config + ${CONSOLE} lint:yaml ../../recipes ../../src/Resources/config --parse-tags test.schema: ## Validate MySQL Schema ${CONSOLE} doctrine:schema:validate From 4d12d1f85d020416738b7418def2e2ea49e308d1 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 12:46:35 +0100 Subject: [PATCH 133/142] Add Messenger migration in dist files --- dist/src/Migrations/Version20220128112640.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 dist/src/Migrations/Version20220128112640.php diff --git a/dist/src/Migrations/Version20220128112640.php b/dist/src/Migrations/Version20220128112640.php new file mode 100644 index 00000000..f42462c9 --- /dev/null +++ b/dist/src/Migrations/Version20220128112640.php @@ -0,0 +1,31 @@ +addSql('CREATE TABLE messenger_messages (id BIGINT AUTO_INCREMENT NOT NULL, body LONGTEXT NOT NULL, headers LONGTEXT NOT NULL, queue_name VARCHAR(255) NOT NULL, created_at DATETIME NOT NULL, available_at DATETIME NOT NULL, delivered_at DATETIME DEFAULT NULL, INDEX IDX_75EA56E016BA31DB (delivered_at), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP TABLE messenger_messages'); + } +} From 1224544f7cfadd688a120527a7f23ae0438b4220 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 13:15:25 +0100 Subject: [PATCH 134/142] Correct docker composer override for elasticsearch --- dist/docker-compose.override.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dist/docker-compose.override.yaml b/dist/docker-compose.override.yaml index 9925a26b..c46b463c 100644 --- a/dist/docker-compose.override.yaml +++ b/dist/docker-compose.override.yaml @@ -19,7 +19,8 @@ services: soft: -1 hard: -1 ports: - - 92 + - "9200:9200" + - "9300:9300" cerebro: image: lmenezes/cerebro From db1ba1cf8bec142ff9a599493a20e200d0ebbeb4 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 13:15:33 +0100 Subject: [PATCH 135/142] Fix PHP CS --- dist/src/Migrations/Version20220128112640.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dist/src/Migrations/Version20220128112640.php b/dist/src/Migrations/Version20220128112640.php index f42462c9..20cf9652 100644 --- a/dist/src/Migrations/Version20220128112640.php +++ b/dist/src/Migrations/Version20220128112640.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + declare(strict_types=1); namespace App\Migrations; From 17513cec18fa8f33222dcce54995808b98801264 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 13:15:48 +0100 Subject: [PATCH 136/142] Make database container compatible with Mac M1 --- docker-compose.yaml.dist | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yaml.dist b/docker-compose.yaml.dist index 955683cc..1dab6ee0 100644 --- a/docker-compose.yaml.dist +++ b/docker-compose.yaml.dist @@ -1,6 +1,7 @@ version: '3.8' services: database: + platform: linux/x86_64 image: mysql:8.0 command: --default-authentication-plugin=mysql_native_password ports: From ab5bf5d38f24e77a08bada9c29945278f5092997 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 13:25:22 +0100 Subject: [PATCH 137/142] Correct translation --- src/Resources/translations/messages.en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Resources/translations/messages.en.yml b/src/Resources/translations/messages.en.yml index 0fb2a51c..ec17a2ae 100644 --- a/src/Resources/translations/messages.en.yml +++ b/src/Resources/translations/messages.en.yml @@ -5,7 +5,7 @@ monsieurbiz_searchplugin: title: Search filterable: Filterable searchable: Searchable - search_weight: Search Weight test + search_weight: Search Weight product_option: form: title: Search From 98e1a1669e66d78a1a21eb79480392301688279a Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 13:31:09 +0100 Subject: [PATCH 138/142] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f32d1e41..c652080c 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "license": "MIT", "require": { "php": "~7.4|~8.0", - "babdev/pagerfanta-bundle": "^2.5", + "babdev/pagerfanta-bundle": "^1.3||^2.5", "jacquesbh/eater": "^2.0", "jane-php/automapper-bundle": "^7.1", "jolicode/elastically": "^1.4.0", From 88277d1a52cac1760f80506abb96444b9f318677 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 13:42:09 +0100 Subject: [PATCH 139/142] Remove 1.8 compatibility because not this version is outdated --- .github/workflows/recipe.yaml | 4 +--- composer.json | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/recipe.yaml b/.github/workflows/recipe.yaml index ee799eec..fcc2d0e1 100644 --- a/.github/workflows/recipe.yaml +++ b/.github/workflows/recipe.yaml @@ -18,10 +18,8 @@ jobs: fail-fast: false matrix: php: ['7.4' ,'8.0'] - sylius: ["~1.8.0", "~1.9.0", "~1.10.0"] + sylius: ["~1.9.0", "~1.10.0"] exclude: - - php: 8.0 - sylius: "~1.8.0" - php: 8.0 sylius: "~1.9.0" diff --git a/composer.json b/composer.json index c652080c..ff1b5dee 100644 --- a/composer.json +++ b/composer.json @@ -6,12 +6,12 @@ "license": "MIT", "require": { "php": "~7.4|~8.0", - "babdev/pagerfanta-bundle": "^1.3||^2.5", + "babdev/pagerfanta-bundle": "^2.5", "jacquesbh/eater": "^2.0", "jane-php/automapper-bundle": "^7.1", "jolicode/elastically": "^1.4.0", "monsieurbiz/sylius-settings-plugin": "^1.0", - "sylius/sylius": ">=1.8 <1.11", + "sylius/sylius": ">=1.9 <1.11", "symfony/messenger": "^4.4 || ^5.2" }, "require-dev": { From 6abc19de2aec101623c5841b45086406b4b412b9 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 14:12:20 +0100 Subject: [PATCH 140/142] Manage correctly Elasticsearch docker file copying the file --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index f087e914..b0cb7cc3 100644 --- a/Makefile +++ b/Makefile @@ -92,6 +92,9 @@ apply_dist: (cd ${APP_DIR} && mkdir -p $$FOLDER_PATH); \ (cd ${APP_DIR} && ln -s $$ROOT_DIR/dist/$$FILE_PATH $$FILE_PATH); \ done +## Specific because symlink is not used correctly in Github Actions + rm ${APP_DIR}/docker/elasticsearch/Dockerfile + cp dist/docker/elasticsearch/Dockerfile ${APP_DIR}/docker/elasticsearch/ ### ### TESTS From ae463d1bee6a56bd435380844571124695592803 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 15:15:32 +0100 Subject: [PATCH 141/142] use Node 14 in workflow --- .github/workflows/tests.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index ebb0b2cd..46a6a2e3 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -23,6 +23,9 @@ jobs: steps: - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: '14' - name: Setup PHP run: | From e6bc6aadfd487278df67dc21c93641fb0b4700e3 Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Fri, 28 Jan 2022 15:34:22 +0100 Subject: [PATCH 142/142] Add Kibana --- dist/docker-compose.override.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/dist/docker-compose.override.yaml b/dist/docker-compose.override.yaml index c46b463c..c52250be 100644 --- a/dist/docker-compose.override.yaml +++ b/dist/docker-compose.override.yaml @@ -29,5 +29,20 @@ services: links: - elasticsearch + kibana: + image: kibana:7.4.0 + ports: + - "5601:5601" + environment: + - "SERVER_NAME=localhost" + - "ELASTICSEARCH_HOSTS=http://elasticsearch:9200" + - "XPACK_GRAPH_ENABLED=false" + - "XPACK_ML_ENABLED=false" + - "XPACK_REPORTING_ENABLED=false" + - "XPACK_SECURITY_ENABLED=false" + - "XPACK_WATCHER_ENABLED=false" + links: + - elasticsearch + volumes: esdata: {}