diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 65bbbbf..288e6eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,12 @@ jobs: fail-fast: false matrix: php: ['8.2', '8.3', '8.4', '8.5'] - symfony: ['^7.0'] + symfony: ['^7.0', '^8.0'] + exclude: + - php: '8.2' + symfony: '^8.0' + - php: '8.3' + symfony: '^8.0' name: PHP ${{ matrix.php }} - Symfony ${{ matrix.symfony }} @@ -55,5 +60,5 @@ jobs: COMPOSER_PROCESS_TIMEOUT: 900 ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }} ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY }} - ALGOLIA_PREFIX: sf_phpunit_${{ matrix.php }}_ + ALGOLIA_PREFIX: sf_phpunit_${{ matrix.php }}_${{ matrix.symfony }}_ run: composer test:unit diff --git a/README.md b/README.md index e1136fe..f982b14 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

- CircleCI + CI Total Downloads Latest Version License @@ -31,11 +31,11 @@ ## ✨ Features * **Simple**: You can get started with only 5 lines of YAML - * **Robust**: It benefits from all the new features of our PHP Client v2, like the [`wait()`](/doc/api-reference/api-methods/wait-task/) method + * **Robust**: It benefits from all the features of our PHP Client v3, like the [`wait()`](/doc/api-reference/api-methods/wait-task/) method * **Flexible**: All methods take optional `$requestOptions` to let you handle your data as you wish * **Dev-friendly**: Auto-completion and type-hinting thanks to an exhaustive documentation -Algolia SearchBundle supports PHP > 7.1 +Algolia SearchBundle requires PHP >= 8.2 and supports Symfony 7 and 8. ## 💡 Getting Started @@ -103,9 +103,7 @@ to get matching results and then will create a doctrine collection. The data are pulled from the database (that's why you need to pass the Doctrine Manager). ```php -$em = $this->getDoctrine()->getManagerForClass(Post::class); - -$posts = $this->searchService->search($em, Post::class, 'query'); +$posts = $this->searchService->search($entityManager, Post::class, 'query'); ``` For full documentation, visit the **[Algolia Symfony Search Bundle](https://www.algolia.com/doc/framework-integration/symfony/getting-started/)**. @@ -120,4 +118,4 @@ If you want to contribute to this project without installing all its dependencie ## 📄 License -Algolia Symfony Search Bundle is an open-sourced software licensed under the [MIT license](LICENSE.md). +Algolia Symfony Search Bundle is an open-sourced software licensed under the [MIT license](LICENSE). diff --git a/composer.json b/composer.json index 7f03425..4281f46 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,6 @@ { "name": "algolia/search-bundle", + "description": "Algolia Search integration for Symfony", "type": "symfony-bundle", "license": "MIT", "authors": [ @@ -13,11 +14,10 @@ "php": ">= 8.2", "algolia/algoliasearch-client-php": "^3.0", "doctrine/event-manager": "^1.1 || ^2.0", - "doctrine/persistence": "^2.1 || ^3.0", - "symfony/filesystem": "^7.0", - "symfony/http-kernel": "^7.0", - "symfony/property-access": "^7.0", - "symfony/serializer": "^7.0" + "doctrine/persistence": "^2.1 || ^3.0 || ^4.0", + "symfony/filesystem": "^7.0 || ^8.0", + "symfony/property-access": "^7.0 || ^8.0", + "symfony/serializer": "^7.0 || ^8.0" }, "autoload": { "psr-4": { @@ -34,17 +34,15 @@ }, "require-dev": { "ext-json": "*", - "doctrine/doctrine-bundle": "^2.12", - "doctrine/orm": "^2.5", + "doctrine/doctrine-bundle": "^2.12 || ^3.0", + "doctrine/orm": "^2.5 || ^3.0", "friendsofphp/php-cs-fixer": "^v3.54.0", "jms/serializer-bundle": "^5.4.0", - "friendsofphp/proxy-manager-lts": "*", "phpunit/phpunit": "^8.5 || ^9.0", "roave/security-advisories": "dev-master", - "symfony/framework-bundle": "^7.0", - "symfony/phpunit-bridge": "^7.0", - "symfony/proxy-manager-bridge": "*", - "symfony/yaml": "^7.0" + "symfony/framework-bundle": "^7.0 || ^8.0", + "symfony/phpunit-bridge": "^7.0 || ^8.0", + "symfony/yaml": "^7.0 || ^8.0" }, "extra": { "branch-alias": { diff --git a/src/Command/SearchClearCommand.php b/src/Command/SearchClearCommand.php index d255f10..cd006ce 100644 --- a/src/Command/SearchClearCommand.php +++ b/src/Command/SearchClearCommand.php @@ -15,10 +15,7 @@ #[AsCommand(name: 'search:clear')] final class SearchClearCommand extends IndexCommand { - /** - * @return void - */ - protected function configure() + protected function configure(): void { $this ->setDescription('Clear index (remove all data but keep index and settings)') diff --git a/src/Command/SearchImportCommand.php b/src/Command/SearchImportCommand.php index 7930170..8139113 100644 --- a/src/Command/SearchImportCommand.php +++ b/src/Command/SearchImportCommand.php @@ -46,10 +46,7 @@ public function __construct( $this->searchClient = $searchClient; } - /** - * @return void - */ - protected function configure() + protected function configure(): void { $this ->setDescription('Import given entity into search engine') diff --git a/src/Command/SearchSettingsBackupCommand.php b/src/Command/SearchSettingsBackupCommand.php index a82c283..f48211e 100644 --- a/src/Command/SearchSettingsBackupCommand.php +++ b/src/Command/SearchSettingsBackupCommand.php @@ -12,10 +12,7 @@ #[AsCommand(name: 'search:settings:backup')] final class SearchSettingsBackupCommand extends SearchSettingsCommand { - /** - * @return void - */ - protected function configure() + protected function configure(): void { $this ->setDescription('Backup search engine settings into your project') diff --git a/src/Command/SearchSettingsPushCommand.php b/src/Command/SearchSettingsPushCommand.php index c1d4e7c..c80f257 100644 --- a/src/Command/SearchSettingsPushCommand.php +++ b/src/Command/SearchSettingsPushCommand.php @@ -12,10 +12,7 @@ #[AsCommand(name: 'search:settings:push')] final class SearchSettingsPushCommand extends SearchSettingsCommand { - /** - * @return void - */ - protected function configure() + protected function configure(): void { $this ->setDescription('Push settings from your project to the search engine') diff --git a/src/DependencyInjection/AlgoliaSearchExtension.php b/src/DependencyInjection/AlgoliaSearchExtension.php index 9aca9e9..4826295 100644 --- a/src/DependencyInjection/AlgoliaSearchExtension.php +++ b/src/DependencyInjection/AlgoliaSearchExtension.php @@ -8,9 +8,9 @@ use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Loader; use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\HttpKernel\Kernel; /** @@ -23,14 +23,12 @@ final class AlgoliaSearchExtension extends Extension { /** - * @return void - * * @throws \InvalidArgumentException|\Exception */ - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $container): void { - $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); - $loader->load('services.xml'); + $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); + $loader->load('services.yaml'); $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml deleted file mode 100644 index 013c24b..0000000 --- a/src/Resources/config/services.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - %env(ALGOLIA_APP_ID)% - %env(ALGOLIA_API_KEY)% - - - - - - - - - - - - - - - diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml new file mode 100644 index 0000000..48dc7f6 --- /dev/null +++ b/src/Resources/config/services.yaml @@ -0,0 +1,47 @@ +services: + Algolia\SearchBundle\Command\: + resource: '../../Command' + autowire: true + autoconfigure: true + + Algolia\SearchBundle\Command\SearchImportCommand: + autowire: true + autoconfigure: true + arguments: + $searchServiceForAtomicReindex: '@search.service_for_atomic_reindex' + + search.search_indexer_subscriber: + class: Algolia\SearchBundle\EventListener\SearchIndexerSubscriber + public: true + arguments: + - '@search.service' + - [] # doctrine subscribed events + tags: + - { name: doctrine.event_listener, event: postPersist, connection: default } + - { name: doctrine.event_listener, event: postUpdate, connection: default } + - { name: doctrine.event_listener, event: preRemove, connection: default } + - { name: doctrine_mongodb.odm.event_subscriber } + + search.client: + class: Algolia\AlgoliaSearch\SearchClient + public: true + lazy: true + factory: ['Algolia\AlgoliaSearch\SearchClient', 'create'] + arguments: + $appId: '%env(ALGOLIA_APP_ID)%' + $apiKey: '%env(ALGOLIA_API_KEY)%' + + Algolia\AlgoliaSearch\SearchClient: + alias: search.client + + Algolia\SearchBundle\SearchService: + alias: search.service + + Algolia\SearchBundle\Settings\SettingsManager: + alias: search.settings_manager + + custom_normalizer: + class: Symfony\Component\Serializer\Normalizer\CustomNormalizer + public: false + tags: + - { name: serializer.normalizer, priority: -800 } diff --git a/tests/Kernel.php b/tests/Kernel.php index aca3c70..e2a1105 100644 --- a/tests/Kernel.php +++ b/tests/Kernel.php @@ -32,7 +32,7 @@ public function registerContainerConfiguration(LoaderInterface $loader): void { $loader->load(__DIR__ . '/config/config.yml'); - $loader->load(__DIR__ . '/../src/Resources/config/services.xml'); + $loader->load(__DIR__ . '/../src/Resources/config/services.yaml'); $loader->load(__DIR__ . '/config/algolia_search.yml'); diff --git a/tests/TestApp/Entity/Post.php b/tests/TestApp/Entity/Post.php index c0fe5fd..dad9957 100644 --- a/tests/TestApp/Entity/Post.php +++ b/tests/TestApp/Entity/Post.php @@ -4,7 +4,7 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\Mapping as ORM; -use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Serializer\Attribute\Groups; #[ORM\Entity] #[ORM\Table('posts')] diff --git a/tests/TestCase/AggregatorTest.php b/tests/TestCase/AggregatorTest.php index 8092956..de63dd7 100644 --- a/tests/TestCase/AggregatorTest.php +++ b/tests/TestCase/AggregatorTest.php @@ -57,6 +57,7 @@ public function testAggregatorProxyClass(): void ]); $this->entityManager->persist($post); $this->entityManager->flush(); + $this->entityManager->clear(); $postMetadata = $this->entityManager->getClassMetadata(Post::class); $this->entityManager->getProxyFactory()->generateProxyClasses([$postMetadata], null); diff --git a/tests/TestCase/ClientProxyTest.php b/tests/TestCase/ClientProxyTest.php index 3f62fbd..62c3fc5 100644 --- a/tests/TestCase/ClientProxyTest.php +++ b/tests/TestCase/ClientProxyTest.php @@ -2,9 +2,9 @@ namespace Algolia\SearchBundle\TestCase; +use Algolia\AlgoliaSearch\SearchClient; use Algolia\SearchBundle\BaseTest; use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException; -use Symfony\Component\VarExporter\LazyObjectInterface; class ClientProxyTest extends BaseTest { @@ -39,17 +39,11 @@ public static function tearDownAfterClass(): void $_SERVER = self::$values['_server']; } - public function testClientIsProxied(): void + public function testClientIsResolvable(): void { $client = $this->get('search.client'); - if (PHP_VERSION_ID >= 80400) { - $reflector = new \ReflectionClass($client); - self::assertTrue($reflector->isUninitializedLazyObject($client)); - } else { - $interfaces = class_implements($client); - self::assertContains(LazyObjectInterface::class, $interfaces); - } + self::assertInstanceOf(SearchClient::class, $client); } public function testProxiedClientFailIfNoEnvVarsFound(): void diff --git a/tests/TestCase/CommandsTest.php b/tests/TestCase/CommandsTest.php index 44d27c2..f26ef27 100644 --- a/tests/TestCase/CommandsTest.php +++ b/tests/TestCase/CommandsTest.php @@ -120,7 +120,7 @@ public function testSearchImportAggregator(): void $searchPost = $this->searchService->rawSearch(ContentAggregator::class); self::assertCount($expectedResult, $searchPost['hits']); // clearup table - $this->connection->executeUpdate($this->platform->getTruncateTableSQL($this->indexName, true)); + $this->connection->executeStatement($this->platform->getTruncateTableSQL($this->indexName, true)); $this->cleanUp(); } @@ -169,7 +169,7 @@ public function testSearchImport($isAtomic): void self::assertCount($expectedResult, $searchPost['hits']); // clearup table - $this->connection->executeUpdate($this->platform->getTruncateTableSQL($this->indexName, true)); + $this->connection->executeStatement($this->platform->getTruncateTableSQL($this->indexName, true)); $this->cleanUp(); } diff --git a/tests/TestCase/EntityProxyTest.php b/tests/TestCase/EntityProxyTest.php index 7db52d9..5b26806 100644 --- a/tests/TestCase/EntityProxyTest.php +++ b/tests/TestCase/EntityProxyTest.php @@ -58,15 +58,6 @@ public function testEntityIsNotProxied(): void self::assertEquals('Algolia\\SearchBundle\\TestApp\\Entity\\Comment', ClassInfo::getClass($comment)); } - public function testEntityIsProxiedWithOPM(): void - { - $factory = new \ProxyManager\Factory\NullObjectFactory(); - $proxy = $factory->createProxy(Comment::class); - - self::assertStringStartsWith('ProxyManagerGeneratedProxy\\__PM__\\Algolia\\SearchBundle\\TestApp\\Entity\\Comment', get_class($proxy)); - self::assertEquals('Algolia\\SearchBundle\\TestApp\\Entity\\Comment', ClassInfo::getClass($proxy)); - } - public function testEntityIsProxiedWithDP(): void { /** @var \Doctrine\ORM\EntityManagerInterface $entityManager */ @@ -77,7 +68,8 @@ public function testEntityIsProxiedWithDP(): void $proxy = $entityManager->getProxyFactory()->getProxy($metadata->getName(), ['id' => 1]); - self::assertStringStartsWith('Proxies\\__CG__\Algolia\\SearchBundle\\TestApp\\Entity\\Comment', get_class($proxy)); + // ORM 3 uses lazy ghost objects instead of __CG__ proxies + // The proxy is the same class, but ClassInfo should still resolve correctly self::assertEquals('Algolia\\SearchBundle\\TestApp\\Entity\\Comment', ClassInfo::getClass($proxy)); } } diff --git a/tests/config/config.yml b/tests/config/config.yml index 28e90d1..3d2c288 100644 --- a/tests/config/config.yml +++ b/tests/config/config.yml @@ -9,7 +9,6 @@ doctrine: url: sqlite:///%kernel.project_dir%/tests/cache/blog.sqlite logging: false orm: - auto_generate_proxy_classes: true naming_strategy: doctrine.orm.naming_strategy.underscore auto_mapping: true mappings: