From 92288a654e5f839ab2158a44cc83eb0be4d13c2e Mon Sep 17 00:00:00 2001 From: Loulier Guillaume Date: Mon, 2 Nov 2020 12:00:00 +0100 Subject: [PATCH] refactor(core): improvements on Search --- .github/CONTRIBUTING.md | 11 + .github/workflows/push.yml | 6 +- composer.json | 1 + composer.lock | 1215 ++++++----------- doc/api_platform.md | 3 - doc/dto.md | 2 +- doc/search.md | 49 +- .../Serializer/UuidDenormalizer.php | 4 +- src/Command/DeleteIndexCommand.php | 14 +- src/DependencyInjection/Configuration.php | 2 +- .../MeiliSearchExtension.php | 2 +- src/Document/DocumentLoader.php | 21 +- src/EventSubscriber/ExceptionSubscriber.php | 25 +- .../SynonymsEventSubscriber.php | 26 +- .../SearchExpressionLanguage.php | 21 + .../SearchExpressionLanguageProvider.php | 40 + src/Index/IndexOrchestrator.php | 4 +- src/MeiliSearchBundle.php | 2 - src/Search/Search.php | 130 +- src/Search/SearchEntryPoint.php | 4 +- .../SearchResultCacheOrchestratorTest.php | 41 +- tests/Command/DeleteIndexCommandTest.php | 24 +- .../DocumentEventSubscriberTest.php | 50 +- .../ExceptionSubscriberTest.php | 4 +- .../IndexEventSubscriberTest.php | 25 +- .../SearchEventSubscriberTest.php | 10 +- .../SynonymsEventSubscriberTest.php | 35 +- .../SearchExpressionLanguageProviderTest.php | 24 + .../SearchExpressionLanguageTest.php | 69 + tests/Index/IndexOrchestratorTest.php | 25 + tests/Index/SynonymsOrchestratorTest.php | 14 +- tests/Messenger/AddIndexMessageTest.php | 7 + tests/Metadata/IndexMetadataTest.php | 17 +- tests/Search/SearchEntryPointTest.php | 91 +- tests/Search/SearchTest.php | 183 +++ .../IndexMetadataDenormalizerTest.php | 1 + 36 files changed, 1268 insertions(+), 934 deletions(-) delete mode 100644 doc/api_platform.md create mode 100644 src/ExpressionLanguage/SearchExpressionLanguage.php create mode 100644 src/ExpressionLanguage/SearchExpressionLanguageProvider.php create mode 100644 tests/ExpressionLanguage/SearchExpressionLanguageProviderTest.php create mode 100644 tests/ExpressionLanguage/SearchExpressionLanguageTest.php diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 2a5f85e..1c72d79 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -17,10 +17,21 @@ Here's the allowed contexts: Here's the allowed scopes: +- bridge +- cache - client - command - dic +- document - collector +- dump +- events +- form +- messenger +- metadata +- provider +- result +- search - docker - tests diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index e2c40f9..e48694c 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -64,14 +64,10 @@ jobs: with: args: --dry-run . - # —— Docker ———————————————————————————————————————————————————————— - - name: Docker setup - run: docker run -d -p 7700:7700 getmeili/meilisearch:latest ./meilisearch --master-key=masterKey --no-analytics=true - # —— PHPUnit ——————————————————————————————————————————————————————— - name: PHPUnit run: php vendor/bin/phpunit tests # —— Infection ————————————————————————————————————————————————————— - name: Infection - run: php vendor/bin/infection --no-progress --min-covered-msi=90 --min-msi=80 + run: php vendor/bin/infection --no-progress --min-covered-msi=90 --min-msi=90 diff --git a/composer.json b/composer.json index 4a89e35..c836a50 100644 --- a/composer.json +++ b/composer.json @@ -115,6 +115,7 @@ "ramsey/uuid": "^3.8", "symfony/cache": "^4.4|^5.0", "symfony/event-dispatcher": "^4.4|^5.0", + "symfony/expression-language": "^4.4|^5.0", "symfony/form": "^4.4|^5.0", "symfony/http-client": "^4.4|^5.0", "symfony/messenger": "^4.4|^5.0", diff --git a/composer.lock b/composer.lock index 05f7e28..101599c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d2d70622b2382ba4340ff8b1a01c787e", + "content-hash": "746591d3d4195fbf954dd345f7358ea5", "packages": [ { "name": "clue/stream-filter", @@ -791,16 +791,16 @@ }, { "name": "symfony/config", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "6ad8be6e1280f6734150d8a04a9160dd34ceb191" + "reference": "11baeefa4c179d6908655a7b6be728f62367c193" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/6ad8be6e1280f6734150d8a04a9160dd34ceb191", - "reference": "6ad8be6e1280f6734150d8a04a9160dd34ceb191", + "url": "https://api.github.com/repos/symfony/config/zipball/11baeefa4c179d6908655a7b6be728f62367c193", + "reference": "11baeefa4c179d6908655a7b6be728f62367c193", "shasum": "" }, "require": { @@ -824,11 +824,6 @@ "symfony/yaml": "To use the yaml reference dumper" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Config\\": "" @@ -867,20 +862,20 @@ "type": "tidelift" } ], - "time": "2020-09-02T16:23:27+00:00" + "time": "2020-10-24T12:01:57+00:00" }, { "name": "symfony/console", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "ae789a8a2ad189ce7e8216942cdb9b77319f5eb8" + "reference": "e0b2c29c0fa6a69089209bbe8fcff4df2a313d0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/ae789a8a2ad189ce7e8216942cdb9b77319f5eb8", - "reference": "ae789a8a2ad189ce7e8216942cdb9b77319f5eb8", + "url": "https://api.github.com/repos/symfony/console/zipball/e0b2c29c0fa6a69089209bbe8fcff4df2a313d0e", + "reference": "e0b2c29c0fa6a69089209bbe8fcff4df2a313d0e", "shasum": "" }, "require": { @@ -917,11 +912,6 @@ "symfony/process": "" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Console\\": "" @@ -960,20 +950,20 @@ "type": "tidelift" } ], - "time": "2020-10-07T15:23:00+00:00" + "time": "2020-10-24T12:01:57+00:00" }, { "name": "symfony/dependency-injection", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "2dea4a3ef2eb79138354c1d49e9372cc921af20b" + "reference": "829ca6bceaf68036a123a13a979f3c89289eae78" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/2dea4a3ef2eb79138354c1d49e9372cc921af20b", - "reference": "2dea4a3ef2eb79138354c1d49e9372cc921af20b", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/829ca6bceaf68036a123a13a979f3c89289eae78", + "reference": "829ca6bceaf68036a123a13a979f3c89289eae78", "shasum": "" }, "require": { @@ -1006,11 +996,6 @@ "symfony/yaml": "" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\DependencyInjection\\": "" @@ -1049,7 +1034,7 @@ "type": "tidelift" } ], - "time": "2020-10-01T12:14:45+00:00" + "time": "2020-10-27T10:11:13+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1117,16 +1102,16 @@ }, { "name": "symfony/error-handler", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "5e4d8ef8d71822922d1eebd130219ae3491a5ca9" + "reference": "a154f2b12fd1ec708559ba73ed58bd1304e55718" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/5e4d8ef8d71822922d1eebd130219ae3491a5ca9", - "reference": "5e4d8ef8d71822922d1eebd130219ae3491a5ca9", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/a154f2b12fd1ec708559ba73ed58bd1304e55718", + "reference": "a154f2b12fd1ec708559ba73ed58bd1304e55718", "shasum": "" }, "require": { @@ -1141,11 +1126,6 @@ "symfony/serializer": "^4.4|^5.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\ErrorHandler\\": "" @@ -1184,20 +1164,20 @@ "type": "tidelift" } ], - "time": "2020-10-02T08:49:02+00:00" + "time": "2020-10-24T12:01:57+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "d5de97d6af175a9e8131c546db054ca32842dd0f" + "reference": "26f4edae48c913fc183a3da0553fe63bdfbd361a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d5de97d6af175a9e8131c546db054ca32842dd0f", - "reference": "d5de97d6af175a9e8131c546db054ca32842dd0f", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/26f4edae48c913fc183a3da0553fe63bdfbd361a", + "reference": "26f4edae48c913fc183a3da0553fe63bdfbd361a", "shasum": "" }, "require": { @@ -1228,11 +1208,6 @@ "symfony/http-kernel": "" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\EventDispatcher\\": "" @@ -1271,7 +1246,7 @@ "type": "tidelift" } ], - "time": "2020-09-18T14:27:32+00:00" + "time": "2020-10-24T12:01:57+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -1351,16 +1326,16 @@ }, { "name": "symfony/filesystem", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "1a8697545a8d87b9f2f6b1d32414199cc5e20aae" + "reference": "df08650ea7aee2d925380069c131a66124d79177" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/1a8697545a8d87b9f2f6b1d32414199cc5e20aae", - "reference": "1a8697545a8d87b9f2f6b1d32414199cc5e20aae", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/df08650ea7aee2d925380069c131a66124d79177", + "reference": "df08650ea7aee2d925380069c131a66124d79177", "shasum": "" }, "require": { @@ -1368,11 +1343,6 @@ "symfony/polyfill-ctype": "~1.8" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Filesystem\\": "" @@ -1411,31 +1381,26 @@ "type": "tidelift" } ], - "time": "2020-09-27T14:02:37+00:00" + "time": "2020-10-24T12:01:57+00:00" }, { "name": "symfony/finder", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "2c3ba7ad6884e6c4451ce2340e2dc23f6fa3e0d8" + "reference": "e70eb5a69c2ff61ea135a13d2266e8914a67b3a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/2c3ba7ad6884e6c4451ce2340e2dc23f6fa3e0d8", - "reference": "2c3ba7ad6884e6c4451ce2340e2dc23f6fa3e0d8", + "url": "https://api.github.com/repos/symfony/finder/zipball/e70eb5a69c2ff61ea135a13d2266e8914a67b3a0", + "reference": "e70eb5a69c2ff61ea135a13d2266e8914a67b3a0", "shasum": "" }, "require": { "php": ">=7.2.5" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Finder\\": "" @@ -1474,7 +1439,7 @@ "type": "tidelift" } ], - "time": "2020-09-02T16:23:27+00:00" + "time": "2020-10-24T12:01:57+00:00" }, { "name": "symfony/http-client-contracts", @@ -1554,16 +1519,16 @@ }, { "name": "symfony/http-foundation", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "353b42e7b4fd1c898aab09a059466c9cea74039b" + "reference": "a2860ec970404b0233ab1e59e0568d3277d32b6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/353b42e7b4fd1c898aab09a059466c9cea74039b", - "reference": "353b42e7b4fd1c898aab09a059466c9cea74039b", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/a2860ec970404b0233ab1e59e0568d3277d32b6f", + "reference": "a2860ec970404b0233ab1e59e0568d3277d32b6f", "shasum": "" }, "require": { @@ -1582,11 +1547,6 @@ "symfony/mime": "To use the file extension guesser" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\HttpFoundation\\": "" @@ -1625,20 +1585,20 @@ "type": "tidelift" } ], - "time": "2020-09-27T14:14:57+00:00" + "time": "2020-10-24T12:01:57+00:00" }, { "name": "symfony/http-kernel", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "1764b87d2f10d5c9ce6e4850fe27934116d89708" + "reference": "a13b3c4d994a4fd051f4c6800c5e33c9508091dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/1764b87d2f10d5c9ce6e4850fe27934116d89708", - "reference": "1764b87d2f10d5c9ce6e4850fe27934116d89708", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/a13b3c4d994a4fd051f4c6800c5e33c9508091dd", + "reference": "a13b3c4d994a4fd051f4c6800c5e33c9508091dd", "shasum": "" }, "require": { @@ -1696,11 +1656,6 @@ "symfony/dependency-injection": "" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\HttpKernel\\": "" @@ -1739,20 +1694,20 @@ "type": "tidelift" } ], - "time": "2020-10-04T07:57:28+00:00" + "time": "2020-10-28T05:55:23+00:00" }, { "name": "symfony/options-resolver", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "4c7e155bf7d93ea4ba3824d5a14476694a5278dd" + "reference": "c6a02905e4ffc7a1498e8ee019db2b477cd1cc02" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/4c7e155bf7d93ea4ba3824d5a14476694a5278dd", - "reference": "4c7e155bf7d93ea4ba3824d5a14476694a5278dd", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/c6a02905e4ffc7a1498e8ee019db2b477cd1cc02", + "reference": "c6a02905e4ffc7a1498e8ee019db2b477cd1cc02", "shasum": "" }, "require": { @@ -1761,11 +1716,6 @@ "symfony/polyfill-php80": "^1.15" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\OptionsResolver\\": "" @@ -1809,24 +1759,24 @@ "type": "tidelift" } ], - "time": "2020-09-27T03:44:28+00:00" + "time": "2020-10-24T12:01:57+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.18.1", + "version": "v1.20.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "1c302646f6efc070cd46856e600e5e0684d6b454" + "reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/1c302646f6efc070cd46856e600e5e0684d6b454", - "reference": "1c302646f6efc070cd46856e600e5e0684d6b454", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f4ba089a5b6366e453971d3aad5fe8e897b37f41", + "reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" }, "suggest": { "ext-ctype": "For best performance" @@ -1834,7 +1784,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.18-dev" + "dev-main": "1.20-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1885,24 +1835,24 @@ "type": "tidelift" } ], - "time": "2020-07-14T12:35:20+00:00" + "time": "2020-10-23T14:02:19+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.18.1", + "version": "v1.20.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b740103edbdcc39602239ee8860f0f45a8eb9aa5" + "reference": "c7cf3f858ec7d70b89559d6e6eb1f7c2517d479c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b740103edbdcc39602239ee8860f0f45a8eb9aa5", - "reference": "b740103edbdcc39602239ee8860f0f45a8eb9aa5", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/c7cf3f858ec7d70b89559d6e6eb1f7c2517d479c", + "reference": "c7cf3f858ec7d70b89559d6e6eb1f7c2517d479c", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" }, "suggest": { "ext-intl": "For best performance" @@ -1910,7 +1860,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.18-dev" + "dev-main": "1.20-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1963,24 +1913,24 @@ "type": "tidelift" } ], - "time": "2020-07-14T12:35:20+00:00" + "time": "2020-10-23T14:02:19+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.18.1", + "version": "v1.20.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e" + "reference": "727d1096295d807c309fb01a851577302394c897" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e", - "reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/727d1096295d807c309fb01a851577302394c897", + "reference": "727d1096295d807c309fb01a851577302394c897", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" }, "suggest": { "ext-intl": "For best performance" @@ -1988,7 +1938,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.18-dev" + "dev-main": "1.20-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2044,24 +1994,24 @@ "type": "tidelift" } ], - "time": "2020-07-14T12:35:20+00:00" + "time": "2020-10-23T14:02:19+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.18.1", + "version": "v1.20.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a" + "reference": "39d483bdf39be819deabf04ec872eb0b2410b531" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/a6977d63bf9a0ad4c65cd352709e230876f9904a", - "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/39d483bdf39be819deabf04ec872eb0b2410b531", + "reference": "39d483bdf39be819deabf04ec872eb0b2410b531", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" }, "suggest": { "ext-mbstring": "For best performance" @@ -2069,7 +2019,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.18-dev" + "dev-main": "1.20-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2121,29 +2071,29 @@ "type": "tidelift" } ], - "time": "2020-07-14T12:35:20+00:00" + "time": "2020-10-23T14:02:19+00:00" }, { "name": "symfony/polyfill-php73", - "version": "v1.18.1", + "version": "v1.20.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "fffa1a52a023e782cdcc221d781fe1ec8f87fcca" + "reference": "8ff431c517be11c78c48a39a66d37431e26a6bed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fffa1a52a023e782cdcc221d781fe1ec8f87fcca", - "reference": "fffa1a52a023e782cdcc221d781fe1ec8f87fcca", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/8ff431c517be11c78c48a39a66d37431e26a6bed", + "reference": "8ff431c517be11c78c48a39a66d37431e26a6bed", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.18-dev" + "dev-main": "1.20-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2197,29 +2147,29 @@ "type": "tidelift" } ], - "time": "2020-07-14T12:35:20+00:00" + "time": "2020-10-23T14:02:19+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.18.1", + "version": "v1.20.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981" + "reference": "e70aa8b064c5b72d3df2abd5ab1e90464ad009de" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/d87d5766cbf48d72388a9f6b85f280c8ad51f981", - "reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/e70aa8b064c5b72d3df2abd5ab1e90464ad009de", + "reference": "e70aa8b064c5b72d3df2abd5ab1e90464ad009de", "shasum": "" }, "require": { - "php": ">=7.0.8" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.18-dev" + "dev-main": "1.20-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2277,20 +2227,20 @@ "type": "tidelift" } ], - "time": "2020-07-14T12:35:20+00:00" + "time": "2020-10-23T14:02:19+00:00" }, { "name": "symfony/property-access", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/property-access.git", - "reference": "4c43f7ff784e1e3ee1c96e15f76b342af6617b39" + "reference": "5d77df9a88600797d02c7937c153965ba3537933" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-access/zipball/4c43f7ff784e1e3ee1c96e15f76b342af6617b39", - "reference": "4c43f7ff784e1e3ee1c96e15f76b342af6617b39", + "url": "https://api.github.com/repos/symfony/property-access/zipball/5d77df9a88600797d02c7937c153965ba3537933", + "reference": "5d77df9a88600797d02c7937c153965ba3537933", "shasum": "" }, "require": { @@ -2305,11 +2255,6 @@ "psr/cache-implementation": "To cache access methods." }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\PropertyAccess\\": "" @@ -2359,20 +2304,20 @@ "type": "tidelift" } ], - "time": "2020-09-02T16:23:27+00:00" + "time": "2020-10-24T12:01:57+00:00" }, { "name": "symfony/property-info", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/property-info.git", - "reference": "22518930091e0bdb249694efc509e3697f7e325e" + "reference": "fc15c51f829887b62a94a917ba793f51e80ea3e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/22518930091e0bdb249694efc509e3697f7e325e", - "reference": "22518930091e0bdb249694efc509e3697f7e325e", + "url": "https://api.github.com/repos/symfony/property-info/zipball/fc15c51f829887b62a94a917ba793f51e80ea3e1", + "reference": "fc15c51f829887b62a94a917ba793f51e80ea3e1", "shasum": "" }, "require": { @@ -2399,11 +2344,6 @@ "symfony/serializer": "To use Serializer metadata" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\PropertyInfo\\": "" @@ -2450,20 +2390,20 @@ "type": "tidelift" } ], - "time": "2020-09-07T05:10:28+00:00" + "time": "2020-10-24T12:01:57+00:00" }, { "name": "symfony/serializer", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/serializer.git", - "reference": "6b673b802dabd2bcf7cab05d04d2d8ef8891b952" + "reference": "20d3c6c58c41344a427488c0d2902bfbfbe17ddb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/6b673b802dabd2bcf7cab05d04d2d8ef8891b952", - "reference": "6b673b802dabd2bcf7cab05d04d2d8ef8891b952", + "url": "https://api.github.com/repos/symfony/serializer/zipball/20d3c6c58c41344a427488c0d2902bfbfbe17ddb", + "reference": "20d3c6c58c41344a427488c0d2902bfbfbe17ddb", "shasum": "" }, "require": { @@ -2504,11 +2444,6 @@ "symfony/yaml": "For using the default YAML mapping loader." }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Serializer\\": "" @@ -2547,7 +2482,7 @@ "type": "tidelift" } ], - "time": "2020-10-03T13:58:17+00:00" + "time": "2020-10-24T12:01:57+00:00" }, { "name": "symfony/service-contracts", @@ -2627,16 +2562,16 @@ }, { "name": "symfony/string", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "4a9afe9d07bac506f75bcee8ed3ce76da5a9343e" + "reference": "a97573e960303db71be0dd8fda9be3bca5e0feea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/4a9afe9d07bac506f75bcee8ed3ce76da5a9343e", - "reference": "4a9afe9d07bac506f75bcee8ed3ce76da5a9343e", + "url": "https://api.github.com/repos/symfony/string/zipball/a97573e960303db71be0dd8fda9be3bca5e0feea", + "reference": "a97573e960303db71be0dd8fda9be3bca5e0feea", "shasum": "" }, "require": { @@ -2654,11 +2589,6 @@ "symfony/var-exporter": "^4.4|^5.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\String\\": "" @@ -2708,20 +2638,20 @@ "type": "tidelift" } ], - "time": "2020-09-15T12:23:47+00:00" + "time": "2020-10-24T12:01:57+00:00" }, { "name": "symfony/var-dumper", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "c976c115a0d788808f7e71834c8eb0844f678d02" + "reference": "4e13f3fcefb1fcaaa5efb5403581406f4e840b9a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/c976c115a0d788808f7e71834c8eb0844f678d02", - "reference": "c976c115a0d788808f7e71834c8eb0844f678d02", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/4e13f3fcefb1fcaaa5efb5403581406f4e840b9a", + "reference": "4e13f3fcefb1fcaaa5efb5403581406f4e840b9a", "shasum": "" }, "require": { @@ -2748,11 +2678,6 @@ "Resources/bin/var-dump-server" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "files": [ "Resources/functions/dump.php" @@ -2798,7 +2723,7 @@ "type": "tidelift" } ], - "time": "2020-09-18T14:27:32+00:00" + "time": "2020-10-27T10:11:13+00:00" } ], "packages-dev": [ @@ -2951,170 +2876,6 @@ ], "time": "2020-08-28T16:23:13+00:00" }, - { - "name": "composer/ca-bundle", - "version": "1.2.8", - "source": { - "type": "git", - "url": "https://github.com/composer/ca-bundle.git", - "reference": "8a7ecad675253e4654ea05505233285377405215" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/8a7ecad675253e4654ea05505233285377405215", - "reference": "8a7ecad675253e4654ea05505233285377405215", - "shasum": "" - }, - "require": { - "ext-openssl": "*", - "ext-pcre": "*", - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8", - "psr/log": "^1.0", - "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\CaBundle\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", - "keywords": [ - "cabundle", - "cacert", - "certificate", - "ssl", - "tls" - ], - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2020-08-23T12:54:47+00:00" - }, - { - "name": "composer/composer", - "version": "1.10.15", - "source": { - "type": "git", - "url": "https://github.com/composer/composer.git", - "reference": "547c9ee73fe26c77af09a0ea16419176b1cdbd12" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/547c9ee73fe26c77af09a0ea16419176b1cdbd12", - "reference": "547c9ee73fe26c77af09a0ea16419176b1cdbd12", - "shasum": "" - }, - "require": { - "composer/ca-bundle": "^1.0", - "composer/semver": "^1.0", - "composer/spdx-licenses": "^1.2", - "composer/xdebug-handler": "^1.1", - "justinrainbow/json-schema": "^5.2.10", - "php": "^5.3.2 || ^7.0", - "psr/log": "^1.0", - "seld/jsonlint": "^1.4", - "seld/phar-utils": "^1.0", - "symfony/console": "^2.7 || ^3.0 || ^4.0 || ^5.0", - "symfony/filesystem": "^2.7 || ^3.0 || ^4.0 || ^5.0", - "symfony/finder": "^2.7 || ^3.0 || ^4.0 || ^5.0", - "symfony/process": "^2.7 || ^3.0 || ^4.0 || ^5.0" - }, - "conflict": { - "symfony/console": "2.8.38" - }, - "require-dev": { - "phpspec/prophecy": "^1.10", - "symfony/phpunit-bridge": "^4.2" - }, - "suggest": { - "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", - "ext-zip": "Enabling the zip extension allows you to unzip archives", - "ext-zlib": "Allow gzip compression of HTTP requests" - }, - "bin": [ - "bin/composer" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\": "src/Composer" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.", - "homepage": "https://getcomposer.org/", - "keywords": [ - "autoload", - "dependency", - "package" - ], - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2020-10-13T13:59:09+00:00" - }, { "name": "composer/package-versions-deprecated", "version": "1.11.99", @@ -3186,28 +2947,29 @@ }, { "name": "composer/semver", - "version": "1.7.1", + "version": "3.2.2", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "38276325bd896f90dfcfe30029aa5db40df387a7" + "reference": "4089fddb67bcf6bf860d91b979e95be303835002" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/38276325bd896f90dfcfe30029aa5db40df387a7", - "reference": "38276325bd896f90dfcfe30029aa5db40df387a7", + "url": "https://api.github.com/repos/composer/semver/zipball/4089fddb67bcf6bf860d91b979e95be303835002", + "reference": "4089fddb67bcf6bf860d91b979e95be303835002", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0" + "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.5 || ^5.0.5" + "phpstan/phpstan": "^0.12.19", + "symfony/phpunit-bridge": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-main": "3.x-dev" } }, "autoload": { @@ -3257,94 +3019,20 @@ "type": "tidelift" } ], - "time": "2020-09-27T13:13:07+00:00" - }, - { - "name": "composer/spdx-licenses", - "version": "1.5.4", - "source": { - "type": "git", - "url": "https://github.com/composer/spdx-licenses.git", - "reference": "6946f785871e2314c60b4524851f3702ea4f2223" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/6946f785871e2314c60b4524851f3702ea4f2223", - "reference": "6946f785871e2314c60b4524851f3702ea4f2223", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Spdx\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - }, - { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" - } - ], - "description": "SPDX licenses list and validation library.", - "keywords": [ - "license", - "spdx", - "validator" - ], - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2020-07-15T15:35:07+00:00" + "time": "2020-10-14T08:51:15+00:00" }, { "name": "composer/xdebug-handler", - "version": "1.4.3", + "version": "1.4.4", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "ebd27a9866ae8254e873866f795491f02418c5a5" + "reference": "6e076a124f7ee146f2487554a94b6a19a74887ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ebd27a9866ae8254e873866f795491f02418c5a5", - "reference": "ebd27a9866ae8254e873866f795491f02418c5a5", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6e076a124f7ee146f2487554a94b6a19a74887ba", + "reference": "6e076a124f7ee146f2487554a94b6a19a74887ba", "shasum": "" }, "require": { @@ -3389,20 +3077,20 @@ "type": "tidelift" } ], - "time": "2020-08-19T10:27:58+00:00" + "time": "2020-10-24T12:39:10+00:00" }, { "name": "doctrine/annotations", - "version": "1.10.4", + "version": "1.11.1", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "bfe91e31984e2ba76df1c1339681770401ec262f" + "reference": "ce77a7ba1770462cd705a91a151b6c3746f9c6ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/bfe91e31984e2ba76df1c1339681770401ec262f", - "reference": "bfe91e31984e2ba76df1c1339681770401ec262f", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/ce77a7ba1770462cd705a91a151b6c3746f9c6ad", + "reference": "ce77a7ba1770462cd705a91a151b6c3746f9c6ad", "shasum": "" }, "require": { @@ -3412,13 +3100,14 @@ }, "require-dev": { "doctrine/cache": "1.*", + "doctrine/coding-standard": "^6.0 || ^8.1", "phpstan/phpstan": "^0.12.20", "phpunit/phpunit": "^7.5 || ^9.1.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9.x-dev" + "dev-master": "1.11.x-dev" } }, "autoload": { @@ -3453,13 +3142,13 @@ } ], "description": "Docblock Annotations Parser", - "homepage": "http://www.doctrine-project.org", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", "keywords": [ "annotations", "docblock", "parser" ], - "time": "2020-08-10T19:35:50+00:00" + "time": "2020-10-26T10:28:16+00:00" }, { "name": "doctrine/cache", @@ -3721,33 +3410,32 @@ }, { "name": "doctrine/dbal", - "version": "2.10.4", + "version": "2.12.0", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "47433196b6390d14409a33885ee42b6208160643" + "reference": "c6d37b4c42aaa3c3ee175f05eca68056f4185646" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/47433196b6390d14409a33885ee42b6208160643", - "reference": "47433196b6390d14409a33885ee42b6208160643", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/c6d37b4c42aaa3c3ee175f05eca68056f4185646", + "reference": "c6d37b4c42aaa3c3ee175f05eca68056f4185646", "shasum": "" }, "require": { "doctrine/cache": "^1.0", "doctrine/event-manager": "^1.0", "ext-pdo": "*", - "php": "^7.2" + "php": "^7.3 || ^8" }, "require-dev": { "doctrine/coding-standard": "^8.1", "jetbrains/phpstorm-stubs": "^2019.1", - "nikic/php-parser": "^4.4", "phpstan/phpstan": "^0.12.40", - "phpunit/phpunit": "^8.5.5", + "phpunit/phpunit": "^9.4", "psalm/plugin-phpunit": "^0.10.0", "symfony/console": "^2.0.5|^3.0|^4.0|^5.0", - "vimeo/psalm": "^3.14.2" + "vimeo/psalm": "^3.17.2" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." @@ -3758,8 +3446,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.10.x-dev", - "dev-develop": "3.0.x-dev" + "dev-master": "4.0.x-dev" } }, "autoload": { @@ -3826,7 +3513,7 @@ "type": "tidelift" } ], - "time": "2020-09-12T21:20:41+00:00" + "time": "2020-10-22T17:26:24+00:00" }, { "name": "doctrine/event-manager", @@ -4344,32 +4031,32 @@ }, { "name": "doctrine/reflection", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/doctrine/reflection.git", - "reference": "55e71912dfcd824b2fdd16f2d9afe15684cfce79" + "reference": "fa587178be682efe90d005e3a322590d6ebb59a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/reflection/zipball/55e71912dfcd824b2fdd16f2d9afe15684cfce79", - "reference": "55e71912dfcd824b2fdd16f2d9afe15684cfce79", + "url": "https://api.github.com/repos/doctrine/reflection/zipball/fa587178be682efe90d005e3a322590d6ebb59a5", + "reference": "fa587178be682efe90d005e3a322590d6ebb59a5", "shasum": "" }, "require": { "doctrine/annotations": "^1.0", "ext-tokenizer": "*", - "php": "^7.1" + "php": "^7.1 || ^8.0" }, "conflict": { "doctrine/common": "<2.9" }, "require-dev": { - "doctrine/coding-standard": "^5.0", + "doctrine/coding-standard": "^6.0 || ^8.2.0", "doctrine/common": "^2.10", - "phpstan/phpstan": "^0.11.0", - "phpstan/phpstan-phpunit": "^0.11.0", - "phpunit/phpunit": "^7.0" + "phpstan/phpstan": "^0.11.0 || ^0.12.20", + "phpstan/phpstan-phpunit": "^0.11.0 || ^0.12.16", + "phpunit/phpunit": "^7.5 || ^9.1.5" }, "type": "library", "extra": { @@ -4418,7 +4105,7 @@ "reflection", "static" ], - "time": "2020-03-27T11:06:43+00:00" + "time": "2020-10-27T21:46:55+00:00" }, { "name": "fig/link-util", @@ -4479,27 +4166,27 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v2.16.4", + "version": "v2.16.7", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "1023c3458137ab052f6ff1e09621a721bfdeca13" + "reference": "4e35806a6d7d8510d6842ae932e8832363d22c87" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/1023c3458137ab052f6ff1e09621a721bfdeca13", - "reference": "1023c3458137ab052f6ff1e09621a721bfdeca13", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/4e35806a6d7d8510d6842ae932e8832363d22c87", + "reference": "4e35806a6d7d8510d6842ae932e8832363d22c87", "shasum": "" }, "require": { - "composer/semver": "^1.4", + "composer/semver": "^1.4 || ^2.0 || ^3.0", "composer/xdebug-handler": "^1.2", "doctrine/annotations": "^1.2", "ext-json": "*", "ext-tokenizer": "*", - "php": "^5.6 || ^7.0", + "php": "^7.1", "php-cs-fixer/diff": "^1.3", - "symfony/console": "^3.4.17 || ^4.1.6 || ^5.0", + "symfony/console": "^3.4.43 || ^4.1.6 || ^5.0", "symfony/event-dispatcher": "^3.0 || ^4.0 || ^5.0", "symfony/filesystem": "^3.0 || ^4.0 || ^5.0", "symfony/finder": "^3.0 || ^4.0 || ^5.0", @@ -4512,14 +4199,14 @@ "require-dev": { "johnkary/phpunit-speedtrap": "^1.1 || ^2.0 || ^3.0", "justinrainbow/json-schema": "^5.0", - "keradus/cli-executor": "^1.2", + "keradus/cli-executor": "^1.4", "mikey179/vfsstream": "^1.6", - "php-coveralls/php-coveralls": "^2.1", + "php-coveralls/php-coveralls": "^2.4.1", "php-cs-fixer/accessible-object": "^1.0", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.1", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.1", - "phpunitgoodpractices/traits": "^1.8", + "phpunitgoodpractices/traits": "^1.9.1", "symfony/phpunit-bridge": "^5.1", "symfony/yaml": "^3.0 || ^4.0 || ^5.0" }, @@ -4572,7 +4259,7 @@ "type": "github" } ], - "time": "2020-06-27T23:57:46+00:00" + "time": "2020-10-27T22:44:27+00:00" }, { "name": "infection/infection", @@ -4790,16 +4477,16 @@ }, { "name": "migrify/migrify-kernel", - "version": "0.3.49", + "version": "0.3.52", "source": { "type": "git", "url": "https://github.com/migrify/migrify-kernel.git", - "reference": "27bb88b615a7c6a5f700425e7b0ab5c476bd63e3" + "reference": "79c8015edeaeafc7d7d27ff0f528ab6165e5d10c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/migrify/migrify-kernel/zipball/27bb88b615a7c6a5f700425e7b0ab5c476bd63e3", - "reference": "27bb88b615a7c6a5f700425e7b0ab5c476bd63e3", + "url": "https://api.github.com/repos/migrify/migrify-kernel/zipball/79c8015edeaeafc7d7d27ff0f528ab6165e5d10c", + "reference": "79c8015edeaeafc7d7d27ff0f528ab6165e5d10c", "shasum": "" }, "require": { @@ -4807,9 +4494,9 @@ "symfony/console": "^4.4|^5.1", "symfony/dependency-injection": "^4.4|^5.1", "symfony/http-kernel": "^4.4|^5.1", - "symplify/autowire-array-parameter": "^8.3.15", - "symplify/package-builder": "^8.3.15", - "symplify/smart-file-system": "^8.3.15" + "symplify/autowire-array-parameter": "^8.3.46", + "symplify/package-builder": "^8.3.46", + "symplify/smart-file-system": "^8.3.46" }, "require-dev": { "tracy/tracy": "^2.7" @@ -4825,24 +4512,24 @@ "MIT" ], "description": "Kernel for small CLI applications", - "time": "2020-09-21T16:33:48+00:00" + "time": "2020-10-25T12:09:47+00:00" }, { "name": "migrify/php-config-printer", - "version": "0.3.49", + "version": "0.3.52", "source": { "type": "git", "url": "https://github.com/migrify/php-config-printer.git", - "reference": "c02a3602260d73ba9d08ebdfb7f9d5dbc4aaa2e6" + "reference": "1c840c83cfa8b738ab20e229e8254177d38df068" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/migrify/php-config-printer/zipball/c02a3602260d73ba9d08ebdfb7f9d5dbc4aaa2e6", - "reference": "c02a3602260d73ba9d08ebdfb7f9d5dbc4aaa2e6", + "url": "https://api.github.com/repos/migrify/php-config-printer/zipball/1c840c83cfa8b738ab20e229e8254177d38df068", + "reference": "1c840c83cfa8b738ab20e229e8254177d38df068", "shasum": "" }, "require": { - "migrify/migrify-kernel": "^0.3.49", + "migrify/migrify-kernel": "^0.3.52", "nette/utils": "^3.1", "nikic/php-parser": "^4.9", "php": ">=7.2", @@ -4850,7 +4537,7 @@ }, "require-dev": { "phpunit/phpunit": "^8.5|^9.0", - "symplify/easy-testing": "^8.3.15" + "symplify/easy-testing": "^8.3.46" }, "type": "library", "autoload": { @@ -4863,7 +4550,7 @@ "MIT" ], "description": "Print Symfony services array with configuration to to plain PHP file format thanks to this simple php-parser wrapper", - "time": "2020-10-12T21:46:55+00:00" + "time": "2020-10-30T10:33:25+00:00" }, { "name": "myclabs/deep-copy", @@ -5701,16 +5388,16 @@ }, { "name": "phpstan/phpstan", - "version": "0.12.50", + "version": "0.12.53", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "b8248f9c81265af75d6d969ca3252aaf3e998f3a" + "reference": "dbbdb0d7c2434ecd5289f6114d16473e694caa67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b8248f9c81265af75d6d969ca3252aaf3e998f3a", - "reference": "b8248f9c81265af75d6d969ca3252aaf3e998f3a", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dbbdb0d7c2434ecd5289f6114d16473e694caa67", + "reference": "dbbdb0d7c2434ecd5289f6114d16473e694caa67", "shasum": "" }, "require": { @@ -5753,7 +5440,7 @@ "type": "tidelift" } ], - "time": "2020-10-16T12:22:23+00:00" + "time": "2020-11-01T14:51:50+00:00" }, { "name": "phpstan/phpstan-phpunit", @@ -6439,33 +6126,33 @@ }, { "name": "rector/rector", - "version": "0.8.40", + "version": "0.8.47", "source": { "type": "git", "url": "https://github.com/rectorphp/rector.git", - "reference": "10ae0d687ab65bec4a80752ed7f0a04c208a684f" + "reference": "7f05247192e372ed7cee4661fbd75f45b1abbaf3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector/zipball/10ae0d687ab65bec4a80752ed7f0a04c208a684f", - "reference": "10ae0d687ab65bec4a80752ed7f0a04c208a684f", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/7f05247192e372ed7cee4661fbd75f45b1abbaf3", + "reference": "7f05247192e372ed7cee4661fbd75f45b1abbaf3", "shasum": "" }, "require": { "composer/xdebug-handler": "^1.4", - "doctrine/annotations": "^1.10.4", + "doctrine/annotations": "^1.11", "doctrine/inflector": "^1.4|^2.0", "ext-json": "*", "jean85/pretty-package-versions": "^1.5.1", - "migrify/php-config-printer": "^0.3.45", + "migrify/php-config-printer": "^0.3.52", "nette/robot-loader": "^3.2", "nette/utils": "^3.1", "nikic/php-parser": "4.10.2", "ondram/ci-detector": "^3.4", "php": "^7.2.4|^8.0", "phpstan/phpdoc-parser": "^0.4.9", - "phpstan/phpstan": "^0.12.43", - "phpstan/phpstan-phpunit": "^0.12.10", + "phpstan/phpstan": "^0.12.52", + "phpstan/phpstan-phpunit": "^0.12.16", "psr/simple-cache": "^1.0", "sebastian/diff": "^3.0|^4.0", "symfony/cache": "^4.4.8|^5.1", @@ -6474,13 +6161,13 @@ "symfony/finder": "^4.4.8|^5.1", "symfony/http-kernel": "^4.4.8|^5.1", "symfony/process": "^4.4.8|^5.1", - "symplify/autowire-array-parameter": "^8.3.40", - "symplify/composer-json-manipulator": "^8.3.40", - "symplify/console-color-diff": "^8.3.40", - "symplify/easy-testing": "^8.3.40", - "symplify/package-builder": "^8.3.40", - "symplify/set-config-resolver": "^8.3.40", - "symplify/smart-file-system": "^8.3.40", + "symplify/autowire-array-parameter": "^8.3.48", + "symplify/composer-json-manipulator": "^8.3.48", + "symplify/console-color-diff": "^8.3.48", + "symplify/easy-testing": "^8.3.48", + "symplify/package-builder": "^8.3.48", + "symplify/set-config-resolver": "^8.3.48", + "symplify/smart-file-system": "^8.3.48", "webmozart/assert": "^1.9" }, "replace": { @@ -6489,7 +6176,9 @@ "rector/symfony-php-config": "self.version" }, "require-dev": { + "cweagans/composer-patches": "^1.7", "friendsofphp/php-cs-fixer": "^2.16", + "migrify/vendor-patches": "^0.3.52", "nette/application": "^3.0", "nette/di": "^3.0", "nette/forms": "^3.0", @@ -6497,24 +6186,31 @@ "php-parallel-lint/php-parallel-lint": "^1.2", "phpunit/phpunit": "^8.5|^9.2", "psr/event-dispatcher": "^1.0", - "symplify/changelog-linker": "^8.3.40", - "symplify/easy-coding-standard": "^8.3.40", - "symplify/easy-testing": "^8.3.40", - "symplify/monorepo-builder": "^8.3.40", - "symplify/phpstan-extensions": "^8.3.40", - "thecodingmachine/phpstan-strict-rules": "^0.12", + "symplify/changelog-linker": "^8.3.48", + "symplify/easy-coding-standard": "^8.3.48", + "symplify/easy-testing": "^8.3.48", + "symplify/monorepo-builder": "^8.3.48", + "symplify/phpstan-extensions": "^8.3.48", "tracy/tracy": "^2.7" }, "bin": [ "bin/rector" ], "type": "library", + "extra": { + "patches": { + "nette/application": [ + "patches/nette-application-src-application-ui-presenter-php.patch" + ] + } + }, "autoload": { "files": [ "packages/symfony-php-config/functions/functions.php", "rules/restoration/tests/Rector/Use_/RestoreFullyQualifiedNameRector/Source/ShortClassOnly.php" ], "psr-4": { + "Rector\\Testing\\": "packages/testing/src", "Rector\\Architecture\\": "rules/architecture/src", "Rector\\AttributeAwarePhpDoc\\": "packages/attribute-aware-php-doc/src", "Rector\\Autodiscovery\\": "rules/autodiscovery/src", @@ -6527,7 +6223,6 @@ "Rector\\ConsoleDiffer\\": "packages/console-differ/src", "Rector\\Core\\": "src", "Rector\\DeadCode\\": "rules/dead-code/src", - "Rector\\Decouple\\": "rules/decouple/src", "Rector\\DoctrineAnnotationGenerated\\": "packages/doctrine-annotation-generated/src", "Rector\\DoctrineCodeQuality\\": "rules/doctrine-code-quality/src", "Rector\\DoctrineGedmoToKnplabs\\": "rules/doctrine-gedmo-to-knplabs/src", @@ -6539,7 +6234,6 @@ "Rector\\ReadWrite\\": "packages/read-write/src", "Rector\\DowngradePhp74\\": "rules/downgrade-php74/src", "Rector\\DowngradePhp80\\": "rules/downgrade-php80/src", - "Rector\\DynamicTypeAnalysis\\": "packages/dynamic-type-analysis/src", "Rector\\FamilyTree\\": "packages/family-tree/src", "Rector\\FileSystemRector\\": "packages/file-system-rector/src", "Rector\\Generic\\": "rules/generic/src", @@ -6643,7 +6337,7 @@ "type": "github" } ], - "time": "2020-10-20T22:01:47+00:00" + "time": "2020-11-02T01:13:55+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -7319,62 +7013,18 @@ ], "time": "2020-08-25T06:56:57+00:00" }, - { - "name": "seld/phar-utils", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/Seldaek/phar-utils.git", - "reference": "8674b1d84ffb47cc59a101f5d5a3b61e87d23796" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/8674b1d84ffb47cc59a101f5d5a3b61e87d23796", - "reference": "8674b1d84ffb47cc59a101f5d5a3b61e87d23796", - "shasum": "" - }, - "require": { - "php": ">=5.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Seld\\PharUtils\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } - ], - "description": "PHAR file format utilities, for when PHP phars you up", - "keywords": [ - "phar" - ], - "time": "2020-07-07T18:42:57+00:00" - }, { "name": "symfony/amqp-messenger", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/amqp-messenger.git", - "reference": "4cbeeb561be52763a814f4e886fde0ee115b82cd" + "reference": "daa895ff04c3a692b8ca3568445a46b44796e254" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/amqp-messenger/zipball/4cbeeb561be52763a814f4e886fde0ee115b82cd", - "reference": "4cbeeb561be52763a814f4e886fde0ee115b82cd", + "url": "https://api.github.com/repos/symfony/amqp-messenger/zipball/daa895ff04c3a692b8ca3568445a46b44796e254", + "reference": "daa895ff04c3a692b8ca3568445a46b44796e254", "shasum": "" }, "require": { @@ -7388,11 +7038,6 @@ "symfony/serializer": "^4.4|^5.0" }, "type": "symfony-bridge", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Messenger\\Bridge\\Amqp\\": "" @@ -7431,20 +7076,20 @@ "type": "tidelift" } ], - "time": "2020-09-14T15:02:15+00:00" + "time": "2020-10-27T10:11:13+00:00" }, { "name": "symfony/cache", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "292cd57b7c2e3c37aa2f0a2fa42dacae567dd5cd" + "reference": "d7bc33e9f9028f49f87057e7944c076d9593f046" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/292cd57b7c2e3c37aa2f0a2fa42dacae567dd5cd", - "reference": "292cd57b7c2e3c37aa2f0a2fa42dacae567dd5cd", + "url": "https://api.github.com/repos/symfony/cache/zipball/d7bc33e9f9028f49f87057e7944c076d9593f046", + "reference": "d7bc33e9f9028f49f87057e7944c076d9593f046", "shasum": "" }, "require": { @@ -7475,14 +7120,10 @@ "psr/simple-cache": "^1.0", "symfony/config": "^4.4|^5.0", "symfony/dependency-injection": "^4.4|^5.0", + "symfony/filesystem": "^4.4|^5.0", "symfony/var-dumper": "^4.4|^5.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Cache\\": "" @@ -7525,7 +7166,7 @@ "type": "tidelift" } ], - "time": "2020-09-27T14:02:37+00:00" + "time": "2020-10-25T23:21:56+00:00" }, { "name": "symfony/cache-contracts", @@ -7605,16 +7246,16 @@ }, { "name": "symfony/doctrine-messenger", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/doctrine-messenger.git", - "reference": "d4299ea85c28ccf5ff773028523dc628071718b2" + "reference": "dbff2dc94784923d91be06bf2296a9fdeb7d329d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-messenger/zipball/d4299ea85c28ccf5ff773028523dc628071718b2", - "reference": "d4299ea85c28ccf5ff773028523dc628071718b2", + "url": "https://api.github.com/repos/symfony/doctrine-messenger/zipball/dbff2dc94784923d91be06bf2296a9fdeb7d329d", + "reference": "dbff2dc94784923d91be06bf2296a9fdeb7d329d", "shasum": "" }, "require": { @@ -7632,11 +7273,6 @@ "symfony/serializer": "^4.4|^5.0" }, "type": "symfony-bridge", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Messenger\\Bridge\\Doctrine\\": "" @@ -7675,20 +7311,81 @@ "type": "tidelift" } ], - "time": "2020-09-27T14:02:37+00:00" + "time": "2020-10-24T12:01:57+00:00" + }, + { + "name": "symfony/expression-language", + "version": "v5.1.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/expression-language.git", + "reference": "2c3f5e7cf4495d9033cc2d212da6ccde0d22b067" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/expression-language/zipball/2c3f5e7cf4495d9033cc2d212da6ccde0d22b067", + "reference": "2c3f5e7cf4495d9033cc2d212da6ccde0d22b067", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/cache": "^4.4|^5.0", + "symfony/polyfill-php80": "^1.15", + "symfony/service-contracts": "^1.1|^2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ExpressionLanguage\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony ExpressionLanguage Component", + "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-24T12:01:57+00:00" }, { "name": "symfony/form", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/form.git", - "reference": "f3a49105e472fd168b743acdb5e0524c66aeb287" + "reference": "3f61496220b9f8672ede9b9acfb87a06c0c752f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/form/zipball/f3a49105e472fd168b743acdb5e0524c66aeb287", - "reference": "f3a49105e472fd168b743acdb5e0524c66aeb287", + "url": "https://api.github.com/repos/symfony/form/zipball/3f61496220b9f8672ede9b9acfb87a06c0c752f2", + "reference": "3f61496220b9f8672ede9b9acfb87a06c0c752f2", "shasum": "" }, "require": { @@ -7735,11 +7432,6 @@ "symfony/validator": "For form validation." }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Form\\": "" @@ -7778,20 +7470,20 @@ "type": "tidelift" } ], - "time": "2020-10-02T12:58:01+00:00" + "time": "2020-10-28T05:25:38+00:00" }, { "name": "symfony/http-client", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "df757997ee95101c0ca94c7ea2b76e16a758e0ca" + "reference": "97a6a1f9f5bb3a6094833107b58a72bc9a9165cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/df757997ee95101c0ca94c7ea2b76e16a758e0ca", - "reference": "df757997ee95101c0ca94c7ea2b76e16a758e0ca", + "url": "https://api.github.com/repos/symfony/http-client/zipball/97a6a1f9f5bb3a6094833107b58a72bc9a9165cc", + "reference": "97a6a1f9f5bb3a6094833107b58a72bc9a9165cc", "shasum": "" }, "require": { @@ -7821,11 +7513,6 @@ "symfony/process": "^4.4|^5.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\HttpClient\\": "" @@ -7864,20 +7551,20 @@ "type": "tidelift" } ], - "time": "2020-10-02T14:24:03+00:00" + "time": "2020-10-24T12:01:57+00:00" }, { "name": "symfony/intl", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/intl.git", - "reference": "9381fd69ce6407041185aa6f1bafbf7d65f0e66a" + "reference": "e353c6c37afa1ff90739b3941f60ff9fa650eec3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/intl/zipball/9381fd69ce6407041185aa6f1bafbf7d65f0e66a", - "reference": "9381fd69ce6407041185aa6f1bafbf7d65f0e66a", + "url": "https://api.github.com/repos/symfony/intl/zipball/e353c6c37afa1ff90739b3941f60ff9fa650eec3", + "reference": "e353c6c37afa1ff90739b3941f60ff9fa650eec3", "shasum": "" }, "require": { @@ -7892,11 +7579,6 @@ "ext-intl": "to use the component with locales other than \"en\"" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Intl\\": "" @@ -7954,20 +7636,20 @@ "type": "tidelift" } ], - "time": "2020-09-27T03:44:28+00:00" + "time": "2020-10-24T12:01:57+00:00" }, { "name": "symfony/messenger", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/messenger.git", - "reference": "363f00e14f9111ccccc2c9dfa00b190cc5aad02f" + "reference": "800acfc4dfc954aea6e6adc192e82b44c84520fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/messenger/zipball/363f00e14f9111ccccc2c9dfa00b190cc5aad02f", - "reference": "363f00e14f9111ccccc2c9dfa00b190cc5aad02f", + "url": "https://api.github.com/repos/symfony/messenger/zipball/800acfc4dfc954aea6e6adc192e82b44c84520fe", + "reference": "800acfc4dfc954aea6e6adc192e82b44c84520fe", "shasum": "" }, "require": { @@ -8001,11 +7683,6 @@ "enqueue/messenger-adapter": "For using the php-enqueue library as a transport." }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Messenger\\": "" @@ -8044,24 +7721,24 @@ "type": "tidelift" } ], - "time": "2020-09-11T11:43:06+00:00" + "time": "2020-10-24T12:01:57+00:00" }, { "name": "symfony/polyfill-intl-icu", - "version": "v1.18.1", + "version": "v1.20.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-icu.git", - "reference": "4e45a6e39041a9cc78835b11abc47874ae302a55" + "reference": "c44d5bf6a75eed79555c6bf37505c6d39559353e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/4e45a6e39041a9cc78835b11abc47874ae302a55", - "reference": "4e45a6e39041a9cc78835b11abc47874ae302a55", + "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/c44d5bf6a75eed79555c6bf37505c6d39559353e", + "reference": "c44d5bf6a75eed79555c6bf37505c6d39559353e", "shasum": "" }, "require": { - "php": ">=5.3.3", + "php": ">=7.1", "symfony/intl": "~2.3|~3.0|~4.0|~5.0" }, "suggest": { @@ -8070,7 +7747,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.18-dev" + "dev-main": "1.20-dev" }, "thanks": { "name": "symfony/polyfill", @@ -8120,47 +7797,35 @@ "type": "tidelift" } ], - "time": "2020-07-14T12:35:20+00:00" + "time": "2020-10-23T14:02:19+00:00" }, { "name": "symfony/polyfill-php70", - "version": "v1.18.1", + "version": "v1.20.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "0dd93f2c578bdc9c72697eaa5f1dd25644e618d3" + "reference": "5f03a781d984aae42cebd18e7912fa80f02ee644" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/0dd93f2c578bdc9c72697eaa5f1dd25644e618d3", - "reference": "0dd93f2c578bdc9c72697eaa5f1dd25644e618d3", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/5f03a781d984aae42cebd18e7912fa80f02ee644", + "reference": "5f03a781d984aae42cebd18e7912fa80f02ee644", "shasum": "" }, "require": { - "paragonie/random_compat": "~1.0|~2.0|~9.99", - "php": ">=5.3.3" + "php": ">=7.1" }, - "type": "library", + "type": "metapackage", "extra": { "branch-alias": { - "dev-master": "1.18-dev" + "dev-main": "1.20-dev" }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php70\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" @@ -8197,29 +7862,29 @@ "type": "tidelift" } ], - "time": "2020-07-14T12:35:20+00:00" + "time": "2020-10-23T14:02:19+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.18.1", + "version": "v1.20.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "639447d008615574653fb3bc60d1986d7172eaae" + "reference": "cede45fcdfabdd6043b3592e83678e42ec69e930" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/639447d008615574653fb3bc60d1986d7172eaae", - "reference": "639447d008615574653fb3bc60d1986d7172eaae", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/cede45fcdfabdd6043b3592e83678e42ec69e930", + "reference": "cede45fcdfabdd6043b3592e83678e42ec69e930", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.18-dev" + "dev-main": "1.20-dev" }, "thanks": { "name": "symfony/polyfill", @@ -8270,20 +7935,20 @@ "type": "tidelift" } ], - "time": "2020-07-14T12:35:20+00:00" + "time": "2020-10-23T14:02:19+00:00" }, { "name": "symfony/process", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "d3a2e64866169586502f0cd9cab69135ad12cee9" + "reference": "f00872c3f6804150d6a0f73b4151daab96248101" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/d3a2e64866169586502f0cd9cab69135ad12cee9", - "reference": "d3a2e64866169586502f0cd9cab69135ad12cee9", + "url": "https://api.github.com/repos/symfony/process/zipball/f00872c3f6804150d6a0f73b4151daab96248101", + "reference": "f00872c3f6804150d6a0f73b4151daab96248101", "shasum": "" }, "require": { @@ -8291,11 +7956,6 @@ "symfony/polyfill-php80": "^1.15" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Process\\": "" @@ -8334,20 +7994,20 @@ "type": "tidelift" } ], - "time": "2020-09-02T16:23:27+00:00" + "time": "2020-10-24T12:01:57+00:00" }, { "name": "symfony/redis-messenger", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/redis-messenger.git", - "reference": "c0b66a724dfa77d41adf54e70c49227071a38411" + "reference": "0c62cc7e04f391fa88fca55dc50d8e5c6b4d4909" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/redis-messenger/zipball/c0b66a724dfa77d41adf54e70c49227071a38411", - "reference": "c0b66a724dfa77d41adf54e70c49227071a38411", + "url": "https://api.github.com/repos/symfony/redis-messenger/zipball/0c62cc7e04f391fa88fca55dc50d8e5c6b4d4909", + "reference": "0c62cc7e04f391fa88fca55dc50d8e5c6b4d4909", "shasum": "" }, "require": { @@ -8359,11 +8019,6 @@ "symfony/serializer": "^4.4|^5.0" }, "type": "symfony-bridge", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Messenger\\Bridge\\Redis\\": "" @@ -8402,20 +8057,20 @@ "type": "tidelift" } ], - "time": "2020-10-02T08:49:02+00:00" + "time": "2020-10-24T12:01:57+00:00" }, { "name": "symfony/stopwatch", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "0f7c58cf81dbb5dd67d423a89d577524a2ec0323" + "reference": "3d9f57c89011f0266e6b1d469e5c0110513859d5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/0f7c58cf81dbb5dd67d423a89d577524a2ec0323", - "reference": "0f7c58cf81dbb5dd67d423a89d577524a2ec0323", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/3d9f57c89011f0266e6b1d469e5c0110513859d5", + "reference": "3d9f57c89011f0266e6b1d469e5c0110513859d5", "shasum": "" }, "require": { @@ -8423,11 +8078,6 @@ "symfony/service-contracts": "^1.0|^2" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Stopwatch\\": "" @@ -8466,20 +8116,20 @@ "type": "tidelift" } ], - "time": "2020-05-20T17:43:50+00:00" + "time": "2020-10-24T12:01:57+00:00" }, { "name": "symfony/var-exporter", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "8b858508e49beb257fd635104c3d449a8113e8fe" + "reference": "b4048bfc6248413592462c029381bdb2f7b6525f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/8b858508e49beb257fd635104c3d449a8113e8fe", - "reference": "8b858508e49beb257fd635104c3d449a8113e8fe", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/b4048bfc6248413592462c029381bdb2f7b6525f", + "reference": "b4048bfc6248413592462c029381bdb2f7b6525f", "shasum": "" }, "require": { @@ -8490,11 +8140,6 @@ "symfony/var-dumper": "^4.4.9|^5.0.9" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\VarExporter\\": "" @@ -8541,7 +8186,7 @@ "type": "tidelift" } ], - "time": "2020-09-08T14:19:54+00:00" + "time": "2020-10-24T12:01:57+00:00" }, { "name": "symfony/web-link", @@ -8629,16 +8274,16 @@ }, { "name": "symfony/yaml", - "version": "v5.1.7", + "version": "v5.1.8", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "e147a68cb66a8b510f4b7481fe4da5b2ab65ec6a" + "reference": "f284e032c3cefefb9943792132251b79a6127ca6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/e147a68cb66a8b510f4b7481fe4da5b2ab65ec6a", - "reference": "e147a68cb66a8b510f4b7481fe4da5b2ab65ec6a", + "url": "https://api.github.com/repos/symfony/yaml/zipball/f284e032c3cefefb9943792132251b79a6127ca6", + "reference": "f284e032c3cefefb9943792132251b79a6127ca6", "shasum": "" }, "require": { @@ -8659,11 +8304,6 @@ "Resources/bin/yaml-lint" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Yaml\\": "" @@ -8702,27 +8342,27 @@ "type": "tidelift" } ], - "time": "2020-09-27T03:44:28+00:00" + "time": "2020-10-24T12:03:25+00:00" }, { "name": "symplify/autowire-array-parameter", - "version": "8.3.41", + "version": "8.3.48", "source": { "type": "git", "url": "https://github.com/symplify/autowire-array-parameter.git", - "reference": "18c1f44bc98830f138ab46f1140c66d3dc436052" + "reference": "991ebd1fc518419e689f7cb77653d03e864464b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/autowire-array-parameter/zipball/18c1f44bc98830f138ab46f1140c66d3dc436052", - "reference": "18c1f44bc98830f138ab46f1140c66d3dc436052", + "url": "https://api.github.com/repos/symplify/autowire-array-parameter/zipball/991ebd1fc518419e689f7cb77653d03e864464b3", + "reference": "991ebd1fc518419e689f7cb77653d03e864464b3", "shasum": "" }, "require": { "nette/utils": "^3.0", "php": ">=7.2", "symfony/dependency-injection": "^4.4|^5.1", - "symplify/package-builder": "^8.3.41" + "symplify/package-builder": "^8.3.48" }, "require-dev": { "phpunit/phpunit": "^8.5|^9.0" @@ -8753,32 +8393,31 @@ "type": "github" } ], - "time": "2020-10-19T16:15:07+00:00" + "time": "2020-10-26T10:38:48+00:00" }, { "name": "symplify/composer-json-manipulator", - "version": "8.3.41", + "version": "8.3.48", "source": { "type": "git", "url": "https://github.com/symplify/composer-json-manipulator.git", - "reference": "3df9dd90d127fbaa4848a70b9b7a36300cee71ab" + "reference": "fa7874b164fb90c22021172d2eead4e442db1aae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/composer-json-manipulator/zipball/3df9dd90d127fbaa4848a70b9b7a36300cee71ab", - "reference": "3df9dd90d127fbaa4848a70b9b7a36300cee71ab", + "url": "https://api.github.com/repos/symplify/composer-json-manipulator/zipball/fa7874b164fb90c22021172d2eead4e442db1aae", + "reference": "fa7874b164fb90c22021172d2eead4e442db1aae", "shasum": "" }, "require": { - "composer/composer": "^1.10|^2.0", "nette/utils": "^3.0", "php": ">=7.2", "symfony/config": "^4.4|^5.1", "symfony/dependency-injection": "^4.4|^5.1", "symfony/filesystem": "^4.4|^5.1", "symfony/http-kernel": "^4.4|^5.1", - "symplify/package-builder": "^8.3.41", - "symplify/smart-file-system": "^8.3.41" + "symplify/package-builder": "^8.3.48", + "symplify/smart-file-system": "^8.3.48" }, "require-dev": { "phpunit/phpunit": "^8.5|^9.0" @@ -8809,20 +8448,20 @@ "type": "github" } ], - "time": "2020-10-19T16:15:07+00:00" + "time": "2020-10-26T10:38:48+00:00" }, { "name": "symplify/console-color-diff", - "version": "8.3.41", + "version": "8.3.48", "source": { "type": "git", "url": "https://github.com/symplify/console-color-diff.git", - "reference": "16c21681dea84cb4fcdcee2deca336df83f00ebd" + "reference": "481f1ee159a7354117b19939c4e58dd6efdba4c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/console-color-diff/zipball/16c21681dea84cb4fcdcee2deca336df83f00ebd", - "reference": "16c21681dea84cb4fcdcee2deca336df83f00ebd", + "url": "https://api.github.com/repos/symplify/console-color-diff/zipball/481f1ee159a7354117b19939c4e58dd6efdba4c1", + "reference": "481f1ee159a7354117b19939c4e58dd6efdba4c1", "shasum": "" }, "require": { @@ -8832,7 +8471,7 @@ "symfony/console": "^4.4|^5.1", "symfony/dependency-injection": "^4.4|^5.1", "symfony/http-kernel": "^4.4|^5.1", - "symplify/package-builder": "^8.3.41" + "symplify/package-builder": "^8.3.48" }, "require-dev": { "phpunit/phpunit": "^8.5|^9.0" @@ -8863,29 +8502,29 @@ "type": "github" } ], - "time": "2020-10-19T16:15:07+00:00" + "time": "2020-10-26T10:38:48+00:00" }, { "name": "symplify/easy-testing", - "version": "8.3.41", + "version": "8.3.48", "source": { "type": "git", "url": "https://github.com/symplify/easy-testing.git", - "reference": "0739ac7f060f550a15b72a2f730d0153e512688e" + "reference": "9e5a47186d016905586bfcc13bdd33db20c6f4e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/easy-testing/zipball/0739ac7f060f550a15b72a2f730d0153e512688e", - "reference": "0739ac7f060f550a15b72a2f730d0153e512688e", + "url": "https://api.github.com/repos/symplify/easy-testing/zipball/9e5a47186d016905586bfcc13bdd33db20c6f4e6", + "reference": "9e5a47186d016905586bfcc13bdd33db20c6f4e6", "shasum": "" }, "require": { "nette/utils": "^3.0", "php": ">=7.2", "symfony/finder": "^4.4|^5.1", - "symplify/package-builder": "^8.3.41", - "symplify/smart-file-system": "^8.3.41", - "symplify/symplify-kernel": "^8.3.41" + "symplify/package-builder": "^8.3.48", + "symplify/smart-file-system": "^8.3.48", + "symplify/symplify-kernel": "^8.3.48" }, "require-dev": { "phpunit/phpunit": "^8.5|^9.0" @@ -8916,20 +8555,20 @@ "type": "github" } ], - "time": "2020-10-19T16:15:07+00:00" + "time": "2020-10-26T10:38:48+00:00" }, { "name": "symplify/package-builder", - "version": "8.3.41", + "version": "8.3.48", "source": { "type": "git", "url": "https://github.com/symplify/package-builder.git", - "reference": "8dadce4f1bdb0f3cb42e0e6a5961bc87165fef18" + "reference": "81a204cf0a5ec893b0f1e1a635c1972d20e0b833" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/package-builder/zipball/8dadce4f1bdb0f3cb42e0e6a5961bc87165fef18", - "reference": "8dadce4f1bdb0f3cb42e0e6a5961bc87165fef18", + "url": "https://api.github.com/repos/symplify/package-builder/zipball/81a204cf0a5ec893b0f1e1a635c1972d20e0b833", + "reference": "81a204cf0a5ec893b0f1e1a635c1972d20e0b833", "shasum": "" }, "require": { @@ -8942,8 +8581,8 @@ "symfony/finder": "^4.4|^5.1", "symfony/http-kernel": "^4.4|^5.1", "symfony/yaml": "^4.4|^5.1", - "symplify/autowire-array-parameter": "^8.3.41", - "symplify/symplify-kernel": "^8.3.41" + "symplify/autowire-array-parameter": "^8.3.48", + "symplify/symplify-kernel": "^8.3.48" }, "require-dev": { "phpunit/phpunit": "^8.5|^9.0" @@ -8974,20 +8613,20 @@ "type": "github" } ], - "time": "2020-10-19T16:15:07+00:00" + "time": "2020-10-26T10:38:48+00:00" }, { "name": "symplify/set-config-resolver", - "version": "8.3.41", + "version": "8.3.48", "source": { "type": "git", "url": "https://github.com/symplify/set-config-resolver.git", - "reference": "7eb7d560e5d9b66947cc2dc79f424dbe53e12d94" + "reference": "574f4913ae784305ed86adc819a20c1992285685" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/set-config-resolver/zipball/7eb7d560e5d9b66947cc2dc79f424dbe53e12d94", - "reference": "7eb7d560e5d9b66947cc2dc79f424dbe53e12d94", + "url": "https://api.github.com/repos/symplify/set-config-resolver/zipball/574f4913ae784305ed86adc819a20c1992285685", + "reference": "574f4913ae784305ed86adc819a20c1992285685", "shasum": "" }, "require": { @@ -8999,8 +8638,8 @@ "symfony/filesystem": "^4.4|^5.1", "symfony/finder": "^4.4|^5.1", "symfony/yaml": "^4.4|^5.1", - "symplify/smart-file-system": "^8.3.41", - "symplify/symplify-kernel": "^8.3.41" + "symplify/smart-file-system": "^8.3.48", + "symplify/symplify-kernel": "^8.3.48" }, "require-dev": { "phpunit/phpunit": "^8.5|^9.0" @@ -9031,20 +8670,20 @@ "type": "github" } ], - "time": "2020-10-19T16:15:07+00:00" + "time": "2020-10-26T10:38:48+00:00" }, { "name": "symplify/smart-file-system", - "version": "8.3.41", + "version": "8.3.48", "source": { "type": "git", "url": "https://github.com/symplify/smart-file-system.git", - "reference": "f59c8a42025f2f0a3078dc29950f8f2de4bdb7ca" + "reference": "1242eb26d309ef2a5a12e9731cef5755509994b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/smart-file-system/zipball/f59c8a42025f2f0a3078dc29950f8f2de4bdb7ca", - "reference": "f59c8a42025f2f0a3078dc29950f8f2de4bdb7ca", + "url": "https://api.github.com/repos/symplify/smart-file-system/zipball/1242eb26d309ef2a5a12e9731cef5755509994b3", + "reference": "1242eb26d309ef2a5a12e9731cef5755509994b3", "shasum": "" }, "require": { @@ -9083,20 +8722,20 @@ "type": "github" } ], - "time": "2020-10-14T11:43:23+00:00" + "time": "2020-10-23T21:04:57+00:00" }, { "name": "symplify/symplify-kernel", - "version": "8.3.41", + "version": "8.3.48", "source": { "type": "git", "url": "https://github.com/symplify/symplify-kernel.git", - "reference": "5688e4d55ab0f3e559fc67879bfc68db44715e95" + "reference": "30c3017346b4a7349e7acbf2ac35b96a7a8f4c3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/symplify-kernel/zipball/5688e4d55ab0f3e559fc67879bfc68db44715e95", - "reference": "5688e4d55ab0f3e559fc67879bfc68db44715e95", + "url": "https://api.github.com/repos/symplify/symplify-kernel/zipball/30c3017346b4a7349e7acbf2ac35b96a7a8f4c3b", + "reference": "30c3017346b4a7349e7acbf2ac35b96a7a8f4c3b", "shasum": "" }, "require": { @@ -9104,8 +8743,8 @@ "symfony/console": "^4.4|^5.1", "symfony/dependency-injection": "^4.4|^5.1", "symfony/http-kernel": "^4.4|^5.1", - "symplify/package-builder": "^8.3.41", - "symplify/smart-file-system": "^8.3.41" + "symplify/package-builder": "^8.3.48", + "symplify/smart-file-system": "^8.3.48" }, "require-dev": { "phpunit/phpunit": "^8.5|^9.0" @@ -9126,7 +8765,7 @@ "MIT" ], "description": "Internal Kernel for Symplify packages", - "time": "2020-10-19T16:15:07+00:00" + "time": "2020-10-26T10:38:48+00:00" }, { "name": "thecodingmachine/safe", @@ -9364,20 +9003,20 @@ }, { "name": "twig/twig", - "version": "v2.13.1", + "version": "v2.14.1", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "57e96259776ddcacf1814885fc3950460c8e18ef" + "reference": "5eb9ac5dfdd20c3f59495c22841adc5da980d312" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/57e96259776ddcacf1814885fc3950460c8e18ef", - "reference": "57e96259776ddcacf1814885fc3950460c8e18ef", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/5eb9ac5dfdd20c3f59495c22841adc5da980d312", + "reference": "5eb9ac5dfdd20c3f59495c22841adc5da980d312", "shasum": "" }, "require": { - "php": ">=7.1.3", + "php": ">=7.2.5", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-mbstring": "^1.3" }, @@ -9388,7 +9027,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.13-dev" + "dev-master": "2.14-dev" } }, "autoload": { @@ -9435,7 +9074,7 @@ "type": "tidelift" } ], - "time": "2020-08-05T15:09:04+00:00" + "time": "2020-10-27T19:25:29+00:00" }, { "name": "webmozart/assert", diff --git a/doc/api_platform.md b/doc/api_platform.md deleted file mode 100644 index 1689803..0000000 --- a/doc/api_platform.md +++ /dev/null @@ -1,3 +0,0 @@ -# API-Platform - -// TODO diff --git a/doc/dto.md b/doc/dto.md index eaacc59..fa74361 100644 --- a/doc/dto.md +++ b/doc/dto.md @@ -60,4 +60,4 @@ final class FooController ``` Thanks to the fourth argument, `SearchEntryPointInterface` will return a `Search` object where -every hits is an instance of the related DTO's (if the `model` key has been defined during the population). +every hit is an instance of the related DTO's (if the `model` key has been defined during the population). diff --git a/doc/search.md b/doc/search.md index 6936e6c..0dbc515 100644 --- a/doc/search.md +++ b/doc/search.md @@ -103,8 +103,28 @@ $search->in('foo')->not('id', '=', 1); $search = Search::within('foo')->not('id', '=', 1); ``` -Keep in mind that filters can be chained: +Want to filter on an "isolated" negative condition? + +```php +in('foo')->where('id', '>', 1)->andNot('id', '=', 5); + +// OR + +$search = Search::within('foo')->where('id', '>', 1)->andNot('id', '=', 5); + +// Both will result on id > 1 AND (NOT id = 5) +``` + +**Note**: Keep in mind that `andNot` cannot be used without an existing `where` condition! + +**Note**: Every `*where*` method define a third (fourth on `where`) argument called `$isolated` which allow to use `()` to isolate the condition. + +Keep in mind that filters can be chained: ```php in('foo')->paginate('id', '>', $result->getLastIdentifier(), 20); $search = Search::within('foo')->paginate('id', '>', $result->getLastIdentifier(), 20); ``` +## Bonus: Building a search using [ExpressionLanguage](https://symfony.com/doc/current/components/expression_language.html) + +This bundle provides a custom `ExpressionLanguage` that brings a shortcut to building searches: + +```php +evaluate('search("foo", "bar", "title > 2", 2)'); + +// Once defined, you can update the Search object via the defined methods +// Ex: + +$search->andWhere('title', '>=', 10); +``` + +**Note**: + +This approach only supports the following building parts of a search: + +- The index +- The query +- The filters (only the ones defined by `where`) +- The limit (defined by `max`) + ## Usage diff --git a/src/Bridge/RamseyUuid/Serializer/UuidDenormalizer.php b/src/Bridge/RamseyUuid/Serializer/UuidDenormalizer.php index dd2eed6..f0513a8 100644 --- a/src/Bridge/RamseyUuid/Serializer/UuidDenormalizer.php +++ b/src/Bridge/RamseyUuid/Serializer/UuidDenormalizer.php @@ -18,7 +18,7 @@ final class UuidDenormalizer implements DenormalizerInterface /** * {@inheritdoc} */ - public function denormalize($data, $type, $format = null, array $context = []): UuidInterface + public function denormalize($data, $type, string $format = null, array $context = []): UuidInterface { return Uuid::fromString($data); } @@ -26,7 +26,7 @@ public function denormalize($data, $type, $format = null, array $context = []): /** * {@inheritdoc} */ - public function supportsDenormalization($data, $type, $format = null): bool + public function supportsDenormalization($data, $type, string $format = null): bool { return (is_string($data) && Uuid::isValid($data)) && is_a($type, UuidInterface::class, true); } diff --git a/src/Command/DeleteIndexCommand.php b/src/Command/DeleteIndexCommand.php index 432103a..444c3de 100644 --- a/src/Command/DeleteIndexCommand.php +++ b/src/Command/DeleteIndexCommand.php @@ -5,6 +5,7 @@ namespace MeiliSearchBundle\Command; use MeiliSearchBundle\Index\IndexOrchestratorInterface; +use MeiliSearchBundle\Metadata\IndexMetadataRegistryInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -27,14 +28,22 @@ final class DeleteIndexCommand extends Command */ private $indexOrchestrator; + /** + * @var IndexMetadataRegistryInterface + */ + private $indexMetadataRegistry; + /** * {@inheritdoc} */ protected static $defaultName = 'meili:delete-index'; - public function __construct(IndexOrchestratorInterface $indexOrchestrator) - { + public function __construct( + IndexOrchestratorInterface $indexOrchestrator, + IndexMetadataRegistryInterface $indexMetadataRegistry + ) { $this->indexOrchestrator = $indexOrchestrator; + $this->indexMetadataRegistry = $indexMetadataRegistry; parent::__construct(); } @@ -65,6 +74,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int try { $this->indexOrchestrator->removeIndex($index); + $this->indexMetadataRegistry->remove($index); $io->success(sprintf('The index "%s" has been removed', $index)); return 0; diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 0cf37f0..a3ddd87 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -32,7 +32,7 @@ public function getConfigTreeBuilder(): TreeBuilder ->defaultNull() ->end() ->scalarNode('metadata_directory') - ->info('Define the directory where are stored the metadata') + ->info('Define the directory filters are stored the metadata') ->defaultValue('%kernel.project_dir%/var/_ms') ->end() ->arrayNode('cache') diff --git a/src/DependencyInjection/MeiliSearchExtension.php b/src/DependencyInjection/MeiliSearchExtension.php index 8cd8cec..899ee0d 100644 --- a/src/DependencyInjection/MeiliSearchExtension.php +++ b/src/DependencyInjection/MeiliSearchExtension.php @@ -158,7 +158,7 @@ private function registerClientAndMetadataRegistry(ContainerBuilder $container, $container->setAlias(IndexMetadataRegistryInterface::class, IndexMetadataRegistry::class); } - public function handleCacheConfiguration(ContainerBuilder $container, array $configuration): void + private function handleCacheConfiguration(ContainerBuilder $container, array $configuration): void { if (!array_key_exists(self::CACHE, $configuration) || !$configuration[self::CACHE]['enabled']) { return; diff --git a/src/Document/DocumentLoader.php b/src/Document/DocumentLoader.php index a620133..c433161 100644 --- a/src/Document/DocumentLoader.php +++ b/src/Document/DocumentLoader.php @@ -14,7 +14,6 @@ use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; use Throwable; -use function array_filter; use function array_replace; use function sprintf; use function usort; @@ -58,7 +57,7 @@ public function load(): void throw new RuntimeException('No providers found'); } - $providers = array_replace($this->filterOnPriority(), $this->documentProviders); + $providers = $this->filterOnPriority(); foreach ($providers as $provider) { try { @@ -94,14 +93,24 @@ public function load(): void */ private function filterOnPriority(): array { - $providers = array_filter($this->documentProviders, function (DocumentDataProviderInterface $provider): bool { - return $provider instanceof PriorityDataProviderInterface; - }); + $defaultProviders = []; + foreach ($this->documentProviders as $provider) { + $defaultProviders[] = $provider; + } + + $providers = []; + foreach ($this->documentProviders as $provider) { + if (!$provider instanceof PriorityDataProviderInterface) { + continue; + } + + $providers[] = $provider; + } usort($providers, function (PriorityDataProviderInterface $provider, PriorityDataProviderInterface $nextProvider): bool { return $provider->getPriority() > $nextProvider->getPriority(); }); - return $providers; + return array_replace($providers, $defaultProviders); } } diff --git a/src/EventSubscriber/ExceptionSubscriber.php b/src/EventSubscriber/ExceptionSubscriber.php index fecac27..b7aefa8 100644 --- a/src/EventSubscriber/ExceptionSubscriber.php +++ b/src/EventSubscriber/ExceptionSubscriber.php @@ -27,16 +27,6 @@ public function __construct(?LoggerInterface $logger = null) $this->logger = $logger ?: new NullLogger(); } - /** - * @return array - */ - public static function getSubscribedEvents(): array - { - return [ - KernelEvents::EXCEPTION => 'onException', - ]; - } - public function onException(ExceptionEvent $event): void { $exception = $event->getThrowable(); @@ -45,10 +35,17 @@ public function onException(ExceptionEvent $event): void } $this->logger->critical(sprintf(self::LOG_MASK, sprintf('An error occurred: %s', $exception->getMessage())), [ - 'context' => $exception->getContext(), - 'code' => $exception->getCode(), - 'trace' => $exception->getTrace(), - 'file' => $exception->getFile(), + 'error' => $exception->getMessage(), ]); } + + /** + * @return array + */ + public static function getSubscribedEvents(): array + { + return [ + KernelEvents::EXCEPTION => 'onException', + ]; + } } diff --git a/src/EventSubscriber/SynonymsEventSubscriber.php b/src/EventSubscriber/SynonymsEventSubscriber.php index ed49ed4..79c17da 100644 --- a/src/EventSubscriber/SynonymsEventSubscriber.php +++ b/src/EventSubscriber/SynonymsEventSubscriber.php @@ -28,19 +28,6 @@ public function __construct(?LoggerInterface $logger = null) $this->logger = $logger ?: new NullLogger(); } - /** - * @return array - */ - public static function getSubscribedEvents(): array - { - return [ - PostResetSynonymsEvent::class => 'onPostResetSynonyms', - PreResetSynonymsEvent::class => 'onPreResetSynonyms', - PostUpdateSynonymsEvent::class => 'onPostUpdateSynonyms', - PreUpdateSynonymsEvent::class => 'onPreUpdateSynonyms', - ]; - } - public function onPostResetSynonyms(PostResetSynonymsEvent $event): void { $this->logger->info(sprintf(self::LOG_MASK, 'The synonyms have been reset'), [ @@ -71,4 +58,17 @@ public function onPreUpdateSynonyms(PreUpdateSynonymsEvent $event): void 'synonyms' => $event->getSynonyms(), ]); } + + /** + * @return array + */ + public static function getSubscribedEvents(): array + { + return [ + PostResetSynonymsEvent::class => 'onPostResetSynonyms', + PreResetSynonymsEvent::class => 'onPreResetSynonyms', + PostUpdateSynonymsEvent::class => 'onPostUpdateSynonyms', + PreUpdateSynonymsEvent::class => 'onPreUpdateSynonyms', + ]; + } } diff --git a/src/ExpressionLanguage/SearchExpressionLanguage.php b/src/ExpressionLanguage/SearchExpressionLanguage.php new file mode 100644 index 0000000..1b0e37c --- /dev/null +++ b/src/ExpressionLanguage/SearchExpressionLanguage.php @@ -0,0 +1,21 @@ + + */ +final class SearchExpressionLanguage extends BaseExpressionLanguage +{ + public function __construct(CacheItemPoolInterface $cache = null, array $providers = []) + { + array_unshift($providers, new SearchExpressionLanguageProvider()); + + parent::__construct($cache, $providers); + } +} diff --git a/src/ExpressionLanguage/SearchExpressionLanguageProvider.php b/src/ExpressionLanguage/SearchExpressionLanguageProvider.php new file mode 100644 index 0000000..cd33c78 --- /dev/null +++ b/src/ExpressionLanguage/SearchExpressionLanguageProvider.php @@ -0,0 +1,40 @@ + + */ +final class SearchExpressionLanguageProvider implements ExpressionFunctionProviderInterface +{ + /** + * @return ExpressionFunction[] + */ + public function getFunctions(): array + { + return [ + new ExpressionFunction('search', function (string $index, string $query, ?string $filters = 'empty', ?int $max = null): string { + return sprintf('IN %s ON %s WHERE %s MAX %d', $index, $query, $filters, $max); + }, function (array $arguments, string $index, string $query, ?string $filters = null, ?int $max = null): Search { + $search = Search::on($index, $query); + + if (null === $max) { + return $search; + } + + if (null !== $filters && count(explode(' ', $filters)) > 1) { + $filters = explode(' ', $filters); + $search->where($filters[0], $filters[1], $filters[2]); + } + + return $search->max($max); + }), + ]; + } +} diff --git a/src/Index/IndexOrchestrator.php b/src/Index/IndexOrchestrator.php index c3f601d..91691ee 100644 --- a/src/Index/IndexOrchestrator.php +++ b/src/Index/IndexOrchestrator.php @@ -126,9 +126,7 @@ public function update(string $uid, array $configuration = []): void $this->handleConfiguration($index, $configuration); } catch (Throwable $throwable) { - $this->logger->error(sprintf('The index cannot be created, error: "%s"', $throwable->getMessage()), [ - 'trace' => $throwable->getTrace(), - ]); + $this->logger->error(sprintf('The index cannot be updated, error: "%s"', $throwable->getMessage())); throw new RuntimeException($throwable->getMessage(), 0, $throwable); } diff --git a/src/MeiliSearchBundle.php b/src/MeiliSearchBundle.php index 10ace27..f03d529 100644 --- a/src/MeiliSearchBundle.php +++ b/src/MeiliSearchBundle.php @@ -28,8 +28,6 @@ public function getContainerExtension(): ?ExtensionInterface */ public function build(ContainerBuilder $container): void { - parent::build($container); - $container->addCompilerPass(new MeiliSearchBundlePass()); } } diff --git a/src/Search/Search.php b/src/Search/Search.php index ebd16ba..7519ab7 100644 --- a/src/Search/Search.php +++ b/src/Search/Search.php @@ -5,12 +5,14 @@ namespace MeiliSearchBundle\Search; use MeiliSearchBundle\Exception\InvalidSearchConfigurationException; +use function count; +use function explode; +use function gettype; use function implode; use function in_array; use function is_array; use function is_numeric; use function is_string; -use function gettype; use function preg_match; use function sprintf; use function strpos; @@ -23,15 +25,16 @@ final class Search private const FILTERS_OPERATORS = ['=', '!=', '>', '>=', '<', '<=']; private const NUMERICAL_FILTERS_OPERATORS = ['>', '>=', '<', '<=']; - /** - * @var string - */ - private $index; + private const PRIMARY = 'root'; + public const AND = 'AND'; + public const OR = 'OR'; + public const NOT = 'NOT'; + public const AND_NOT = 'AND NOT'; /** * @var string */ - private $search; + private $index; /** * @var int @@ -51,7 +54,12 @@ final class Search /** * @var array */ - private $where = []; + private $filters = []; + + /** + * @var string|null + */ + private $computedFilters; /** * @var bool @@ -99,32 +107,32 @@ public static function on(string $index, string $query): self return $self; } - public function max(int $limit): self + public function query(string $query): self { - $this->limit = $limit; + if (preg_match('#\s#', $query) && false === strpos('"', $query)) { + throw new InvalidSearchConfigurationException('A compound query must be enclosed via double-quotes'); + } + + $this->query = $query; return $this; } - public function offset(int $offset): self + public function max(int $limit): self { - $this->offset = $offset; + $this->limit = $limit; return $this; } - public function query(string $query): self + public function offset(int $offset): self { - if (preg_match('#\s#', $query) && false === strpos('"', $query)) { - throw new InvalidSearchConfigurationException('A compound query must be enclosed via double-quotes'); - } - - $this->query = $query; + $this->offset = $offset; return $this; } - public function where(string $field, string $operator, $value): self + public function where(string $field, string $operator, $value, bool $isolated = false, string $criteria = self::PRIMARY): self { if (!in_array($operator, self::FILTERS_OPERATORS)) { throw new InvalidSearchConfigurationException('The given operator is not supported'); @@ -132,36 +140,73 @@ public function where(string $field, string $operator, $value): self if (in_array($operator, self::NUMERICAL_FILTERS_OPERATORS) && !is_numeric($value)) { throw new InvalidSearchConfigurationException(sprintf( - 'The value must be numeric when using a numeric related operator, given %s', + 'The value must be numeric when using a numeric related operator, given "%s"', gettype($value) )); } - $this->where[$field] = [ + if (self::PRIMARY === $criteria && count($this->filters) !== 0) { + throw new InvalidSearchConfigurationException(sprintf('The %s() cannot be used on an existing search', __METHOD__)); + } + + $value = (is_string($value) && preg_match('#\s#', $value)) ? sprintf('"%s"', $value) : $value; + + $this->filters[] = [ + 'field' => $field, 'operator' => $operator, - 'value' => (is_string($value) && preg_match('#\s#', $value)) ? sprintf('"%s"', $value) : $value, + 'value' => $value, + 'type' => self::PRIMARY, ]; + if (self::AND_NOT === $criteria && $isolated) { + $filter = explode('AND ', $field); + + $this->computedFilters .= ' ' . self::AND . ' ' . '(' . $filter[1] . ' ' . $operator . ' ' . $value . ')'; + + return $this; + } + + $this->computedFilters .= $isolated ? '(' . sprintf('%s %s ', $field, $operator) . $value . ')' : sprintf('%s %s ', $field, $operator) . $value; + + return $this; + } + + public function andWhere(string $field, string $operator, $value, bool $isolated = false): self + { + if (count($this->filters) === 0) { + throw new InvalidSearchConfigurationException(sprintf('The %s() cannot be used on an empty search', __METHOD__)); + } + + $this->where(sprintf(' %s %s', self::AND, $field), $operator, $value, $isolated, self::AND); + return $this; } - public function andWhere(string $field, string $operator, $value): self + public function orWhere(string $field, string $operator, $value, bool $isolated = false): self { - $this->where(sprintf(' AND %s', $field), $operator, $value); + if (count($this->filters) === 0) { + throw new InvalidSearchConfigurationException(sprintf('The %s() cannot be used on an empty search', __METHOD__)); + } + + $this->where(sprintf(' %s %s', self::OR, $field), $operator, $value, $isolated, self::OR); return $this; } - public function orWhere(string $field, string $operator, $value): self + public function not(string $field, string $operator, $value, bool $isolated = false): self { - $this->where(sprintf(' OR %s', $field), $operator, $value); + $this->where(sprintf('%s %s', self::NOT, $field), $operator, $value, $isolated, self::NOT); return $this; } - public function not(string $field, string $operator, $value): self + public function andNot(string $field, string $operator, $value, bool $isolated = false): self { - $this->where(sprintf('NOT %s', $field), $operator, $value); + if (count($this->filters) === 0) { + throw new InvalidSearchConfigurationException(sprintf('The %s() cannot be used on an empty search', __METHOD__)); + } + + $this->where(sprintf(' %s %s %s', self::AND, self::NOT, $field), $operator, $value, $isolated, self::AND_NOT); return $this; } @@ -228,7 +273,7 @@ public function addAndFacetFilter(string $key, string $value, string $secondKey, public function paginate(string $field, string $operator, $value, int $limit): self { - empty($this->where) ? $this->where($field, $operator, $value) : $this->andWhere($field, $operator, $value); + empty($this->filters) ? $this->where($field, $operator, $value) : $this->andWhere($field, $operator, $value); $this->max($limit); @@ -245,34 +290,39 @@ public function getQuery(): string return $this->query; } + public function getComputedFilters(): ?string + { + return $this->computedFilters; + } + public function getLimit(): int { return $this->limit; } + public function getOffset(): int + { + return $this->offset; + } + + public function shouldReturnMatches(): bool + { + return $this->match; + } + public function getRaw(): array { return [ 'index' => $this->index, - 'search' => $this->search, 'query' => $this->query, 'offset' => $this->offset, 'limit' => $this->limit, - 'filters' => $this->warmWhereFilters(), + 'filters' => $this->computedFilters, + 'rawFilters' => $this->filters, 'matches' => $this->match, 'attributesToRetrieve' => $this->retrievedAttributes, 'attributesToHighlight' => $this->highLightedAttributes, 'facetFilters' => $this->facetFilters, ]; } - - private function warmWhereFilters(): string - { - $filters = ''; - foreach ($this->where as $field => $configuration) { - $filters .= sprintf('%s %s ', $field, $configuration['operator']) . $configuration['value']; - } - - return $filters; - } } diff --git a/src/Search/SearchEntryPoint.php b/src/Search/SearchEntryPoint.php index 9aa425e..3351deb 100644 --- a/src/Search/SearchEntryPoint.php +++ b/src/Search/SearchEntryPoint.php @@ -88,10 +88,10 @@ public function search(string $index, string $query, array $options = []): Searc self::OPTIONS => $options, ])); - $this->logger->info('A query has been made', array_merge($options, [ + $this->logger->info('A query has been made', [ self::INDEX => $index, self::QUERY => $query, - ])); + ]); try { $result = $index->search($query, $options); diff --git a/tests/Cache/SearchResultCacheOrchestratorTest.php b/tests/Cache/SearchResultCacheOrchestratorTest.php index 356ef44..a1d444d 100644 --- a/tests/Cache/SearchResultCacheOrchestratorTest.php +++ b/tests/Cache/SearchResultCacheOrchestratorTest.php @@ -46,7 +46,10 @@ public function testOrchestratorCanAddNewItemWithoutLogger(): void public function testOrchestratorCanAddNewItemWithLogger(): void { $logger = $this->createMock(LoggerInterface::class); - $logger->expects(self::once())->method('info'); + $logger->expects(self::once())->method('info')->with(self::equalTo('A search result has been saved'), [ + 'identifier' => 'foo', + 'result' => [], + ]); $searchResult = $this->createMock(SearchResultInterface::class); $searchResult->expects(self::exactly(2))->method('toArray')->willReturn([]); @@ -56,16 +59,41 @@ public function testOrchestratorCanAddNewItemWithLogger(): void $orchestrator->add('foo', $searchResult); } - public function testOrchestratorCannotReturnAnInvalidSearchResult(): void + public function testOrchestratorCannotReturnAnUndefinedSearchResult(): void { $cacheItemPool = new ArrayAdapter(); $orchestrator = new SearchResultCacheOrchestrator($cacheItemPool); static::expectException(InvalidArgumentException::class); + static::expectExceptionMessage('The desired search result cannot be found'); + static::expectExceptionCode(0); $orchestrator->get('foo'); } - public function testOrchestratorCanReturnAnExistingSearchResult(): void + public function testOrchestratorCannotReturnAnInvalidSearchResult(): void + { + $searchResult = $this->createMock(SearchResultInterface::class); + $searchResult->expects(self::never())->method('toArray'); + + $logger = $this->createMock(LoggerInterface::class); + $logger->expects(self::once())->method('info')->with(self::equalTo('A search result has been retrieved'), [ + 'identifier' => 'foo', + ]); + + $cacheItemPool = new ArrayAdapter(); + $item = $cacheItemPool->getItem('foo'); + $item->set('foo'); + $cacheItemPool->save($item); + + $orchestrator = new SearchResultCacheOrchestrator($cacheItemPool, $logger); + + static::expectException(InvalidArgumentException::class); + static::expectExceptionMessage('The desired search result does not contain valid data'); + static::expectExceptionCode(0); + $orchestrator->get('foo'); + } + + public function testOrchestratorCanReturnAValidSearchResult(): void { $searchResult = $this->createMock(SearchResultInterface::class); $searchResult->expects(self::exactly(2))->method('toArray')->willReturn([ @@ -80,8 +108,11 @@ public function testOrchestratorCanReturnAnExistingSearchResult(): void 'facetsDistribution' => [], ]); + $logger = $this->createMock(LoggerInterface::class); + $logger->expects(self::exactly(2))->method('info'); + $cacheItemPool = new ArrayAdapter(); - $orchestrator = new SearchResultCacheOrchestrator($cacheItemPool); + $orchestrator = new SearchResultCacheOrchestrator($cacheItemPool, $logger); $orchestrator->add('foo', $searchResult); @@ -98,6 +129,8 @@ public function testOrchestratorCannotClear(): void $orchestrator = new SearchResultCacheOrchestrator($cacheItemPool); static::expectException(RuntimeException::class); + static::expectExceptionMessage('The cache pool cannot be cleared'); + static::expectExceptionCode(0); $orchestrator->clear(); } diff --git a/tests/Command/DeleteIndexCommandTest.php b/tests/Command/DeleteIndexCommandTest.php index 01d2d7c..08feffd 100644 --- a/tests/Command/DeleteIndexCommandTest.php +++ b/tests/Command/DeleteIndexCommandTest.php @@ -8,6 +8,7 @@ use Generator; use MeiliSearchBundle\Index\IndexOrchestratorInterface; use MeiliSearchBundle\Command\DeleteIndexCommand; +use MeiliSearchBundle\Metadata\IndexMetadataRegistryInterface; use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Tester\CommandTester; use function sprintf; @@ -20,8 +21,9 @@ final class DeleteIndexCommandTest extends TestCase public function testCommandIsConfigured(): void { $orchestrator = $this->createMock(IndexOrchestratorInterface::class); + $indexMetadataRegistry = $this->createMock(IndexMetadataRegistryInterface::class); - $command = new DeleteIndexCommand($orchestrator); + $command = new DeleteIndexCommand($orchestrator, $indexMetadataRegistry); static::assertSame('meili:delete-index', $command->getName()); static::assertTrue($command->getDefinition()->hasArgument('index')); @@ -37,7 +39,10 @@ public function testCommandCannotDeleteInvalidIndex(string $index): void $orchestrator = $this->createMock(IndexOrchestratorInterface::class); $orchestrator->expects(self::once())->method('removeIndex')->willThrowException(new Exception('An error occurred')); - $command = new DeleteIndexCommand($orchestrator); + $indexMetadataRegistry = $this->createMock(IndexMetadataRegistryInterface::class); + $indexMetadataRegistry->expects(self::never())->method('remove'); + + $command = new DeleteIndexCommand($orchestrator, $indexMetadataRegistry); $tester = new CommandTester($command); $tester->setInputs(['yes']); @@ -61,7 +66,10 @@ public function testCommandCanDeleteValidIndexWithoutConfirmation(string $index) $orchestrator = $this->createMock(IndexOrchestratorInterface::class); $orchestrator->expects(self::never())->method('removeIndex'); - $command = new DeleteIndexCommand($orchestrator); + $indexMetadataRegistry = $this->createMock(IndexMetadataRegistryInterface::class); + $indexMetadataRegistry->expects(self::never())->method('remove'); + + $command = new DeleteIndexCommand($orchestrator, $indexMetadataRegistry); $tester = new CommandTester($command); $tester->execute([ @@ -80,7 +88,10 @@ public function testCommandCanDeleteValidIndex(string $index): void $orchestrator = $this->createMock(IndexOrchestratorInterface::class); $orchestrator->expects(self::once())->method('removeIndex')->with(self::equalTo($index)); - $command = new DeleteIndexCommand($orchestrator); + $indexMetadataRegistry = $this->createMock(IndexMetadataRegistryInterface::class); + $indexMetadataRegistry->expects(self::once())->method('remove')->with(self::equalTo($index)); + + $command = new DeleteIndexCommand($orchestrator, $indexMetadataRegistry); $tester = new CommandTester($command); $tester->setInputs(['yes']); @@ -100,7 +111,10 @@ public function testCommandCanDeleteValidIndexWithForceOption(string $index): vo $orchestrator = $this->createMock(IndexOrchestratorInterface::class); $orchestrator->expects(self::once())->method('removeIndex')->with(self::equalTo($index)); - $command = new DeleteIndexCommand($orchestrator); + $indexMetadataRegistry = $this->createMock(IndexMetadataRegistryInterface::class); + $indexMetadataRegistry->expects(self::once())->method('remove')->with(self::equalTo($index)); + + $command = new DeleteIndexCommand($orchestrator, $indexMetadataRegistry); $tester = new CommandTester($command); $tester->execute([ diff --git a/tests/EventSubscriber/DocumentEventSubscriberTest.php b/tests/EventSubscriber/DocumentEventSubscriberTest.php index 9203746..2fc145f 100644 --- a/tests/EventSubscriber/DocumentEventSubscriberTest.php +++ b/tests/EventSubscriber/DocumentEventSubscriberTest.php @@ -36,7 +36,10 @@ public function testSubscriberIsConfigured(): void public function testSubscriberCanListenOnPostDocumentCreation(): void { $logger = $this->createMock(LoggerInterface::class); - $logger->expects(self::once())->method('info'); + $logger->expects(self::once())->method('info')->with(self::equalTo('A document has been created'), [ + 'index' => 'foo', + 'update' => 1, + ]); $index = $this->createMock(Indexes::class); $index->expects(self::once())->method('getUid')->willReturn('foo'); @@ -53,7 +56,9 @@ public function testSubscriberCanListenOnPostDocumentCreation(): void public function testSubscriberCanListenOnPostDocumentDeletion(): void { $logger = $this->createMock(LoggerInterface::class); - $logger->expects(self::once())->method('info'); + $logger->expects(self::once())->method('info')->with(self::equalTo('A document has been deleted'), [ + 'update' => 1, + ]); $event = new PostDocumentDeletionEvent(1); @@ -67,7 +72,13 @@ public function testSubscriberCanListenOnPostDocumentDeletion(): void public function testSubscriberCanListenOnPostDocumentRetrieve(): void { $logger = $this->createMock(LoggerInterface::class); - $logger->expects(self::once())->method('info'); + $logger->expects(self::once())->method('info')->with(self::equalTo('A document has been retrieved'), [ + 'index' => 'foo', + 'document' => [ + 'id' => 1, + 'title' => 'foo', + ], + ]); $index = $this->createMock(Indexes::class); $index->expects(self::once())->method('getUid')->willReturn('foo'); @@ -87,7 +98,9 @@ public function testSubscriberCanListenOnPostDocumentRetrieve(): void public function testSubscriberCanListenOnPostDocumentUpdate(): void { $logger = $this->createMock(LoggerInterface::class); - $logger->expects(self::once())->method('info'); + $logger->expects(self::once())->method('info')->with(self::equalTo('A document has been updated'), [ + 'update' => 1, + ]); $event = new PostDocumentUpdateEvent(1); @@ -101,7 +114,13 @@ public function testSubscriberCanListenOnPostDocumentUpdate(): void public function testSubscriberCanListenOnPreDocumentCreation(): void { $logger = $this->createMock(LoggerInterface::class); - $logger->expects(self::once())->method('info'); + $logger->expects(self::once())->method('info')->with(self::equalTo('A document is about to be created'), [ + 'index' => 'bar', + 'document' => [ + 'id' => 1, + 'title' => 'foo', + ], + ]); $index = $this->createMock(Indexes::class); $index->expects(self::once())->method('getUid')->willReturn('bar'); @@ -120,7 +139,13 @@ public function testSubscriberCanListenOnPreDocumentCreation(): void public function testSubscriberCanListenOnPreDocumentDeletion(): void { $logger = $this->createMock(LoggerInterface::class); - $logger->expects(self::once())->method('info'); + $logger->expects(self::once())->method('info')->with(self::equalTo('A document is about to be deleted'), [ + 'index' => 'bar', + 'document' => [ + 'id' => 1, + 'title' => 'foo', + ], + ]); $index = $this->createMock(Indexes::class); $index->expects(self::once())->method('getUid')->willReturn('bar'); @@ -139,7 +164,10 @@ public function testSubscriberCanListenOnPreDocumentDeletion(): void public function testSubscriberCanListenOnPreDocumentRetrieve(): void { $logger = $this->createMock(LoggerInterface::class); - $logger->expects(self::once())->method('info'); + $logger->expects(self::once())->method('info')->with(self::equalTo('A document is about to be retrieved'), [ + 'index' => 'foo', + 'document' => 1, + ]); $index = $this->createMock(Indexes::class); $index->expects(self::once())->method('getUid')->willReturn('foo'); @@ -155,7 +183,13 @@ public function testSubscriberCanListenOnPreDocumentRetrieve(): void public function testSubscriberCanListenOnPreDocumentUpdate(): void { $logger = $this->createMock(LoggerInterface::class); - $logger->expects(self::once())->method('info'); + $logger->expects(self::once())->method('info')->with(self::equalTo('A document is about to be updated'), [ + 'index' => 'bar', + 'document' => [ + 'id' => 1, + 'title' => 'foo', + ], + ]); $index = $this->createMock(Indexes::class); $index->expects(self::once())->method('getUid')->willReturn('bar'); diff --git a/tests/EventSubscriber/ExceptionSubscriberTest.php b/tests/EventSubscriber/ExceptionSubscriberTest.php index 41c52f5..54087d8 100644 --- a/tests/EventSubscriber/ExceptionSubscriberTest.php +++ b/tests/EventSubscriber/ExceptionSubscriberTest.php @@ -55,7 +55,9 @@ public function testSubscriberCannotBeCalledOnNullLogger(): void public function testSubscriberCanBeCalled(): void { $logger = $this->createMock(LoggerInterface::class); - $logger->expects(self::once())->method('critical')->with(self::equalTo('[MeiliSearch] An error occurred: An error occurred')); + $logger->expects(self::once())->method('critical')->with(self::equalTo('[MeiliSearch] An error occurred: An error occurred'), [ + 'error' => 'An error occurred', + ]); $kernel = $this->createMock(KernelInterface::class); $request = $this->createMock(Request::class); diff --git a/tests/EventSubscriber/IndexEventSubscriberTest.php b/tests/EventSubscriber/IndexEventSubscriberTest.php index 0eb609f..a4318b9 100644 --- a/tests/EventSubscriber/IndexEventSubscriberTest.php +++ b/tests/EventSubscriber/IndexEventSubscriberTest.php @@ -43,11 +43,14 @@ public function testSubscriberCanListenToIndexCreationWithoutLogger(): void public function testSubscriberCanListenToIndexCreationWithLogger(): void { - $logger = $this->createMock(LoggerInterface::class); - $logger->expects(self::once())->method('info'); - $index = $this->createMock(Indexes::class); + $logger = $this->createMock(LoggerInterface::class); + $logger->expects(self::once())->method('info')->with(self::equalTo('[MeiliSearch] An index has been created'), [ + 'index' => $index, + 'configuration' => [], + ]); + $event = new IndexCreatedEvent([], $index); $list = $this->createMock(IndexEventListInterface::class); @@ -73,11 +76,13 @@ public function testSubscriberCanListenToIndexDeletionWithoutLogger(): void public function testSubscriberCanListenToIndexDeletionWithLogger(): void { - $logger = $this->createMock(LoggerInterface::class); - $logger->expects(self::once())->method('info'); - $event = new IndexRemovedEvent('foo'); + $logger = $this->createMock(LoggerInterface::class); + $logger->expects(self::once())->method('info')->with(self::equalTo('[MeiliSearch] An index has been removed'), [ + 'index' => 'foo', + ]); + $list = $this->createMock(IndexEventListInterface::class); $list->expects(self::once())->method('add')->with($event); @@ -104,12 +109,14 @@ public function testSubscriberCanListenToIndexRetrievedWithoutLogger(): void public function testSubscriberCanListenToIndexRetrievedWithLogger(): void { - $logger = $this->createMock(LoggerInterface::class); - $logger->expects(self::once())->method('info'); - $index = $this->createMock(Indexes::class); $index->expects(self::once())->method('getUid')->willReturn('foo'); + $logger = $this->createMock(LoggerInterface::class); + $logger->expects(self::once())->method('info')->with(self::equalTo('[MeiliSearch] An index has been retrieved'), [ + 'index' => 'foo', + ]); + $event = new IndexRetrievedEvent($index); $list = $this->createMock(IndexEventListInterface::class); diff --git a/tests/EventSubscriber/SearchEventSubscriberTest.php b/tests/EventSubscriber/SearchEventSubscriberTest.php index aa64930..89f8f1a 100644 --- a/tests/EventSubscriber/SearchEventSubscriberTest.php +++ b/tests/EventSubscriber/SearchEventSubscriberTest.php @@ -46,10 +46,12 @@ public function testSubscriberCanListenPostSearchWithoutLogger(): void public function testSubscriberCanListenPostSearch(): void { $logger = $this->createMock(LoggerInterface::class); - $logger->expects(self::once())->method('info'); + $logger->expects(self::once())->method('info')->with(self::equalTo('[MeiliSearch] A search has been made'), [ + 'results' => [], + ]); $searchResult = $this->createMock(SearchResultInterface::class); - $searchResult->expects(self::once())->method('toArray'); + $searchResult->expects(self::once())->method('toArray')->willReturn([]); $event = new PostSearchEvent($searchResult); @@ -77,7 +79,9 @@ public function testSubscriberCanListenPreSearchWithoutLogger(): void public function testSubscriberCanListenPreSearch(): void { $logger = $this->createMock(LoggerInterface::class); - $logger->expects(self::once())->method('info'); + $logger->expects(self::once())->method('info')->with(self::equalTo('[MeiliSearch] A search is about to be made'), [ + 'configuration' => [], + ]); $event = new PreSearchEvent([]); diff --git a/tests/EventSubscriber/SynonymsEventSubscriberTest.php b/tests/EventSubscriber/SynonymsEventSubscriberTest.php index d5043e9..d6b0881 100644 --- a/tests/EventSubscriber/SynonymsEventSubscriberTest.php +++ b/tests/EventSubscriber/SynonymsEventSubscriberTest.php @@ -41,11 +41,14 @@ public function testSubscriberCanListenOnPostResetWithoutLogger(): void public function testSubscriberCanListenOnPostReset(): void { - $logger = $this->createMock(LoggerInterface::class); - $logger->expects(self::once())->method('info'); - $index = $this->createMock(Indexes::class); + $logger = $this->createMock(LoggerInterface::class); + $logger->expects(self::once())->method('info')->with(self::equalTo('[MeiliSearch] The synonyms have been reset'), [ + 'index' => $index, + 'update' => 1, + ]); + $event = new PostResetSynonymsEvent($index, 1); $subscriber = new SynonymsEventSubscriber($logger); @@ -67,11 +70,14 @@ public function testSubscriberCanListenOnPostUpdateWithoutLogger(): void public function testSubscriberCanListenOnPostUpdate(): void { - $logger = $this->createMock(LoggerInterface::class); - $logger->expects(self::once())->method('info'); - $index = $this->createMock(Indexes::class); + $logger = $this->createMock(LoggerInterface::class); + $logger->expects(self::once())->method('info')->with(self::equalTo('[MeiliSearch] The synonyms have been updated'), [ + 'index' => $index, + 'update' => 1, + ]); + $event = new PostUpdateSynonymsEvent($index, 1); $subscriber = new SynonymsEventSubscriber($logger); @@ -93,11 +99,13 @@ public function testSubscriberCanListenOnPreResetWithoutLogger(): void public function testSubscriberCanListenOnPreReset(): void { - $logger = $this->createMock(LoggerInterface::class); - $logger->expects(self::once())->method('info'); - $index = $this->createMock(Indexes::class); + $logger = $this->createMock(LoggerInterface::class); + $logger->expects(self::once())->method('info')->with(self::equalTo('[MeiliSearch] The synonyms are about to been reset'), [ + 'index' => $index, + ]); + $event = new PreResetSynonymsEvent($index); $subscriber = new SynonymsEventSubscriber($logger); @@ -119,11 +127,14 @@ public function testSubscriberCanListenOnPreUpdateWithoutLogger(): void public function testSubscriberCanListenOnPreUpdate(): void { - $logger = $this->createMock(LoggerInterface::class); - $logger->expects(self::once())->method('info'); - $index = $this->createMock(Indexes::class); + $logger = $this->createMock(LoggerInterface::class); + $logger->expects(self::once())->method('info')->with(self::equalTo('[MeiliSearch] The synonyms are about to been updated'), [ + 'index' => $index, + 'synonyms' => [], + ]); + $event = new PreUpdateSynonymsEvent($index, []); $subscriber = new SynonymsEventSubscriber($logger); diff --git a/tests/ExpressionLanguage/SearchExpressionLanguageProviderTest.php b/tests/ExpressionLanguage/SearchExpressionLanguageProviderTest.php new file mode 100644 index 0000000..ee4b257 --- /dev/null +++ b/tests/ExpressionLanguage/SearchExpressionLanguageProviderTest.php @@ -0,0 +1,24 @@ + + */ +final class SearchExpressionLanguageProviderTest extends TestCase +{ + public function testProviderIsRegistered(): void + { + $provider = new SearchExpressionLanguageProvider(); + + static::assertNotEmpty($provider->getFunctions()); + static::assertInstanceOf(ExpressionFunction::class, $provider->getFunctions()[0]); + static::assertSame('search', $provider->getFunctions()[0]->getName()); + } +} diff --git a/tests/ExpressionLanguage/SearchExpressionLanguageTest.php b/tests/ExpressionLanguage/SearchExpressionLanguageTest.php new file mode 100644 index 0000000..813a688 --- /dev/null +++ b/tests/ExpressionLanguage/SearchExpressionLanguageTest.php @@ -0,0 +1,69 @@ + + */ +final class SearchExpressionLanguageTest extends TestCase +{ + public function testSearchExpressionLanguageCanCompileSearch(): void + { + $expressionLanguage = new SearchExpressionLanguage(); + + static::assertSame('IN "foo" ON "bar" WHERE empty MAX 0', $expressionLanguage->compile('search("foo", "bar")')); + } + + public function testSearchExpressionLanguageCanCompileSearchWithFilters(): void + { + $expressionLanguage = new SearchExpressionLanguage(); + + static::assertSame('IN "foo" ON "bar" WHERE "title > 2" MAX 0', $expressionLanguage->compile('search("foo", "bar", "title > 2")')); + } + + public function testSearchExpressionLanguageCanCompileSearchWithMax(): void + { + $expressionLanguage = new SearchExpressionLanguage(); + + static::assertSame('IN "foo" ON "bar" WHERE "" MAX 2', $expressionLanguage->compile('search("foo", "bar", "", 2)')); + } + + public function testSearchExpressionLanguageCanHandleSearch(): void + { + $expressionLanguage = new SearchExpressionLanguage(); + $search = $expressionLanguage->evaluate('search("foo", "bar")'); + + static::assertInstanceOf(Search::class, $search); + static::assertSame('foo', $search->getIndex()); + static::assertSame('bar', $search->getQuery()); + } + + public function testSearchExpressionLanguageCanHandleSearchWithFilters(): void + { + $expressionLanguage = new SearchExpressionLanguage(); + $search = $expressionLanguage->evaluate('search("foo", "bar", "title > 2", 2)'); + + static::assertInstanceOf(Search::class, $search); + static::assertSame('foo', $search->getIndex()); + static::assertSame('bar', $search->getQuery()); + static::assertSame('title > 2', $search->getComputedFilters()); + static::assertSame(2, $search->getLimit()); + } + + public function testSearchExpressionLanguageCanHandleSearchWithMax(): void + { + $expressionLanguage = new SearchExpressionLanguage(); + $search = $expressionLanguage->evaluate('search("foo", "bar", "", 2)'); + + static::assertInstanceOf(Search::class, $search); + static::assertSame('foo', $search->getIndex()); + static::assertSame('bar', $search->getQuery()); + static::assertSame(2, $search->getLimit()); + } +} diff --git a/tests/Index/IndexOrchestratorTest.php b/tests/Index/IndexOrchestratorTest.php index 3fb837f..3bf8fab 100644 --- a/tests/Index/IndexOrchestratorTest.php +++ b/tests/Index/IndexOrchestratorTest.php @@ -8,6 +8,7 @@ use MeiliSearch\Client; use MeiliSearch\Endpoints\Indexes; use MeiliSearchBundle\Event\Index\IndexCreatedEvent; +use MeiliSearchBundle\Exception\RuntimeException as InternalRuntimeException; use MeiliSearchBundle\Index\IndexOrchestrator; use MeiliSearchBundle\Exception\RuntimeException as MeiliSeachBundleRuntimeException; use PHPUnit\Framework\TestCase; @@ -126,6 +127,30 @@ public function testIndexCannotBeUpdatedWithException(): void $orchestrator->update('foo', ['synonyms' => ['xmen' => ['wolverine']]]); } + public function testIndexCannotBeUpdatedWithExceptionOnUpdate(): void + { + $eventDispatcher = $this->createMock(EventDispatcherInterface::class); + $eventDispatcher->expects(self::once())->method('dispatch'); + + $logger = $this->createMock(LoggerInterface::class); + $logger->expects(self::once())->method('info'); + $logger->expects(self::once())->method('error')->with(self::equalTo('The index cannot be updated, error: "An error occurred"')); + + $index = $this->createMock(Indexes::class); + $index->expects(self::once())->method('updateSynonyms')->willThrowException(new RuntimeException('An error occurred')); + $index->expects(self::once())->method('getPrimaryKey')->willReturn('id'); + + $client = $this->createMock(Client::class); + $client->expects(self::once())->method('getIndex')->willReturn($index); + + $orchestrator = new IndexOrchestrator($client, $eventDispatcher, $logger); + + static::expectException(InternalRuntimeException::class); + static::expectExceptionMessage('An error occurred'); + static::expectExceptionCode(0); + $orchestrator->update('test', ['synonyms' => ['xmen' => ['wolverine']]]); + } + public function testIndexCanBeUpdated(): void { $eventDispatcher = $this->createMock(EventDispatcherInterface::class); diff --git a/tests/Index/SynonymsOrchestratorTest.php b/tests/Index/SynonymsOrchestratorTest.php index 534f824..97e85d9 100644 --- a/tests/Index/SynonymsOrchestratorTest.php +++ b/tests/Index/SynonymsOrchestratorTest.php @@ -21,7 +21,10 @@ final class SynonymsOrchestratorTest extends TestCase public function testSynonymsCannotBeReturnedWithException(): void { $logger = $this->createMock(LoggerInterface::class); - $logger->expects(self::once())->method('error'); + $logger->expects(self::once())->method('error')->with(self::equalTo('An error occurred when trying to fetch the synonyms'), [ + 'index' => 'foo', + 'error' => 'An error occurred', + ]); $eventDispatcher = $this->createMock(EventDispatcherInterface::class); @@ -31,6 +34,8 @@ public function testSynonymsCannotBeReturnedWithException(): void $orchestrator = new SynonymsOrchestrator($indexOrchestrator, $eventDispatcher, $logger); static::expectException(Exception::class); + static::expectExceptionMessage('An error occurred'); + static::expectExceptionCode(0); $orchestrator->getSynonyms('foo'); } @@ -149,7 +154,10 @@ public function testSynonymsCanBeUpdatedWithoutEventDispatcher(): void public function testSynonymsCannotBeResetWithException(): void { $logger = $this->createMock(LoggerInterface::class); - $logger->expects(self::once())->method('error'); + $logger->expects(self::once())->method('error')->with(self::equalTo('An error occurred when trying to reset the synonyms'), [ + 'index' => 'foo', + 'error' => 'An error occurred', + ]); $eventDispatcher = $this->createMock(EventDispatcherInterface::class); $eventDispatcher->expects(self::never())->method('dispatch'); @@ -160,6 +168,8 @@ public function testSynonymsCannotBeResetWithException(): void $orchestrator = new SynonymsOrchestrator($indexOrchestrator, $eventDispatcher, $logger); static::expectException(Exception::class); + static::expectExceptionMessage('An error occurred'); + static::expectExceptionCode(0); $orchestrator->resetSynonyms('foo'); } diff --git a/tests/Messenger/AddIndexMessageTest.php b/tests/Messenger/AddIndexMessageTest.php index 16b10ca..b0338c7 100644 --- a/tests/Messenger/AddIndexMessageTest.php +++ b/tests/Messenger/AddIndexMessageTest.php @@ -18,6 +18,13 @@ public function testIndexDataCanBeRetrievedWithNullPrimaryKey(): void static::assertSame('foo', $message->getUid()); static::assertNull($message->getPrimaryKey()); + static::assertArrayHasKey('distinctAttribute', $message->getConfiguration()); + static::assertArrayHasKey('facetedAttributes', $message->getConfiguration()); + static::assertArrayHasKey('searchableAttributes', $message->getConfiguration()); + static::assertArrayHasKey('displayedAttributes', $message->getConfiguration()); + static::assertArrayHasKey('rankingRules', $message->getConfiguration()); + static::assertArrayHasKey('stopWords', $message->getConfiguration()); + static::assertArrayHasKey('synonyms', $message->getConfiguration()); } public function testIndexDataCanBeRetrieved(): void diff --git a/tests/Metadata/IndexMetadataTest.php b/tests/Metadata/IndexMetadataTest.php index 45927dd..7b55c73 100644 --- a/tests/Metadata/IndexMetadataTest.php +++ b/tests/Metadata/IndexMetadataTest.php @@ -14,10 +14,10 @@ final class IndexMetadataTest extends TestCase { public function testMetadataCanBeRetrieved(): void { - $metadata = new IndexMetadata('foo', true, 'id'); + $metadata = new IndexMetadata('foo', false, 'id'); static::assertSame('foo', $metadata->getUid()); - static::assertTrue($metadata->isAsync()); + static::assertFalse($metadata->isAsync()); static::assertSame('id', $metadata->getPrimaryKey()); static::assertEmpty($metadata->getRankingRules()); static::assertEmpty($metadata->getStopWords()); @@ -25,5 +25,18 @@ public function testMetadataCanBeRetrieved(): void static::assertEmpty($metadata->getSearchableAttributes()); static::assertEmpty($metadata->getDisplayedAttributes()); static::assertNull($metadata->getDistinctAttribute()); + + static::assertArrayHasKey('primaryKey', $metadata->toArray()); + static::assertArrayHasKey('rankingRules', $metadata->toArray()); + static::assertArrayHasKey('stopWords', $metadata->toArray()); + static::assertArrayHasKey('distinctAttribute', $metadata->toArray()); + static::assertArrayHasKey('facetedAttributes', $metadata->toArray()); + static::assertArrayHasKey('searchableAttributes', $metadata->toArray()); + static::assertArrayHasKey('displayedAttributes', $metadata->toArray()); + static::assertArrayHasKey('synonyms', $metadata->toArray()); + + $metadata = new IndexMetadata('foo', true, 'id'); + + static::assertTrue($metadata->isAsync()); } } diff --git a/tests/Search/SearchEntryPointTest.php b/tests/Search/SearchEntryPointTest.php index b60d3a8..06b9865 100644 --- a/tests/Search/SearchEntryPointTest.php +++ b/tests/Search/SearchEntryPointTest.php @@ -6,13 +6,17 @@ use Exception; use MeiliSearch\Endpoints\Indexes; +use MeiliSearchBundle\Event\PostSearchEvent; +use MeiliSearchBundle\Event\PreSearchEvent; use MeiliSearchBundle\Exception\RuntimeException; use MeiliSearchBundle\Index\IndexOrchestratorInterface; use MeiliSearchBundle\Result\ResultBuilderInterface; use MeiliSearchBundle\Search\SearchEntryPoint; +use MeiliSearchBundle\Search\SearchResult; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; +use function array_merge; /** * @author Guillaume Loulier @@ -47,10 +51,6 @@ public function testSearchCannotOccurWithExceptionOnSearch(): void $eventDispatcher = $this->createMock(EventDispatcherInterface::class); $eventDispatcher->expects(self::once())->method('dispatch'); - $logger = $this->createMock(LoggerInterface::class); - $logger->expects(self::once())->method('info'); - $logger->expects(self::once())->method('error')->with(self::equalTo('The query has failed')); - $index = $this->createMock(Indexes::class); $index->expects(self::once())->method('search')->willThrowException(new Exception('An error occurred')); @@ -59,10 +59,19 @@ public function testSearchCannotOccurWithExceptionOnSearch(): void $resultBuilder = $this->createMock(ResultBuilderInterface::class); + $logger = $this->createMock(LoggerInterface::class); + $logger->expects(self::once())->method('info'); + $logger->expects(self::once())->method('error')->with(self::equalTo('The query has failed'), [ + 'error' => 'An error occurred', + 'query' => 'bar', + 'options' => [], + ]); + $searchEntryPoint = new SearchEntryPoint($orchestrator, $resultBuilder, $eventDispatcher, $logger); static::expectException(RuntimeException::class); static::expectExceptionMessage('An error occurred'); + static::expectExceptionCode(0); $searchEntryPoint->search('foo', 'bar'); } @@ -158,6 +167,80 @@ public function testSearchCanOccurWithModels(): void static::assertSame(0, $result->getOffset()); static::assertSame('bar', $result->getQuery()); } + + public function testSearchCanOccursWithPrefix(): void + { + $index = $this->createMock(Indexes::class); + $index->expects(self::once())->method('search')->willReturn([ + 'hits' => [ + [ + 'id' => 1, + 'title' => 'bar', + ], + [ + 'id' => 2, + 'title' => 'foo', + ], + ], + "offset" => 0, + "limit" => 20, + "nbHits" => 2, + "exhaustiveNbHits" => false, + "processingTimeMs" => 35, + "query" => 'bar' + ]); + + $orchestrator = $this->createMock(IndexOrchestratorInterface::class); + $orchestrator->expects(self::once())->method('getIndex')->with(self::equalTo('_app_foo'))->willReturn($index); + + $resultBuilder = $this->createMock(ResultBuilderInterface::class); + + $eventDispatcher = $this->createMock(EventDispatcherInterface::class); + $eventDispatcher->expects(self::exactly(2))->method('dispatch')->withConsecutive( + [ + new PreSearchEvent([ + 'index' => $index, + 'query' => 'bar', + 'options' => [], + ]), + ], + [ + new PostSearchEvent(SearchResult::create( + [ + [ + 'id' => 1, + 'title' => 'bar', + ], + [ + 'id' => 2, + 'title' => 'foo', + ], + ], + 0, + 20, + 2, + false, + 35, + 'bar' + )), + ] + ); + + $logger = $this->createMock(LoggerInterface::class); + $logger->expects(self::once())->method('info')->with(self::equalTo('A query has been made'), [ + 'index' => $index, + 'query' => 'bar', + ]); + + $searchEntryPoint = new SearchEntryPoint($orchestrator, $resultBuilder, $eventDispatcher, $logger, '_app_'); + $result = $searchEntryPoint->search('foo', 'bar'); + + static::assertNotEmpty($result->getHits()); + static::assertSame(2, $result->count()); + static::assertSame(20, $result->getLimit()); + static::assertSame(0, $result->getOffset()); + static::assertSame('bar', $result->getQuery()); + } } final class FooModel diff --git a/tests/Search/SearchTest.php b/tests/Search/SearchTest.php index cbb9f41..2346227 100644 --- a/tests/Search/SearchTest.php +++ b/tests/Search/SearchTest.php @@ -26,6 +26,26 @@ public function testSearchCanBeBuiltOnIndex(): void static::assertSame('foo', $search->getIndex()); } + public function testSearchCannotBeBuiltOnSpecificQueryWithSpaces(): void + { + $search = new Search(); + + static::expectExceptionMessage(InvalidSearchConfigurationException::class); + static::expectExceptionMessage('A compound query must be enclosed via double-quotes'); + static::expectExceptionCode(0); + $search->in('foo')->query('hello world'); + + static::expectExceptionMessage(InvalidSearchConfigurationException::class); + static::expectExceptionMessage('A compound query must be enclosed via double-quotes'); + static::expectExceptionCode(0); + Search::within('foo')->query('hello world'); + + static::expectExceptionMessage(InvalidSearchConfigurationException::class); + static::expectExceptionMessage('A compound query must be enclosed via double-quotes'); + static::expectExceptionCode(0); + Search::on('foo', 'hello world'); + } + public function testSearchCanBeBuiltOnSpecificQuery(): void { $search = new Search(); @@ -71,30 +91,100 @@ public function testSearchCanTargetSpecificIndexAndOffset(): void static::assertSame('foo', $search->getRaw()['index']); static::assertSame(10, $search->getRaw()['offset']); + static::assertSame(10, $search->getOffset()); $search = Search::within('foo')->offset(10); static::assertSame('foo', $search->getRaw()['index']); static::assertSame(10, $search->getRaw()['offset']); + static::assertSame(10, $search->getOffset()); } public function testSearchCannotBeBuiltWithInvalidWhereCondition(): void { $search = new Search(); static::expectException(InvalidSearchConfigurationException::class); + static::expectExceptionMessage('The given operator is not supported'); + static::expectExceptionCode(0); $search->in('foo')->where('id', '===', 1); static::expectException(InvalidSearchConfigurationException::class); + static::expectExceptionMessage('The given operator is not supported'); + static::expectExceptionCode(0); Search::within('foo')->where('id', '===', 1); } + public function testSearchCannotBeBuiltWithInvalidWhereNumericalOperator(): void + { + $search = new Search(); + static::expectException(InvalidSearchConfigurationException::class); + static::expectExceptionMessage('The value must be numeric when using a numeric related operator, given "string"'); + static::expectExceptionCode(0); + $search->in('foo')->where('id', '<', '[]'); + + static::expectException(InvalidSearchConfigurationException::class); + static::expectExceptionMessage('The value must be numeric when using a numeric related operator, given "string"'); + static::expectExceptionCode(0); + Search::within('foo')->where('id', '<', '[]'); + } + + public function testSearchCannotBeBuiltWithExistingWhereCondition(): void + { + $search = new Search(); + $search->in('foo')->where('id', '=', 1); + + static::expectException(InvalidSearchConfigurationException::class); + static::expectExceptionMessage('The MeiliSearchBundle\Search\Search::where() cannot be used on an existing search'); + static::expectExceptionCode(0); + $search->where('id', '=', 2); + + $search = Search::within('foo')->where('id', '=', 1); + + static::expectException(InvalidSearchConfigurationException::class); + static::expectExceptionMessage('The MeiliSearchBundle\Search\Search::where() cannot be used on an existing search'); + static::expectExceptionCode(0); + $search->where('id', '=', 2); + } + public function testSearchCanBeBuiltWithValidWhereCondition(): void { $search = new Search(); $search->in('foo')->where('id', '=', 1); static::assertSame('id = 1', $search->getRaw()['filters']); + static::assertArrayHasKey(0, $search->getRaw()['rawFilters']); + + $filter = $search->getRaw()['rawFilters'][0]; + static::assertArrayHasKey('field', $filter); + static::assertSame('id', $filter['field']); + static::assertArrayHasKey('operator', $filter); + static::assertSame('=', $filter['operator']); + static::assertArrayHasKey('value', $filter); + static::assertSame(1, $filter['value']); + static::assertArrayHasKey('type', $filter); + static::assertSame('root', $filter['type']); $search = Search::within('foo')->where('id', '=', 1); static::assertSame('id = 1', $search->getRaw()['filters']); + static::assertArrayHasKey(0, $search->getRaw()['rawFilters']); + + $filter = $search->getRaw()['rawFilters'][0]; + static::assertArrayHasKey('field', $filter); + static::assertSame('id', $filter['field']); + static::assertArrayHasKey('operator', $filter); + static::assertSame('=', $filter['operator']); + static::assertArrayHasKey('value', $filter); + static::assertSame(1, $filter['value']); + static::assertArrayHasKey('type', $filter); + static::assertSame('root', $filter['type']); + } + + public function testSearchCanBeBuiltWithValidIsolatedWhereCondition(): void + { + $search = new Search(); + $search->in('foo')->where('id', '=', 1, true); + static::assertSame('(id = 1)', $search->getComputedFilters()); + + $search = Search::within('foo')->where('id', '=', 1, true); + static::assertSame('(id = 1)', $search->getComputedFilters()); } public function testSearchCanBeBuiltWithCompoundWhereCondition(): void @@ -107,6 +197,16 @@ public function testSearchCanBeBuiltWithCompoundWhereCondition(): void static::assertSame('id = "Hello World"', $search->getRaw()['filters']); } + public function testSearchCanBeBuiltWithCompoundAndIsolatedWhereCondition(): void + { + $search = new Search(); + $search->in('foo')->where('id', '=', 'Hello World', true); + static::assertSame('(id = "Hello World")', $search->getComputedFilters()); + + $search = Search::within('foo')->where('id', '=', 'Hello World', true); + static::assertSame('(id = "Hello World")', $search->getComputedFilters()); + } + public function testSearchCannotBeBuiltWithInvalidAndWhereCondition(): void { $search = new Search(); @@ -118,6 +218,21 @@ public function testSearchCannotBeBuiltWithInvalidAndWhereCondition(): void Search::within('foo')->where('id', '=', 1)->andWhere('title', '!==', 'Random'); } + public function testSearchCannotBeBuiltWithValidAndWhereConditionOnEmptySearch(): void + { + $search = new Search(); + + static::expectException(InvalidSearchConfigurationException::class); + static::expectExceptionMessage('The MeiliSearchBundle\Search\Search::andWhere() cannot be used on an empty search'); + static::expectExceptionCode(0); + $search->in('foo')->andWhere('title', '!=', 'Random'); + + static::expectException(InvalidSearchConfigurationException::class); + static::expectExceptionMessage('The MeiliSearchBundle\Search\Search::andWhere() cannot be used on an empty search'); + static::expectExceptionCode(0); + Search::within('foo')->andWhere('title', '!=', 'Random'); + } + public function testSearchCanBeBuiltWithValidAndWhereCondition(): void { $search = new Search(); @@ -128,6 +243,21 @@ public function testSearchCanBeBuiltWithValidAndWhereCondition(): void static::assertSame('id = 1 AND title != Random', $search->getRaw()['filters']); } + public function testSearchCannotBeBuiltWithOrWhereConditionOnEmptyConditions(): void + { + $search = new Search(); + + static::expectException(InvalidSearchConfigurationException::class); + static::expectExceptionMessage('The MeiliSearchBundle\Search\Search::orWhere() cannot be used on an empty search'); + static::expectExceptionCode(0); + $search->in('foo')->orWhere('title', '!==', 'Random'); + + static::expectException(InvalidSearchConfigurationException::class); + static::expectExceptionMessage('The MeiliSearchBundle\Search\Search::orWhere() cannot be used on an empty search'); + static::expectExceptionCode(0); + Search::within('foo')->orWhere('title', '!==', 'Random'); + } + public function testSearchCannotBeBuiltWithInvalidOrWhereCondition(): void { $search = new Search(); @@ -170,6 +300,57 @@ public function testSearchCanBeBuiltWithValidNotCondition(): void static::assertSame('NOT id = 1', $search->getRaw()['filters']); } + public function testSearchCannotBeBuiltWithValidAndNotConditionButEmptyConditions(): void + { + $search = new Search(); + + static::expectException(InvalidSearchConfigurationException::class); + static::expectExceptionMessage('The MeiliSearchBundle\Search\Search::andNot() cannot be used on an empty search'); + static::expectExceptionCode(0); + $search->in('foo')->andNot('id', '=', 1); + + static::expectException(InvalidSearchConfigurationException::class); + static::expectExceptionMessage('The MeiliSearchBundle\Search\Search::andNot() cannot be used on an empty search'); + static::expectExceptionCode(0); + Search::within('foo')->andNot('id', '=', 1); + } + + public function testSearchCanBeBuiltWithValidAndNotCondition(): void + { + $search = new Search(); + $search->in('foo')->where('id', '>', '10')->andNot('id', '>', 15); + static::assertSame('id > 10 AND NOT id > 15', $search->getComputedFilters()); + + $search = Search::within('foo')->where('id', '>', '10')->andNot('id', '>', 15); + static::assertSame('id > 10 AND NOT id > 15', $search->getComputedFilters()); + } + + public function testSearchCanBeBuiltWithValidIsolatedAndNotCondition(): void + { + $search = new Search(); + $search->in('foo')->where('id', '>', '10')->andNot('id', '>', 15, true); + static::assertSame('id > 10 AND (NOT id > 15)', $search->getComputedFilters()); + + $search = Search::within('foo')->where('id', '>', '10')->andNot('id', '>', 15, true); + static::assertSame('id > 10 AND (NOT id > 15)', $search->getComputedFilters()); + } + + public function testSearchCanBeBuiltWithMatches(): void + { + $search = new Search(); + $search->in('foo')->match(true); + static::assertTrue($search->getRaw()['matches']); + static::assertTrue($search->shouldReturnMatches()); + + $search = Search::within('foo')->match(true); + static::assertTrue($search->getRaw()['matches']); + static::assertTrue($search->shouldReturnMatches()); + + $search = Search::within('foo')->match(); + static::assertFalse($search->getRaw()['matches']); + static::assertFalse($search->shouldReturnMatches()); + } + public function testSearchCanDefineDisplayedFields(): void { $search = new Search(); @@ -259,10 +440,12 @@ public function testSearchCanPaginate(): void static::assertNotEmpty($search->getRaw()['filters']); static::assertSame('id > 100', $search->getRaw()['filters']); static::assertSame(20, $search->getRaw()['limit']); + static::assertSame(20, $search->getLimit()); $search = Search::within('foo')->paginate('id', '>', 100, 20); static::assertNotEmpty($search->getRaw()['filters']); static::assertSame('id > 100', $search->getRaw()['filters']); static::assertSame(20, $search->getRaw()['limit']); + static::assertSame(20, $search->getLimit()); } } diff --git a/tests/Serializer/IndexMetadataDenormalizerTest.php b/tests/Serializer/IndexMetadataDenormalizerTest.php index 4131090..f830b3e 100644 --- a/tests/Serializer/IndexMetadataDenormalizerTest.php +++ b/tests/Serializer/IndexMetadataDenormalizerTest.php @@ -21,6 +21,7 @@ public function testDenormalizerSupport(): void static::assertFalse($denormalizer->supportsDenormalization('', IndexMetadata::class)); static::assertTrue($denormalizer->supportsDenormalization([], IndexMetadata::class)); + static::assertTrue($denormalizer->supportsDenormalization([], IndexMetadataInterface::class)); } public function testDenormalizerCanDenormalize(): void