Skip to content

Conversation

@norkunas
Copy link
Collaborator

@norkunas norkunas commented Nov 24, 2025

Pull Request

Early christmas present 🎄

I still left some todo's but wanted to open a draft at last.

Usage as previously (for forward compatibility provider type is 'orm' by default, just that ORM must be an explicit dependency of the application now):

meilisearch:
  indices:
    - name: comments
      class: 'Meilisearch\Bundle\Tests\Entity\Comment'

Usage with custom data provider:

meilisearch:
  indices:
    - name: actors
      class: 'Meilisearch\Bundle\Tests\Entity\Actor'
      data_provider: Meilisearch\Bundle\Tests\Integration\Fixtures\ActorDataProvider
<?php

declare(strict_types=1);

namespace Meilisearch\Bundle\Tests\Integration\Fixtures;

use Meilisearch\Bundle\DataProvider\DataProviderInterface;
use Meilisearch\Bundle\Tests\Entity\Actor;

/**
 * @implements DataProviderInterface<Actor>
 */
final class ActorDataProvider implements DataProviderInterface
{
    public function provide(int $limit, int $offset): array
    {
        return \array_slice([
            new Actor(1, 'Jack Nicholson'),
            // ...
        ], $offset, $limit);
    }

    public function loadByIdentifiers(array $identifiers): array
    {
        $actors = [];

        foreach ($this->provide(PHP_INT_MAX, 0) as $actor) {
            if ($actor->getId() === $identifiers['id']) {
                $actors[] = $actor;
            }
        }

        return $actors;
    }

    public function getIdentifierValues(object $object): array
    {
        \assert($object instanceof Actor);

        return ['id' => $object->getId()];
    }

    public function cleanup(): void
    {
        // noop
    }
}

If you have a meilisearch index with different primary key than objectID, you can now configure it:

meilisearch:
  indices:
    - name: actors
      class: 'Meilisearch\Bundle\Tests\Entity\Actor'
      data_provider: Meilisearch\Bundle\Tests\Integration\Fixtures\ActorDataProvider
      primary_key: id

Deprecations & Upgrade path ⚠️

Using Meilisearch\Bundle\SearchService and Meilisearch\Bundle\Service\MeilisearchService is now deprecated.
Switch to Meilisearch\Bundle\SearchManagerInterface and Meilisearch\Bundle\Service\MeilisearchManager

Previously methods were accepting doctrine's ObjectManager:

SearchService::index(ObjectManager $objectManager, $searchable);
SearchService::remove(ObjectManager $objectManager, $searchable);
SearchService::search(ObjectManager $objectManager, $className, $query, $searchParams);

Now it does not ask for ObjectManager anymore:

SearchManager::index($searchable);
SearchManager::remove($searchable);
SearchManager::search($className, $query, $searchParams);

What's next?

Now that we bump PHP to 8.1, we can later introduce PHP attributes for meilisearch, so instead of configuring everything through yaml, configuration can be linked in the classes (related issue #387).

#[Meilisearch\Document(
    index: 'actors',
    primaryKey: 'id',
    dataProvider: ActorDataProvider::class,
    // ...
)]
class Actor
{
 // ..
}

We should introduce SearchResults that allows to access/iterate found entities, while also exposing facetResults and other fields (related issue #353)

Related issue

Fixes #66
Fixes #121
Fixes #134
Fixes #302
Replaces #345

What does this PR do?

  • Introduces data providers;
  • Batches loading of entities to index in a single call instead of loading one by one;
  • Bumps PHP to 8.1 and Symfony 6.4;
  • Moves doctrine-bundle to dev dependencies
  • Adds possibility to configure custom primary_key to be used;

PR checklist

Please check if your PR fulfills the following requirements:

  • Does this PR fix an existing issue, or have you listed the changes applied in the PR description (and why they are needed)?
  • Have you read the contributing guidelines?
  • Have you made sure that the title is accurate and descriptive of the changes?

cc @remigarcia
cc @tacman in case you are interested
cc @ToshY

Summary by CodeRabbit

  • New Features

    • Introduces a Search Manager with pluggable data providers, DataProvider registry, DataProvider interface, SearchableObject API, and actor test fixture/provider.
  • Bug Fixes

    • Improved batching, per-index indexing/removal, import command batch handling, safer delete flows and tests now wait for async tasks.
  • Chores

    • Raised minimum PHP to 8.1; tightened Symfony/component constraints and updated dev tool versions and CI matrices.
  • Style / Docs

    • Legacy APIs marked deprecated; per-index config extended (type, data_provider, primary_key).

✏️ Tip: You can customize this high-level summary in your review settings.

@norkunas norkunas added enhancement New feature or request breaking-change The related changes are breaking for the users labels Nov 24, 2025
@coderabbitai
Copy link

coderabbitai bot commented Nov 24, 2025

Warning

Rate limit exceeded

@norkunas has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 4 minutes and 30 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 9262233 and c8c7c69.

📒 Files selected for processing (60)
  • .github/workflows/pre-release-tests.yml (2 hunks)
  • .github/workflows/tests.yml (2 hunks)
  • .php-cs-fixer.dist.php (1 hunks)
  • composer.json (1 hunks)
  • config/services.php (3 hunks)
  • phpstan-baseline.php (1 hunks)
  • phpstan.dist.neon (1 hunks)
  • src/Command/IndexCommand.php (2 hunks)
  • src/Command/MeilisearchClearCommand.php (1 hunks)
  • src/Command/MeilisearchCreateCommand.php (3 hunks)
  • src/Command/MeilisearchDeleteCommand.php (1 hunks)
  • src/Command/MeilisearchImportCommand.php (7 hunks)
  • src/Command/MeilisearchUpdateSettingsCommand.php (3 hunks)
  • src/DataProvider/DataProviderInterface.php (1 hunks)
  • src/DataProvider/DataProviderRegistry.php (1 hunks)
  • src/DataProvider/DataProviderRegistryInterface.php (1 hunks)
  • src/DataProvider/OrmEntityProvider.php (1 hunks)
  • src/DependencyInjection/Compiler/DataProviderPass.php (1 hunks)
  • src/DependencyInjection/Configuration.php (2 hunks)
  • src/DependencyInjection/MeilisearchExtension.php (4 hunks)
  • src/Engine.php (5 hunks)
  • src/Event/SettingsUpdatedEvent.php (1 hunks)
  • src/EventListener/ConsoleOutputSubscriber.php (1 hunks)
  • src/EventListener/DoctrineEventSubscriber.php (1 hunks)
  • src/Exception/DataProviderNotFoundException.php (1 hunks)
  • src/Exception/InvalidIndiceException.php (1 hunks)
  • src/Exception/LogicException.php (1 hunks)
  • src/Exception/NotSearchableException.php (1 hunks)
  • src/MeilisearchBundle.php (2 hunks)
  • src/Model/Aggregator.php (3 hunks)
  • src/SearchManagerInterface.php (1 hunks)
  • src/SearchService.php (1 hunks)
  • src/Searchable.php (1 hunks)
  • src/SearchableEntity.php (2 hunks)
  • src/SearchableObject.php (1 hunks)
  • src/Services/MeilisearchManager.php (1 hunks)
  • src/Services/MeilisearchService.php (9 hunks)
  • src/Services/SettingsUpdater.php (2 hunks)
  • src/Services/UnixTimestampNormalizer.php (1 hunks)
  • tests/BaseKernelTestCase.php (5 hunks)
  • tests/Entity/Actor.php (1 hunks)
  • tests/Entity/Link.php (2 hunks)
  • tests/Entity/SelfNormalizable.php (2 hunks)
  • tests/Entity/Tag.php (2 hunks)
  • tests/Integration/AggregatorTest.php (2 hunks)
  • tests/Integration/Command/MeilisearchClearCommandTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchCreateCommandTest.php (2 hunks)
  • tests/Integration/Command/MeilisearchDeleteCommandTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchImportCommandTest.php (1 hunks)
  • tests/Integration/EngineTest.php (3 hunks)
  • tests/Integration/EventListener/DoctrineEventSubscriberTest.php (8 hunks)
  • tests/Integration/Fixtures/ActorDataProvider.php (1 hunks)
  • tests/Integration/SearchTest.php (4 hunks)
  • tests/Kernel.php (1 hunks)
  • tests/Unit/ConfigurationTest.php (8 hunks)
  • tests/Unit/SerializationTest.php (2 hunks)
  • tests/baseline-ignore (1 hunks)
  • tests/config/doctrine_php7.yaml (0 hunks)
  • tests/config/framework.yaml (1 hunks)
  • tests/config/meilisearch.yaml (1 hunks)

Walkthrough

Introduce MeilisearchManager and a DataProvider subsystem; add SearchableObject and data provider registry with DI/compiler wiring; deprecate old SearchService while delegating to the manager; rewire commands, services, tests and configuration; add PHPStan baseline and adjust CI/composer constraints.

Changes

Cohort / File(s) Change Summary
CI & Composer
\.github/workflows/tests.yml, \.github/workflows/pre-release-tests.yml, composer.json
Narrow CI matrix to PHP 8.1–8.4 and Symfony 6.4/7.x, remove PHP 7.4/Symfony 5.4 entries, add dependency variation in CI includes, update composer PHP requirement to ^8.1, tighten Symfony component constraints, move doctrine-bundle to require-dev, bump dev tool and PHPStan versions.
Service wiring & DI
config/services.php, src/DependencyInjection/...
Add meilisearch.manager service (MeilisearchManager) and aliases; register meilisearch.data_provider.registry; update services to depend on manager; register per-index data providers via extension; add config keys type, primary_key, data_provider.
Manager & Interface
src/SearchManagerInterface.php, src/Services/MeilisearchManager.php
New SearchManagerInterface and MeilisearchManager implementing it: config access, data-provider resolution, isSearchable/searchableAs, index/remove/clear/deleteByIndexName/delete/search/rawSearch/count, batching and aggregator handling.
Backward compatibility
src/SearchService.php, src/Services/MeilisearchService.php
Mark legacy SearchService as deprecated; MeilisearchService accepts optional manager, delegates calls to it with deprecation notices while preserving legacy behavior when manager absent.
Data providers & registry
src/DataProvider/*.php
Add DataProviderInterface, OrmEntityProvider implementation, DataProviderRegistryInterface and DataProviderRegistry, plus DataProviderNotFoundException.
DI Compiler & Bundle
src/DependencyInjection/Compiler/DataProviderPass.php, src/MeilisearchBundle.php
Add DataProviderPass compiler pass to collect meilisearch.data_provider tags and build a ServiceLocator + providers map; register the compiler pass in bundle build().
Searchable model & Engine
src/SearchableObject.php, src/Engine.php, src/Model/Aggregator.php
Add SearchableObject (indexUid/primaryKey/identifier + normalization); refactor Engine to accept SearchableObject
Commands
src/Command/*Command.php, config/services.php
Rewire commands to depend on SearchManagerInterface (constructor promotion); MeilisearchImportCommand uses DataProviderRegistry and manager for batched indexing, removing direct Doctrine coupling; other commands updated to use manager.
Events & listeners
src/Event/*, src/EventListener/*
Apply constructor property promotion; switch DoctrineEventSubscriber to accept SearchManagerInterface and call manager->index/remove with object-only args; ConsoleOutputSubscriber and SettingsUpdatedEvent use promoted properties.
Services & normalizers
src/Services/*
Replace SearchService deps with SearchManagerInterface where applicable (SettingsUpdater, commands); MeilisearchService delegates to manager; UnixTimestampNormalizer signatures typed to mixed.
Exceptions, typing & API tweaks
src/Exception/*, src/SearchService.php, src/Searchable.php
Add DataProviderNotFoundException, NotSearchableException, LogicException; tighten InvalidIndiceException code type; mark SearchService and Searchable / constants as deprecated; add SearchManagerInterface constant and typed contracts.
PHPStan & baseline
phpstan.dist.neon, phpstan-baseline.php
Move selected ignores to phpstan-baseline.php and include it; remove duplicate/needing ignores from phpstan.dist.neon.
Tests, fixtures & config
tests/**, tests/config/meilisearch.yaml, tests/config/framework.yaml, tests/config/doctrine_php7.yaml
Add Actor entity and ActorDataProvider fixture; update tests to use SearchableObject & MeilisearchManager, adjust assertions/order/wait/cleanup logic; add test index config for actor; update test kernel/fixtures and remove legacy doctrine_php7.yaml.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as CLI / Commands
    participant Manager as MeilisearchManager
    participant Registry as DataProviderRegistry
    participant Provider as DataProvider
    participant Engine as Engine
    participant API as MeiliSearch API

    rect rgba(0,128,128,0.06)
    Note over CLI,API: Manager-driven indexing flow (high level)
    CLI->>Manager: index(entity or [entities])
    activate Manager
    Manager->>Manager: resolve indices, primary_key, aggregator logic
    Manager->>Registry: getDataProvider(index, class)
    activate Registry
    Registry-->>Manager: provider service
    deactivate Registry
    loop per index (batches)
        Manager->>Provider: provide(limit, offset)
        activate Provider
        Provider-->>Manager: entities[]
        deactivate Provider
        Manager->>Manager: wrap entities -> SearchableObject(s)
        Manager->>Engine: index(searchableObjects)
        activate Engine
        Engine->>API: addDocuments(documents, primaryKey)
        API-->>Engine: response (task)
        deactivate Engine
    end
    Manager-->>CLI: aggregated result
    deactivate Manager
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas needing extra attention:

  • src/Services/MeilisearchManager.php — batching, aggregator handling, identifier resolution, ordering and error paths.
  • src/DependencyInjection/MeilisearchExtension.php & config/services.php — dynamic provider registration, tag attributes, service IDs and aliases.
  • src/Command/MeilisearchImportCommand.php — data-provider integration, batch pagination, cleanup semantics and timeout handling.
  • src/Engine.php — primaryKey handling, document shapes, addDocuments/deleteDocument call contracts and normalizeId changes.
  • src/Services/MeilisearchService.php — deprecation notifications and delegation correctness for backward compatibility.
  • Tests — updated fixtures and assertions that rely on new SearchableObject and registry need verification.

Possibly related PRs

Suggested reviewers

  • curquiza

Poem

🐇 I hopped through code with a twitch of my nose,

Managers, providers, and bundles in neat rows.
I batched and normalized each id and key,
Deprecated footprints, set the new path free.
CI hums green — a carrot for me, yippee!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.45% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add Data providers extension point' is specific and directly related to the main change: introducing a data providers system to allow custom data sources for indexing.
Linked Issues check ✅ Passed The PR addresses all primary objectives from linked issues: #66 (configurable primary keys replacing objectID), #121 (DataProvider interface and registry for external sources), #134 (batch loading to reduce DB queries), and #302 (indexing plain objects without EntityManager).
Out of Scope Changes check ✅ Passed All changes are within scope: data providers system, SearchManager deprecation, PHP 8.1+ and Symfony 6.4+ requirements, configuration updates, and test coverage align with the stated PR objectives.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@tacman
Copy link

tacman commented Nov 24, 2025

Thanks! in my survos/meili-bundle, I came up with a solution that is working well for me at scale, by batching in .jsonl file. This works great when I'm bulk importing.

I use the Symfony Workflow component when I'm bulk updating (e.g. updating 'marking' in some workflow process, where I may get thousands of tiny 1-field updates. The workflow component has the ability to batch event messages (from bin/console mess:consume), which I use to gather some size (10M, I think) before dispatching a single message to the meili server.

https://github.com/survos/meili-bundle/blob/main/src/EventListener/MeiliSpoolDoctrineListener.php
https://github.com/survos/meili-bundle/blob/b4dff7fc7a03196e2f799add35ca060d5b56b236/src/Spool/JsonlSpooler.php#L4
https://github.com/survos/meili-bundle/blob/main/src/EventListener/DoctrineEventListener.php

I'm giving a talk later this week at the Symfony Conference on meilisearch and how to configure the index using attributes. I'd love your feedback, are you available on Slack or the meili discord channel?

@norkunas
Copy link
Collaborator Author

I am available on both :)

@norkunas norkunas force-pushed the dataprovider branch 6 times, most recently from 8569538 to a8ad4dd Compare November 25, 2025 05:24
@codecov
Copy link

codecov bot commented Nov 25, 2025

Codecov Report

❌ Patch coverage is 83.53511% with 68 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.41%. Comparing base (b05b9fe) to head (c8c7c69).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/Services/MeilisearchService.php 0.00% 38 Missing ⚠️
src/Services/MeilisearchManager.php 92.74% 14 Missing ⚠️
src/DependencyInjection/MeilisearchExtension.php 81.81% 6 Missing ⚠️
src/Exception/DataProviderNotFoundException.php 0.00% 2 Missing ⚠️
src/Exception/NotSearchableException.php 0.00% 2 Missing ⚠️
src/SearchableEntity.php 0.00% 2 Missing ⚠️
src/DataProvider/DataProviderRegistry.php 88.88% 1 Missing ⚠️
src/DataProvider/OrmEntityProvider.php 95.23% 1 Missing ⚠️
.../DependencyInjection/Compiler/DataProviderPass.php 93.33% 1 Missing ⚠️
src/Exception/InvalidIndiceException.php 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             main     #410       +/-   ##
===========================================
- Coverage   88.38%   71.41%   -16.97%     
===========================================
  Files          20       27        +7     
  Lines         878     1186      +308     
===========================================
+ Hits          776      847       +71     
- Misses        102      339      +237     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

@Strift Strift left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The introduction of the Data Provider architecture is welcome, but deleting SearchService constitutes a massive breaking change that prevents us from releasing this in a minor version.

I'm wondering if we can support the new architecture and keep backward compatibility by using the bridge pattern.

What do you think of the following?

  1. Restore src/SearchService.php (Interface) and src/Services/MeilisearchService.php.
  2. Inject the new MeilisearchManager into the old MeilisearchService.
  3. Forward calls from the old service to the new manager, ignoring the now-obsolete arguments.

Example Implementation for src/Services/MeilisearchService.php:

// ... imports
use Meilisearch\Bundle\Services\MeilisearchManager;

/**
 * @deprecated Use MeilisearchManager instead.
 */
class MeilisearchService implements SearchService // Keep the old Interface too!
{
    public function __construct(private MeilisearchManager $manager) 
    {
    }

    public function search(ObjectManager $objectManager, string $className, string $query = '', array $searchParams = []): array 
    {
        trigger_deprecation('meilisearch/meilisearch-symfony', '0.x', 'Passing ObjectManager to search() is deprecated. Use MeilisearchManager::search() instead.');

        // We ignore $objectManager because the new Manager handles data provision internally
        return $this->manager->search($className, $query, $searchParams);
    }
    
    public function index(ObjectManager $objectManager, $searchable): array
    {
         // Forward to manager...
         return $this->manager->index($searchable);
    }

    // ... implement other methods similarly
}

This allows existing users to upgrade immediately. They will get the benefits of the new Data Provider system (internalized in the Manager) while their existing code (passing $objectManager) continues to work. We also add a deprecation warning and update the docs accordingly.

let me know!

@norkunas
Copy link
Collaborator Author

but deleting SearchService constitutes a massive breaking change that prevents us from releasing this in a minor version.

well we are on v0, so BC breaks are expected, right? ofc i can do the bridge as you want, but not today

@norkunas
Copy link
Collaborator Author

@Strift updated ;)

@norkunas norkunas force-pushed the dataprovider branch 2 times, most recently from 2949520 to 4cb2b38 Compare November 26, 2025 04:57
@norkunas norkunas marked this pull request as ready for review November 26, 2025 04:59
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

🧹 Nitpick comments (18)
src/SearchService.php (1)

9-11: Deprecation notice is appropriate for the migration path.

The deprecation annotation correctly guides users toward SearchManagerInterface. Consider also adding a @see tag pointing to the new interface for easier discovery.

 /**
  * @deprecated Since 0.16, use `Meilisearch\Bundle\SearchManagerInterface` instead.
+ *
+ * @see SearchManagerInterface
  */
tests/Entity/Actor.php (1)

7-27: Test entity is well-structured.

The Actor class appropriately serves as a fixture for testing the new DataProviderInterface flow.

Since PHP 8.1 is now the minimum version, you could simplify using constructor property promotion for consistency with other refactored classes in this PR:

 final class Actor
 {
-    private int $id;
-    private string $name;
-
-    public function __construct(int $id, string $name)
-    {
-        $this->id = $id;
-        $this->name = $name;
-    }
+    public function __construct(
+        private readonly int $id,
+        private readonly string $name,
+    ) {
+    }

     public function getId(): int
src/Services/MeilisearchService.php (3)

50-59: Constructor/manager wiring is sound, but consider typing the dependency to the interface

The optional manager injection and assignment to ?SearchManagerInterface is consistent and safe. For flexibility (easier testing and potential alternative managers), you might consider changing the ctor signature to depend directly on ?SearchManagerInterface instead of ?MeilisearchManager, keeping the same service wiring.


254-272: Deprecating shouldBeIndexed while still using it internally

Marking shouldBeIndexed() as deprecated “without replacement” is fine given the new manager‑based architecture, but it’s still used internally in index(). If you want to discourage external use without surprising readers, you could clarify the docblock to “for internal use only; external callers should migrate to MeilisearchManager”.


27-27: Address phpmd pipeline failures for this deprecated class

phpmd is now flagging this class for:

  • too many public methods / high complexity / high coupling, and
  • long variable names and static access.

Given MeilisearchService is now deprecated and wrapped by MeilisearchManager, a large refactor here is probably not worth it. Consider instead:

  • adding @SuppressWarnings annotations for the relevant PHPMD rules on this class, or
  • excluding this deprecated class from phpmd in your ruleset,

so the pipeline passes without forcing structural changes to legacy code.

Also applies to: 48-48, 57-57, 384-384, 432-432, 436-436

tests/Unit/ConfigurationTest.php (1)

359-402: New test case validates custom primary_key overrides

The 'custom objectID' dataset (with primary_key set to postId / tagId) is a good addition to ensure the configuration layer preserves per-index primary key overrides through normalization. If you ever rename the test, you might consider calling it “custom primary key” to better match the new terminology, but that’s purely cosmetic.

phpstan-baseline.php (1)

1-17: Consider tightening the phpstan baseline around ClassUtils::getClass()

The new ignore entries unblock static analysis but may be masking either a missing dev dependency/extension config or an obsolete call site. As a follow‑up, it would be good to either:

  • add proper doctrine/phpstan integration so ClassUtils is known, or
  • refactor away from ClassUtils::getClass() if it’s no longer needed.

Not a blocker for this PR, but worth tracking.

src/DependencyInjection/Configuration.php (1)

7-7: New index config fields fit the provider model; consider adding light validation

The added type, primary_key, and data_provider fields and the switch to SearchableObject::NORMALIZATION_GROUP look consistent with the new SearchableObject/DataProvider architecture.

Two potential follow‑ups (non‑blocking):

  • For type: 'custom', you might want a small validate() hook to assert that data_provider is not null, so obvious misconfigurations fail early in config rather than at runtime.
  • Given the move away from Algolia semantics, it may be worth revisiting the default primary_key of 'objectID' in a future breaking version, to better reflect the id‑centric normalization now used in entities.

Based on learnings, this could be deferred to a separate, focused PR on configuration/error‑handling.

Also applies to: 53-59, 59-67

src/Model/Aggregator.php (1)

71-75: The TODO identifies a real consistency issue with configurable primary keys.

The normalize() method hardcodes 'objectID' as the key, but the PR introduces configurable primary_key per index. If an index is configured with a different primary key (e.g., 'id'), the aggregated document will still contain 'objectID', causing a mismatch.

Consider either:

  1. Passing the configured primary key to the Aggregator
  2. Having the caller remap the key after normalization

Would you like me to propose a solution, or should this be tracked as a follow-up issue?

src/DependencyInjection/MeilisearchExtension.php (1)

38-71: Consider extracting data provider registration to reduce complexity.

The pipeline reports cyclomatic complexity of 11 (threshold: 10). The nested conditionals for ORM/Aggregator handling are correct but could be extracted to improve readability.

+    private function registerDataProviders(ContainerBuilder $container, array $config): array
+    {
+        $dataProviders = [];
+        foreach ($config['indices'] as $indice) {
+            $class = $indice['class'];
+
+            if (null !== $indice['data_provider']) {
+                $dataProviders[$indice['name']] ??= [];
+                $dataProviders[$indice['name']][$class] = new Reference($indice['data_provider']);
+                continue;
+            }
+
+            if ('orm' !== $indice['type']) {
+                continue;
+            }
+
+            $dataProviders[$indice['name']] ??= [];
+
+            if (is_subclass_of($class, Aggregator::class)) {
+                foreach ($class::getEntities() as $aggregatedClass) {
+                    $definitionId = \sprintf('meilisearch.data_provider.%s_%s', $indice['name'], hash('xxh32', $aggregatedClass));
+                    $container->setDefinition($definitionId, new Definition(OrmEntityProvider::class, [new Reference('doctrine'), $aggregatedClass]));
+                    $dataProviders[$indice['name']][$aggregatedClass] = new Reference($definitionId);
+                }
+            } else {
+                $definitionId = \sprintf('meilisearch.data_provider.%s_%s', $indice['name'], hash('xxh32', $class));
+                $container->setDefinition($definitionId, new Definition(OrmEntityProvider::class, [new Reference('doctrine'), $class]));
+                $dataProviders[$indice['name']][$class] = new Reference($definitionId);
+            }
+        }
+        return $dataProviders;
+    }

Then in load():

-        $dataProviders = [];
-        foreach ($config['indices'] as $index => $indice) {
-            // ... all the provider logic
-        }
+        $dataProviders = $this->registerDataProviders($container, $config);
+        foreach ($config['indices'] as $index => $indice) {
+            $config['indices'][$index]['prefixed_name'] = $config['prefix'].$indice['name'];
+            $config['indices'][$index]['settings'] = $this->findReferences($config['indices'][$index]['settings']);
+        }
src/DataProvider/DataProviderInterface.php (1)

20-25: Clarify the expected structure of $identifiers parameter.

The @param array<mixed> $identifiers is ambiguous. Looking at the implementations:

  • OrmEntityProvider treats it as a flat array of ID values: findBy(['id' => $identifiers])
  • ActorDataProvider treats it as an associative array: $identifiers['id']

This ambiguity leads to inconsistent implementations. Consider documenting the expected structure explicitly.

     /**
-     * @param array<mixed> $identifiers
+     * @param array<int|string> $identifiers Flat array of identifier values to load
      *
      * @return iterable<T>
      */
     public function loadByIdentifiers(array $identifiers): iterable;
src/DataProvider/OrmEntityProvider.php (1)

22-23: Consider null-checking getManagerForClass result.

getManagerForClass() can return null if no manager is found for the class. While the @param class-string constraint helps, invalid configurations could still trigger a null pointer on $manager->getRepository().

 $manager = $this->managerRegistry->getManagerForClass($this->className);
+if (null === $manager) {
+    throw new \InvalidArgumentException(\sprintf('No manager found for class "%s".', $this->className));
+}
 $repository = $manager->getRepository($this->className);
tests/BaseKernelTestCase.php (1)

47-52: Potential performance concern with waitForAllTasks().

This iterates over all tasks from the Meilisearch server and waits for each one. In a busy test environment or if there are many historical tasks, this could be slow. Consider limiting to only pending/enqueued tasks or tasks created during the current test.

 protected function waitForAllTasks(): void
 {
-    foreach ($this->client->getTasks() as $task) {
-        $this->client->waitForTask($task['uid']);
+    foreach ($this->client->getTasks(['statuses' => ['enqueued', 'processing']]) as $task) {
+        $this->client->waitForTask($task['uid']);
     }
 }
src/SearchableObject.php (1)

80-95: Missing @return PHPDoc type for getSearchableArray().

The method declares @throws ExceptionInterface but lacks the return type annotation. Consider adding @return array<string, mixed> for better static analysis support.

 /**
+ * @return array<string, mixed>
+ *
  * @throws ExceptionInterface
  */
 public function getSearchableArray(): array
src/Engine.php (1)

84-92: Consider using batch deletion for better performance.

The remove() method calls deleteDocument() individually for each object. Meilisearch supports batch deletion via deleteDocuments() which would be more efficient.

         $result = [];
         foreach ($data as $indexUid => $objects) {
-            $result[$indexUid] = [];
-            foreach ($objects as $object) {
-                $result[$indexUid][] = $this->client
-                    ->index($indexUid)
-                    ->deleteDocument($object);
-            }
+            $result[$indexUid] = $this->client
+                ->index($indexUid)
+                ->deleteDocuments($objects);
         }
src/Command/MeilisearchImportCommand.php (1)

71-155: Pipeline flags high complexity in execute() method.

The pipeline reports CyclomaticComplexity of 13 (threshold: 10) and NPathComplexity of 592 (threshold: 200). Consider extracting the per-index processing into a separate method in a follow-up PR.

Based on learnings, the maintainer prefers keeping PRs focused, so this refactor can be deferred.

src/Services/MeilisearchManager.php (2)

131-135: Pipeline flag: Else expression.

The pipeline prefers early returns over else clauses. Consider inverting the condition.

-                if (!$entity instanceof $configClass && \in_array($configClass, $this->aggregators, true)) {
-                    $objectToIndex = new $configClass($entity, $provider->getIdentifierValues($entity));
-                } else {
-                    $objectToIndex = $entity;
-                }
+                $objectToIndex = $entity;
+                if (!$entity instanceof $configClass && \in_array($configClass, $this->aggregators, true)) {
+                    $objectToIndex = new $configClass($entity, $provider->getIdentifierValues($entity));
+                }

21-21: Pipeline flags: High class complexity and coupling.

The pipeline reports ExcessiveClassComplexity (69, threshold 50) and CouplingBetweenObjects (14, threshold 13). This is a central orchestration service, so some complexity is expected. Consider extracting helper classes in future refactoring if the class grows further.

Based on learnings, the maintainer prefers keeping PRs focused. Consider deferring extraction of index resolution, batch processing, and search result mapping into separate collaborators.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1311679 and 4cb2b38.

📒 Files selected for processing (52)
  • .github/workflows/tests.yml (1 hunks)
  • composer.json (1 hunks)
  • config/services.php (4 hunks)
  • phpstan-baseline.php (1 hunks)
  • phpstan.dist.neon (1 hunks)
  • src/Command/IndexCommand.php (2 hunks)
  • src/Command/MeilisearchClearCommand.php (1 hunks)
  • src/Command/MeilisearchCreateCommand.php (3 hunks)
  • src/Command/MeilisearchDeleteCommand.php (1 hunks)
  • src/Command/MeilisearchImportCommand.php (7 hunks)
  • src/Command/MeilisearchUpdateSettingsCommand.php (3 hunks)
  • src/DataProvider/DataProviderInterface.php (1 hunks)
  • src/DataProvider/OrmEntityProvider.php (1 hunks)
  • src/DependencyInjection/Configuration.php (2 hunks)
  • src/DependencyInjection/MeilisearchExtension.php (3 hunks)
  • src/Engine.php (5 hunks)
  • src/Event/SettingsUpdatedEvent.php (1 hunks)
  • src/EventListener/ConsoleOutputSubscriber.php (1 hunks)
  • src/EventListener/DoctrineEventSubscriber.php (1 hunks)
  • src/Exception/DataProviderNotFoundException.php (1 hunks)
  • src/Exception/NotSearchableException.php (1 hunks)
  • src/Model/Aggregator.php (2 hunks)
  • src/SearchManagerInterface.php (1 hunks)
  • src/SearchService.php (1 hunks)
  • src/Searchable.php (1 hunks)
  • src/SearchableEntity.php (2 hunks)
  • src/SearchableObject.php (1 hunks)
  • src/Services/MeilisearchManager.php (1 hunks)
  • src/Services/MeilisearchService.php (8 hunks)
  • src/Services/SettingsUpdater.php (1 hunks)
  • src/Services/UnixTimestampNormalizer.php (1 hunks)
  • tests/BaseKernelTestCase.php (5 hunks)
  • tests/Entity/Actor.php (1 hunks)
  • tests/Entity/Link.php (2 hunks)
  • tests/Entity/SelfNormalizable.php (2 hunks)
  • tests/Entity/Tag.php (2 hunks)
  • tests/Integration/AggregatorTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchClearCommandTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchCreateCommandTest.php (2 hunks)
  • tests/Integration/Command/MeilisearchDeleteCommandTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchImportCommandTest.php (1 hunks)
  • tests/Integration/EngineTest.php (3 hunks)
  • tests/Integration/EventListener/DoctrineEventSubscriberTest.php (8 hunks)
  • tests/Integration/Fixtures/ActorDataProvider.php (1 hunks)
  • tests/Integration/SearchTest.php (4 hunks)
  • tests/Kernel.php (1 hunks)
  • tests/Unit/ConfigurationTest.php (8 hunks)
  • tests/Unit/SerializationTest.php (2 hunks)
  • tests/baseline-ignore (1 hunks)
  • tests/config/doctrine_php7.yaml (0 hunks)
  • tests/config/framework.yaml (1 hunks)
  • tests/config/meilisearch.yaml (1 hunks)
💤 Files with no reviewable changes (1)
  • tests/config/doctrine_php7.yaml
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: norkunas
Repo: meilisearch/meilisearch-symfony PR: 391
File: src/Command/MeilisearchImportCommand.php:117-120
Timestamp: 2025-08-05T04:46:01.119Z
Learning: In meilisearch-symfony project, the maintainer norkunas prefers to keep PRs focused and defer error handling improvements for settings updates to separate PRs, particularly when it involves decisions about partial import behavior when some indexes fail.
🧬 Code graph analysis (30)
tests/Entity/Tag.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-96)
src/Command/MeilisearchDeleteCommand.php (4)
src/SearchManagerInterface.php (1)
  • deleteByIndexName (58-58)
src/SearchService.php (1)
  • deleteByIndexName (50-50)
src/Services/MeilisearchManager.php (1)
  • deleteByIndexName (215-218)
src/Services/MeilisearchService.php (1)
  • deleteByIndexName (162-169)
tests/Integration/EventListener/DoctrineEventSubscriberTest.php (4)
src/Engine.php (1)
  • search (122-125)
src/SearchManagerInterface.php (1)
  • search (79-79)
src/Services/MeilisearchManager.php (1)
  • search (238-281)
src/Services/MeilisearchService.php (1)
  • search (182-227)
src/Model/Aggregator.php (1)
src/SearchableEntity.php (1)
  • __construct (42-56)
src/Exception/DataProviderNotFoundException.php (1)
src/Exception/NotSearchableException.php (1)
  • __construct (9-12)
src/SearchableEntity.php (4)
tests/Entity/Link.php (1)
  • normalize (74-85)
tests/Entity/SelfNormalizable.php (1)
  • normalize (64-75)
tests/Entity/Tag.php (1)
  • normalize (88-100)
src/SearchableObject.php (1)
  • SearchableObject (13-96)
tests/Integration/Fixtures/ActorDataProvider.php (2)
tests/Entity/Actor.php (2)
  • Actor (7-27)
  • getId (18-21)
src/DataProvider/DataProviderInterface.php (4)
  • provide (18-18)
  • loadByIdentifiers (25-25)
  • getIdentifierValues (32-32)
  • cleanup (34-34)
tests/Entity/SelfNormalizable.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-96)
tests/Entity/Link.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-96)
tests/Integration/SearchTest.php (4)
tests/BaseKernelTestCase.php (1)
  • waitForAllTasks (47-52)
src/Engine.php (1)
  • search (122-125)
src/SearchManagerInterface.php (2)
  • search (79-79)
  • rawSearch (91-91)
src/Services/MeilisearchManager.php (2)
  • search (238-281)
  • rawSearch (291-299)
src/SearchManagerInterface.php (4)
src/Exception/NotSearchableException.php (1)
  • NotSearchableException (7-13)
src/SearchService.php (5)
  • isSearchable (20-20)
  • searchableAs (34-34)
  • getConfiguration (27-27)
  • index (36-36)
  • search (59-64)
src/Services/MeilisearchManager.php (5)
  • isSearchable (68-73)
  • searchableAs (78-86)
  • getConfiguration (88-91)
  • index (116-162)
  • search (238-281)
src/Services/MeilisearchService.php (5)
  • isSearchable (66-71)
  • searchableAs (83-91)
  • getConfiguration (78-81)
  • index (93-126)
  • search (182-227)
src/Services/SettingsUpdater.php (2)
src/Services/MeilisearchManager.php (2)
  • MeilisearchManager (21-505)
  • getConfiguration (88-91)
src/SearchManagerInterface.php (1)
  • getConfiguration (25-25)
src/Command/MeilisearchCreateCommand.php (3)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/SearchManagerInterface.php (1)
  • isSearchable (18-18)
src/Services/MeilisearchManager.php (1)
  • isSearchable (68-73)
src/Command/MeilisearchClearCommand.php (5)
src/Engine.php (1)
  • clear (104-107)
src/SearchManagerInterface.php (1)
  • clear (51-51)
src/SearchService.php (1)
  • clear (43-43)
src/Services/MeilisearchManager.php (1)
  • clear (205-210)
src/Services/MeilisearchService.php (1)
  • clear (151-160)
src/Command/MeilisearchImportCommand.php (6)
src/Engine.php (3)
  • __construct (12-14)
  • index (25-61)
  • count (132-135)
src/EventListener/ConsoleOutputSubscriber.php (1)
  • __construct (13-15)
src/EventListener/DoctrineEventSubscriber.php (1)
  • __construct (12-14)
src/SearchManagerInterface.php (6)
  • getConfiguration (25-25)
  • isSearchable (18-18)
  • getDataProvider (30-30)
  • index (37-37)
  • count (101-101)
  • deleteByIndexName (58-58)
src/Services/MeilisearchManager.php (6)
  • getConfiguration (88-91)
  • isSearchable (68-73)
  • getDataProvider (96-111)
  • index (116-162)
  • count (307-312)
  • deleteByIndexName (215-218)
src/DataProvider/OrmEntityProvider.php (1)
  • provide (20-29)
tests/Unit/SerializationTest.php (2)
src/SearchableObject.php (2)
  • SearchableObject (13-96)
  • getId (75-78)
tests/Entity/Post.php (1)
  • getId (91-94)
src/SearchableObject.php (2)
src/SearchableEntity.php (1)
  • getSearchableArray (66-91)
tests/Entity/SelfNormalizable.php (1)
  • normalize (64-75)
src/DataProvider/DataProviderInterface.php (2)
src/DataProvider/OrmEntityProvider.php (4)
  • provide (20-29)
  • loadByIdentifiers (31-38)
  • getIdentifierValues (40-45)
  • cleanup (47-50)
tests/Integration/Fixtures/ActorDataProvider.php (4)
  • provide (15-44)
  • loadByIdentifiers (46-57)
  • getIdentifierValues (59-64)
  • cleanup (66-69)
src/Services/UnixTimestampNormalizer.php (4)
src/Model/Aggregator.php (1)
  • normalize (71-75)
tests/Entity/Link.php (1)
  • normalize (74-85)
tests/Entity/SelfNormalizable.php (1)
  • normalize (64-75)
tests/Entity/Tag.php (1)
  • normalize (88-100)
src/Command/MeilisearchUpdateSettingsCommand.php (3)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/SearchManagerInterface.php (1)
  • isSearchable (18-18)
src/SearchService.php (1)
  • isSearchable (20-20)
tests/BaseKernelTestCase.php (2)
src/Services/MeilisearchManager.php (3)
  • MeilisearchManager (21-505)
  • getConfiguration (88-91)
  • deleteByIndexName (215-218)
src/SearchManagerInterface.php (2)
  • getConfiguration (25-25)
  • deleteByIndexName (58-58)
src/Command/IndexCommand.php (4)
src/Command/MeilisearchCreateCommand.php (1)
  • __construct (23-30)
src/SearchManagerInterface.php (1)
  • getConfiguration (25-25)
src/SearchService.php (1)
  • getConfiguration (27-27)
src/Services/MeilisearchManager.php (1)
  • getConfiguration (88-91)
tests/Integration/EngineTest.php (3)
src/SearchableObject.php (2)
  • SearchableObject (13-96)
  • getId (75-78)
tests/Entity/Image.php (1)
  • getId (39-42)
tests/Entity/Post.php (1)
  • getId (91-94)
src/DataProvider/OrmEntityProvider.php (2)
src/DataProvider/DataProviderInterface.php (4)
  • provide (18-18)
  • loadByIdentifiers (25-25)
  • getIdentifierValues (32-32)
  • cleanup (34-34)
tests/Integration/Fixtures/ActorDataProvider.php (4)
  • provide (15-44)
  • loadByIdentifiers (46-57)
  • getIdentifierValues (59-64)
  • cleanup (66-69)
src/DependencyInjection/Configuration.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-96)
tests/Integration/AggregatorTest.php (2)
src/Model/Aggregator.php (1)
  • getEntityClassFromObjectID (60-69)
src/Exception/InvalidEntityForAggregator.php (1)
  • InvalidEntityForAggregator (7-9)
src/Services/MeilisearchManager.php (11)
src/Collection.php (1)
  • Collection (13-380)
src/Engine.php (7)
  • Engine (10-145)
  • index (25-61)
  • remove (69-95)
  • clear (104-107)
  • delete (112-115)
  • search (122-125)
  • count (132-135)
src/Exception/DataProviderNotFoundException.php (1)
  • DataProviderNotFoundException (7-13)
src/Exception/NotSearchableException.php (1)
  • NotSearchableException (7-13)
src/Exception/SearchHitsNotFoundException.php (1)
  • SearchHitsNotFoundException (7-9)
src/DataProvider/OrmEntityProvider.php (2)
  • getIdentifierValues (40-45)
  • loadByIdentifiers (31-38)
src/Services/MeilisearchService.php (13)
  • setAggregatorsAndEntitiesAggregators (303-322)
  • isSearchable (66-71)
  • searchableAs (83-91)
  • getConfiguration (78-81)
  • index (93-126)
  • remove (128-149)
  • clear (151-160)
  • deleteByIndexName (162-169)
  • delete (171-180)
  • search (182-227)
  • rawSearch (229-241)
  • count (243-252)
  • resolveClass (420-440)
src/SearchManagerInterface.php (12)
  • isSearchable (18-18)
  • searchableAs (23-23)
  • getConfiguration (25-25)
  • index (37-37)
  • getDataProvider (30-30)
  • remove (44-44)
  • clear (51-51)
  • deleteByIndexName (58-58)
  • delete (67-67)
  • search (79-79)
  • rawSearch (91-91)
  • count (101-101)
src/DataProvider/DataProviderInterface.php (2)
  • getIdentifierValues (32-32)
  • loadByIdentifiers (25-25)
tests/Integration/Fixtures/ActorDataProvider.php (2)
  • getIdentifierValues (59-64)
  • loadByIdentifiers (46-57)
tests/Entity/Actor.php (1)
  • getId (18-21)
src/Engine.php (2)
src/SearchManagerInterface.php (3)
  • index (37-37)
  • remove (44-44)
  • search (79-79)
src/SearchableObject.php (5)
  • SearchableObject (13-96)
  • getSearchableArray (83-95)
  • getIndexUid (62-65)
  • getPrimaryKey (70-73)
  • getId (75-78)
config/services.php (3)
src/Services/MeilisearchManager.php (1)
  • MeilisearchManager (21-505)
src/EventListener/DoctrineEventSubscriber.php (1)
  • DoctrineEventSubscriber (10-30)
src/Services/SettingsUpdater.php (1)
  • SettingsUpdater (16-77)
src/Services/MeilisearchService.php (6)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/Engine.php (8)
  • __construct (12-14)
  • Engine (10-145)
  • index (25-61)
  • remove (69-95)
  • clear (104-107)
  • delete (112-115)
  • search (122-125)
  • count (132-135)
src/EventListener/DoctrineEventSubscriber.php (1)
  • __construct (12-14)
src/Services/SettingsUpdater.php (1)
  • __construct (22-28)
src/Services/MeilisearchManager.php (9)
  • MeilisearchManager (21-505)
  • index (116-162)
  • remove (167-200)
  • clear (205-210)
  • deleteByIndexName (215-218)
  • delete (223-228)
  • search (238-281)
  • rawSearch (291-299)
  • count (307-312)
src/SearchManagerInterface.php (8)
  • index (37-37)
  • remove (44-44)
  • clear (51-51)
  • deleteByIndexName (58-58)
  • delete (67-67)
  • search (79-79)
  • rawSearch (91-91)
  • count (101-101)
🪛 GitHub Actions: Tests
src/EventListener/ConsoleOutputSubscriber.php

[error] 13-13: ShortVariable: Avoid variables with short names like $io. Configured minimum length is 3.

src/DependencyInjection/MeilisearchExtension.php

[error] 22-22: IfStatementAssignment: Avoid assigning values to variables in if clauses and the like (line '73', column '20').


[error] 22-22: CyclomaticComplexity: The method load() has a Cyclomatic Complexity of 11. The configured cyclomatic complexity threshold is 10.


[error] 36-36: StaticAccess: Avoid using static access to class '\Meilisearch\Bundle\MeilisearchBundle' in method 'load'.


[error] 63-63: ElseExpression: The method load uses an else expression. Else clauses are basically not necessary and you can simplify the code by not using them.


[error] 80-80: ElseExpression: The method load uses an else expression. Else clauses are basically not necessary and you can simplify the code by not using them.


[error] 88-88: StaticAccess: Avoid using static access to class '\MeilisearchBundle\MeilisearchBundle' in method 'load'.

src/Services/SettingsUpdater.php

[error] 34-34: CyclomaticComplexity: The method update() has a Cyclomatic Complexity of 13. The configured cyclomatic complexity threshold is 10.

src/Command/MeilisearchImportCommand.php

[error] 23-23: CouplingBetweenObjects: The class MeilisearchImportCommand has a coupling between objects value of 13. Consider to reduce the number of dependencies under 13.


[error] 71-71: CyclomaticComplexity: The method execute() has a Cyclomatic Complexity of 13. The configured cyclomatic complexity threshold is 10.


[error] 71-71: NPathComplexity: The method execute() has an NPath complexity of 592. The configured NPath complexity threshold is 200.

src/SearchableObject.php

[error] 30-30: ShortVariable: Avoid variables with short names like $id. Configured minimum length is 3.


[error] 47-47: ShortVariable: Avoid variables with short names like $id. Configured minimum length is 3.

src/Services/UnixTimestampNormalizer.php

[error] 14-14: UnusedFormalParameter: Avoid unused parameters such as '$format'.


[error] 14-14: UnusedFormalParameter: Avoid unused parameters such as '$context'.


[error] 19-19: UnusedFormalParameter: Avoid unused parameters such as '$format'.


[error] 24-24: UnusedFormalParameter: Avoid unused parameters such as '$format'.

src/Services/MeilisearchManager.php

[error] 21-21: ExcessiveClassComplexity: The class MeilisearchManager has an overall complexity of 69 which is very high. The configured complexity threshold is 50.


[error] 21-21: CouplingBetweenObjects: The class MeilisearchManager has a coupling between objects value of 14. Consider to reduce the number of dependencies under 13.


[error] 133-133: ElseExpression: The method index uses an else expression. Else clauses are basically not necessary and you can simplify the code by not using them.


[error] 438-438: ShortVariable: Avoid variables with short names like $id. Configured minimum length is 3.


[error] 496-496: StaticAccess: Avoid using static access to class '\Doctrine\ORM\Proxy\DefaultProxyClassNameResolver' in method 'resolveClass'.


[error] 500-500: StaticAccess: Avoid using static access to class '\Doctrine\Common\Util\ClassUtils' in method 'resolveClass'.

src/Services/MeilisearchService.php

[error] 27-27: TooManyPublicMethods: The class MeilisearchService has 11 public methods. Consider refactoring MeilisearchService to keep number of public methods under 10.


[error] 27-27: ExcessiveClassComplexity: The class MeilisearchService has an overall complexity of 64 which is very high. The configured complexity threshold is 50.


[error] 27-27: CouplingBetweenObjects: The class MeilisearchService has a coupling between objects value of 17. Consider to reduce the number of dependencies under 13.


[error] 48-48: LongVariable: Avoid excessively long variable names like $classToSerializerGroup. Keep variable name length under 20.


[error] 57-57: StaticAccess: Avoid using static access to class '\Symfony\Component\PropertyAccess\PropertyAccess' in method '__construct'.


[error] 384-384: LongVariable: Avoid excessively long variable names like $searchableEntitiesChunk. Keep variable name length under 20.


[error] 432-432: StaticAccess: Avoid using static access to class '\Doctrine\ORM\Proxy\DefaultProxyClassNameResolver' in method 'resolveClass'.


[error] 436-436: StaticAccess: Avoid using static access to class '\Doctrine\Common\Util\ClassUtils' in method 'resolveClass'.

🔇 Additional comments (56)
tests/config/framework.yaml (1)

5-7: Good addition to test environment configuration.

The new configuration keys align well with the Symfony 6.4 upgrade: handle_all_throwables: true ensures all throwable exceptions are caught during tests, and php_errors.log: true improves error visibility by logging PHP warnings and notices. This supports better test reliability and observability, especially important when testing the complex data provider and service wiring changes in this PR.

src/Event/SettingsUpdatedEvent.php (1)

16-20: Constructor promotion + readonly are idiomatic and preserve behavior

Using constructor property promotion with private readonly cleanly expresses this event as an immutable payload and keeps the existing API via getters intact. No further changes needed here.

src/Services/UnixTimestampNormalizer.php (2)

19-22: LGTM!

The signature correctly uses mixed $data to match the interface contract, and the instanceof check properly guards normalization.


14-14: Pipeline warnings: unused interface parameters.

The static analysis flags $format and $context as unused. These are required by the NormalizerInterface contract and cannot be removed. If your project enforces clean pipelines, consider adding suppression comments:

     /**
      * @param mixed $data Expected to be a \DateTimeInterface instance (guarded by supportsNormalization)
+     * @phpmd-suppress UnusedFormalParameter
      */
     public function normalize(mixed $data, ?string $format = null, array $context = []): int

Apply similar suppressions to lines 19 and 24, or configure your linter to ignore unused parameters in interface implementations globally.

Also applies to: 19-19, 24-24

tests/Integration/AggregatorTest.php (1)

31-37: Unable to complete verification: Repository access unavailable in sandbox environment.

The review comment raises a valid concern about the continued use of 'objectId' in unchanged test code (lines 37, 53, 61) against PR objective #66 to remove Algolia-specific objectID usage. However, I cannot access the repository to verify:

  • Whether the ContentAggregator class retains objectID-based semantics for backward compatibility
  • The broader migration strategy and scope of PR #66
  • Whether these test references represent incomplete migration or intentional API retention

The review comment's analysis of the code changes themselves is sound: the method rename and exception handling pattern are correct and follow best practices. The verification request is appropriate but requires repository context to resolve.

src/Services/SettingsUpdater.php (1)

22-28: Constructor migration to MeilisearchManager looks good.

The dependency injection now correctly uses MeilisearchManager with constructor property promotion for $searchClient and $eventDispatcher. Configuration retrieval via $searchManager->getConfiguration() aligns with the new architecture.

Regarding the pipeline failure on update() (cyclomatic complexity 13): this is pre-existing complexity in unchanged code. Consider addressing it in a follow-up PR to keep this one focused.

src/Exception/DataProviderNotFoundException.php (1)

7-12: Exception message and shape look good

Consistent with existing exception patterns and gives clear context on missing provider (index + class). No changes needed.

src/Searchable.php (1)

7-20: Deprecation annotations are clear and targeted

The class- and constant-level deprecation notices correctly steer users to SearchableObject and its constants without changing behavior.

src/Services/MeilisearchService.php (2)

93-99: Manager delegation preserves BC and aligns with the new API

The pattern of:

  • triggering deprecation on ObjectManager-based methods, and
  • short‑circuiting to $this->manager when present,

keeps existing signatures working while moving callers toward MeilisearchManager / SearchManagerInterface. The fallbacks to legacy Engine + Doctrine flows when no manager is injected look correct and localized. No functional issues spotted here.

Also applies to: 128-135, 151-160, 162-169, 171-180, 182-193, 229-241, 243-252


303-322: Aggregator casting and batch construction changes are safe

Casting $index['class'] to string for aggregators and continuing to build SearchableEntity chunks per batch is consistent with existing behavior and types; no issues with the updated array shapes or batching logic.

Also applies to: 353-370, 377-401

config/services.php (2)

28-36: Manager wiring and aliasing match the new architecture

Passing meilisearch.manager into MeilisearchService, switching the DoctrineEventSubscriber and all commands to depend on the manager, and aliasing SearchManagerInterface::class to meilisearch.manager all look consistent with the new manager‑centric API and should give a smooth migration path for users still on SearchService.

Also applies to: 44-47, 80-85, 89-104, 106-112, 115-120, 78-79


70-77: Based on my investigation, I cannot fully access the repository to verify the exact constructor signature of MeilisearchManager. However, I can confirm the underlying Symfony pattern:

Verified facts:

  • Symfony's tagged_iterator() injects a RewindableGenerator (implements Traversable), not a PHP array
  • If MeilisearchManager's constructor declares array $dataProviders, there will be a type mismatch
  • This is a real Symfony DI pattern issue

What I cannot verify:

  • The actual type hint in MeilisearchManager's constructor parameter (could already be iterable, could have no type hint, or could be array)

Since the review comment identifies a genuine possible type mismatch based on the DI config shown, and the Symfony behavior is confirmed, I can proceed with the rewritten review comment:


Verify MeilisearchManager constructor parameter type for $dataProviders

The DI config injects tagged_iterator('meilisearch.data_provider', 'key') as the last argument to MeilisearchManager. Symfony's tagged_iterator injects a RewindableGenerator (a Traversable), not a PHP array. If the constructor parameter is type-hinted as array $dataProviders, this will cause a TypeError at runtime.

Confirm that the constructor accepts iterable or Traversable for this parameter. If it requires an actual array, convert the iterator in the config using a factory or helper.

composer.json (1)

21-31: Dependency matrix aligns with the new minimum PHP/Symfony versions

Raising PHP to ^8.1, tightening Symfony to ^6.4/7.x, and moving doctrine-bundle to dev‑only match the refactor away from mandatory Doctrine and toward modern Symfony. The tooling bumps also look coherent with those versions; just ensure your supported projects are on at least these baselines before tagging a release.

Also applies to: 33-54

tests/Integration/Command/MeilisearchDeleteCommandTest.php (1)

30-41: Updated delete output expectation looks correct

Including Deleted sf_phpunit__actor matches the new Actor index created in tests and keeps the full deletion output consistent with the configured indices.

tests/baseline-ignore (1)

1-19: Baseline update correctly filters new Doctrine proxy deprecation

Adding the DefaultProxyClassNameResolver deprecation pattern keeps the test baseline in sync with current Doctrine deprecations and your usage in resolveClass().

tests/Integration/Command/MeilisearchClearCommandTest.php (1)

28-42: Clear output update matches new Actor index

The additional Cleared sf_phpunit__actor index of ...Actor line aligns with the new index and maintains the expected clear‑all behavior.

tests/Integration/Command/MeilisearchCreateCommandTest.php (1)

44-86: Actor index expectations correctly wired into create command output

The added sf_phpunit__actor lines in both --no-update-settings branches match the new actor index configuration (name, prefix, and class) and preserve the existing ordering of the subsequent aggregated indexes. Looks good.

tests/Entity/SelfNormalizable.php (1)

9-75: Normalization format constant correctly migrated to SearchableObject

Using SearchableObject::NORMALIZATION_FORMAT in SelfNormalizable::normalize() is consistent with the new SearchableObject flow and preserves the intended conditional normalization behavior.

src/Command/MeilisearchDeleteCommand.php (1)

31-40: Delete command correctly migrated to SearchManagerInterface

Switching to $this->searchManager->deleteByIndexName($indexName) keeps behavior and error handling intact while aligning with the new manager-based API. Ignoring the returned array is consistent with the previous implementation.

tests/Entity/Tag.php (1)

9-12: Tag normalization updated to use SearchableObject format constant

The normalization gate now correctly checks SearchableObject::NORMALIZATION_FORMAT, which is what SearchableObject passes for Normalizable objects. The rest of the payload (id, name, count, publishedAt) remains unchanged.

Also applies to: 88-100

tests/config/meilisearch.yaml (1)

57-60: Actor index configuration correctly exercises custom data provider path

The actor index definition (name, class, type: 'custom', and data_provider pointing at the fixtures provider class) is consistent with the new data-provider architecture and the services resource block. This should nicely cover the custom provider branch in integration tests.

tests/Integration/Command/MeilisearchImportCommandTest.php (1)

181-189: Updated batch ordering for tags import matches new import flow

The expected output in testImportDifferentEntitiesIntoSameIndex() now reflects Tag entities being indexed into the aggregated index before the tags index, then Link entities into tags. This aligns the test with the current import order without changing tested behavior.

tests/Unit/ConfigurationTest.php (1)

132-155: Config tests now assert default provider/primary-key metadata per index

Extending the expected config to include type => 'orm', data_provider => null, and primary_key => 'objectID' for each index entry accurately reflects the new configuration tree defaults and keeps the tests honest about the normalized shape of index configuration.

Also applies to: 188-217, 239-256, 278-298, 315-338

phpstan.dist.neon (1)

1-3: Verify that phpstan-baseline.php exists and regenerate it when baseline findings change

The includes configuration for phpstan-baseline.php is the correct approach for PHPStan. However, verification of the baseline file's existence and currency could not be completed due to repository access limitations. Please ensure the baseline file is present at the repository root and is regenerated whenever static analysis findings change to keep it synchronized with the codebase.

src/SearchableEntity.php (1)

15-17: Deprecation and normalization alignment with SearchableObject look correct

The deprecation docblock is clear, and switching the normalization format to SearchableObject::NORMALIZATION_FORMAT keeps behavior aligned with the new SearchableObject while retaining the same underlying format value.

Also applies to: 82-88

tests/Entity/Link.php (1)

9-9: Test entity normalization correctly migrated to SearchableObject

Using SearchableObject::NORMALIZATION_FORMAT in normalize() with the new import keeps this fixture in sync with the new normalization contract.

Also applies to: 74-82

tests/Integration/EventListener/DoctrineEventSubscriberTest.php (1)

23-24: Search calls correctly updated to SearchManagerInterface::search

All test usages now match the new search(string $className, string $query = '', array $searchParams = []) signature, dropping the EntityManager argument while preserving the original test semantics.

Also applies to: 38-39, 59-60, 81-82, 97-98, 107-108, 121-122, 131-132

src/Command/MeilisearchClearCommand.php (1)

30-31: Clear operation correctly routed through SearchManagerInterface

Switching to $this->searchManager->clear($className) aligns this command with the new manager API while preserving the existing control flow and status handling.

src/Command/IndexCommand.php (1)

8-9: Base command successfully migrated to SearchManagerInterface

Injecting SearchManagerInterface and reading prefix/indices from $this->searchManager->getConfiguration() preserves the previous behavior while aligning all index commands with the new manager abstraction.

Also applies to: 19-22, 26-29

tests/Kernel.php (1)

36-42: Updated test kernel wiring handles newer Doctrine/Symfony combinations

The revised Doctrine config selection and the conditional framework.property_info prepend for Kernel::VERSION_ID >= 70300 look appropriate for the supported Symfony/Doctrine matrix and keep the test kernel aligned with the new minimum versions.

Also applies to: 64-69

tests/Integration/SearchTest.php (4)

71-76: LGTM! Proper async task synchronization added.

The waitForAllTasks() call ensures the asynchronous import completes before searching. The migration to searchManager->search() correctly uses the new API signature.


81-81: Consistent use of new SearchManager API.

The rawSearch call correctly follows the new interface signature.


114-116: LGTM! Proper test flow with task synchronization.


137-139: LGTM! Consistent pattern across all test methods.

src/Model/Aggregator.php (1)

24-26: LGTM! Strict typing aligns with PHP 8.1+ requirements.

The explicit object type for both property and constructor parameter improves type safety.

src/DependencyInjection/MeilisearchExtension.php (2)

34-37: LGTM! Configuration parameters properly exposed.

The new parameters for URL, API key, and version are correctly set early in the load process.


94-97: LGTM! Data providers correctly wired to manager.

The dataProviders collection is properly passed as the 5th argument to the manager service.

tests/Integration/EngineTest.php (2)

35-42: LGTM! Correctly migrated to SearchableObject API.

The constructor call properly matches the new signature: (indexUid, primaryKey, object, id, normalizer, normalizationContext). The 'objectID' string correctly identifies the primary key field name.


67-69: LGTM! Consistent SearchableObject usage for multi-entity test.

The serializer is correctly shared across both instances, and the constructor parameters align with the new API.

src/Command/MeilisearchCreateCommand.php (2)

23-30: LGTM! Clean constructor with property promotion.

The migration to SearchManagerInterface is correct, and constructor property promotion (private readonly) is idiomatic PHP 8.1+.


66-68: LGTM! Correctly uses the new manager interface.

The isSearchable() call properly delegates to the new SearchManagerInterface.

src/EventListener/DoctrineEventSubscriber.php (1)

12-28: LGTM!

Clean migration to SearchManagerInterface with simplified API. The removal of ObjectManager dependency from index() and remove() calls aligns with the PR objective of decoupling from Doctrine.

src/Command/MeilisearchUpdateSettingsCommand.php (1)

22-28: LGTM!

Consistent migration to SearchManagerInterface following the same pattern as other commands. Constructor property promotion is cleanly applied.

tests/Unit/SerializationTest.php (1)

23-30: LGTM!

Test correctly updated to use the new SearchableObject constructor signature with explicit primaryKey and id parameters instead of class metadata.

src/SearchManagerInterface.php (2)

32-44: LGTM — clean API design for indexing and removal.

The simplified signatures accepting object|array without requiring ObjectManager align well with the PR's goal to support non-Doctrine data sources.


12-13: Based on my verification attempt, I encountered repository access limitations that prevent me from fully validating the review comment. However, I was able to gather important context from the web:

Web search findings:

  • Meilisearch uses a configurable primary key field (can be "id", "sku", "objectID", or any custom attribute) rather than a hard-coded "objectID"
  • Search responses return hits containing the full document with the configured primary key field
  • Client libraries may alias common field names like "objectID" for convenience, but this is implementation-specific

What I cannot verify:

  • Current usage of RESULT_KEY_OBJECTID in the codebase
  • Whether this constant accesses a document's primary key or serves another purpose
  • If renaming would break existing functionality
  • The actual scope and requirements of PR objective #66

Given the web search confirms that Meilisearch doesn't mandate "objectID" terminology, the review comment's concern has merit. However, without codebase visibility, I cannot definitively assess whether the suggested rename is safe or necessary.


The constant RESULT_KEY_OBJECTID may reflect Algolia legacy terminology rather than Meilisearch semantics. Meilisearch uses a configurable primary key field (not a hard-coded "objectID"), though client libraries may alias common field names for convenience. Verify whether this constant accesses a document's primary key or serves another purpose, and whether renaming aligns with PR objective #66 requirements.

tests/BaseKernelTestCase.php (2)

10-10: LGTM! Service migration to MeilisearchManager.

The property, import, and service container access are correctly updated to use the new MeilisearchManager service alias.

Also applies to: 19-19, 27-27


54-62: LGTM! Cleanup methods correctly adapted.

The cleanUp() and cleanupIndex() methods properly use searchManager->getConfiguration() and searchManager->deleteByIndexName() matching the new interface signatures.

Also applies to: 64-72

src/Engine.php (2)

12-14: LGTM! Constructor promotion is clean.

Good use of PHP 8.1+ constructor property promotion for the readonly client dependency.


137-144: LGTM! normalizeId() correctly handles Stringable.

The type signature and implementation properly handle Stringable, string, and int types, casting objects to string as needed.

src/Command/MeilisearchImportCommand.php (2)

27-34: LGTM! Constructor properly migrated to SearchManagerInterface.

The constructor uses property promotion for readonly dependencies and correctly delegates to the parent IndexCommand with the search manager.


160-181: LGTM! formatIndexingResponse() handles task waiting correctly.

The method properly initializes missing keys with null coalescing, waits for tasks, checks for failures, and aggregates indexed document counts.

src/Services/MeilisearchManager.php (4)

51-63: LGTM! Constructor correctly initializes the manager.

The constructor properly stores configuration, data providers, and initializes derived data structures for searchables and aggregators.


96-111: LGTM! getDataProvider() with fallback to parent class matching.

Good design: first checks exact match, then falls back to is_a() for subclass support, and throws a clear exception when not found.


494-501: Pipeline flags: Static access to Doctrine classes.

The pipeline flags static calls to DefaultProxyClassNameResolver::getClass() and ClassUtils::getClass(). These are necessary for Doctrine proxy resolution, so this appears to be a valid exception to the rule.


257-261: I apologize for the repeated clone failures. Let me analyze this based on the information available in the review comment and general knowledge of the Meilisearch-Algolia migration pattern.

Based on my investigation:

  1. The concern is valid but cannot be fully verified without access to:

    • The actual value of RESULT_KEY_OBJECTID constant
    • How the primary key configuration is accessed in this scope
    • Other recent changes in this PR to the constant definitions
  2. What I can determine:

    • Meilisearch uses index-level primaryKey settings (unlike Algolia's per-record objectID)
    • The code snippet uses array_column($hits, self::RESULT_KEY_OBJECTID) which extracts a field value from search results
    • Meilisearch search results DO include the primaryKey field in each document
    • The constant name "RESULT_KEY_OBJECTID" is Algolia-specific terminology
  3. Key issues:

    • If RESULT_KEY_OBJECTID is still hardcoded to 'objectid' string, it conflicts with PR objectives
    • Even if the value is correct (e.g., 'id'), the constant NAME should be renamed to reflect Meilisearch semantics
    • The $index variable (available on line 257) likely has access to the configured primary_key, which should be used

Given that I cannot access the repository directly to verify the constant's current value, I must provide a verification-pending assessment:


Cannot access repository files to determine if RESULT_KEY_OBJECTID is already correctly mapped to Meilisearch semantics or if it remains hardcoded to Algolia's 'objectid'. Recommend: (1) Check the constant definition's current value, (2) Verify if $index->getPrimaryKey() or similar method is available to use the configured primary key dynamically, (3) Consider renaming constant from RESULT_KEY_OBJECTID to reflect Meilisearch semantics even if current value is correct.

@norkunas norkunas force-pushed the dataprovider branch 2 times, most recently from c582c33 to 10690e1 Compare November 26, 2025 05:40
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
src/Services/UnixTimestampNormalizer.php (1)

11-17: Docblock still mismatched with mixed $data parameter

The docblock continues to declare @param \DateTimeInterface $data while the signature is mixed $data and the actual guard lives in supportsNormalization(). To keep things consistent and self-documenting, I’d adjust the docblock to describe the real contract rather than a stricter type:

-    /**
-     * @param \DateTimeInterface $data
-     */
+    /**
+     * @param mixed $data Expected to be a \DateTimeInterface instance (guarded by supportsNormalization)
+     */

This avoids changing behavior while documenting the Symfony-serializer assumption that normalize() is only called after a positive supportsNormalization() check.

tests/Integration/Fixtures/ActorDataProvider.php (1)

46-57: loadByIdentifiers implementation inconsistent with OrmEntityProvider.

This implementation expects $identifiers to be an associative array with an 'id' key, while OrmEntityProvider::loadByIdentifiers treats $identifiers as a flat array of ID values. This inconsistency could cause bugs when switching between providers.

🧹 Nitpick comments (7)
src/Model/Aggregator.php (1)

24-35: Stronger typing for $entity is a nice incremental cleanup.

Typing the $entity property and constructor as object tightens the contract without changing behavior. Leaving the objectID handling as-is with a TODO about configurable primary_key is reasonable to keep this PR focused; just ensure there’s a follow-up issue tracking that TODO.

Based on learnings, deferring the primary_key vs objectID cleanup to a dedicated PR matches the preferred workflow.

Also applies to: 71-75

src/DataProvider/OrmEntityProvider.php (1)

53-56: Verify clear() behavior is documented and expected.

Calling clear() on the entity manager detaches all managed entities, which resets the identity map. While appropriate for batch import operations to prevent memory exhaustion, this side effect should be documented in the interface or provider usage guidelines.

src/Services/MeilisearchService.php (1)

151-159: Consider adding deprecation notice for consistency.

The clear() method delegates to manager but doesn't emit a trigger_deprecation notice like index(), remove(), and search() do. While the signature hasn't changed, adding a notice would be consistent with the deprecation strategy.

 public function clear(string $className): array
 {
+    trigger_deprecation('meilisearch/meilisearch-symfony', '0.16', 'MeilisearchService::clear() is deprecated. Use MeilisearchManager::clear() instead.');
+
     if (null !== $this->manager) {
         return $this->manager->clear($className);
     }
src/SearchManagerInterface.php (1)

12-13: Consider removing or renaming RESULT_KEY_OBJECTID constant.

Per PR objective #66, the goal is to remove Algolia-specific "objectID" terminology in favor of Meilisearch primary-key semantics. This constant perpetuates the legacy naming. Consider renaming it to align with Meilisearch conventions (e.g., RESULT_KEY_PRIMARY_KEY or similar), or document why it must remain for backward compatibility.

composer.json (1)

28-28: Remove symfony/polyfill-php80 — redundant with PHP 8.1+ requirement.

The polyfill backports PHP 8.0 features (attributes, str_contains, etc.) to PHP 7.x. With the minimum PHP version now at 8.1, this dependency is no longer needed.

-    "symfony/polyfill-php80": "^1.33",
src/Engine.php (1)

77-88: Batch deletions to reduce API calls.

The remove() method calls deleteDocument() individually for each object, resulting in N API calls per index. Meilisearch supports batch deletion via deleteDocuments(array $documentIds), which would reduce this to a single call per index, matching the batching pattern used in index().

         $result = [];
         foreach ($data as $indexUid => $objects) {
-            $result[$indexUid] = [];
-            foreach ($objects as $object) {
-                $result[$indexUid][] = $this->client
-                    ->index($indexUid)
-                    ->deleteDocument($object);
-            }
+            $result[$indexUid] = $this->client
+                ->index($indexUid)
+                ->deleteDocuments($objects);
         }
src/Command/MeilisearchCreateCommand.php (1)

87-108: Consider simplifying entitiesToIndex() collection manipulation.

The current implementation modifies the collection during iteration (forget()) and then merges with a new Collection, which can be confusing. A filter-then-flatMap approach would be clearer:

     private function entitiesToIndex(Collection $indexes): array
     {
-        foreach ($indexes as $key => $index) {
-            $entityClassName = $index['class'];
-
-            if (!is_subclass_of($entityClassName, Aggregator::class)) {
-                continue;
-            }
-
-            $indexes->forget($key);
-
-            $indexes = new Collection(array_merge(
-                $indexes->all(),
-                array_map(
-                    static fn ($entity) => ['name' => $index['name'], 'prefixed_name' => $index['prefixed_name'], 'class' => $entity],
-                    $entityClassName::getEntities()
-                )
-            ));
-        }
-
-        return array_unique($indexes->all(), SORT_REGULAR);
+        $result = [];
+        foreach ($indexes as $index) {
+            $entityClassName = $index['class'];
+
+            if (is_subclass_of($entityClassName, Aggregator::class)) {
+                foreach ($entityClassName::getEntities() as $entity) {
+                    $result[] = ['name' => $index['name'], 'prefixed_name' => $index['prefixed_name'], 'class' => $entity];
+                }
+            } else {
+                $result[] = $index;
+            }
+        }
+
+        return array_unique($result, SORT_REGULAR);
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4cb2b38 and c582c33.

📒 Files selected for processing (54)
  • .github/workflows/pre-release-tests.yml (2 hunks)
  • .github/workflows/tests.yml (1 hunks)
  • composer.json (1 hunks)
  • config/services.php (4 hunks)
  • phpstan-baseline.php (1 hunks)
  • phpstan.dist.neon (1 hunks)
  • src/Command/IndexCommand.php (2 hunks)
  • src/Command/MeilisearchClearCommand.php (1 hunks)
  • src/Command/MeilisearchCreateCommand.php (3 hunks)
  • src/Command/MeilisearchDeleteCommand.php (1 hunks)
  • src/Command/MeilisearchImportCommand.php (7 hunks)
  • src/Command/MeilisearchUpdateSettingsCommand.php (3 hunks)
  • src/DataProvider/DataProviderInterface.php (1 hunks)
  • src/DataProvider/OrmEntityProvider.php (1 hunks)
  • src/DependencyInjection/Configuration.php (2 hunks)
  • src/DependencyInjection/MeilisearchExtension.php (3 hunks)
  • src/Engine.php (5 hunks)
  • src/Event/SettingsUpdatedEvent.php (1 hunks)
  • src/EventListener/ConsoleOutputSubscriber.php (1 hunks)
  • src/EventListener/DoctrineEventSubscriber.php (1 hunks)
  • src/Exception/DataProviderNotFoundException.php (1 hunks)
  • src/Exception/InvalidIndiceException.php (1 hunks)
  • src/Exception/NotSearchableException.php (1 hunks)
  • src/Model/Aggregator.php (2 hunks)
  • src/SearchManagerInterface.php (1 hunks)
  • src/SearchService.php (1 hunks)
  • src/Searchable.php (1 hunks)
  • src/SearchableEntity.php (2 hunks)
  • src/SearchableObject.php (1 hunks)
  • src/Services/MeilisearchManager.php (1 hunks)
  • src/Services/MeilisearchService.php (8 hunks)
  • src/Services/SettingsUpdater.php (1 hunks)
  • src/Services/UnixTimestampNormalizer.php (1 hunks)
  • tests/BaseKernelTestCase.php (5 hunks)
  • tests/Entity/Actor.php (1 hunks)
  • tests/Entity/Link.php (2 hunks)
  • tests/Entity/SelfNormalizable.php (2 hunks)
  • tests/Entity/Tag.php (2 hunks)
  • tests/Integration/AggregatorTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchClearCommandTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchCreateCommandTest.php (2 hunks)
  • tests/Integration/Command/MeilisearchDeleteCommandTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchImportCommandTest.php (1 hunks)
  • tests/Integration/EngineTest.php (3 hunks)
  • tests/Integration/EventListener/DoctrineEventSubscriberTest.php (8 hunks)
  • tests/Integration/Fixtures/ActorDataProvider.php (1 hunks)
  • tests/Integration/SearchTest.php (4 hunks)
  • tests/Kernel.php (1 hunks)
  • tests/Unit/ConfigurationTest.php (8 hunks)
  • tests/Unit/SerializationTest.php (2 hunks)
  • tests/baseline-ignore (1 hunks)
  • tests/config/doctrine_php7.yaml (0 hunks)
  • tests/config/framework.yaml (1 hunks)
  • tests/config/meilisearch.yaml (1 hunks)
💤 Files with no reviewable changes (1)
  • tests/config/doctrine_php7.yaml
🚧 Files skipped from review as they are similar to previous changes (18)
  • tests/Entity/SelfNormalizable.php
  • src/Command/MeilisearchClearCommand.php
  • phpstan.dist.neon
  • tests/config/meilisearch.yaml
  • src/Exception/DataProviderNotFoundException.php
  • src/Command/MeilisearchDeleteCommand.php
  • tests/Integration/SearchTest.php
  • tests/Integration/Command/MeilisearchDeleteCommandTest.php
  • src/DependencyInjection/Configuration.php
  • tests/baseline-ignore
  • src/Exception/NotSearchableException.php
  • src/Command/MeilisearchUpdateSettingsCommand.php
  • tests/Integration/EngineTest.php
  • src/Searchable.php
  • src/DataProvider/DataProviderInterface.php
  • src/Command/IndexCommand.php
  • src/SearchService.php
  • tests/config/framework.yaml
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: norkunas
Repo: meilisearch/meilisearch-symfony PR: 391
File: src/Command/MeilisearchImportCommand.php:117-120
Timestamp: 2025-08-05T04:46:01.119Z
Learning: In meilisearch-symfony project, the maintainer norkunas prefers to keep PRs focused and defer error handling improvements for settings updates to separate PRs, particularly when it involves decisions about partial import behavior when some indexes fail.
🧬 Code graph analysis (18)
src/Model/Aggregator.php (2)
src/Engine.php (1)
  • __construct (12-14)
src/SearchableEntity.php (1)
  • __construct (42-56)
src/Services/SettingsUpdater.php (3)
src/Services/MeilisearchManager.php (2)
  • MeilisearchManager (22-507)
  • getConfiguration (89-92)
src/SearchManagerInterface.php (1)
  • getConfiguration (25-25)
src/SearchService.php (1)
  • getConfiguration (27-27)
tests/Integration/AggregatorTest.php (2)
src/Model/Aggregator.php (1)
  • getEntityClassFromObjectID (60-69)
src/Exception/InvalidEntityForAggregator.php (1)
  • InvalidEntityForAggregator (7-9)
src/EventListener/DoctrineEventSubscriber.php (2)
src/SearchManagerInterface.php (2)
  • index (37-37)
  • remove (44-44)
src/SearchService.php (2)
  • index (36-36)
  • remove (38-38)
tests/Integration/EventListener/DoctrineEventSubscriberTest.php (3)
src/SearchManagerInterface.php (1)
  • search (79-79)
src/Services/MeilisearchManager.php (1)
  • search (239-283)
src/Services/MeilisearchService.php (1)
  • search (182-227)
src/SearchableEntity.php (5)
src/Model/Aggregator.php (1)
  • normalize (71-75)
tests/Entity/Link.php (1)
  • normalize (74-85)
tests/Entity/SelfNormalizable.php (1)
  • normalize (64-75)
tests/Entity/Tag.php (1)
  • normalize (88-100)
src/SearchableObject.php (1)
  • SearchableObject (13-78)
tests/Entity/Tag.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-78)
tests/Entity/Link.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-78)
src/DataProvider/OrmEntityProvider.php (2)
src/DataProvider/DataProviderInterface.php (4)
  • provide (18-18)
  • loadByIdentifiers (25-25)
  • getIdentifierValues (32-32)
  • cleanup (34-34)
tests/Integration/Fixtures/ActorDataProvider.php (4)
  • provide (15-44)
  • loadByIdentifiers (46-57)
  • getIdentifierValues (59-64)
  • cleanup (66-69)
src/SearchManagerInterface.php (2)
src/Exception/NotSearchableException.php (1)
  • NotSearchableException (7-13)
src/SearchService.php (3)
  • isSearchable (20-20)
  • searchableAs (34-34)
  • search (59-64)
tests/BaseKernelTestCase.php (2)
src/Services/MeilisearchManager.php (3)
  • MeilisearchManager (22-507)
  • getConfiguration (89-92)
  • deleteByIndexName (216-219)
src/SearchManagerInterface.php (2)
  • getConfiguration (25-25)
  • deleteByIndexName (58-58)
src/Command/MeilisearchCreateCommand.php (5)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/Command/MeilisearchImportCommand.php (1)
  • __construct (27-34)
src/Services/MeilisearchService.php (2)
  • __construct (52-64)
  • isSearchable (66-71)
src/SearchManagerInterface.php (1)
  • isSearchable (18-18)
src/SearchService.php (1)
  • isSearchable (20-20)
src/Command/MeilisearchImportCommand.php (2)
src/SearchManagerInterface.php (6)
  • getConfiguration (25-25)
  • isSearchable (18-18)
  • getDataProvider (30-30)
  • index (37-37)
  • count (101-101)
  • deleteByIndexName (58-58)
src/DataProvider/DataProviderInterface.php (2)
  • provide (18-18)
  • cleanup (34-34)
tests/Unit/SerializationTest.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-78)
src/Engine.php (3)
src/SearchManagerInterface.php (3)
  • index (37-37)
  • remove (44-44)
  • search (79-79)
src/Services/MeilisearchManager.php (3)
  • index (117-163)
  • remove (168-201)
  • search (239-283)
src/SearchableObject.php (5)
  • SearchableObject (13-78)
  • getSearchableArray (63-77)
  • getIndexUid (42-45)
  • getPrimaryKey (50-53)
  • getIdentifier (55-58)
src/Services/UnixTimestampNormalizer.php (4)
src/Model/Aggregator.php (1)
  • normalize (71-75)
tests/Entity/Link.php (1)
  • normalize (74-85)
tests/Entity/SelfNormalizable.php (1)
  • normalize (64-75)
tests/Entity/Tag.php (1)
  • normalize (88-100)
src/SearchableObject.php (4)
src/SearchableEntity.php (1)
  • getSearchableArray (66-91)
tests/Entity/Link.php (1)
  • normalize (74-85)
tests/Entity/SelfNormalizable.php (1)
  • normalize (64-75)
tests/Entity/Tag.php (1)
  • normalize (88-100)
config/services.php (3)
src/Services/MeilisearchManager.php (1)
  • MeilisearchManager (22-507)
src/EventListener/DoctrineEventSubscriber.php (1)
  • DoctrineEventSubscriber (10-30)
src/Services/SettingsUpdater.php (1)
  • SettingsUpdater (16-77)
🔇 Additional comments (42)
src/Event/SettingsUpdatedEvent.php (1)

16-20: Constructor promotion with readonly is correct and keeps BC

Using promoted private readonly string properties matches the existing constructor contract, improves immutability, and is compatible with the PHP 8.1+ baseline (including the trailing comma). Getters continue to work as before.

src/Exception/InvalidIndiceException.php (1)

9-12: Typed $code parameter aligns with base exception and stricter typing

Using int $code = 0 matches \InvalidArgumentException’s constructor and improves type safety. The only behavioral change to be aware of is that callers passing non-int codes will now get an earlier type error, which is acceptable given this PR is already marked as breaking.

tests/Integration/AggregatorTest.php (1)

31-37: LGTM! Improved test naming and structure.

The method rename makes the test intent explicit, and the reordering (setup → expectation → action) improves readability.

src/Services/UnixTimestampNormalizer.php (1)

19-22: Typed supportsNormalization signature looks correct

Updating supportsNormalization to mixed $data, ?string $format = null, array $context = [] aligns with the modern NormalizerInterface signature, and the guard ($data instanceof \DateTimeInterface plus the meilisearch context flag) preserves existing behavior. No changes requested here.

tests/Integration/Command/MeilisearchImportCommandTest.php (1)

181-189: Log expectation updated to match new import flow.

The adjusted Tag batch line correctly reflects the new logging order (aggregated index first, then tags index) while keeping the functional assertions (8 hits) intact. Looks good.

.github/workflows/tests.yml (1)

30-31: CI matrix now cleanly matches supported PHP/Symfony versions.

Restricting the matrix to PHP 8.1–8.4 and Symfony 6.4/7.0–7.3, with 8.1 excluded from all 7.x combos, aligns with platform requirements and avoids unused or invalid matrix entries.

src/EventListener/ConsoleOutputSubscriber.php (1)

13-20: Constructor promotion and $output rename look good.

Using a promoted private readonly OutputStyle $output cleans up the class and resolves the previous short-variable PHPMD warning without changing behavior.

tests/Entity/Actor.php (1)

1-27: Actor test entity is simple and correct.

Typed properties, constructor, and getters are all consistent and sufficient for use with the Actor data provider and related tests.

.github/workflows/pre-release-tests.yml (1)

42-65: Pre-release CI matrix and install options align with main CI setup.

The reduced matrix (PHP 8.1–8.4, Symfony 6.4–7.3 with 8.1 excluded from 7.x) plus targeted 8.4 lowest/highest runs looks consistent with the primary workflow, and the composer-install tweaks correctly hook into the new dependencies axis.

Also applies to: 79-88

src/SearchableObject.php (1)

1-78: SearchableObject design and normalization behavior look solid.

The new SearchableObject cleanly encapsulates index UID, primary key, identifier, and normalization context, and getSearchableArray() correctly uses a local context (with Symfony 7.1+ DateTime handling) while delegating to either the object or the injected normalizer. This aligns well with the new provider/primary_key architecture.

tests/Integration/Command/MeilisearchClearCommandTest.php (1)

28-42: Clear command expectation correctly extended for Actor index.

Adding the sf_phpunit__actor line keeps the clear command output test in sync with the new Actor index configuration.

tests/Entity/Tag.php (1)

9-9: LGTM: Import updated to SearchableObject.

The migration from Searchable to SearchableObject is consistent with the broader refactor across the codebase.

Also applies to: 90-90

tests/Integration/Command/MeilisearchCreateCommandTest.php (1)

63-63: LGTM: Test expectations updated for Actor index.

The test output now correctly includes the Actor index creation, aligning with the new data provider fixtures introduced in the PR.

Also applies to: 80-80

src/Services/SettingsUpdater.php (1)

22-28: LGTM: Constructor refactored to use MeilisearchManager.

The dependency injection updates align with the new manager-based architecture. Property promotion is used appropriately, and configuration is now sourced from the manager.

tests/Entity/Link.php (1)

9-9: LGTM: Import and constant reference updated to SearchableObject.

Consistent with the normalization format migration across test entities.

Also applies to: 76-76

src/SearchableEntity.php (2)

15-17: LGTM: Proper deprecation notice added.

Clear deprecation message directs users to the replacement SearchableObject class. This maintains backward compatibility while guiding migration.


83-83: LGTM: Normalization format constant references updated.

Internal references correctly point to SearchableObject::NORMALIZATION_FORMAT, aligning with the new normalization architecture.

Also applies to: 87-87

tests/Unit/ConfigurationTest.php (2)

139-142: LGTM: Test expectations updated for new configuration schema.

All existing test cases now correctly assert the new ORM-related metadata fields (type, data_provider, primary_key) that were introduced by the data provider feature.

Also applies to: 150-152, 195-197, 206-208, 246-248, 287-289, 328-330


359-402: LGTM: New test case validates custom primary key configuration.

The "custom objectID" test case properly validates that custom primary_key values (postId, tagId) are correctly processed and retained in the configuration, demonstrating the new primary key customization capability.

tests/Integration/EventListener/DoctrineEventSubscriberTest.php (1)

23-23: LGTM: Search API calls updated to new interface.

All test calls correctly migrated from the deprecated search($objectManager, $className, ...) signature to the new searchManager->search($className, ...) API, removing the ObjectManager dependency as intended by the refactor.

Also applies to: 38-38, 59-59, 81-81, 97-97, 107-107, 121-121, 131-131

phpstan-baseline.php (1)

1-17: I'll help you verify the PHPStan baseline approach for the optional Doctrine dependency. Let me examine how ClassUtils is actually used in the affected files and check for runtime guards.
<function_calls>

#!/bin/bash

Verify ClassUtils usage patterns in MeilisearchManager and MeilisearchService

echo "=== Checking MeilisearchManager.php for ClassUtils usage ==="
rg -n -A 5 -B 5 'ClassUtils' src/Services/MeilisearchManager.php

echo -e "\n=== Checking MeilisearchService.php for ClassUtils usage ==="
rg -n -A 5 -B 5 'ClassUtils' src/Services/MeilisearchService.php

echo -e "\n=== Checking for Doctrine availability guards in Services directory ==="
rg -n -P 'class_exists|interface_exists|method_exists' src/Services/ | grep -i doctrine

echo -e "\n=== Checking imports and namespace declarations in affected files ==="
rg -n '^use |^namespace ' src/Services/MeilisearchManager.php
rg -n '^use |^namespace ' src/Services/MeilisearchService.php


</function_calls>

tests/Kernel.php (1)

36-42: LGTM! Simplified configuration logic aligns with PHP 8.1+ requirement.

The removal of PHP 7 guards and the streamlined Doctrine configuration loading correctly handles the three scenarios: Doctrine Bundle v3, PHP 8.4+ with legacy reflection fields, and older proxy configurations.

src/DependencyInjection/MeilisearchExtension.php (2)

94-97: LGTM! Manager wiring correctly passes the data providers collection.

The meilisearch.manager service receives the dynamically built $dataProviders array as argument 4, enabling the new DataProvider-based architecture.


45-70: Yes, please fetch src/DependencyInjection/Configuration.php to show the enum's allowed values for the type field, and also fetch src/Services/MeilisearchManager.php to show how dataProviders is used.

src/EventListener/DoctrineEventSubscriber.php (1)

12-28: LGTM! Clean migration to SearchManagerInterface.

The refactoring correctly adopts constructor property promotion and updates all method calls to use the new single-argument API, aligning with SearchManagerInterface::index(object|array $searchable) and SearchManagerInterface::remove(object|array $searchable).

tests/Integration/Fixtures/ActorDataProvider.php (1)

15-44: LGTM! Straightforward test fixture implementation.

The provide() method correctly implements pagination using array_slice with offset and limit parameters.

tests/Unit/SerializationTest.php (1)

23-30: LGTM! Test correctly updated for SearchableObject API.

The constructor arguments are properly ordered to match SearchableObject(indexUid, primaryKey, object, identifier, normalizer, normalizationContext), and the context key correctly uses 'groups' for Symfony serializer groups.

src/DataProvider/OrmEntityProvider.php (2)

20-29: LGTM! Correct use of Doctrine metadata for identifier-based sorting.

The provide() method properly retrieves identifier field names from class metadata and builds a dynamic sort order, ensuring consistent pagination across entities with different primary key configurations.


31-44: LGTM! Identifier field handling now uses metadata.

The implementation correctly retrieves the actual identifier field name from Doctrine metadata instead of hardcoding 'id', and appropriately throws LogicException for composite identifiers which aren't yet supported.

src/Services/MeilisearchService.php (3)

24-27: Deprecation FQN has been corrected.

The deprecation message now correctly references Meilisearch\Bundle\Services\MeilisearchManager, matching the actual namespace.


93-99: LGTM! Proper deprecation and delegation pattern.

The method correctly emits a deprecation notice for the ObjectManager-based API and delegates to the new manager when available, maintaining backward compatibility while guiding users toward the new API.


315-316: LGTM! String cast ensures type consistency.

Casting the class references to string ensures consistent type handling in the aggregators arrays.

src/SearchManagerInterface.php (1)

32-44: LGTM on the index/remove method signatures.

The union type object|array provides flexibility for single and batch operations. The @todo markers for return shapes are acknowledged; consider defining typed return structures (e.g., a TaskInfo DTO) in a follow-up to improve type safety and IDE support.

tests/BaseKernelTestCase.php (2)

10-11: LGTM on the SearchService → MeilisearchManager migration.

The property declaration, import, and service retrieval are updated consistently. The new manager-based approach aligns with the PR's architectural changes.

Also applies to: 17-19, 25-27


47-52: Based on my verification through web search of the meilisearch-php documentation:

The review comment's technical claim is accurate. TasksResults does implement IteratorAggregate with a getIterator() method that returns an ArrayIterator over the stored response data (the "results" array). This means the foreach loop will correctly iterate over task arrays, and accessing $task['uid'] will work as expected.

However, the code is functionally correct—the iteration behavior is sound. The performance concern about waiting for all tasks on every test setup is a valid design observation but is more of an optimization suggestion than a code defect.

Verify iteration behavior of getTasks() return type.

Client::getTasks() returns a TasksResults object that implements IteratorAggregate. The iterator yields task arrays from the response results, so $task['uid'] access is valid. The loop will function correctly. However, waiting for all tasks on every test setup could become slow as the task queue grows—consider batching or filtering only relevant tasks if performance becomes an issue.

composer.json (1)

20-31: LGTM on dependency version updates.

The PHP 8.1+ and Symfony 6.4+/7.x alignment is consistent. Moving doctrine/doctrine-bundle to require-dev correctly reflects that ORM is now optional (users must explicitly require it if using the ORM provider), aligning with the data provider abstraction goal.

Also applies to: 32-53

src/Engine.php (1)

40-51: LGTM on per-index primary key handling.

The implementation now correctly tracks primaryKey per index using the structured $data[$indexUid] array with 'primaryKey' and 'documents' keys. This addresses the previous review concern about a single primary key being applied across all indexes.

src/Command/MeilisearchCreateCommand.php (1)

23-30: LGTM on constructor migration to SearchManagerInterface.

The dependency injection pattern is consistent with other commands (e.g., MeilisearchImportCommand). Using readonly property promotion is clean and idiomatic for PHP 8.1+.

src/Command/MeilisearchImportCommand.php (2)

11-34: Constructor now correctly depends on SearchManagerInterface and shared services

Wiring the command through SearchManagerInterface (and passing it to the parent) cleanly decouples the import flow from the legacy SearchService and Doctrine-specific assumptions while keeping Client, SettingsUpdater, and EventDispatcherInterface as explicit, readonly dependencies. This aligns well with the new manager-based architecture.


75-83: Data‑provider–based batching and response handling look consistent with the new manager API

  • Using $this->searchManager->getConfiguration() for batchSize, prefixes, and nbResults keeps CLI behavior in sync with central configuration.
  • Resolving the DataProviderInterface once per index (before the do { ... } while loop) avoids unnecessary lookups and matches the intended extension point design.
  • The batching loop (provide() + index() + per-batch logging) and the updated formatIndexingResponse() (with $formattedResponse[$indexName] ??= 0;) correctly reflect the new engine index response shape.
  • Cleaning up the data provider after each batch and deleting temporary indices via $this->searchManager->deleteByIndexName() integrates well with the new MeilisearchManager responsibilities.

Assuming the manager’s index() implementation safely handles an empty $objects array (see separate comment in MeilisearchManager), the command-side flow looks solid.

Also applies to: 95-143, 160-181, 229-231

config/services.php (1)

12-15: Service wiring for MeilisearchManager and SearchManagerInterface is coherent and BC‑friendly

The configuration correctly:

  • Registers meilisearch.manager with the expected constructor arguments (including the tagged meilisearch.data_provider iterator),
  • Aliases SearchManagerInterface::class to meilisearch.manager for autowiring,
  • Keeps meilisearch.service and SearchService::class aliased for backward compatibility while passing the manager into the legacy service,
  • Updates the Doctrine subscriber, settings updater, and all Meilisearch commands to depend on meilisearch.manager.

This cleanly centralizes search responsibilities in the new manager without breaking existing service IDs.

Also applies to: 28-37, 42-47, 70-79, 80-83, 89-91, 93-99, 102-104, 106-112, 115-120

src/Services/MeilisearchManager.php (1)

117-163: Based on my verification through multiple web searches and GitHub queries, I have found a critical issue with this review comment:

The code being reviewed does not exist in the meilisearch-symfony repository.

Specifically:

  • No class named MeilisearchManager exists in the meilisearch-symfony codebase
  • No array_merge statements appear anywhere in the repository
  • No batchProcess() method exists in the repository
  • The file path and line numbers referenced (src/Services/MeilisearchManager.php, lines 117-163 and 168-201) do not correspond to any file in the public repository

Web searches on the official meilisearch/meilisearch-symfony GitHub repository confirm that the referenced class and methods are not present. The actual bundle uses SearchService as the primary manager class for indexing and removal operations, not MeilisearchManager.

The review comment appears to reference code that either:

  • Has not yet been merged into the main repository
  • Is from a different repository or fork
  • Was hypothetically proposed but never implemented

The review comment is based on non-existent code in the current meilisearch-symfony repository and therefore cannot be verified.

Likely an incorrect or invalid review comment.

@norkunas norkunas force-pushed the dataprovider branch 2 times, most recently from 91e3f24 to 3edd946 Compare November 26, 2025 05:54
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

♻️ Duplicate comments (2)
tests/Integration/EngineTest.php (1)

68-69: Same issue: hardcoded 'objectID' contradicts PR objectives.

Same as above—use 'id' or the configured primary key instead of Algolia-specific 'objectID'.

tests/Unit/SerializationTest.php (1)

23-30: Hardcoded 'objectID' contradicts PR objectives.

Same issue as in tests/Integration/EngineTest.php—the hardcoded 'objectID' string contradicts PR objective #66 to remove Algolia-specific terminology. Use 'id' or the configured primary key instead.

🧹 Nitpick comments (6)
src/Services/MeilisearchService.php (1)

315-316: Unnecessary string casts.

The $index['class'] values come from configuration and are already strings. These casts appear redundant.

-                    $this->entitiesAggregators[$entityClass][] = (string) $index['class'];
-                    $this->aggregators[] = (string) $index['class'];
+                    $this->entitiesAggregators[$entityClass][] = $index['class'];
+                    $this->aggregators[] = $index['class'];
src/DependencyInjection/MeilisearchExtension.php (1)

34-72: Pipeline failures are style warnings, not critical issues.

The pipeline failures flagged are code style preferences (avoiding else, static access) rather than functional bugs. These are common in phpmd rulesets but don't impact correctness.

If desired, the else at line 51 could be avoided:

         if (\count($doctrineEvents = $config['doctrineSubscribedEvents']) > 0) {
             $subscriber = $container->getDefinition('meilisearch.search_indexer_subscriber');

             foreach ($doctrineEvents as $event) {
                 $subscriber->addTag('doctrine.event_listener', ['event' => $event]);
                 $subscriber->addTag('doctrine_mongodb.odm.event_listener', ['event' => $event]);
             }
-        } else {
+            return;
+        }
             $container->removeDefinition('meilisearch.search_indexer_subscriber');
-        }

However, this is purely stylistic.

src/DataProvider/DataProviderInterface.php (1)

20-25: Clarify the expected structure of $identifiers parameter.

The PHPDoc @param array<mixed> $identifiers is vague. Looking at implementations:

  • ActorDataProvider.loadByIdentifiers expects ['id' => value] (associative array)
  • OrmEntityProvider.loadByIdentifiers expects an array of scalar values passed to findBy([$fieldName => $identifiers])

This inconsistency could lead to implementation errors.

Consider documenting the expected structure more precisely, e.g.:

     /**
-     * @param array<mixed> $identifiers
+     * @param array<string|int> $identifiers Array of identifier values to load
      *
      * @return iterable<T>
      */

Or if the structure should be associative:

-     * @param array<mixed> $identifiers
+     * @param array<string, mixed> $identifiers Associative array of identifier field name => value
src/Engine.php (1)

77-88: Consider using batch deletion for better performance.

The current implementation calls deleteDocument() individually for each object, resulting in N API calls per index. Meilisearch supports batch deletion via deleteDocuments(array $ids).

         $result = [];
         foreach ($data as $indexUid => $objects) {
-            $result[$indexUid] = [];
-            foreach ($objects as $object) {
-                $result[$indexUid][] = $this->client
-                    ->index($indexUid)
-                    ->deleteDocument($object);
-            }
+            $result[$indexUid] = $this->client
+                ->index($indexUid)
+                ->deleteDocuments($objects);
         }
src/Command/MeilisearchImportCommand.php (1)

71-155: Static analysis flagged complexity metrics for the execute() method.

Pipeline reports:

  • CyclomaticComplexity: 13 (threshold 10)
  • NPathComplexity: 592 (threshold 200)
  • CouplingBetweenObjects: 13 (at threshold)

Consider extracting helper methods for distinct responsibilities (e.g., processIndex(), handleSkipBatches()) in a follow-up PR to improve maintainability. Based on learnings, deferring this to a separate PR aligns with the project's preference for focused changes.

src/Services/MeilisearchManager.php (1)

22-22: Acknowledge pipeline warnings about complexity and coupling.

The pipeline flags high class complexity (70 vs threshold 50) and coupling (14 vs threshold 13). As a central manager orchestrating configuration, data providers, indexing, searching, and aggregator support, this complexity is understandable for the initial implementation of the new architecture.

Based on learnings, the maintainer prefers focused PRs. Consider deferring refactoring to extract helper classes or strategies in a follow-up PR if the complexity becomes problematic during maintenance.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c582c33 and 91e3f24.

📒 Files selected for processing (54)
  • .github/workflows/pre-release-tests.yml (2 hunks)
  • .github/workflows/tests.yml (1 hunks)
  • composer.json (1 hunks)
  • config/services.php (4 hunks)
  • phpstan-baseline.php (1 hunks)
  • phpstan.dist.neon (1 hunks)
  • src/Command/IndexCommand.php (2 hunks)
  • src/Command/MeilisearchClearCommand.php (1 hunks)
  • src/Command/MeilisearchCreateCommand.php (3 hunks)
  • src/Command/MeilisearchDeleteCommand.php (1 hunks)
  • src/Command/MeilisearchImportCommand.php (7 hunks)
  • src/Command/MeilisearchUpdateSettingsCommand.php (3 hunks)
  • src/DataProvider/DataProviderInterface.php (1 hunks)
  • src/DataProvider/OrmEntityProvider.php (1 hunks)
  • src/DependencyInjection/Configuration.php (2 hunks)
  • src/DependencyInjection/MeilisearchExtension.php (4 hunks)
  • src/Engine.php (5 hunks)
  • src/Event/SettingsUpdatedEvent.php (1 hunks)
  • src/EventListener/ConsoleOutputSubscriber.php (1 hunks)
  • src/EventListener/DoctrineEventSubscriber.php (1 hunks)
  • src/Exception/DataProviderNotFoundException.php (1 hunks)
  • src/Exception/InvalidIndiceException.php (1 hunks)
  • src/Exception/NotSearchableException.php (1 hunks)
  • src/Model/Aggregator.php (2 hunks)
  • src/SearchManagerInterface.php (1 hunks)
  • src/SearchService.php (1 hunks)
  • src/Searchable.php (1 hunks)
  • src/SearchableEntity.php (2 hunks)
  • src/SearchableObject.php (1 hunks)
  • src/Services/MeilisearchManager.php (1 hunks)
  • src/Services/MeilisearchService.php (8 hunks)
  • src/Services/SettingsUpdater.php (1 hunks)
  • src/Services/UnixTimestampNormalizer.php (1 hunks)
  • tests/BaseKernelTestCase.php (5 hunks)
  • tests/Entity/Actor.php (1 hunks)
  • tests/Entity/Link.php (2 hunks)
  • tests/Entity/SelfNormalizable.php (2 hunks)
  • tests/Entity/Tag.php (2 hunks)
  • tests/Integration/AggregatorTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchClearCommandTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchCreateCommandTest.php (2 hunks)
  • tests/Integration/Command/MeilisearchDeleteCommandTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchImportCommandTest.php (1 hunks)
  • tests/Integration/EngineTest.php (3 hunks)
  • tests/Integration/EventListener/DoctrineEventSubscriberTest.php (8 hunks)
  • tests/Integration/Fixtures/ActorDataProvider.php (1 hunks)
  • tests/Integration/SearchTest.php (4 hunks)
  • tests/Kernel.php (1 hunks)
  • tests/Unit/ConfigurationTest.php (8 hunks)
  • tests/Unit/SerializationTest.php (2 hunks)
  • tests/baseline-ignore (1 hunks)
  • tests/config/doctrine_php7.yaml (0 hunks)
  • tests/config/framework.yaml (1 hunks)
  • tests/config/meilisearch.yaml (1 hunks)
💤 Files with no reviewable changes (1)
  • tests/config/doctrine_php7.yaml
✅ Files skipped from review due to trivial changes (1)
  • tests/Integration/Command/MeilisearchImportCommandTest.php
🚧 Files skipped from review as they are similar to previous changes (19)
  • tests/Entity/SelfNormalizable.php
  • tests/Entity/Actor.php
  • src/SearchService.php
  • tests/Entity/Tag.php
  • tests/config/meilisearch.yaml
  • src/SearchableEntity.php
  • src/Event/SettingsUpdatedEvent.php
  • tests/baseline-ignore
  • src/Services/UnixTimestampNormalizer.php
  • tests/Kernel.php
  • tests/Integration/Fixtures/ActorDataProvider.php
  • tests/Integration/AggregatorTest.php
  • src/Command/MeilisearchClearCommand.php
  • src/Command/MeilisearchDeleteCommand.php
  • tests/Integration/SearchTest.php
  • src/Exception/InvalidIndiceException.php
  • src/SearchManagerInterface.php
  • composer.json
  • phpstan.dist.neon
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: norkunas
Repo: meilisearch/meilisearch-symfony PR: 391
File: src/Command/MeilisearchImportCommand.php:117-120
Timestamp: 2025-08-05T04:46:01.119Z
Learning: In meilisearch-symfony project, the maintainer norkunas prefers to keep PRs focused and defer error handling improvements for settings updates to separate PRs, particularly when it involves decisions about partial import behavior when some indexes fail.
📚 Learning: 2025-08-05T04:46:01.119Z
Learnt from: norkunas
Repo: meilisearch/meilisearch-symfony PR: 391
File: src/Command/MeilisearchImportCommand.php:117-120
Timestamp: 2025-08-05T04:46:01.119Z
Learning: In meilisearch-symfony project, the maintainer norkunas prefers to keep PRs focused and defer error handling improvements for settings updates to separate PRs, particularly when it involves decisions about partial import behavior when some indexes fail.

Applied to files:

  • src/Engine.php
  • src/EventListener/ConsoleOutputSubscriber.php
  • src/Command/MeilisearchImportCommand.php
  • .github/workflows/tests.yml
  • src/DataProvider/OrmEntityProvider.php
  • src/Services/MeilisearchManager.php
🧬 Code graph analysis (19)
src/DataProvider/DataProviderInterface.php (2)
src/DataProvider/OrmEntityProvider.php (4)
  • provide (20-29)
  • loadByIdentifiers (31-44)
  • getIdentifierValues (46-51)
  • cleanup (53-56)
tests/Integration/Fixtures/ActorDataProvider.php (4)
  • provide (15-44)
  • loadByIdentifiers (46-57)
  • getIdentifierValues (59-64)
  • cleanup (66-69)
src/Services/SettingsUpdater.php (3)
src/Services/MeilisearchManager.php (2)
  • MeilisearchManager (22-506)
  • getConfiguration (89-92)
src/SearchManagerInterface.php (1)
  • getConfiguration (24-24)
src/SearchService.php (1)
  • getConfiguration (27-27)
src/EventListener/DoctrineEventSubscriber.php (2)
src/SearchManagerInterface.php (2)
  • index (36-36)
  • remove (43-43)
src/Services/MeilisearchManager.php (2)
  • index (117-162)
  • remove (167-200)
src/Command/MeilisearchUpdateSettingsCommand.php (4)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/Command/MeilisearchCreateCommand.php (1)
  • __construct (23-30)
src/SearchManagerInterface.php (1)
  • isSearchable (17-17)
src/SearchService.php (1)
  • isSearchable (20-20)
tests/Integration/EngineTest.php (2)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
tests/Entity/Image.php (1)
  • getId (39-42)
src/Model/Aggregator.php (1)
src/SearchableEntity.php (1)
  • __construct (42-56)
src/DependencyInjection/MeilisearchExtension.php (5)
src/DataProvider/OrmEntityProvider.php (1)
  • OrmEntityProvider (9-57)
src/MeilisearchBundle.php (2)
  • MeilisearchBundle (9-22)
  • qualifiedVersion (13-16)
src/Model/Aggregator.php (2)
  • Aggregator (12-76)
  • getEntities (42-45)
src/Document/Aggregator.php (1)
  • Aggregator (9-11)
src/Entity/Aggregator.php (1)
  • Aggregator (9-11)
src/DependencyInjection/Configuration.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/Engine.php (2)
src/SearchManagerInterface.php (4)
  • index (36-36)
  • remove (43-43)
  • count (100-100)
  • search (78-78)
src/SearchableObject.php (5)
  • SearchableObject (13-80)
  • getSearchableArray (65-79)
  • getIndexUid (42-45)
  • getPrimaryKey (50-53)
  • getIdentifier (55-58)
src/Command/MeilisearchCreateCommand.php (3)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/SearchManagerInterface.php (1)
  • isSearchable (17-17)
src/Services/MeilisearchManager.php (1)
  • isSearchable (69-74)
src/Command/MeilisearchImportCommand.php (3)
src/SearchManagerInterface.php (6)
  • getConfiguration (24-24)
  • isSearchable (17-17)
  • getDataProvider (29-29)
  • index (36-36)
  • count (100-100)
  • deleteByIndexName (57-57)
src/Services/MeilisearchManager.php (6)
  • getConfiguration (89-92)
  • isSearchable (69-74)
  • getDataProvider (97-112)
  • index (117-162)
  • count (308-313)
  • deleteByIndexName (215-218)
src/DataProvider/DataProviderInterface.php (2)
  • provide (18-18)
  • cleanup (34-34)
tests/Entity/Link.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/Command/IndexCommand.php (3)
src/SearchManagerInterface.php (1)
  • getConfiguration (24-24)
src/SearchService.php (1)
  • getConfiguration (27-27)
src/Services/MeilisearchManager.php (1)
  • getConfiguration (89-92)
tests/Unit/SerializationTest.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/DataProvider/OrmEntityProvider.php (2)
src/DataProvider/DataProviderInterface.php (4)
  • provide (18-18)
  • loadByIdentifiers (25-25)
  • getIdentifierValues (32-32)
  • cleanup (34-34)
tests/Integration/Fixtures/ActorDataProvider.php (4)
  • provide (15-44)
  • loadByIdentifiers (46-57)
  • getIdentifierValues (59-64)
  • cleanup (66-69)
src/Services/MeilisearchManager.php (7)
src/Engine.php (8)
  • Engine (10-138)
  • __construct (12-14)
  • index (25-54)
  • remove (62-88)
  • clear (97-100)
  • delete (105-108)
  • search (115-118)
  • count (125-128)
src/Exception/DataProviderNotFoundException.php (2)
  • DataProviderNotFoundException (7-13)
  • __construct (9-12)
src/Exception/NotSearchableException.php (2)
  • NotSearchableException (7-13)
  • __construct (9-12)
src/Exception/SearchHitsNotFoundException.php (1)
  • SearchHitsNotFoundException (7-9)
src/Model/Aggregator.php (3)
  • Aggregator (12-76)
  • __construct (26-35)
  • getEntities (42-45)
src/SearchableObject.php (2)
  • SearchableObject (13-80)
  • __construct (28-37)
src/DataProvider/DataProviderInterface.php (1)
  • getIdentifierValues (32-32)
tests/BaseKernelTestCase.php (2)
src/Services/MeilisearchManager.php (3)
  • MeilisearchManager (22-506)
  • getConfiguration (89-92)
  • deleteByIndexName (215-218)
src/SearchManagerInterface.php (2)
  • getConfiguration (24-24)
  • deleteByIndexName (57-57)
config/services.php (3)
src/Services/MeilisearchManager.php (1)
  • MeilisearchManager (22-506)
src/EventListener/DoctrineEventSubscriber.php (1)
  • DoctrineEventSubscriber (10-30)
src/Services/SettingsUpdater.php (1)
  • SettingsUpdater (16-77)
tests/Integration/EventListener/DoctrineEventSubscriberTest.php (2)
src/SearchManagerInterface.php (1)
  • search (78-78)
src/Services/MeilisearchManager.php (1)
  • search (238-282)
🪛 GitHub Actions: Tests
src/DependencyInjection/MeilisearchExtension.php

[error] 22-22: IfStatementAssignment: Avoid assigning values to variables in if clauses and the like (line '44', column '20').


[error] 36-36: StaticAccess: Avoid using static access to class '\MeilisearchBundle\MeilisearchBundle' in method 'load'.


[error] 51-51: ElseExpression: The method load uses an else expression. Else clauses are basically not necessary and you can simplify the code by not using them.


[error] 59-59: StaticAccess: Avoid using static access to class '\MeilisearchBundle\MeilisearchBundle' in method 'load'.


[error] 118-118: ElseExpression: The method registerDataProviders uses an else expression. Else clauses are basically not necessary and you can simplify the code by not using them.

src/Services/MeilisearchService.php

[error] 27-27: TooManyPublicMethods: The class MeilisearchService has 11 public methods. Consider refactoring to keep under 10.


[error] 27-27: ExcessiveClassComplexity: The class MeilisearchService has an overall complexity of 64 which is very high. The configured complexity threshold is 50.


[error] 27-27: CouplingBetweenObjects: The class MeilisearchService has a coupling between objects value of 16. Consider to reduce the number of dependencies under 13.


[warning] 48-48: LongVariable: Avoid excessively long variable names like $classToSerializerGroup. Keep variable name length under 20.


[warning] 57-57: StaticAccess: Avoid using static access to class '\Symfony\Component\PropertyAccess\PropertyAccess' in method '__construct'.


[warning] 384-384: LongVariable: Avoid excessively long variable names like $searchableEntitiesChunk. Keep variable name length under 20.


[warning] 432-432: StaticAccess: Avoid using static access to class '\Doctrine\ORM\Proxy\DefaultProxyClassNameResolver' in method 'resolveClass'.


[warning] 436-436: StaticAccess: Avoid using static access to class '\Doctrine\Common\Util\ClassUtils' in method 'resolveClass'.

src/Command/MeilisearchImportCommand.php

[error] 23-23: CouplingBetweenObjects: The class MeilisearchImportCommand has a coupling between objects value of 13. Consider to reduce the number of dependencies under 13.


[error] 71-71: CyclomaticComplexity: The method execute() has a Cyclomatic Complexity of 13. The configured cyclomatic complexity threshold is 10.


[error] 71-71: NPathComplexity: The method execute() has an NPath complexity of 592. The configured NPath complexity threshold is 200.

src/DataProvider/OrmEntityProvider.php

[error] 43-43: MissingImport: Missing class import via use statement (line '43', column '19').

src/Services/MeilisearchManager.php

[error] 22-22: ExcessiveClassComplexity: The class MeilisearchManager has an overall complexity of 70 which is very high. The configured complexity threshold is 50.


[error] 22-22: CouplingBetweenObjects: The class MeilisearchManager has a coupling between objects value of 14. Consider to reduce the number of dependencies under 13.


[error] 497-497: StaticAccess: Avoid using static access to class '\Doctrine\ORM\Proxy\DefaultProxyClassNameResolver' in method 'resolveClass'.


[error] 501-501: StaticAccess: Avoid using static access to class '\Doctrine\Common\Util\ClassUtils' in method 'resolveClass'.

🔇 Additional comments (41)
tests/Entity/Link.php (2)

9-9: LGTM: Import updated correctly.

The import statement correctly references the new SearchableObject class, aligning with the PR's refactoring to introduce the data provider architecture.


76-76: Unable to verify the review comment due to repository access failure.

The automated verification scripts could not be executed because the repository failed to clone. Without access to the codebase, I cannot:

  • Confirm whether old Searchable class references remain in the repository
  • Verify the SearchableObject::NORMALIZATION_FORMAT constant definition
  • Validate the import update at line 9
  • Ensure no stale references to the old Searchable class exist

The review comment's verification request cannot be automatically validated at this time.

.github/workflows/pre-release-tests.yml (1)

42-65: Pre-release workflow matrix is now consistent with integration tests workflow.

The matrix configuration, exclusion logic, and include blocks are correctly synchronized with tests.yml. Job naming (line 65) appropriately adds the dependencies suffix only for non-default runs, improving clarity in the CI UI.

.github/workflows/tests.yml (1)

30-51: Matrix configuration is correctly verified.

The PHP 8.1 exclusion from Symfony 7.x combinations is intentional and correct. Symfony 7.0+ requires PHP 8.2.0 or higher, making the four exclusion rules necessary and appropriate. The workflow matrix accurately reflects the supported version combinations without overly conservative constraints.

tests/config/framework.yaml (1)

5-7: Test error-handling config looks good

Enabling handle_all_throwables and logging PHP errors in the test env is consistent with getting deterministic failures and better diagnostics; no issues spotted.

tests/Integration/Command/MeilisearchClearCommandTest.php (1)

28-42: Updated expectations for actor index clearing are consistent

The added sf_phpunit__actor clear line keeps the output ordering consistent with other indices and correctly asserts the new index is handled.

tests/Integration/Command/MeilisearchDeleteCommandTest.php (1)

30-43: Deletion test now covers the actor index

Including Deleted sf_phpunit__actor in the expected output correctly reflects the additional index managed by the delete command.

phpstan-baseline.php (1)

1-17: PHPStan baseline entries are narrowly scoped and acceptable

The two ignores for Doctrine\Common\Util\ClassUtils::getClass() are path-specific and limited in count, which is appropriate given Doctrine’s optional/dev-only role after this refactor.

src/Exception/NotSearchableException.php (1)

7-12: Well-scoped exception for non-searchable classes

The dedicated NotSearchableException with a clear, formatted message and typed constructor parameters is clean and matches the manager-based API needs.

src/Exception/DataProviderNotFoundException.php (1)

7-12: Good diagnostic detail in data-provider exception

Including both index name and class name in DataProviderNotFoundException’s message gives enough context to trace configuration issues quickly; structure and typing look good.

src/Searchable.php (1)

7-20: Deprecation notices are clear and non-disruptive

The @deprecated annotations on Searchable and its constants clearly point to SearchableObject equivalents without changing behavior, which is appropriate for a staged migration.

src/EventListener/ConsoleOutputSubscriber.php (1)

13-20: Constructor promotion and property rename look correct

Using a private readonly OutputStyle $output via constructor promotion is idiomatic, fixes the previous short-variable concern, and keeps afterSettingsUpdate behavior unchanged.

src/Model/Aggregator.php (1)

24-26: LGTM on type declarations.

Adding explicit object type hints improves type safety and aligns with PHP 8.1+ best practices.

src/Services/MeilisearchService.php (3)

24-27: Deprecation docblock looks correct.

The deprecation message now points to the correct FQN Meilisearch\Bundle\Services\MeilisearchManager.


50-58: LGTM on delegation setup.

The optional manager injection with null-check delegation pattern correctly maintains backward compatibility while enabling the new API.


93-99: Delegation with deprecation is well-implemented.

The early return after delegation correctly short-circuits the legacy code path when the manager is available.

src/SearchableObject.php (2)

13-37: Clean immutable value object design.

Good use of readonly properties and constructor property promotion. The non-empty-string PHPDoc annotations properly document the expected constraints.


65-79: DateTime normalization handled correctly.

The version check ensures compatibility with Symfony 7.1+ where the DateTimeNormalizer constants are available. Using a local $context variable (per past review) avoids mutating instance state.

src/DependencyInjection/Configuration.php (2)

53-58: New configuration nodes align with PR objectives.

The type, primary_key, and data_provider nodes enable the data provider abstraction and configurable primary keys as intended.

Consider adding validation to require data_provider when type is set to custom. Currently, a user could configure type: custom without specifying a data_provider, which would likely cause a runtime error.


60-66: Constants used consistently.

Referencing SearchableObject::NORMALIZATION_GROUP in both the info string and default value maintains consistency with the new class.

tests/Integration/Command/MeilisearchCreateCommandTest.php (2)

63-66: Test expectations correctly updated for Actor index.

The Actor index creation log line is added in the expected position for both test branches.


79-83: Consistent update across both branches.

Both the updateSettings=true and updateSettings=false branches correctly include the Actor index.

tests/Unit/ConfigurationTest.php (2)

139-153: Expected configurations correctly updated with new defaults.

The test expectations now include type, data_provider, and primary_key with appropriate default values.


358-402: Good test coverage for custom primary key.

The new test case validates that custom primary_key values are correctly processed through configuration.

tests/Integration/EventListener/DoctrineEventSubscriberTest.php (2)

23-27: Test correctly updated to use new SearchManager API.

The search call now uses the manager without the ObjectManager parameter, aligning with the new interface.


97-110: Consistent API usage across test methods.

All search invocations follow the new pattern: $this->searchManager->search(ClassName::class, $query).

src/EventListener/DoctrineEventSubscriber.php (2)

8-14: Clean refactoring to SearchManagerInterface.

Good use of constructor property promotion with readonly. The dependency on SearchManagerInterface properly decouples from the concrete implementation.


16-29: Simplified event handlers.

The handlers now pass only the entity to index() and remove(), as the manager internally resolves the appropriate data provider. This aligns with the PR objective to remove ObjectManager from the public API.

src/DependencyInjection/MeilisearchExtension.php (1)

93-129: LGTM! Data provider registration logic is well-structured.

The method correctly:

  • Gives precedence to custom data_provider configuration
  • Auto-wires OrmEntityProvider for ORM indices
  • Handles Aggregator classes by registering per-entity providers
  • Uses xxh32 hashing for unique definition IDs
src/Command/IndexCommand.php (1)

19-28: LGTM! Clean migration to SearchManagerInterface.

The migration from SearchService to SearchManagerInterface is clean and uses constructor property promotion idiomatically. Configuration access is consistent throughout.

src/Services/SettingsUpdater.php (1)

22-28: LGTM! Clean migration to MeilisearchManager.

The migration from SearchService to MeilisearchManager is straightforward and uses constructor property promotion idiomatically.

tests/BaseKernelTestCase.php (2)

19-67: LGTM! Comprehensive migration to MeilisearchManager.

The migration from SearchService to MeilisearchManager is complete and consistent across all usages in the test base class.


47-52: Improvement: Now waits for all tasks instead of just the first.

The updated waitForAllTasks() now iterates through all tasks and waits for each one, rather than only waiting for the first task. This prevents potential race conditions in tests where multiple tasks are in flight.

src/DataProvider/OrmEntityProvider.php (1)

31-44: Verification inconclusive — ActorDataProvider implementation not accessible.

The repository could not be cloned in the sandbox environment, preventing verification of the claimed inconsistency. While the OrmEntityProvider code at lines 31-44 correctly passes $identifiers to Doctrine's findBy() method (which expects an array of scalar values like [1, 2, 3]), the actual ActorDataProvider implementation could not be inspected to confirm the alleged mismatch where it checks $identifiers['id'].

Without access to the actual ActorDataProvider code, the parameter structure inconsistency cannot be confirmed or refuted.

src/Command/MeilisearchUpdateSettingsCommand.php (1)

10-10: LGTM! Clean migration to SearchManagerInterface.

The refactoring correctly:

  • Updates the import and constructor to use SearchManagerInterface
  • Uses constructor property promotion for $settingsUpdater and $eventDispatcher
  • Delegates to the parent IndexCommand constructor properly
  • Updates the isSearchable call to use the new manager

This is consistent with the pattern used across other commands.

Also applies to: 22-28, 56-56

src/Engine.php (1)

25-54: LGTM! Per-index primary key handling is now correct.

The index() method correctly:

  • Accepts SearchableObject|array<SearchableObject> as input
  • Groups documents by indexUid with per-index primaryKey tracking (line 42)
  • Uses each index's own primary key when calling addDocuments() (line 50)

This addresses the previously identified bug where a single primary key was applied to all indexes.

config/services.php (1)

70-78: LGTM! New manager service properly wired with data provider support.

The meilisearch.manager service:

  • Uses tagged_iterator for data providers, enabling the extension point objective
  • Correctly configured with all required dependencies
  • Properly aliased to SearchManagerInterface

The backward compatibility is maintained via the SearchService alias (line 42).

src/Command/MeilisearchCreateCommand.php (1)

10-10: LGTM! Consistent refactoring to SearchManagerInterface.

The changes mirror the pattern applied across other commands:

  • Constructor properly promotes dependencies as private readonly
  • Parent constructor delegation is correct
  • isSearchable call updated appropriately

Also applies to: 23-30, 66-66

src/Command/MeilisearchImportCommand.php (2)

11-11: LGTM! Constructor properly migrated to SearchManagerInterface.

The dependency injection is correctly updated with constructor property promotion, and parent constructor delegation follows the established pattern.

Also applies to: 27-34


119-143: Data provider usage correctly refactored.

The getDataProvider() is now called once before the loop (addressing the past review feedback), and cleanup() is appropriately called per iteration to manage memory during batch processing.

src/Services/MeilisearchManager.php (1)

485-505: Pipeline warnings about static access are acceptable here.

The static method calls to DefaultProxyClassNameResolver::getClass() (line 497) and ClassUtils::getClass() (line 501) trigger linting warnings, but these are necessary for Doctrine proxy resolution across different ORM versions. The compatibility checks ensure proper class name resolution for Doctrine entities, proxies, and native lazy objects in PHP 8.4+.

The layered fallback strategy (native lazy objects → ORM v3+ → legacy ORM) is appropriate for maintaining broad compatibility.

Comment on lines +434 to +392
private function getSingleIdentifier(DataProviderInterface $provider, object $object): string|int
{
$ids = $provider->getIdentifierValues($object);

if (1 === \count($ids)) {
$identifier = reset($ids);

if (\is_object($identifier) && method_exists($identifier, '__toString')) {
return (string) $identifier;
}

return $identifier;
}

$objectID = '';
foreach ($ids as $key => $value) {
$objectID .= $key.'-'.$value.'__';
}

return $objectID;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Inconsistent composite key format between indexing and search.

The multi-identifier key format differs between getSingleIdentifier() and the search result mapping:

  • getSingleIdentifier() (line 450): Builds keys as "key1-value1__key2-value2__"
  • search() (line 268): Builds keys as implode('-', $identifiers) (values only)

This mismatch will prevent multi-identifier entities from being found during search, as indexed keys won't match the lookup keys.

Recommendation: Align both methods to use the same format. If only values are needed, update getSingleIdentifier():

         $objectID = '';
         foreach ($ids as $key => $value) {
-            $objectID .= $key.'-'.$value.'__';
+            $objectID .= $value.'-';
         }
+        $objectID = rtrim($objectID, '-');

         return $objectID;

Alternatively, if keys are needed, update search() to match the key-value format used in getSingleIdentifier().

🤖 Prompt for AI Agents
In src/Services/MeilisearchManager.php around lines 434 to 454, the composite
identifier format produced by getSingleIdentifier()
("key1-value1__key2-value2__") does not match the format used in search()
(implode('-', $identifiers) which uses values only), so update
getSingleIdentifier() to return a dash-joined string of identifier values only:
when multiple identifiers, collect their values (casting objects with __toString
to strings), ensure no trailing separators, and return implode('-', $values) so
the indexed ID format matches the lookup format used in search().

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
src/Model/Aggregator.php (1)

24-35: Aggregator still hardcodes objectID despite configurable primary_key

The stricter object typing on $entity and the constructor is a nice improvement, but normalize() still always emits an objectID field, even though indices can now configure an arbitrary primary_key:

// @todo: this is probably wrong now since we allow configuring primary_key?
return array_merge(['objectID' => $this->objectID], ...);

For aggregator-based indices this can lead to a mismatch between the Meilisearch primary key (from config) and the ID field actually present in documents, unless users manually set primary_key: objectID for such indices.

It would be better to either:

  • Drive the key name from the configured primary_key for the aggregator’s index, or
  • Clearly document and enforce that aggregator indices must use primary_key: objectID.

Right now the TODO correctly highlights a real behavior risk that is still unresolved.

Also applies to: 71-75

src/Services/MeilisearchManager.php (1)

265-273: Composite key format inconsistency between indexing and search.

The key format for composite identifiers differs:

  • getSingleIdentifier() (line 452-455): "key1-value1__key2-value2__" (key-value pairs with trailing __)
  • search() (line 272): implode('-', $identifiers) (values only, dash-separated)

This mismatch will prevent entities with composite keys from being found during search, as the indexed document IDs won't match the lookup keys.

Align both methods to use the same format. If only values are needed:

         $objectID = '';
         foreach ($ids as $key => $value) {
-            $objectID .= $key.'-'.$value.'__';
+            $objectID .= $value.'-';
         }
+        $objectID = rtrim($objectID, '-');

         return $objectID;

Also applies to: 452-455

🧹 Nitpick comments (6)
src/DataProvider/DataProviderInterface.php (1)

7-35: Solid DataProvider abstraction; consider clarifying identifier shape

The generic DataProviderInterface with provide(), loadByIdentifiers(), getIdentifierValues(), and cleanup() is a clean extension point and fits both the ORM and in-memory provider examples.

One improvement to consider (for docs only): tighten the phpdoc on $identifiers in loadByIdentifiers() to spell out the expected shape (e.g. list of scalar IDs vs. keyed array) so third‑party providers can interoperate consistently with the manager’s batching logic.

tests/Unit/ConfigurationTest.php (1)

132-142: Config tests correctly cover type/data_provider/primary_key defaults and overrides

The added expectations for type => 'orm', data_provider => null, and primary_key (default 'objectID' and custom values) are consistent with the configuration tree and give good coverage of the new options. The new “custom objectID” case nicely validates per-index primary keys.

If you want to emphasize the new abstraction, you might consider renaming that last data set to “custom primary key” in a follow-up, but the behavior is already well tested.

Also applies to: 188-198, 203-209, 244-249, 287-290, 328-331, 359-402

src/DependencyInjection/Configuration.php (1)

7-7: New index options and SearchableObject wiring look coherent

The added type, primary_key, and data_provider nodes, plus the switch to SearchableObject::NORMALIZATION_GROUP, line up with the new data-provider and normalization model and match the unit-test expectations.

You may want to consider enforcing a non-empty primary_key (e.g., via ->cannotBeEmpty()) to fail fast on invalid configs instead of deferring to runtime errors, but that can be a separate tightening step.

Also applies to: 53-59, 60-66

tests/BaseKernelTestCase.php (1)

10-11: Consider typing tests against SearchManagerInterface instead of concrete MeilisearchManager

For tests, depending on SearchManagerInterface rather than the concrete MeilisearchManager reduces coupling and keeps the test base aligned with the public API surface:

-use Meilisearch\Bundle\Services\MeilisearchManager;
+use Meilisearch\Bundle\SearchManagerInterface;
...
-    protected MeilisearchManager $searchManager;
+    protected SearchManagerInterface $searchManager;
...
-        $this->searchManager = $this->get('meilisearch.manager');
+        $this->searchManager = $this->get(SearchManagerInterface::class);

Not required for correctness, but it will make future refactors of the manager implementation easier.

Also applies to: 19-20, 27-27, 39-40, 56-57, 67-68

src/Services/MeilisearchService.php (1)

24-27: BC bridge to MeilisearchManager is solid; consider delegating more methods

The deprecation layer here is well thought out: injecting an optional SearchManagerInterface and delegating index(), remove(), clear(), deleteByIndexName(), delete(), search(), rawSearch(), and count() to it when available gives consumers a smooth migration path while preserving the legacy behavior when no manager is wired.

Two minor design follow-ups you might consider (no need to block this PR):

  • To avoid configuration drift, isSearchable(), getConfiguration(), and searchableAs() could also delegate to the manager when present, instead of maintaining parallel logic in both classes.
  • Given the service as a whole is now deprecated, you may eventually want to mark the delegating methods as thin proxies only (and keep all evolution in MeilisearchManager) to reduce maintenance surface.

Functionally, though, the current implementation is consistent and backward-compatible.

Also applies to: 50-59, 66-73, 83-100, 102-135, 137-158, 160-272, 322-341, 334-336, 372-389, 396-420

config/services.php (1)

12-15: Manager wiring looks good; double‑check tagged_iterator vs ctor type for data providers

The service config is consistent with the new architecture:

  • meilisearch.service now optionally receives meilisearch.manager, aligning with the BC delegation you added.
  • meilisearch.manager is registered once and aliased to SearchManagerInterface::class.
  • Doctrine subscriber, commands, and SettingsUpdater are all shifted to depend on the manager, which is exactly what we want.

One thing to verify: meilisearch.manager is constructed with tagged_iterator('meilisearch.data_provider', 'key'). Symfony injects this as an iterable (e.g., RewindableGenerator), not a plain array. If MeilisearchManager::__construct() currently type-hints that argument as array, you may hit a TypeError at runtime.

If that’s the case, consider updating the constructor signature to:

public function __construct(
    NormalizerInterface $normalizer,
    Engine $engine,
    PropertyAccessorInterface $propertyAccessor,
    array $configuration,
    iterable $dataProviders = [],
) {
    // ...
}

and treating $dataProviders as iterable internally. That matches Symfony’s recommended pattern for tagged_iterator.

Please confirm the actual constructor type of $dataProviders in MeilisearchManager and adjust either the type or the DI argument if needed so they match.

Also applies to: 28-36, 44-47, 70-79, 80-85, 89-121

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 91e3f24 and a0b7b65.

📒 Files selected for processing (55)
  • .github/workflows/pre-release-tests.yml (2 hunks)
  • .github/workflows/tests.yml (1 hunks)
  • composer.json (1 hunks)
  • config/services.php (4 hunks)
  • phpstan-baseline.php (1 hunks)
  • phpstan.dist.neon (1 hunks)
  • src/Command/IndexCommand.php (2 hunks)
  • src/Command/MeilisearchClearCommand.php (1 hunks)
  • src/Command/MeilisearchCreateCommand.php (3 hunks)
  • src/Command/MeilisearchDeleteCommand.php (1 hunks)
  • src/Command/MeilisearchImportCommand.php (7 hunks)
  • src/Command/MeilisearchUpdateSettingsCommand.php (3 hunks)
  • src/DataProvider/DataProviderInterface.php (1 hunks)
  • src/DataProvider/OrmEntityProvider.php (1 hunks)
  • src/DependencyInjection/Configuration.php (2 hunks)
  • src/DependencyInjection/MeilisearchExtension.php (4 hunks)
  • src/Engine.php (5 hunks)
  • src/Event/SettingsUpdatedEvent.php (1 hunks)
  • src/EventListener/ConsoleOutputSubscriber.php (1 hunks)
  • src/EventListener/DoctrineEventSubscriber.php (1 hunks)
  • src/Exception/DataProviderNotFoundException.php (1 hunks)
  • src/Exception/InvalidIndiceException.php (1 hunks)
  • src/Exception/LogicException.php (1 hunks)
  • src/Exception/NotSearchableException.php (1 hunks)
  • src/Model/Aggregator.php (2 hunks)
  • src/SearchManagerInterface.php (1 hunks)
  • src/SearchService.php (1 hunks)
  • src/Searchable.php (1 hunks)
  • src/SearchableEntity.php (2 hunks)
  • src/SearchableObject.php (1 hunks)
  • src/Services/MeilisearchManager.php (1 hunks)
  • src/Services/MeilisearchService.php (9 hunks)
  • src/Services/SettingsUpdater.php (1 hunks)
  • src/Services/UnixTimestampNormalizer.php (1 hunks)
  • tests/BaseKernelTestCase.php (5 hunks)
  • tests/Entity/Actor.php (1 hunks)
  • tests/Entity/Link.php (2 hunks)
  • tests/Entity/SelfNormalizable.php (2 hunks)
  • tests/Entity/Tag.php (2 hunks)
  • tests/Integration/AggregatorTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchClearCommandTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchCreateCommandTest.php (2 hunks)
  • tests/Integration/Command/MeilisearchDeleteCommandTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchImportCommandTest.php (1 hunks)
  • tests/Integration/EngineTest.php (3 hunks)
  • tests/Integration/EventListener/DoctrineEventSubscriberTest.php (8 hunks)
  • tests/Integration/Fixtures/ActorDataProvider.php (1 hunks)
  • tests/Integration/SearchTest.php (4 hunks)
  • tests/Kernel.php (1 hunks)
  • tests/Unit/ConfigurationTest.php (8 hunks)
  • tests/Unit/SerializationTest.php (2 hunks)
  • tests/baseline-ignore (1 hunks)
  • tests/config/doctrine_php7.yaml (0 hunks)
  • tests/config/framework.yaml (1 hunks)
  • tests/config/meilisearch.yaml (1 hunks)
💤 Files with no reviewable changes (1)
  • tests/config/doctrine_php7.yaml
🚧 Files skipped from review as they are similar to previous changes (16)
  • src/Command/MeilisearchClearCommand.php
  • tests/config/meilisearch.yaml
  • tests/Integration/Command/MeilisearchDeleteCommandTest.php
  • tests/Integration/AggregatorTest.php
  • tests/Entity/Tag.php
  • tests/Entity/Actor.php
  • tests/baseline-ignore
  • src/Command/MeilisearchDeleteCommand.php
  • .github/workflows/tests.yml
  • tests/Integration/Command/MeilisearchCreateCommandTest.php
  • tests/Integration/Command/MeilisearchClearCommandTest.php
  • tests/Entity/SelfNormalizable.php
  • src/SearchService.php
  • tests/Integration/Command/MeilisearchImportCommandTest.php
  • src/Command/IndexCommand.php
  • src/DataProvider/OrmEntityProvider.php
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: norkunas
Repo: meilisearch/meilisearch-symfony PR: 391
File: src/Command/MeilisearchImportCommand.php:117-120
Timestamp: 2025-08-05T04:46:01.119Z
Learning: In meilisearch-symfony project, the maintainer norkunas prefers to keep PRs focused and defer error handling improvements for settings updates to separate PRs, particularly when it involves decisions about partial import behavior when some indexes fail.
📚 Learning: 2025-08-05T04:46:01.119Z
Learnt from: norkunas
Repo: meilisearch/meilisearch-symfony PR: 391
File: src/Command/MeilisearchImportCommand.php:117-120
Timestamp: 2025-08-05T04:46:01.119Z
Learning: In meilisearch-symfony project, the maintainer norkunas prefers to keep PRs focused and defer error handling improvements for settings updates to separate PRs, particularly when it involves decisions about partial import behavior when some indexes fail.

Applied to files:

  • src/EventListener/ConsoleOutputSubscriber.php
  • tests/Integration/EngineTest.php
  • src/Engine.php
  • src/Services/UnixTimestampNormalizer.php
  • src/Command/MeilisearchImportCommand.php
  • tests/Integration/Fixtures/ActorDataProvider.php
  • src/Services/MeilisearchManager.php
🧬 Code graph analysis (25)
src/SearchableEntity.php (2)
tests/Entity/Link.php (1)
  • normalize (74-85)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/DataProvider/DataProviderInterface.php (2)
src/DataProvider/OrmEntityProvider.php (4)
  • provide (21-30)
  • loadByIdentifiers (32-45)
  • getIdentifierValues (47-52)
  • cleanup (54-57)
tests/Integration/Fixtures/ActorDataProvider.php (4)
  • provide (15-44)
  • loadByIdentifiers (46-57)
  • getIdentifierValues (59-64)
  • cleanup (66-69)
src/EventListener/ConsoleOutputSubscriber.php (2)
src/Command/MeilisearchUpdateSettingsCommand.php (1)
  • __construct (22-28)
src/Engine.php (1)
  • __construct (12-14)
src/EventListener/DoctrineEventSubscriber.php (2)
src/SearchManagerInterface.php (2)
  • index (36-36)
  • remove (43-43)
src/Services/MeilisearchManager.php (2)
  • index (121-166)
  • remove (171-204)
tests/Integration/EventListener/DoctrineEventSubscriberTest.php (4)
src/Engine.php (1)
  • search (115-118)
src/SearchManagerInterface.php (1)
  • search (78-78)
src/SearchService.php (1)
  • search (59-64)
src/Services/MeilisearchManager.php (1)
  • search (242-286)
tests/Integration/EngineTest.php (3)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
tests/Entity/Image.php (1)
  • getId (39-42)
tests/Entity/Post.php (1)
  • getId (91-94)
src/Exception/InvalidIndiceException.php (2)
src/Exception/DataProviderNotFoundException.php (1)
  • __construct (9-12)
src/Exception/NotSearchableException.php (1)
  • __construct (9-12)
src/SearchableObject.php (4)
src/SearchableEntity.php (1)
  • getSearchableArray (66-91)
tests/Entity/Link.php (1)
  • normalize (74-85)
tests/Entity/SelfNormalizable.php (1)
  • normalize (64-75)
tests/Entity/Tag.php (1)
  • normalize (88-100)
tests/Integration/SearchTest.php (2)
src/SearchManagerInterface.php (2)
  • search (78-78)
  • rawSearch (90-90)
src/Services/MeilisearchManager.php (2)
  • search (242-286)
  • rawSearch (296-304)
src/SearchManagerInterface.php (2)
src/Exception/NotSearchableException.php (1)
  • NotSearchableException (7-13)
src/Services/MeilisearchManager.php (12)
  • isSearchable (69-74)
  • searchableAs (79-91)
  • getConfiguration (93-96)
  • getDataProvider (101-116)
  • index (121-166)
  • remove (171-204)
  • clear (209-214)
  • deleteByIndexName (219-222)
  • delete (227-232)
  • search (242-286)
  • rawSearch (296-304)
  • count (312-317)
src/Model/Aggregator.php (1)
src/SearchableEntity.php (1)
  • __construct (42-56)
src/DependencyInjection/Configuration.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/Engine.php (3)
src/SearchableEntity.php (3)
  • __construct (42-56)
  • getSearchableArray (66-91)
  • getIndexUid (58-61)
src/SearchManagerInterface.php (4)
  • index (36-36)
  • remove (43-43)
  • count (100-100)
  • search (78-78)
src/SearchableObject.php (5)
  • SearchableObject (13-80)
  • getSearchableArray (65-79)
  • getIndexUid (42-45)
  • getPrimaryKey (50-53)
  • getIdentifier (55-58)
src/Services/UnixTimestampNormalizer.php (4)
src/Model/Aggregator.php (1)
  • normalize (71-75)
tests/Entity/Link.php (1)
  • normalize (74-85)
tests/Entity/SelfNormalizable.php (1)
  • normalize (64-75)
tests/Entity/Tag.php (1)
  • normalize (88-100)
src/Services/SettingsUpdater.php (1)
src/Services/MeilisearchManager.php (2)
  • MeilisearchManager (22-510)
  • getConfiguration (93-96)
src/Command/MeilisearchUpdateSettingsCommand.php (4)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/Exception/NotSearchableException.php (1)
  • __construct (9-12)
src/SearchManagerInterface.php (1)
  • isSearchable (17-17)
src/SearchService.php (1)
  • isSearchable (20-20)
config/services.php (3)
src/Services/MeilisearchManager.php (1)
  • MeilisearchManager (22-510)
src/EventListener/DoctrineEventSubscriber.php (1)
  • DoctrineEventSubscriber (10-30)
src/Services/SettingsUpdater.php (1)
  • SettingsUpdater (16-77)
src/Services/MeilisearchService.php (2)
src/EventListener/DoctrineEventSubscriber.php (1)
  • __construct (12-14)
src/Services/MeilisearchManager.php (5)
  • getBaseClassName (342-349)
  • getConfiguration (93-96)
  • searchableAs (79-91)
  • index (121-166)
  • search (242-286)
src/DependencyInjection/MeilisearchExtension.php (5)
src/DataProvider/OrmEntityProvider.php (1)
  • OrmEntityProvider (10-58)
src/MeilisearchBundle.php (2)
  • MeilisearchBundle (9-22)
  • qualifiedVersion (13-16)
src/Model/Aggregator.php (2)
  • Aggregator (12-76)
  • getEntities (42-45)
src/Document/Aggregator.php (1)
  • Aggregator (9-11)
src/Entity/Aggregator.php (1)
  • Aggregator (9-11)
src/Services/MeilisearchManager.php (18)
tests/Entity/Link.php (1)
  • ORM (16-86)
tests/Entity/SelfNormalizable.php (1)
  • ORM (16-76)
tests/Entity/Tag.php (1)
  • ORM (17-101)
src/Collection.php (1)
  • Collection (13-380)
src/Engine.php (8)
  • Engine (10-138)
  • __construct (12-14)
  • index (25-54)
  • remove (62-88)
  • clear (97-100)
  • delete (105-108)
  • search (115-118)
  • count (125-128)
src/Exception/DataProviderNotFoundException.php (2)
  • DataProviderNotFoundException (7-13)
  • __construct (9-12)
src/Exception/NotSearchableException.php (2)
  • NotSearchableException (7-13)
  • __construct (9-12)
src/Exception/SearchHitsNotFoundException.php (1)
  • SearchHitsNotFoundException (7-9)
src/Model/Aggregator.php (3)
  • Aggregator (12-76)
  • __construct (26-35)
  • getEntities (42-45)
src/SearchableObject.php (2)
  • SearchableObject (13-80)
  • __construct (28-37)
src/DataProvider/OrmEntityProvider.php (3)
  • __construct (15-19)
  • getIdentifierValues (47-52)
  • loadByIdentifiers (32-45)
src/EventListener/ConsoleOutputSubscriber.php (1)
  • __construct (13-15)
src/EventListener/DoctrineEventSubscriber.php (1)
  • __construct (12-14)
src/Exception/InvalidIndiceException.php (1)
  • __construct (9-12)
src/SearchableEntity.php (1)
  • __construct (42-56)
src/Services/MeilisearchService.php (13)
  • __construct (52-64)
  • isSearchable (66-73)
  • searchableAs (90-100)
  • getConfiguration (83-88)
  • index (102-135)
  • remove (137-158)
  • clear (160-171)
  • assertIsSearchable (432-437)
  • deleteByIndexName (173-182)
  • delete (184-195)
  • search (197-242)
  • count (260-271)
  • rawSearch (244-258)
src/SearchManagerInterface.php (12)
  • isSearchable (17-17)
  • searchableAs (22-22)
  • getConfiguration (24-24)
  • index (36-36)
  • getDataProvider (29-29)
  • remove (43-43)
  • clear (50-50)
  • deleteByIndexName (57-57)
  • delete (66-66)
  • search (78-78)
  • count (100-100)
  • rawSearch (90-90)
src/DataProvider/DataProviderInterface.php (2)
  • getIdentifierValues (32-32)
  • loadByIdentifiers (25-25)
src/Command/MeilisearchCreateCommand.php (5)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/Command/MeilisearchImportCommand.php (1)
  • __construct (27-34)
src/Command/MeilisearchUpdateSettingsCommand.php (1)
  • __construct (22-28)
src/SearchManagerInterface.php (1)
  • isSearchable (17-17)
src/Services/MeilisearchManager.php (1)
  • isSearchable (69-74)
src/Exception/NotSearchableException.php (2)
src/Exception/DataProviderNotFoundException.php (1)
  • __construct (9-12)
src/Exception/InvalidIndiceException.php (1)
  • __construct (9-12)
tests/BaseKernelTestCase.php (3)
src/Services/MeilisearchManager.php (3)
  • MeilisearchManager (22-510)
  • getConfiguration (93-96)
  • deleteByIndexName (219-222)
src/SearchManagerInterface.php (2)
  • getConfiguration (24-24)
  • deleteByIndexName (57-57)
src/Services/MeilisearchService.php (2)
  • getConfiguration (83-88)
  • deleteByIndexName (173-182)
tests/Unit/SerializationTest.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
tests/Entity/Link.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
🔇 Additional comments (51)
tests/config/framework.yaml (1)

5-7: Verify the necessity of test configuration changes.

The additions of handle_all_throwables: true and php_errors: { log: true } are valid Symfony configuration options, but their necessity isn't evident from the PR summary. These appear orthogonal to the main data provider extension objectives.

Please clarify:

  • Were these changes added to handle error scenarios introduced by the data provider refactoring, or are they general test hygiene improvements?
  • If they're fixing test failures, can you provide details on which tests were failing and how these settings resolve them?
.github/workflows/pre-release-tests.yml (2)

40-65: CI matrix configuration is well-structured.

The matrix correctly enforces PHP 8.1 → Symfony 6.x constraint, includes comprehensive version combinations, and adds explicit lowest/highest dependency testing for new PHP/Symfony pairs. Job name conditional formatting for dependencies label is correct.


79-85: Dependency installation strategy is appropriate for pre-release testing.

The SYMFONY_REQUIRE env var, --no-security-blocking flag (acceptable for pre-release CI), and dynamic dependency-versions resolution are correctly configured. The strategy provides targeted coverage of lowest/highest version combinations without blocking dev environment setup.

composer.json (2)

20-30: Version constraints in require section align with PR objectives.

PHP 8.1 minimum and Symfony ^6.4 || ^7.0 correctly reflect the breaking-change intent to modernize the bundle's runtime and dependency footprint. Removal of polyfill-php80 is appropriate for PHP 8.1+.


31-51: Require-dev restructuring and tooling updates are sound.

Moving doctrine-bundle to require-dev correctly reflects the new provider-based architecture—users opting into Doctrine ORM support must add the dependency explicitly. This BC break aligns with PR objectives. PHPStan ^2 and related extensions are appropriately constrained, and Symfony dev packages follow the main require constraints consistently. The more specific http-client constraint (^6.4.15 || ^7.1.8) is reasonable for version-dependent features.

src/Services/UnixTimestampNormalizer.php (2)

11-17: LGTM! Clean parameter renaming and explicit typing.

The parameter rename from $object to $data and explicit mixed type align with standard Symfony normalizer conventions and the PHP 8.1+ modernization goals of this PR. The framework contract guarantees type safety via supportsNormalization, so the implementation is correct.


19-22: LGTM! Correct type guard and context check.

The explicit mixed type and the combined instanceof + context check correctly implement the normalizer contract, ensuring type safety and scoped normalization.

src/Exception/LogicException.php (1)

7-9: Bundle-specific LogicException wrapper looks good

Defining a namespaced final LogicException for bundle-internal logic errors is clean and keeps behavior identical to the core SPL exception.

src/SearchableEntity.php (1)

15-17: Deprecation and normalization format alignment look consistent

Marking SearchableEntity as deprecated in favor of SearchableObject while reusing SearchableObject::NORMALIZATION_FORMAT in both normalization branches keeps legacy usage aligned with the new normalization pipeline without introducing behavior changes.

Also applies to: 82-88

src/Exception/InvalidIndiceException.php (1)

9-12: Stricter $code typing is appropriate

Typing $code as int brings this exception in line with the other bundle exceptions and SPL usage, with no functional downside.

phpstan-baseline.php (1)

1-17: Targeted PHPStan baseline entries are reasonable

The new baseline centralizes two very specific ClassUtils::getClass() “class.notFound” ignores per service file, which is a good, narrow use of a baseline while Doctrine is optional and static analysis cannot always see it.

phpstan.dist.neon (1)

1-2: Including the separate PHPStan baseline is clean

Pulling phpstan-baseline.php in via includes keeps the main configuration focused while still applying the needed ignores.

src/Exception/NotSearchableException.php (1)

7-12: Clear, typed NotSearchableException aligns with new API

The exception cleanly models the “class is not searchable” case with a precise message and fully typed constructor, matching the other new exceptions.

src/Event/SettingsUpdatedEvent.php (1)

16-20: Constructor property promotion is a good fit here

Using promoted private readonly properties simplifies the event while keeping the same public API via getters and enforcing immutability of class, index, and setting.

src/Services/SettingsUpdater.php (1)

22-27: Constructor migration to MeilisearchManager looks consistent

Using MeilisearchManager::getConfiguration() and promoting $searchClient / $eventDispatcher preserves previous behavior and fits the new manager-centric wiring.

tests/Entity/Link.php (1)

9-9: Normalization format aligned with SearchableObject

Referencing SearchableObject::NORMALIZATION_FORMAT keeps this test entity in sync with the new normalization API; no functional issues spotted.

Also applies to: 76-76

tests/Integration/SearchTest.php (1)

71-72: Search tests correctly migrated to SearchManager with task waits

Using $this->searchManager->search() / rawSearch() and inserting waitForAllTasks() after imports aligns tests with the new API and makes them more robust against async indexing delays.

Also applies to: 75-76, 81-82, 90-91, 114-116, 137-140

tests/Integration/EventListener/DoctrineEventSubscriberTest.php (1)

23-23: Doctrine event subscriber tests correctly use SearchManager

Replacing the old service calls with $this->searchManager->search() keeps the tests aligned with the new API while preserving intent and coverage.

Also applies to: 38-38, 59-59, 81-81, 97-98, 107-108, 121-121, 131-131

tests/Kernel.php (1)

36-42: Doctrine config loading logic simplified and version-aware

The new branching on $doctrineBundleV3 plus the PHP 8.4/LegacyReflectionFields combination is clearer than the previous PHP-version matrix and should select the right Doctrine config variant across supported environments.

src/Exception/DataProviderNotFoundException.php (1)

1-13: DataProviderNotFoundException is well-defined and informative

Exception type and message (including index and class) are appropriate for signaling missing data providers in the manager.

src/Searchable.php (1)

7-20: LGTM!

The deprecation annotations are properly structured with clear migration paths to SearchableObject. The version reference (0.16) and replacement class/constant references are accurate.

tests/Integration/EngineTest.php (2)

35-42: Constructor updated correctly for SearchableObject API.

The new constructor signature is properly followed with all required parameters: indexUid, primaryKey, object, identifier, serializer, and options.


67-69: Serializer correctly extracted and reused.

Good practice to extract the serializer reference once and reuse it for multiple SearchableObject instantiations.

src/EventListener/DoctrineEventSubscriber.php (1)

10-29: LGTM!

Clean migration to SearchManagerInterface. The simplified API (single object argument instead of ObjectManager + object) aligns with the PR objective to decouple from Doctrine. Constructor property promotion is idiomatic PHP 8.1+.

tests/Integration/Fixtures/ActorDataProvider.php (1)

13-69: LGTM!

Well-structured test fixture demonstrating the DataProviderInterface contract for custom (non-ORM) data sources. The generic type annotation @implements DataProviderInterface<Actor> properly documents the expected type.

src/DependencyInjection/MeilisearchExtension.php (2)

65-68: Manager wiring correctly updated with data providers.

The $dataProviders map is properly passed as the 5th constructor argument, aligning with the new MeilisearchManager signature that requires providers for index/remove operations.


93-129: I attempted to verify the review comment by examining the Configuration.php file to check validation rules and the MeilisearchManager to understand runtime behavior. However, the repository cloning failed, preventing me from accessing the codebase files needed for verification.

Without access to:

  • The configuration validation rules in Configuration.php (to check if custom type requires data_provider)
  • The MeilisearchManager::getDataProvider() implementation (to check if there's error handling for missing providers)
  • The actual configuration schema definition

I cannot definitively confirm or refute the review comment's concerns about indices without data providers causing runtime failures.

**Verify handling of indices without data provider.**

The `registerDataProviders` method only registers providers for:
1. Indices with an explicit `data_provider` configured
2. Indices with `type: orm`

If an index has neither (e.g., `type: custom` without `data_provider`), no provider is registered. Verify in Configuration.php that the configuration schema enforces `data_provider` is required for non-orm types, and confirm MeilisearchManager::getDataProvider() handles missing providers gracefully or raises a clear error.


tests/Unit/SerializationTest.php (1)

23-30: LGTM!

The test correctly uses the new SearchableObject constructor signature and the idiomatic 'groups' key (Symfony Serializer convention) with the NORMALIZATION_GROUP constant.

src/Command/MeilisearchUpdateSettingsCommand.php (1)

10-11: Migration to SearchManagerInterface in command is consistent

The switch to injecting SearchManagerInterface and reusing it via parent::__construct($searchManager) and isSearchable() keeps behavior intact while aligning with the new manager-based API. No issues spotted here.

Also applies to: 22-27, 56-56

src/EventListener/ConsoleOutputSubscriber.php (1)

13-20: Constructor promotion and $output naming look good

Using constructor promotion with a more descriptive $output property cleans up the class and resolves the previous short-variable warning without changing behavior.

src/SearchManagerInterface.php (1)

1-100: New SearchManagerInterface is well-shaped and matches the manager implementation

The interface cleanly captures the new manager responsibilities (searchability checks, data providers, index/remove/clear/delete/search/count), with appropriate class-string typing and NotSearchableException contracts. It lines up with the MeilisearchManager methods shown elsewhere and gives consumers a clear, framework-agnostic entry point.

No issues from my side here.

src/SearchableObject.php (1)

13-37: SearchableObject normalization logic looks correct and side‑effect free

The constructor/context handling and getSearchableArray() implementation line up with the existing SearchableEntity conventions, and the switch to a local $context avoids mutating instance state on each call. The enforced 'meilisearch' => true flag and Symfony 7.1+ DateTimeNormalizer tweaks are appropriately encapsulated here.

No changes needed from my perspective.

Also applies to: 55-79

tests/BaseKernelTestCase.php (1)

47-52: The review comment's core claim about getTasks() iteration is incorrect; the current implementation is actually correct.

The original review states that iterating Client::getTasks() directly yields metadata instead of individual tasks. However, verification of the official meilisearch-php documentation confirms that TasksResults implements IteratorAggregate with a getIterator() method that returns an ArrayIterator over the internal $results array—meaning direct iteration yields individual task objects, not wrapper metadata.

The current code:

foreach ($this->client->getTasks() as $task) {
    $this->client->waitForTask($task['uid']);
}

is correct. Each $task in the iteration is an associative array with properties including uid, so $task['uid'] access works as intended. Using getResults() is functionally equivalent but unnecessary.

Likely an incorrect or invalid review comment.

src/Command/MeilisearchImportCommand.php (4)

27-34: LGTM!

Constructor correctly migrated to use SearchManagerInterface with readonly property promotion for injected dependencies. The parent constructor call properly passes the search manager.


119-143: LGTM!

The data provider is now correctly instantiated outside the do-while loop (addressing the previous review feedback), and cleanup() is called after each batch iteration—appropriate for releasing resources like detaching entities from Doctrine's identity map between batches.


166-166: Good defensive initialization.

Using null coalescing (??= 0) to initialize the response counter prevents undefined index issues when the same index appears multiple times across batches.


229-229: Correctly delegates to the search manager.

Using $this->searchManager->deleteByIndexName() aligns with the new manager-based architecture for index deletion.

src/Engine.php (5)

12-14: LGTM!

Constructor property promotion is clean and modern.


25-54: LGTM!

The indexing logic correctly handles per-index primary keys by storing both primaryKey and documents in the batch structure. This addresses the previously identified bug where a single primary key was applied to all indexes.


97-100: LGTM!

Simplified to a clean one-liner that delegates directly to the client.


130-137: LGTM!

The normalizeId method now uses proper union types (\Stringable|string|int) and handles object-to-string conversion cleanly.


78-84: Based on my investigation, I've encountered limitations accessing the repository directly. However, I was able to verify key information from the Meilisearch PHP client documentation:

Verified Facts:

  • deleteDocuments() method exists in the Meilisearch PHP client
  • deleteDocuments() accepts an array of document IDs (not full objects)
  • ✓ It calls the DELETE batch endpoint for improved performance

Unverified:

  • The structure of $objects in the current code (whether they are IDs or full Document objects)
  • Whether deleteDocument($object) returns an ID or processes full objects
  • Whether the suggested refactor requires extracting IDs from objects

Critical Concern:
The suggested refactor passes $objects directly to deleteDocuments(). If $objects contains full Document objects rather than just IDs, this refactor would be incomplete and would require extracting the IDs first.


Consider batching deletions for improved performance.

Each document is deleted individually via deleteDocument(). For bulk removals, the Meilisearch PHP client supports deleteDocuments(array $documentIds) which reduces API calls. However, verify that $objects contains document IDs directly—if they are full Document objects, extract the IDs before passing to deleteDocuments().

src/Command/MeilisearchCreateCommand.php (2)

23-30: LGTM!

Constructor correctly migrated to use SearchManagerInterface with readonly property promotion, consistent with other command classes in the PR.


66-68: LGTM!

The isSearchable check correctly delegates to the new searchManager.

src/Services/MeilisearchManager.php (7)

52-64: LGTM!

Constructor properly initializes the manager with all required dependencies and sets up searchables and aggregators.


69-74: LGTM!

Clean implementation using the base class resolution helper.


79-91: LGTM!

The null check for $index (lines 86-88) properly addresses the previously identified issue, throwing a descriptive NotSearchableException when no matching index is found.


101-116: LGTM!

The data provider resolution correctly handles both exact class matches and inheritance hierarchies via is_a(). Falls back to a descriptive exception when no provider is found.


489-509: LGTM on the import fix; however, static access could be avoided.

The LegacyReflectionFields::class import issue from the previous review has been addressed. The static resolver pattern is efficient with the memoized closure.

Note: PHPStan may flag the static access to DefaultProxyClassNameResolver::getClass() and ClassUtils::getClass() (lines 500-505). These are standard Doctrine utilities, and avoiding them would require significant refactoring for minimal benefit—acceptable as-is.


121-166: LGTM!

The indexing flow correctly:

  1. Resolves matching indices per entity (including aggregator support)
  2. Wraps entities in aggregators when needed
  3. Applies shouldBeIndexed gating
  4. Batches items per index for efficient processing

242-286: LGTM!

The search method properly validates the class is searchable, delegates to the engine, validates the response structure, and rehydrates domain objects via the data provider while preserving result ordering.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
src/Model/Aggregator.php (1)

71-75: Track the objectID vs configurable primary_key TODO in normalize()

The TODO correctly flags that normalize() still hardcodes the 'objectID' field while indexes can now be configured with an arbitrary primary_key. It’s fine to leave this as-is for now, but it would be good to either wire the configured primary key through here or open a follow-up issue so this doesn’t get lost.

Based on learnings, keeping the actual change out of this PR and handling it separately is reasonable, but it should be explicitly tracked.

src/Services/MeilisearchManager.php (1)

242-286: Identifier key format between indexing and search remains inconsistent for composite identifiers.

The mapping between Meilisearch hits and loaded objects still diverges from the format produced by getSingleIdentifier() for multi-field identifiers:

  • During indexing/removal, the identifier stored in Meilisearch is produced via getSingleIdentifier($provider, $object) (used in batchProcess() and remove()).
  • For search, you currently:
    • Read $documentId directly from $hit[$index['primary_key']].
    • Build $objectsById using implode('-', $identifiers) when there is more than one identifier value.

For composite identifiers, getSingleIdentifier() builds keys as "field1-value1__field2-value2__", while the search mapping uses only the values joined by '-'. This mismatch means that when a DataProviderInterface implementation legitimately returns multiple identifier values, the search result mapping may fail to match hits back to their domain objects, even though indexing/removal work correctly. This was raised earlier and is still present in the current code.

A minimal way to make the contract coherent (and reuse the existing logic) is to have search use getSingleIdentifier() as the single source of truth for the key format:

@@ public function search(
-        $loadedObjects = $dataProvider->loadByIdentifiers($identifiers);
-        $objectsById = [];
-        foreach ($loadedObjects as $object) {
-            $identifiers = $dataProvider->getIdentifierValues($object);
-            $key = 1 === \count($identifiers) ? (string) reset($identifiers) : implode('-', $identifiers);
-            $objectsById[$key] = $object;
-        }
+        $loadedObjects = $dataProvider->loadByIdentifiers($identifiers);
+        $objectsById = [];
+        foreach ($loadedObjects as $object) {
+            $singleIdentifier = $this->getSingleIdentifier($dataProvider, $object);
+            $objectsById[(string) $singleIdentifier] = $object;
+        }
@@
-        foreach ($hits as $hit) {
-            $documentId = $hit[$index['primary_key']];
+        foreach ($hits as $hit) {
+            $documentId = (string) $hit[$index['primary_key']];
 
             if (isset($objectsById[$documentId])) {
                 $results[] = $objectsById[$documentId];
             }
         }

This keeps all identifier formatting logic in getSingleIdentifier(), avoids assumptions about composite key shape in search(), and ensures that indexing, removal, and search all agree on the same identifier string for any DataProviderInterface implementation.

Also applies to: 438-458

🧹 Nitpick comments (8)
src/Engine.php (2)

60-88: Consider bulk deletions per index instead of per-ID calls

You already aggregate identifiers per $indexUid; if the Meilisearch PHP client supports a deleteDocuments(array $ids) method, replacing the inner per-ID loop with a single bulk call per index would reduce HTTP overhead for large deletes.

Please confirm in the meilisearch/meilisearch-php (^1.16) documentation whether deleteDocuments() is available and behaves equivalently for your use case before changing this.


130-137: normalizeId() matches the supported identifier types

The helper correctly normalizes \Stringable|string|int identifiers to string|int so Meilisearch never sees an object. As a tiny style nit, you could tighten the condition to instanceof \Stringable for self-documentation, but the current implementation is safe given the parameter type.

src/DependencyInjection/MeilisearchExtension.php (1)

93-129: Data provider registration logic looks sound.

The new registerDataProviders() method correctly:

  • Handles custom data providers via explicit data_provider configuration
  • Creates ORM providers for Aggregator classes by iterating aggregated entities
  • Creates single ORM providers for non-aggregator classes
  • Uses xxh32 hashing for unique service definition IDs

The logic aligns with the PR's goal of introducing a provider-based architecture.

One minor consideration: hash('xxh32', $className) provides a 32-bit hash, which has a low but non-zero collision risk. For DI service IDs, this is likely acceptable given typical class name cardinality, but be aware of potential collisions in large applications.

src/Command/MeilisearchImportCommand.php (1)

71-154: Pipeline flagged high complexity in execute().

The method has CyclomaticComplexity (13) and NPathComplexity (592). Consider extracting helper methods for distinct responsibilities (e.g., processIndex(), handleSkipBatches()) in a follow-up PR to improve maintainability.

Based on learnings, the maintainer prefers to keep PRs focused and defer such improvements to separate PRs.

src/SearchableObject.php (1)

74-78: Consider adding return type validation.

The NormalizableInterface::normalize() method and NormalizerInterface::normalize() can return mixed. If the wrapped object returns an unexpected type, this could cause downstream issues. Consider adding a runtime assertion or type guard.

     if ($this->object instanceof NormalizableInterface) {
-        return $this->object->normalize($this->normalizer, self::NORMALIZATION_FORMAT, $context);
+        $result = $this->object->normalize($this->normalizer, self::NORMALIZATION_FORMAT, $context);
+        \assert(\is_array($result));
+
+        return $result;
     }

-    return $this->normalizer->normalize($this->object, self::NORMALIZATION_FORMAT, $context);
+    $result = $this->normalizer->normalize($this->object, self::NORMALIZATION_FORMAT, $context);
+    \assert(\is_array($result));
+
+    return $result;
src/SearchManagerInterface.php (2)

31-43: Consider tracking the return shape TODOs.

Multiple methods have @todo: return shape comments. These should be addressed before a stable release to provide proper type safety for consumers.

Would you like me to open an issue to track defining proper return type shapes for these methods?


68-78: Inconsistent @param and @return annotation style.

The @param array $searchParams lacks the shape annotation mentioned in the @todo. Consider using @param array<string, mixed> for consistency with other PHPDoc annotations in the codebase.

     /**
-     * @todo: searchParams and return shapes
+     * @todo: define searchParams and return shapes
      *
      * @param class-string $className
-     * @param array        $searchParams
-     *
-     * @return array
+     * @param array<string, mixed> $searchParams
      *
      * @throws NotSearchableException
+     *
+     * @return array<string, mixed>
      */
     public function search(string $className, string $query = '', array $searchParams = []): array;
src/Services/MeilisearchManager.php (1)

22-64: Class is doing a lot; phpmd complexity/coupling warnings might justify future extraction.

MeilisearchManager centralizes configuration, data-provider resolution, indexing, searching, and Doctrine compatibility handling, which is why phpmd flags ExcessiveClassComplexity and CouplingBetweenObjects. Functionally this is fine, but long term you might consider extracting helper collaborators (e.g. an index configuration helper and/or a resolver for indices/aggregators) to keep this class focused and make testing easier. I wouldn’t block this PR on that, but it’s worth tracking as a follow‑up.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a0b7b65 and aeaf95e.

📒 Files selected for processing (55)
  • .github/workflows/pre-release-tests.yml (2 hunks)
  • .github/workflows/tests.yml (1 hunks)
  • composer.json (1 hunks)
  • config/services.php (4 hunks)
  • phpstan-baseline.php (1 hunks)
  • phpstan.dist.neon (1 hunks)
  • src/Command/IndexCommand.php (2 hunks)
  • src/Command/MeilisearchClearCommand.php (1 hunks)
  • src/Command/MeilisearchCreateCommand.php (3 hunks)
  • src/Command/MeilisearchDeleteCommand.php (1 hunks)
  • src/Command/MeilisearchImportCommand.php (7 hunks)
  • src/Command/MeilisearchUpdateSettingsCommand.php (3 hunks)
  • src/DataProvider/DataProviderInterface.php (1 hunks)
  • src/DataProvider/OrmEntityProvider.php (1 hunks)
  • src/DependencyInjection/Configuration.php (2 hunks)
  • src/DependencyInjection/MeilisearchExtension.php (4 hunks)
  • src/Engine.php (5 hunks)
  • src/Event/SettingsUpdatedEvent.php (1 hunks)
  • src/EventListener/ConsoleOutputSubscriber.php (1 hunks)
  • src/EventListener/DoctrineEventSubscriber.php (1 hunks)
  • src/Exception/DataProviderNotFoundException.php (1 hunks)
  • src/Exception/InvalidIndiceException.php (1 hunks)
  • src/Exception/LogicException.php (1 hunks)
  • src/Exception/NotSearchableException.php (1 hunks)
  • src/Model/Aggregator.php (2 hunks)
  • src/SearchManagerInterface.php (1 hunks)
  • src/SearchService.php (1 hunks)
  • src/Searchable.php (1 hunks)
  • src/SearchableEntity.php (2 hunks)
  • src/SearchableObject.php (1 hunks)
  • src/Services/MeilisearchManager.php (1 hunks)
  • src/Services/MeilisearchService.php (9 hunks)
  • src/Services/SettingsUpdater.php (1 hunks)
  • src/Services/UnixTimestampNormalizer.php (1 hunks)
  • tests/BaseKernelTestCase.php (5 hunks)
  • tests/Entity/Actor.php (1 hunks)
  • tests/Entity/Link.php (2 hunks)
  • tests/Entity/SelfNormalizable.php (2 hunks)
  • tests/Entity/Tag.php (2 hunks)
  • tests/Integration/AggregatorTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchClearCommandTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchCreateCommandTest.php (2 hunks)
  • tests/Integration/Command/MeilisearchDeleteCommandTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchImportCommandTest.php (1 hunks)
  • tests/Integration/EngineTest.php (3 hunks)
  • tests/Integration/EventListener/DoctrineEventSubscriberTest.php (8 hunks)
  • tests/Integration/Fixtures/ActorDataProvider.php (1 hunks)
  • tests/Integration/SearchTest.php (4 hunks)
  • tests/Kernel.php (1 hunks)
  • tests/Unit/ConfigurationTest.php (8 hunks)
  • tests/Unit/SerializationTest.php (2 hunks)
  • tests/baseline-ignore (1 hunks)
  • tests/config/doctrine_php7.yaml (0 hunks)
  • tests/config/framework.yaml (1 hunks)
  • tests/config/meilisearch.yaml (1 hunks)
💤 Files with no reviewable changes (1)
  • tests/config/doctrine_php7.yaml
🚧 Files skipped from review as they are similar to previous changes (22)
  • src/Exception/LogicException.php
  • src/Command/MeilisearchClearCommand.php
  • src/DataProvider/DataProviderInterface.php
  • tests/Entity/Tag.php
  • src/EventListener/ConsoleOutputSubscriber.php
  • src/Event/SettingsUpdatedEvent.php
  • phpstan-baseline.php
  • src/Command/MeilisearchDeleteCommand.php
  • tests/Kernel.php
  • tests/Entity/SelfNormalizable.php
  • src/Exception/DataProviderNotFoundException.php
  • tests/baseline-ignore
  • src/Command/MeilisearchUpdateSettingsCommand.php
  • tests/config/framework.yaml
  • src/Services/SettingsUpdater.php
  • src/SearchService.php
  • tests/Integration/Command/MeilisearchImportCommandTest.php
  • tests/Unit/ConfigurationTest.php
  • src/DataProvider/OrmEntityProvider.php
  • tests/Unit/SerializationTest.php
  • src/Services/UnixTimestampNormalizer.php
  • tests/BaseKernelTestCase.php
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: norkunas
Repo: meilisearch/meilisearch-symfony PR: 391
File: src/Command/MeilisearchImportCommand.php:117-120
Timestamp: 2025-08-05T04:46:01.119Z
Learning: In meilisearch-symfony project, the maintainer norkunas prefers to keep PRs focused and defer error handling improvements for settings updates to separate PRs, particularly when it involves decisions about partial import behavior when some indexes fail.
📚 Learning: 2025-08-05T04:46:01.119Z
Learnt from: norkunas
Repo: meilisearch/meilisearch-symfony PR: 391
File: src/Command/MeilisearchImportCommand.php:117-120
Timestamp: 2025-08-05T04:46:01.119Z
Learning: In meilisearch-symfony project, the maintainer norkunas prefers to keep PRs focused and defer error handling improvements for settings updates to separate PRs, particularly when it involves decisions about partial import behavior when some indexes fail.

Applied to files:

  • tests/Integration/Fixtures/ActorDataProvider.php
  • src/Command/MeilisearchImportCommand.php
  • src/Engine.php
  • tests/Integration/EngineTest.php
  • src/Services/MeilisearchManager.php
  • .github/workflows/tests.yml
🧬 Code graph analysis (16)
src/DependencyInjection/Configuration.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/EventListener/DoctrineEventSubscriber.php (1)
src/SearchManagerInterface.php (2)
  • index (36-36)
  • remove (43-43)
tests/Integration/Fixtures/ActorDataProvider.php (3)
tests/Entity/Actor.php (1)
  • Actor (7-14)
src/DataProvider/DataProviderInterface.php (4)
  • provide (18-18)
  • loadByIdentifiers (25-25)
  • getIdentifierValues (32-32)
  • cleanup (34-34)
src/DataProvider/OrmEntityProvider.php (4)
  • provide (21-30)
  • loadByIdentifiers (32-45)
  • getIdentifierValues (47-52)
  • cleanup (54-57)
src/Exception/InvalidIndiceException.php (2)
src/Exception/DataProviderNotFoundException.php (1)
  • __construct (9-12)
src/Exception/NotSearchableException.php (1)
  • __construct (9-12)
src/DependencyInjection/MeilisearchExtension.php (6)
src/DataProvider/OrmEntityProvider.php (1)
  • OrmEntityProvider (10-58)
src/MeilisearchBundle.php (2)
  • MeilisearchBundle (9-22)
  • qualifiedVersion (13-16)
src/Model/Aggregator.php (2)
  • Aggregator (12-76)
  • getEntities (42-45)
src/Document/Aggregator.php (1)
  • Aggregator (9-11)
src/Entity/Aggregator.php (1)
  • Aggregator (9-11)
tests/Entity/ContentAggregator.php (1)
  • getEntities (21-27)
tests/Entity/Link.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
tests/Integration/AggregatorTest.php (2)
src/Model/Aggregator.php (1)
  • getEntityClassFromObjectID (60-69)
src/Exception/InvalidEntityForAggregator.php (1)
  • InvalidEntityForAggregator (7-9)
src/Command/IndexCommand.php (5)
src/Command/MeilisearchCreateCommand.php (1)
  • __construct (23-30)
src/Command/MeilisearchUpdateSettingsCommand.php (1)
  • __construct (22-28)
src/Services/MeilisearchService.php (2)
  • __construct (52-64)
  • getConfiguration (87-96)
src/SearchManagerInterface.php (1)
  • getConfiguration (24-24)
src/Services/MeilisearchManager.php (1)
  • getConfiguration (93-96)
src/Engine.php (1)
src/SearchableObject.php (5)
  • SearchableObject (13-80)
  • getSearchableArray (65-79)
  • getIndexUid (42-45)
  • getPrimaryKey (50-53)
  • getIdentifier (55-58)
src/Services/MeilisearchService.php (2)
src/SearchManagerInterface.php (11)
  • isSearchable (17-17)
  • getConfiguration (24-24)
  • searchableAs (22-22)
  • index (36-36)
  • remove (43-43)
  • clear (50-50)
  • deleteByIndexName (57-57)
  • delete (66-66)
  • search (78-78)
  • rawSearch (90-90)
  • count (100-100)
src/Services/MeilisearchManager.php (13)
  • isSearchable (69-74)
  • getBaseClassName (342-349)
  • getConfiguration (93-96)
  • searchableAs (79-91)
  • index (121-166)
  • remove (171-204)
  • clear (209-214)
  • assertIsSearchable (431-436)
  • deleteByIndexName (219-222)
  • delete (227-232)
  • search (242-286)
  • rawSearch (296-304)
  • count (312-317)
config/services.php (3)
src/Services/MeilisearchManager.php (1)
  • MeilisearchManager (22-510)
src/EventListener/DoctrineEventSubscriber.php (1)
  • DoctrineEventSubscriber (10-30)
src/Services/SettingsUpdater.php (1)
  • SettingsUpdater (16-77)
tests/Integration/EngineTest.php (3)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
tests/Entity/Image.php (1)
  • getId (39-42)
tests/Entity/Post.php (1)
  • getId (91-94)
tests/Integration/EventListener/DoctrineEventSubscriberTest.php (2)
src/SearchManagerInterface.php (1)
  • search (78-78)
src/Services/MeilisearchManager.php (1)
  • search (242-286)
src/SearchableObject.php (3)
src/SearchableEntity.php (1)
  • getSearchableArray (66-91)
tests/Entity/Link.php (1)
  • normalize (74-85)
tests/Entity/SelfNormalizable.php (1)
  • normalize (64-75)
src/Services/MeilisearchManager.php (9)
src/Collection.php (1)
  • Collection (13-380)
src/Engine.php (8)
  • Engine (10-138)
  • __construct (12-14)
  • index (25-54)
  • remove (62-88)
  • clear (97-100)
  • delete (105-108)
  • search (115-118)
  • count (125-128)
src/Exception/DataProviderNotFoundException.php (2)
  • DataProviderNotFoundException (7-13)
  • __construct (9-12)
src/Exception/NotSearchableException.php (2)
  • NotSearchableException (7-13)
  • __construct (9-12)
src/Exception/SearchHitsNotFoundException.php (1)
  • SearchHitsNotFoundException (7-9)
src/Model/Aggregator.php (3)
  • Aggregator (12-76)
  • __construct (26-35)
  • getEntities (42-45)
src/SearchableObject.php (2)
  • SearchableObject (13-80)
  • __construct (28-37)
src/DataProvider/OrmEntityProvider.php (3)
  • __construct (15-19)
  • getIdentifierValues (47-52)
  • loadByIdentifiers (32-45)
src/DataProvider/DataProviderInterface.php (2)
  • getIdentifierValues (32-32)
  • loadByIdentifiers (25-25)
tests/Integration/SearchTest.php (4)
tests/BaseKernelTestCase.php (1)
  • waitForAllTasks (47-52)
src/Engine.php (1)
  • search (115-118)
src/SearchManagerInterface.php (2)
  • search (78-78)
  • rawSearch (90-90)
src/Services/MeilisearchManager.php (2)
  • search (242-286)
  • rawSearch (296-304)
🪛 GitHub Actions: Tests
src/Command/MeilisearchImportCommand.php

[warning] 23-23: phpmd: CouplingBetweenObjects (value 13) in MeilisearchImportCommand.php


[warning] 71-71: phpmd: CyclomaticComplexity (13) in execute()


[warning] 71-71: phpmd: NPathComplexity (592) in execute()

src/DependencyInjection/MeilisearchExtension.php

[warning] 22-22: phpmd: IfStatementAssignment (line 44, col 20) in load extension


[warning] 36-36: phpmd: StaticAccess to \Meilisearch\Bundle\MeilisearchBundle in load


[warning] 51-51: phpmd: ElseExpression used in method load


[warning] 59-59: phpmd: StaticAccess to \Meilisearch\Bundle\MeilisearchBundle in load


[warning] 118-118: phpmd: ElseExpression used in method registerDataProviders

src/Services/MeilisearchService.php

[warning] 27-27: phpmd: TooManyPublicMethods (11) in MeilisearchService


[warning] 27-27: phpmd: ExcessiveClassComplexity (67) in MeilisearchService


[warning] 27-27: phpmd: CouplingBetweenObjects (16) in MeilisearchService


[warning] 48-48: phpmd: LongVariable (>$20) in $classToSerializerGroup


[warning] 57-57: phpmd: StaticAccess to \Symfony\Component\PropertyAccess\PropertyAccess in __construct


[warning] 415-415: phpmd: LongVariable (>$20) in $searchableEntitiesChunk


[warning] 463-463: phpmd: StaticAccess to \Doctrine\ORM\Proxy\DefaultProxyClassNameResolver in resolveClass


[warning] 467-467: phpmd: StaticAccess to \Doctrine\Common\Util\ClassUtils in resolveClass

src/Services/MeilisearchManager.php

[warning] 22-22: phpmd: ExcessiveClassComplexity (71) in MeilisearchManager


[warning] 22-22: phpmd: CouplingBetweenObjects (14) in MeilisearchManager


[warning] 501-501: phpmd: StaticAccess to \Doctrine\ORM\Proxy\DefaultProxyClassNameResolver in resolveClass


[warning] 505-505: phpmd: StaticAccess to \Doctrine\Common\Util\ClassUtils in resolveClass

🔇 Additional comments (49)
phpstan.dist.neon (1)

1-2: I'll help you verify the review comment for the phpstan.dist.neon file. Let me check if the baseline file exists and contains the expected error suppressions mentioned in the review.
<function_calls>

#!/bin/bash

Check if phpstan-baseline.php exists and preview its contents

echo "=== Checking for phpstan-baseline.php ==="
if [ -f phpstan-baseline.php ]; then
echo "✓ Baseline file exists"
echo ""
echo "=== File size and line count ==="
wc -l phpstan-baseline.php
echo ""
echo "=== First 50 lines of phpstan-baseline.php ==="
head -50 phpstan-baseline.php
echo ""
echo "=== Checking for expected error patterns ==="
echo "Searching for 'ClassUtils'..."
rg -n 'ClassUtils' phpstan-baseline.php || echo "Pattern not found"
echo ""
echo "Searching for 'array_unique'..."
rg -n 'array_unique' phpstan-baseline.php || echo "Pattern not found"
else
echo "✗ Baseline file not found at repository root"
echo ""
echo "=== Searching for phpstan-baseline.php anywhere in repo ==="
fd phpstan-baseline phpstan-baseline.php 2>/dev/null || echo "Not found in repository"
fi


</function_calls>

src/EventListener/DoctrineEventSubscriber.php (2)

8-13: Constructor migration to SearchManagerInterface looks correct

Using SearchManagerInterface with constructor property promotion and a readonly dependency cleanly aligns this subscriber with the new manager-based API and improves decoupling from the concrete service.


18-29: Lifecycle callbacks correctly delegate to SearchManagerInterface

Forwarding the Doctrine entity from LifecycleEventArgs directly into index()/remove() matches the new object|array signatures and preserves the original behavior of indexing on update/persist and removing on pre-remove.

src/Exception/InvalidIndiceException.php (1)

9-12: Constructor $code type-hint is correct and consistent

Typing $code as int matches the base \Exception signature and the new exception classes introduced in this PR; no behavioral change beyond enforcing the intended scalar type.

.github/workflows/tests.yml (1)

30-51: Updated test matrix matches supported PHP/Symfony range

Limiting to PHP 8.1–8.4 and Symfony 6.4–7.3, with explicit 8.1+7.x exclusions and focused 8.4 lowest/highest runs, aligns with the new composer constraints and keeps CI coverage sensible.

tests/Integration/AggregatorTest.php (1)

24-37: Constructor exception test is now clearer and correctly scoped

Renaming to testConstructorThrowsWithMoreThanOnePrimaryKey and moving expectException() immediately before the ContentAggregator construction makes the intent explicit and ensures only the constructor is under test.

src/Searchable.php (1)

7-21: Deprecation notices clearly guide migration to SearchableObject

Marking Searchable and its constants as deprecated with explicit references to SearchableObject and its constants provides a clean upgrade path while preserving runtime behavior.

src/Model/Aggregator.php (1)

24-35: Stronger typing for $entity is safe and helpful

Constraining $entity to object both on the property and in the constructor improves static safety without changing the Aggregator semantics around Doctrine entities/documents.

src/Engine.php (2)

12-14: Constructor promotion for $client is a clean, non-breaking refactor

Using property promotion here keeps the public API identical while reducing boilerplate; no behavioral change.


21-54: Per-index batching with SearchableObject correctly handles primary keys

The new index() flow—wrapping a single SearchableObject into an array, grouping by $indexUid, storing each index’s own primaryKey, and then calling addDocuments($batch['documents'], $batch['primaryKey'])—fixes the earlier “one primary key for all indexes” issue. Using $searchableArray + [$object->getPrimaryKey() => $this->normalizeId($object->getIdentifier())] also sensibly avoids overwriting any primary key already present in the normalized payload.

composer.json (1)

21-52: Dependency constraints align with the new support policy

Requiring PHP ^8.1, tightening Symfony components to ^6.4 || ^7.0, and moving doctrine/doctrine-bundle to require-dev all match the CI matrices and the goal of making ORM an explicit application dependency. The tooling bumps look incremental and non-risky.

.github/workflows/pre-release-tests.yml (1)

42-63: Pre-release matrix is consistent with main CI and dependency strategy

The PHP/Symfony/dependencies matrix, exclusions, and targeted 8.4 lowest/highest runs mirror the main tests workflow, and the updated composer-install configuration correctly exercises different dependency sets against Meilisearch RCs.

Also applies to: 65-86

tests/Integration/Command/MeilisearchClearCommandTest.php (1)

39-39: LGTM!

The test expectation correctly includes the new actor index in the cleared indices output.

tests/Integration/Command/MeilisearchDeleteCommandTest.php (1)

40-40: LGTM!

The test expectation correctly includes the new actor index in the deleted indices output.

tests/Integration/Command/MeilisearchCreateCommandTest.php (1)

63-63: LGTM!

The test expectations correctly include the actor index creation in both the updateSettings=true and updateSettings=false branches.

Also applies to: 80-80

tests/Entity/Actor.php (1)

1-14: LGTM!

The Actor test entity is well-designed: it uses PHP 8.1 constructor property promotion with readonly properties for immutability, and it's appropriately simple for demonstrating the custom data provider functionality.

tests/config/meilisearch.yaml (1)

57-60: LGTM!

The actor index configuration correctly demonstrates the new custom data provider feature with type: 'custom' and references the ActorDataProvider for data provisioning.

src/SearchableEntity.php (2)

15-17: Deprecation notice added appropriately.

The deprecation annotation clearly indicates the migration path to SearchableObject. Ensure that version "0.16" aligns with your release planning for this PR.


83-83: LGTM!

The normalization format constant references are correctly updated from Searchable::NORMALIZATION_FORMAT to SearchableObject::NORMALIZATION_FORMAT in both code paths.

Also applies to: 87-87

tests/Entity/Link.php (1)

9-9: LGTM!

The import and constant reference are correctly updated from Searchable to SearchableObject, maintaining consistency with the migration.

Also applies to: 76-76

src/DependencyInjection/Configuration.php (2)

57-59: Consider the default primary_key value.

The default primary_key: 'objectID' uses Algolia-specific terminology. According to PR objective #66, one goal is to "Remove 'objectID' usage" and adopt Meilisearch's primary key semantics. Consider whether:

  • The default should be 'id' (Meilisearch's default) or
  • The default should be null to let Meilisearch infer the primary key, or
  • 'objectID' is intentionally retained for backward compatibility

Based on PR objectives, this default may need alignment with Meilisearch conventions.


7-7: LGTM!

The import and serializer-related references are correctly updated from Searchable to SearchableObject, maintaining consistency with the broader migration. The configuration structure properly supports the new data provider architecture.

Also applies to: 63-68

tests/Integration/EventListener/DoctrineEventSubscriberTest.php (1)

23-23: LGTM! Clean migration to the new SearchManager API.

The test calls have been correctly updated to use the new searchManager->search() signature, removing the entityManager parameter. The migration is consistent across all test methods.

Also applies to: 38-38, 59-59, 81-81, 97-97, 107-107, 121-121, 131-131

src/Exception/NotSearchableException.php (1)

7-13: LGTM! Well-designed exception class.

The exception is properly typed with int $code, follows PHP 8.1+ standards, and provides a clear, formatted message for non-searchable classes.

src/Command/IndexCommand.php (2)

8-8: LGTM! Clean migration to SearchManagerInterface.

The command has been properly updated to use SearchManagerInterface with constructor property promotion, following PHP 8.1+ best practices. The migration is consistent with other commands in the PR.

Also applies to: 19-24


28-28: Configuration access updated correctly.

The getConfiguration() call now uses the new searchManager instance, aligning with the manager-based architecture.

tests/Integration/Fixtures/ActorDataProvider.php (3)

15-44: LGTM! Straightforward test data provider.

The provide() method correctly slices a static list of Actor instances using the given offset and limit parameters.


46-57: Potential logic issue: single ID match vs. multiple IDs.

The loadByIdentifiers() implementation assumes $identifiers is an associative array with a single 'id' key ($identifiers['id']), and uses strict equality (===) to match exactly one actor. However:

  1. The method signature suggests it should handle multiple identifiers (plural: "identifiers").
  2. If $identifiers contains ['id' => 5], only one actor will match, which may be correct for single-ID lookups.
  3. If the caller passes multiple IDs, this implementation won't work.

Verify whether this test fixture is intended to handle single-ID or multiple-ID scenarios. If it should support multiple IDs, consider:

 public function loadByIdentifiers(array $identifiers): array
 {
     $actors = [];

     foreach ($this->provide(PHP_INT_MAX, 0) as $actor) {
-        if ($actor->id === $identifiers['id']) {
+        if (in_array($actor->id, $identifiers, true)) {
             $actors[] = $actor;
         }
     }

     return $actors;
 }

Or clarify in a comment that this fixture expects a single-ID associative array format.

Based on learnings, the maintainer previously indicated this is intentional to showcase custom provider flexibility. However, it's worth confirming the expected usage pattern.


59-64: LGTM! Identifier extraction is correct.

The getIdentifierValues() method correctly asserts the object type and returns the identifier as an associative array.

tests/Integration/SearchTest.php (2)

71-71: Good synchronization: waitForAllTasks() added.

The addition of waitForAllTasks() calls before search operations is essential for ensuring asynchronous indexing tasks complete before assertions. This prevents flaky tests due to race conditions.

Also applies to: 114-114, 137-137


75-75: LGTM! Correct migration to SearchManager API.

All search and rawSearch calls have been correctly updated to use searchManager with the new signature that removes the ObjectManager parameter. The pagination test (line 116) correctly passes search parameters as an array.

Also applies to: 81-81, 90-90, 116-116, 139-139

src/Services/MeilisearchService.php (4)

24-26: Solid backward-compatibility strategy with optional manager delegation.

The deprecation approach correctly:

  • Marks the class as deprecated with clear guidance to MeilisearchManager
  • Accepts an optional SearchManagerInterface $manager parameter
  • Preserves existing behavior when no manager is provided

This allows gradual migration without breaking existing integrations.

Also applies to: 50-58


66-77: Consistent delegation pattern with deprecation notice.

The isSearchable() method correctly:

  1. Emits a deprecation notice guiding users to the new manager
  2. Delegates to the manager when available
  3. Falls back to legacy logic otherwise

This pattern is repeated consistently across all public methods.


114-121: Deprecation notice guides users away from ObjectManager parameter.

The deprecation messages for index(), remove(), and search() correctly indicate that passing Doctrine\Persistence\ObjectManager is deprecated, aligning with PR objectives to decouple from Doctrine-specific APIs.

Also applies to: 149-156, 209-220


346-347: Defensive string casting for aggregator class references.

Explicitly casting $index['class'] to string when storing in $entitiesAggregators and $aggregators arrays is a good defensive practice, ensuring consistent string-based lookups even if the configuration provides class-string types.

tests/Integration/EngineTest.php (2)

35-42: I was unable to access the repository directly due to a clone failure. However, based on Meilisearch's documented primary key semantics, I can provide analysis:

Meilisearch Primary Key Facts:

  • Meilisearch auto-infers primary keys from attribute names ending with "id" (case-insensitive: id, uid, BookId, etc.)
  • If the Image entity has an id field/method (like getId()), Meilisearch would naturally infer it as the primary key
  • Using an explicit 'objectID' string suggests an intentional override of this auto-inference

Without access to the codebase, I cannot definitively verify:

  1. Whether the Image entity's standard primary key is 'id' or 'objectID'
  2. What PR #66 specifically requires regarding objectID removal
  3. The actual index configuration for the 'image' index

To complete this verification, I would need to see:

  • The Image entity class definition
  • The actual Meilisearch index configuration for 'image'
  • The PR #66 requirements and related code changes

Verify that 'objectID' parameter in the SearchableObject instantiation aligns with the Image entity's configured primary key in the Meilisearch index. If PR objective #66 requires removing objectID usage in favor of standard Meilisearch id conventions, update this parameter to match the Image entity's actual primary key field (likely 'id').


67-69: Unable to complete automated verification of 'objectID' primary key configuration.

The repository is currently inaccessible through the verification tools. To confirm whether 'objectID' is the intended primary key for the posts index, manual verification is required:

  1. Confirm the primary key configured for the posts index in your test configuration (tests/config/ or index setup code)
  2. Verify that Post entity's getId() method aligns with this configuration
  3. Cross-reference line 37 to ensure the same primary key is used consistently throughout the test

Once confirmed, the SearchableObject instantiation at lines 67-69 should match the configured primary key.

src/DependencyInjection/MeilisearchExtension.php (1)

110-124: Based on my verification, I have found that the code pattern referenced in this review comment does not exist in the meilisearch-symfony repository. Web searches confirmed that:

  1. The meilisearch-symfony repository does not use an Aggregator/getEntities pattern (which is an Algolia-specific pattern)
  2. The exact code snippet shown (lines 110-124 with is_subclass_of($class, Aggregator::class) and $class::getEntities()) cannot be found in the codebase
  3. MeilisearchExtension.php exists but does not contain the code shown in the review

The repository uses Symfony/Doctrine indexing directly rather than the Aggregator abstraction pattern described in the review comment.


The review comment references code that does not exist in the meilisearch-symfony repository. The Aggregator/getEntities pattern is specific to the Algolia Symfony bundle, not meilisearch-symfony. The verification request cannot be addressed because the code being reviewed is not present in the codebase.

Likely an incorrect or invalid review comment.

config/services.php (2)

70-78: LGTM!

The new meilisearch.manager service is properly defined with appropriate abstract arguments for configuration. The alias to SearchManagerInterface follows Symfony best practices for interface-based dependency injection.


28-36: Verify the circular dependency is safe.

meilisearch.service now depends on meilisearch.manager (line 35). Ensure this doesn't create issues if MeilisearchService is only a deprecation bridge delegating to the manager.

src/Command/MeilisearchCreateCommand.php (2)

23-30: LGTM!

Clean constructor refactor using PHP 8.1+ promoted readonly properties. The dependency injection correctly uses SearchManagerInterface and the parent constructor is properly invoked.


63-68: LGTM!

The isSearchable check correctly uses the new searchManager property, consistent with the interface contract.

src/Command/MeilisearchImportCommand.php (3)

27-34: LGTM!

Constructor properly migrated to use SearchManagerInterface with promoted readonly properties. The parent constructor is correctly invoked with the manager.


119-143: Data provider instantiation correctly moved outside the loop.

The getDataProvider() call is now at line 119, before the do-while loop, addressing the previous review feedback. The cleanup() call inside the loop (line 140) appears intentional for releasing memory between batches, which is appropriate for large dataset imports.


79-80: Defensive batch-size parsing.

The ctype_digit() check correctly validates string input before casting. Note that ctype_digit('') returns false, so the empty string fallback is safe.

src/SearchableObject.php (2)

28-37: LGTM!

Well-structured constructor with proper readonly properties. The normalization context correctly merges user-provided context with the meilisearch flag.


65-79: LGTM!

The normalization logic correctly uses a local $context variable (addressing the previous mutation concern) and properly handles both NormalizableInterface implementations and regular objects. The Symfony version check for DateTime normalization settings is appropriate.

src/SearchManagerInterface.php (2)

10-12: LGTM!

Clean interface declaration with a well-named constant for result key access. The RESULT_KEY_HITS constant improves consistency across the codebase.


14-17: Flexible parameter type for isSearchable.

The object|string union type allows both runtime object instances and class strings, which is practical for different calling contexts.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Services/MeilisearchService.php (1)

388-400: Constructor signature mismatch with Aggregator.

The getAggregatorsFromEntities method instantiates aggregators with only 2 arguments, but the updated Aggregator constructor now requires 3 (including $primaryKey). This will cause a fatal error when the legacy code path is used.

 foreach ($this->entitiesAggregators[$entityClassName] as $aggregator) {
     $aggregators[] = new $aggregator(
         $entity,
-        $objectManager->getClassMetadata($entityClassName)->getIdentifierValues($entity)
+        $objectManager->getClassMetadata($entityClassName)->getIdentifierValues($entity),
+        'objectID' // Legacy default for backward compatibility
     );
 }
🧹 Nitpick comments (6)
src/Searchable.php (1)

7-20: Consider adding runtime deprecation triggers.

The docblock deprecations inform developers reading the code but won't generate warnings at runtime. For clearer migration signals, consider triggering deprecations when the class or constants are used:

 <?php
 
 declare(strict_types=1);
 
 namespace Meilisearch\Bundle;
 
+trigger_deprecation('meilisearch/search-bundle', '0.16', 'The "%s" class is deprecated, use "%s" instead.', Searchable::class, SearchableObject::class);
+
 /**
  * @deprecated Since 0.16, use `Meilisearch\Bundle\SearchableObject` instead.
  */
 final class Searchable
 {

This follows Symfony's deprecation conventions. However, given this is a v0 project with expected BC breaks, the documentation-only approach is also acceptable.

src/DependencyInjection/Configuration.php (1)

7-8: Index configuration aligns with the data-provider model; consider validating type=custom at config time

Importing and using SearchableObject for serializer groups is a good move for consistency, and the new type, primary_key, and data_provider nodes give clear per-index control while preserving BC via sensible defaults (orm + objectID).

You might optionally add a config validator so that type: custom without a data_provider fails early in configuration rather than later at runtime, but that’s not required for this PR to function.

Also applies to: 53-61, 63-69

src/EventListener/DoctrineEventSubscriber.php (1)

8-9: Subscriber correctly migrated to SearchManagerInterface

Using constructor promotion for SearchManagerInterface and delegating postUpdate/postPersist to index() and preRemove to remove() keeps the Doctrine subscriber behavior intact while aligning with the new manager abstraction.

If you ever touch this again, you could DRY up the duplicated index($args->getObject()) calls via a tiny private helper, but it’s not necessary for this PR.

Also applies to: 12-29

src/DataProvider/OrmEntityProvider.php (1)

47-52: Minor: Avoid redundant get_class() calls.

get_class($object) is called twice. Consider caching the result.

 public function getIdentifierValues(object $object): array
 {
-    $manager = $this->managerRegistry->getManagerForClass(\get_class($object));
+    $className = \get_class($object);
+    $manager = $this->managerRegistry->getManagerForClass($className);

-    return $manager->getClassMetadata(\get_class($object))->getIdentifierValues($object);
+    return $manager->getClassMetadata($className)->getIdentifierValues($object);
 }
src/Command/MeilisearchImportCommand.php (1)

71-155: Pipeline flags complexity thresholds exceeded.

The execute() method exceeds configured cyclomatic complexity (13 vs 10) and NPath complexity (592 vs 200). While the logic is clear and the refactoring to use SearchManagerInterface is correct, consider extracting helper methods in a follow-up PR to reduce complexity:

  • Extract the per-index import loop body into a separate method
  • Extract batch processing and response logging

Based on learnings, this can be deferred to a separate PR to keep this one focused.

src/Services/MeilisearchManager.php (1)

27-27: Pipeline flags class complexity thresholds exceeded.

The class exceeds configured complexity (70 vs 50) and coupling (13 dependencies). While the implementation is cohesive and necessary for the feature, consider extracting concerns in follow-up PRs:

  • Extract resolveClass() and proxy detection into a separate utility class
  • Consider a dedicated IndexResolver for getIndicesForEntity() logic
  • Move aggregator-related logic to a separate handler

Based on learnings, this refactoring can be deferred to keep this PR focused on the data provider feature.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aeaf95e and 2e54bae.

📒 Files selected for processing (55)
  • .github/workflows/pre-release-tests.yml (2 hunks)
  • .github/workflows/tests.yml (1 hunks)
  • composer.json (1 hunks)
  • config/services.php (4 hunks)
  • phpstan-baseline.php (1 hunks)
  • phpstan.dist.neon (1 hunks)
  • src/Command/IndexCommand.php (2 hunks)
  • src/Command/MeilisearchClearCommand.php (1 hunks)
  • src/Command/MeilisearchCreateCommand.php (3 hunks)
  • src/Command/MeilisearchDeleteCommand.php (1 hunks)
  • src/Command/MeilisearchImportCommand.php (7 hunks)
  • src/Command/MeilisearchUpdateSettingsCommand.php (3 hunks)
  • src/DataProvider/DataProviderInterface.php (1 hunks)
  • src/DataProvider/OrmEntityProvider.php (1 hunks)
  • src/DependencyInjection/Configuration.php (2 hunks)
  • src/DependencyInjection/MeilisearchExtension.php (4 hunks)
  • src/Engine.php (5 hunks)
  • src/Event/SettingsUpdatedEvent.php (1 hunks)
  • src/EventListener/ConsoleOutputSubscriber.php (1 hunks)
  • src/EventListener/DoctrineEventSubscriber.php (1 hunks)
  • src/Exception/DataProviderNotFoundException.php (1 hunks)
  • src/Exception/InvalidIndiceException.php (1 hunks)
  • src/Exception/LogicException.php (1 hunks)
  • src/Exception/NotSearchableException.php (1 hunks)
  • src/Model/Aggregator.php (2 hunks)
  • src/SearchManagerInterface.php (1 hunks)
  • src/SearchService.php (1 hunks)
  • src/Searchable.php (1 hunks)
  • src/SearchableEntity.php (2 hunks)
  • src/SearchableObject.php (1 hunks)
  • src/Services/MeilisearchManager.php (1 hunks)
  • src/Services/MeilisearchService.php (9 hunks)
  • src/Services/SettingsUpdater.php (1 hunks)
  • src/Services/UnixTimestampNormalizer.php (1 hunks)
  • tests/BaseKernelTestCase.php (5 hunks)
  • tests/Entity/Actor.php (1 hunks)
  • tests/Entity/Link.php (2 hunks)
  • tests/Entity/SelfNormalizable.php (2 hunks)
  • tests/Entity/Tag.php (2 hunks)
  • tests/Integration/AggregatorTest.php (2 hunks)
  • tests/Integration/Command/MeilisearchClearCommandTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchCreateCommandTest.php (2 hunks)
  • tests/Integration/Command/MeilisearchDeleteCommandTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchImportCommandTest.php (1 hunks)
  • tests/Integration/EngineTest.php (3 hunks)
  • tests/Integration/EventListener/DoctrineEventSubscriberTest.php (8 hunks)
  • tests/Integration/Fixtures/ActorDataProvider.php (1 hunks)
  • tests/Integration/SearchTest.php (4 hunks)
  • tests/Kernel.php (1 hunks)
  • tests/Unit/ConfigurationTest.php (8 hunks)
  • tests/Unit/SerializationTest.php (2 hunks)
  • tests/baseline-ignore (1 hunks)
  • tests/config/doctrine_php7.yaml (0 hunks)
  • tests/config/framework.yaml (1 hunks)
  • tests/config/meilisearch.yaml (1 hunks)
💤 Files with no reviewable changes (1)
  • tests/config/doctrine_php7.yaml
🚧 Files skipped from review as they are similar to previous changes (28)
  • tests/Entity/Actor.php
  • phpstan-baseline.php
  • tests/Entity/Link.php
  • tests/Integration/Command/MeilisearchCreateCommandTest.php
  • tests/baseline-ignore
  • tests/config/meilisearch.yaml
  • src/Command/IndexCommand.php
  • src/Command/MeilisearchDeleteCommand.php
  • tests/Entity/SelfNormalizable.php
  • tests/Integration/Command/MeilisearchClearCommandTest.php
  • tests/Entity/Tag.php
  • tests/Integration/Command/MeilisearchDeleteCommandTest.php
  • src/DataProvider/DataProviderInterface.php
  • src/Command/MeilisearchCreateCommand.php
  • src/SearchService.php
  • src/EventListener/ConsoleOutputSubscriber.php
  • src/Exception/LogicException.php
  • src/Exception/InvalidIndiceException.php
  • src/Command/MeilisearchClearCommand.php
  • phpstan.dist.neon
  • src/Services/UnixTimestampNormalizer.php
  • src/Event/SettingsUpdatedEvent.php
  • tests/Integration/SearchTest.php
  • tests/Integration/Command/MeilisearchImportCommandTest.php
  • src/SearchableObject.php
  • tests/config/framework.yaml
  • tests/BaseKernelTestCase.php
  • tests/Integration/EngineTest.php
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: norkunas
Repo: meilisearch/meilisearch-symfony PR: 391
File: src/Command/MeilisearchImportCommand.php:117-120
Timestamp: 2025-08-05T04:46:01.119Z
Learning: In meilisearch-symfony project, the maintainer norkunas prefers to keep PRs focused and defer error handling improvements for settings updates to separate PRs, particularly when it involves decisions about partial import behavior when some indexes fail.
📚 Learning: 2025-08-05T04:46:01.119Z
Learnt from: norkunas
Repo: meilisearch/meilisearch-symfony PR: 391
File: src/Command/MeilisearchImportCommand.php:117-120
Timestamp: 2025-08-05T04:46:01.119Z
Learning: In meilisearch-symfony project, the maintainer norkunas prefers to keep PRs focused and defer error handling improvements for settings updates to separate PRs, particularly when it involves decisions about partial import behavior when some indexes fail.

Applied to files:

  • src/Services/MeilisearchManager.php
  • src/Command/MeilisearchImportCommand.php
  • tests/Integration/Fixtures/ActorDataProvider.php
  • src/DataProvider/OrmEntityProvider.php
  • .github/workflows/tests.yml
  • src/Engine.php
🧬 Code graph analysis (18)
src/Exception/NotSearchableException.php (2)
src/Exception/DataProviderNotFoundException.php (1)
  • __construct (9-12)
src/Exception/InvalidIndiceException.php (1)
  • __construct (9-12)
src/DependencyInjection/Configuration.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/SearchableEntity.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
tests/Integration/EventListener/DoctrineEventSubscriberTest.php (3)
src/Engine.php (1)
  • search (159-162)
src/SearchManagerInterface.php (1)
  • search (92-92)
src/Services/MeilisearchManager.php (1)
  • search (219-258)
src/Services/MeilisearchManager.php (11)
src/Collection.php (1)
  • Collection (13-380)
src/Engine.php (8)
  • Engine (44-182)
  • __construct (46-48)
  • index (61-90)
  • remove (100-126)
  • clear (137-140)
  • delete (147-150)
  • search (159-162)
  • count (169-172)
src/Exception/DataProviderNotFoundException.php (2)
  • DataProviderNotFoundException (7-13)
  • __construct (9-12)
src/Exception/NotSearchableException.php (2)
  • NotSearchableException (7-13)
  • __construct (9-12)
src/Model/Aggregator.php (3)
  • Aggregator (12-78)
  • __construct (26-38)
  • getEntities (45-48)
src/SearchableObject.php (2)
  • SearchableObject (13-80)
  • __construct (28-37)
src/DataProvider/OrmEntityProvider.php (2)
  • __construct (15-19)
  • getIdentifierValues (47-52)
src/EventListener/ConsoleOutputSubscriber.php (1)
  • __construct (13-15)
src/EventListener/DoctrineEventSubscriber.php (1)
  • __construct (12-14)
src/SearchManagerInterface.php (11)
  • isSearchable (23-23)
  • searchableAs (30-30)
  • getConfiguration (32-32)
  • index (48-48)
  • remove (55-55)
  • clear (64-64)
  • deleteByIndexName (71-71)
  • delete (80-80)
  • search (92-92)
  • count (110-110)
  • rawSearch (102-102)
src/DataProvider/DataProviderInterface.php (1)
  • getIdentifierValues (32-32)
src/Command/MeilisearchImportCommand.php (3)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/SearchManagerInterface.php (6)
  • getConfiguration (32-32)
  • isSearchable (23-23)
  • getDataProvider (41-41)
  • index (48-48)
  • count (110-110)
  • deleteByIndexName (71-71)
src/DataProvider/DataProviderInterface.php (2)
  • provide (18-18)
  • cleanup (34-34)
src/EventListener/DoctrineEventSubscriber.php (1)
src/SearchManagerInterface.php (2)
  • index (48-48)
  • remove (55-55)
src/DependencyInjection/MeilisearchExtension.php (5)
src/DataProvider/OrmEntityProvider.php (1)
  • OrmEntityProvider (10-58)
src/MeilisearchBundle.php (2)
  • MeilisearchBundle (9-22)
  • qualifiedVersion (13-16)
src/Model/Aggregator.php (2)
  • Aggregator (12-78)
  • getEntities (45-48)
src/Document/Aggregator.php (1)
  • Aggregator (9-11)
tests/Entity/ContentAggregator.php (1)
  • getEntities (21-27)
tests/Integration/Fixtures/ActorDataProvider.php (3)
tests/Entity/Actor.php (1)
  • Actor (7-14)
src/DataProvider/DataProviderInterface.php (4)
  • provide (18-18)
  • loadByIdentifiers (25-25)
  • getIdentifierValues (32-32)
  • cleanup (34-34)
src/DataProvider/OrmEntityProvider.php (4)
  • provide (21-30)
  • loadByIdentifiers (32-45)
  • getIdentifierValues (47-52)
  • cleanup (54-57)
src/DataProvider/OrmEntityProvider.php (2)
src/Exception/LogicException.php (1)
  • LogicException (7-9)
src/DataProvider/DataProviderInterface.php (4)
  • provide (18-18)
  • loadByIdentifiers (25-25)
  • getIdentifierValues (32-32)
  • cleanup (34-34)
src/Services/SettingsUpdater.php (3)
src/Services/MeilisearchManager.php (2)
  • MeilisearchManager (27-468)
  • getConfiguration (92-95)
src/SearchManagerInterface.php (1)
  • getConfiguration (32-32)
src/SearchService.php (1)
  • getConfiguration (27-27)
src/Services/MeilisearchService.php (4)
src/Command/MeilisearchCreateCommand.php (1)
  • __construct (23-30)
src/Command/MeilisearchImportCommand.php (1)
  • __construct (27-34)
src/EventListener/DoctrineEventSubscriber.php (1)
  • __construct (12-14)
src/Services/MeilisearchManager.php (13)
  • isSearchable (71-76)
  • getBaseClassName (300-307)
  • getConfiguration (92-95)
  • searchableAs (78-90)
  • index (114-163)
  • remove (165-198)
  • clear (200-205)
  • assertIsSearchable (389-394)
  • deleteByIndexName (207-210)
  • delete (212-217)
  • search (219-258)
  • rawSearch (260-268)
  • count (270-275)
config/services.php (3)
src/Services/MeilisearchManager.php (1)
  • MeilisearchManager (27-468)
src/EventListener/DoctrineEventSubscriber.php (1)
  • DoctrineEventSubscriber (10-30)
src/Services/SettingsUpdater.php (1)
  • SettingsUpdater (16-77)
tests/Unit/SerializationTest.php (4)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
tests/Entity/Comment.php (1)
  • getId (69-72)
tests/Entity/Post.php (1)
  • getId (91-94)
tests/BaseKernelTestCase.php (1)
  • get (42-45)
src/Command/MeilisearchUpdateSettingsCommand.php (3)
src/Services/MeilisearchManager.php (1)
  • isSearchable (71-76)
src/SearchManagerInterface.php (1)
  • isSearchable (23-23)
src/SearchService.php (1)
  • isSearchable (20-20)
src/SearchManagerInterface.php (1)
src/Exception/NotSearchableException.php (1)
  • NotSearchableException (7-13)
tests/Integration/AggregatorTest.php (2)
src/Model/Aggregator.php (2)
  • getEntityClassFromObjectID (63-72)
  • normalize (74-77)
src/Exception/InvalidEntityForAggregator.php (1)
  • InvalidEntityForAggregator (7-9)
src/Engine.php (2)
src/SearchManagerInterface.php (4)
  • index (48-48)
  • remove (55-55)
  • count (110-110)
  • search (92-92)
src/SearchableObject.php (5)
  • SearchableObject (13-80)
  • getSearchableArray (65-79)
  • getIndexUid (42-45)
  • getPrimaryKey (50-53)
  • getIdentifier (55-58)
🪛 GitHub Actions: Tests
src/Services/MeilisearchManager.php

[error] 27-27: ExcessiveClassComplexity The class MeilisearchManager has an overall complexity of 70 which is very high. The configured complexity threshold is 50.


[error] 27-27: CouplingBetweenObjects The class MeilisearchManager has a coupling between objects value of 13. Consider to reduce the number of dependencies under 13.

src/Command/MeilisearchImportCommand.php

[error] 23-23: CouplingBetweenObjects The class MeilisearchImportCommand has a coupling between objects value of 13. Consider to reduce the number of dependencies under 13.


[error] 71-71: CyclomaticComplexity The method execute() has a Cyclomatic Complexity of 13. The configured cyclomatic complexity threshold is 10.


[error] 71-71: NPathComplexity The method execute() has an NPath complexity of 592. The configured NPath complexity threshold is 200.

src/DependencyInjection/MeilisearchExtension.php

[error] 44-44: IfStatementAssignment Avoid assigning values to variables in if clauses and the like (line '44', column '20').


[error] 36-36: StaticAccess Avoid using static access to class '\Meilisearch\Bundle\MeilisearchBundle' in method 'load'.


[error] 51-51: ElseExpression The method load uses an else expression. Else clauses are basically not necessary and you can simplify the code by not using them.


[error] 59-59: StaticAccess Avoid using static access to class '\Meilisearch\Bundle\MeilisearchBundle' in method 'load'.

src/Services/SettingsUpdater.php

[error] 34-34: CyclomaticComplexity The method update() has a Cyclomatic Complexity of 13. The configured cyclomatic complexity threshold is 10.

src/Services/MeilisearchService.php

[error] 27-27: TooManyPublicMethods The class MeilisearchService has 11 public methods. Consider refactoring MeilisearchService to keep number of public methods under 10.


[error] 27-27: ExcessiveClassComplexity The class MeilisearchService has an overall complexity of 67 which is very high. The configured complexity threshold is 50.


[error] 27-27: CouplingBetweenObjects The class MeilisearchService has a coupling between objects value of 16. Consider to reduce the number of dependencies under 13.


[error] 48-48: LongVariable Avoid excessively long variable names like $classToSerializerGroup. Keep variable name length under 20.


[error] 57-57: StaticAccess Avoid using static access to class '\Symfony\Component\PropertyAccess\PropertyAccess' in method '__construct'.


[error] 415-415: LongVariable Avoid excessively long variable names like $searchableEntitiesChunk. Keep variable name length under 20.


[error] 463-463: StaticAccess Avoid using static access to class '\Doctrine\ORM\Proxy\DefaultProxyClassNameResolver' in method 'resolveClass'.


[error] 467-467: StaticAccess Avoid using static access to class '\Doctrine\Common\Util\ClassUtils' in method 'resolveClass'.

🔇 Additional comments (47)
tests/Integration/AggregatorTest.php (2)

27-28: LGTM!

Formatting improvement adds visual separation before the method call.


31-38: Since I'm unable to access the repository directly to verify the Post entity structure and ContentAggregator constructor signature, I cannot complete the verification. Let me provide a rewritten review comment that reflects this limitation:


Verify that the primary key field names 'objectId' and 'url' exist on the Post entity and match the ContentAggregator constructor expectations.

The test passes ['objectId', 'url'] as an array of primary keys to verify the constructor throws InvalidEntityForAggregator when given multiple keys. Confirm these field names correspond to actual properties on the Post entity and that the constructor's third parameter ('objectId') correctly identifies the single primary key being tested.

tests/Integration/EventListener/DoctrineEventSubscriberTest.php (1)

23-23: LGTM! Consistent migration to the new SearchManagerInterface API.

All search calls are correctly updated to use the new searchManager->search(className, query) signature, removing the entityManager parameter. The test coverage for persist, update, and remove operations with both regular and ObjectId-based entities is appropriate.

Also applies to: 38-38, 59-59, 81-81, 97-97, 107-107, 121-121, 131-131

tests/Unit/ConfigurationTest.php (2)

139-153: LGTM! Configuration tests properly updated for new ORM data provider fields.

The expected configuration correctly includes the new type, data_provider, and primary_key fields with appropriate defaults ('orm', null, 'objectID').


358-402: Good addition of custom primary key test case.

This test case validates the fix for issue #66, ensuring user-defined primary keys (postId, tagId) are properly preserved in the processed configuration rather than defaulting to objectID.

.github/workflows/pre-release-tests.yml (2)

42-65: LGTM! CI matrix correctly updated for new PHP/Symfony requirements.

The matrix properly reflects the version bumps to PHP 8.1+ and Symfony 6.4+. The excludes for PHP 8.1 with Symfony 7.x are correct since Symfony 7 requires PHP 8.2+. The addition of lowest/highest dependency testing provides good edge-case coverage.


84-85: Good improvement to dependency installation.

Using dependency-versions: "${{ matrix.dependencies }}" with the matrix dimension enables proper testing of both lowest and highest dependency versions, improving compatibility assurance.

composer.json (3)

21-29: LGTM! Version constraints properly updated for PHP 8.1+ and Symfony 6.4+.

The unified Symfony version constraints (^6.4 || ^7.0) across all packages ensure consistent compatibility. Removal of symfony/polyfill-php80 is correct since PHP 8.1 is now the minimum.


38-51: Dev dependency updates look appropriate.

Minor version bumps for linting and static analysis tools are safe. The Symfony dev package constraints correctly mirror the production requirements.


32-32: I'll help you verify the review comment about the doctrine/doctrine-bundle breaking change. Let me search for upgrade documentation and verify the actual changes.
<function_calls>

#!/bin/bash

Check for upgrade documentation mentioning the doctrine-bundle change

rg -i -n "doctrine.*bundle|require.*doctrine|breaking" README.md CHANGELOG.md UPGRADE.md UPGRADING.md 2>/dev/null || echo "No matches found in common upgrade docs"

List all markdown files at root to see what documentation exists

echo "=== Documentation files at repository root ==="
fd -e md -x basename {} ; 2>/dev/null | sort


</function_calls>

Now let me verify the actual change in composer.json:
<function_calls>

#!/bin/bash

Check the current state of composer.json

echo "=== Current composer.json (showing relevant sections) ==="
cat composer.json | head -50

echo ""
echo "=== Searching for doctrine-bundle in composer.json ==="
rg -n "doctrine.*bundle" composer.json -A 1 -B 1


</function_calls>

src/SearchableEntity.php (1)

15-17: Deprecation marker and normalization constant update are consistent with the new SearchableObject flow

The deprecation docblock cleanly points callers to Meilisearch\Bundle\SearchableObject, and switching to SearchableObject::NORMALIZATION_FORMAT in both normalization paths keeps this legacy wrapper aligned with the new normalization semantics without changing behavior.

Also applies to: 82-88

.github/workflows/tests.yml (1)

30-31: CI matrix now matches supported PHP/Symfony versions

Restricting the matrix to PHP 8.1–8.4 and Symfony 6.4–7.3, with targeted include/exclude rules, correctly reflects the new platform constraints and avoids unused PHP 7.4 entries.

src/Exception/NotSearchableException.php (1)

1-13: Exception is well-typed and consistent with existing exception patterns

The NotSearchableException constructor signature and message format match the other bundle exceptions and PHP 8.1 typing expectations; nothing to change here.

tests/Kernel.php (1)

34-42: Doctrine test-kernel config switching matches the new support window

Using BlacklistSchemaAssetFilter and LegacyReflectionFields as feature flags to select doctrine.yaml / doctrine_v2.yaml / doctrine_old_proxy.yaml is a reasonable simplification now that PHP 7 support is gone, and it keeps tests adaptable across Doctrine bundle/ORM versions.

src/Exception/DataProviderNotFoundException.php (1)

1-13: Clear, typed exception for missing data providers

The DataProviderNotFoundException cleanly encodes both index and class in the message and uses a properly typed $code parameter, matching the style of other exceptions in the bundle.

src/Command/MeilisearchUpdateSettingsCommand.php (1)

10-11: Command is correctly wired to the new SearchManagerInterface

Passing SearchManagerInterface into the parent IndexCommand and switching the guard to $this->searchManager->isSearchable($entityClassName) aligns this command with the new manager abstraction, while leaving the existing settings-update logic intact.

Also applies to: 22-28, 56-57

tests/Integration/Fixtures/ActorDataProvider.php (1)

1-70: LGTM - Clean test fixture demonstrating custom data provider implementation.

The implementation correctly showcases the flexibility of the DataProviderInterface, with in-memory actor data and appropriate handling for the test context.

src/DependencyInjection/MeilisearchExtension.php (2)

34-42: LGTM - Data provider registration integrated cleanly.

The early parameter initialization and registerDataProviders call before index iteration ensures data providers are available when configuring the manager service.


93-129: Well-structured data provider registration logic.

The method correctly handles three scenarios:

  1. Explicit data_provider configuration → uses service reference
  2. ORM type with Aggregator → creates per-entity OrmEntityProvider definitions
  3. ORM type without Aggregator → creates single OrmEntityProvider definition

The use of xxh32 hash ensures unique service IDs without collisions.

src/Model/Aggregator.php (2)

26-38: Good implementation of configurable primary key.

The constructor now accepts primaryKey as a promoted property, and the normalize() method correctly uses it instead of the hardcoded 'objectID' key. This addresses PR objective #66 (removing objectID assumptions).


74-77: I'm experiencing a technical issue accessing the repository for verification. Let me attempt to provide guidance based on the information available:

Unable to complete automated verification due to repository access issues.

The review comment requests verification that all Aggregator instantiation sites have been updated to pass the primaryKey argument as a breaking change. To properly verify this, I would need to:

  1. Search for all new Aggregator( instantiations across the codebase
  2. Examine MeilisearchManager::index() to confirm it passes 3 arguments
  3. Examine MeilisearchService::getAggregatorsFromEntities() to confirm it passes 3 arguments
  4. Check for any remaining 2-argument instantiation patterns

Since I cannot access the repository at this time, I cannot definitively verify whether:

  • All instantiation sites have been properly updated
  • There are any remaining outdated 2-argument calls
  • The breaking change has been fully propagated

Please retry verification once repository access is restored, or manually confirm the instantiation sites mentioned in the comment have been updated with the new 3-argument primaryKey parameter.

tests/Unit/SerializationTest.php (1)

23-30: LGTM - Test correctly updated for new SearchableObject API.

The test properly uses the new constructor signature with explicit primaryKey parameter and the renamed groups option key.

src/Services/SettingsUpdater.php (1)

22-28: LGTM - Clean dependency injection update.

The constructor correctly uses MeilisearchManager for configuration retrieval while maintaining promoted properties for the client and event dispatcher.

src/DataProvider/OrmEntityProvider.php (2)

1-58: LGTM - Solid ORM data provider implementation.

The implementation correctly uses Doctrine metadata to handle identifier fields dynamically, addressing the previous concern about hardcoded 'id' fields.


54-57: Review requires manual verification of usage context.

The concern raised is theoretically valid based on Doctrine/Symfony architecture. getManagerForClass() returns the same EntityManager service instance (shared by default), and calling clear() on it detaches all managed entities from that instance. This could indeed affect concurrent operations or mid-batch processing if other code is using entities from the same manager.

However, the actual risk depends on when and how cleanup() is called in practice:

  • Safe: If called only at absolute end-of-operation boundaries (after all batch items processed, all indices handled)
  • Risky: If called during active processing where other code still references entities from that manager

Unable to verify the actual call sites and timing due to repository access issues. Manual code inspection is needed to confirm whether cleanup() is consistently called at safe boundaries (as expected in batch/import context) or if there's genuine concurrent access risk.

src/Services/MeilisearchService.php (2)

24-27: LGTM - Deprecation notice correctly points to MeilisearchManager.

The namespace typo from the previous review has been fixed.


50-64: Clean optional manager injection for backward compatibility.

The nullable $manager parameter allows existing code to continue working while enabling delegation to the new manager when injected.

config/services.php (2)

70-78: LGTM - Manager service correctly wired.

The meilisearch.manager service definition matches the MeilisearchManager constructor signature with all required abstract arguments.


28-46: LGTM - Legacy service updated with manager delegation.

The meilisearch.service now receives the manager as its 5th argument, enabling the deprecation bridge pattern. The DoctrineEventSubscriber correctly uses the manager directly.

src/Command/MeilisearchImportCommand.php (4)

27-34: Constructor refactored to use SearchManagerInterface.

The dependency injection now correctly uses the new SearchManagerInterface abstraction, aligning with the provider-based architecture. The parent constructor call properly passes the manager.


119-143: Data provider correctly obtained outside the loop.

The getDataProvider() call is now properly placed before the do-while loop, addressing the previous review feedback. The cleanup() call per iteration is appropriate for releasing resources (e.g., Doctrine's clear()) after each batch.

One observation: the loop condition count($objects) >= $batchSize means if the last batch returns exactly $batchSize objects, an extra iteration with 0 results will occur. This is typically acceptable but worth noting.


160-181: LGTM - Defensive null-coalescing added.

The $formattedResponse[$indexName] ??= 0 on line 166 correctly handles the case where an index hasn't been seen yet, preventing undefined index notices.


206-232: Index swap and deletion logic updated to use manager.

The swapIndices() method correctly uses $this->searchManager->deleteByIndexName() (line 229) instead of direct client calls, maintaining consistency with the new manager-based architecture.

src/SearchManagerInterface.php (3)

1-18: Well-defined interface contract with proper PHPStan types.

The interface is well-structured with clear method contracts and appropriate PHPStan type imports from Engine. The RESULT_KEY_HITS constant provides a consistent way to access search results.


34-41: Template annotation for type-safe data provider.

Good use of generics with @template T of object to ensure type safety between the class parameter and the returned DataProviderInterface<T>.


82-92: Search method returns domain objects, not raw hits.

The search() method correctly returns list<T> (domain objects), distinguishing it from rawSearch() which returns the raw SearchResponse. This separation of concerns is a good design choice.

src/Engine.php (4)

46-48: Clean constructor with property promotion.

Good use of PHP 8.1+ constructor property promotion for concise initialization.


61-90: Index method correctly handles per-index primary keys.

The restructured batch format with ['primaryKey' => ..., 'documents' => []] (lines 78-79) ensures each index uses its own primary key when calling addDocuments() (line 86). This addresses the previous review feedback about primary key handling.


174-181: Simplified ID normalization with Stringable support.

The normalizeId() method now properly handles \Stringable objects by casting to string (line 177), which is cleaner than the previous method_exists check.


115-126: Consider batching document deletions for performance.

The remove() method currently calls deleteDocument() individually for each identifier (lines 119-121). Meilisearch supports batch deletion via deleteDocuments(array $ids) which reduces API calls and improves performance.

However, this refactoring changes the return type from a list of individual document deletion tasks to a single batch task response. Before applying this optimization, verify:

  1. How calling code handles the return value and whether it can adapt to receiving a single task instead of a list
  2. Whether the current per-document approach is intentional for tracking purposes

If callers can handle the return type change, the suggested refactoring is the recommended approach per Meilisearch documentation.

src/Services/MeilisearchManager.php (7)

7-9: Doctrine reflection classes properly imported.

The LegacyReflectionFields and DefaultProxyClassNameResolver classes are now properly imported, addressing the previous review feedback about unimported class constants.


71-76: isSearchable correctly resolves base class name.

The method properly resolves proxy/lazy objects to their base class before checking searchability.


78-90: Null check added for searchableAs.

The null guard on line 85-87 correctly throws NotSearchableException when the index is not found, addressing previous review feedback.


114-163: Index method properly handles aggregators and batching.

The index() method correctly:

  • Resolves matching indices including aggregator support
  • Wraps entities in aggregator objects when needed (lines 130-136)
  • Respects shouldBeIndexed gating (lines 138-140)
  • Batches items per index for efficient processing

165-198: Remove method batches deletions correctly.

The remove() method properly chunks objects by batchSize (line 192) before calling the engine. The array_merge(...$responses) is safe for empty arrays since PHP 7.4+, addressing the previous review feedback.


219-258: Search method loads domain objects via data provider.

The implementation correctly:

  • Validates searchability
  • Extracts identifiers from hits
  • Bulk-loads entities via loadByIdentifiers() (reducing DB queries per PR objective #134)
  • Preserves search result ordering

447-467: Robust class resolution for Doctrine proxies and PHP 8.4 lazy objects.

The resolveClass() method correctly handles:

  • PHP 8.4 native lazy objects (line 453-455)
  • Doctrine ORM v3+ proxies (lines 458-460)
  • Legacy Doctrine ORM (line 463)

The static memoization of the resolver callable is a good optimization.

@norkunas norkunas force-pushed the dataprovider branch 2 times, most recently from 44eb1a0 to a224b01 Compare November 27, 2025 04:47
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
tests/Integration/AggregatorTest.php (1)

53-61: Assertion expects literal string instead of actual entity identifier.

The assertion on Line 61 expects $serializedData['objectId'] to equal the string literal 'objectId', but it should equal the actual Post entity's identifier (the $postId captured on Line 48). The serialized data should contain the real object identifier from the proxy, not the key name itself.

Apply this diff to fix the assertion:

         $proxy = $this->entityManager->getReference(Post::class, $postId);
         $this->assertInstanceOf(Proxy::class, $proxy);
         $contentAggregator = new ContentAggregator($proxy, ['objectId'], 'objectId');
 
         /** @var Serializer $serializer */
         $serializer = $this->get('serializer');
 
         $serializedData = $contentAggregator->normalize($serializer);
 
         $this->assertNotEmpty($serializedData);
-        $this->assertEquals('objectId', $serializedData['objectId']);
+        $this->assertEquals($postId, $serializedData['objectId']);
tests/Integration/EngineTest.php (1)

35-41: Avoid reintroducing Algolia-specific 'objectID' in SearchableObject tests

Both SearchableObject instances still hardcode 'objectID' as the primary key, which goes against the PR’s goal of moving to Meilisearch-style primary_key (typically id) instead of Algolia terminology.

Consider switching these to the configured primary key (very likely 'id') so tests reflect the new semantics:

-        $searchableImage = new SearchableObject(
-            $this->getPrefix().'image',
-            'objectID',
+        $searchableImage = new SearchableObject(
+            $this->getPrefix().'image',
+            'id',
             $image,
             $image->getId(),
             $this->get('serializer'),
             ['groups' => ['force_empty']],
         );
@@
-        $postSearchable1 = new SearchableObject($this->getPrefix().'posts', 'objectID', $post1, $post1->getId(), $serializer);
-        $postSearchable2 = new SearchableObject($this->getPrefix().'posts', 'objectID', $post2, $post2->getId(), $serializer);
+        $postSearchable1 = new SearchableObject($this->getPrefix().'posts', 'id', $post1, $post1->getId(), $serializer);
+        $postSearchable2 = new SearchableObject($this->getPrefix().'posts', 'id', $post2, $post2->getId(), $serializer);

Also applies to: 67-69

src/Services/MeilisearchManager.php (1)

241-246: CRITICAL: Composite key format mismatch between search and indexing.

The composite identifier format in search() differs from getSingleIdentifier():

  • Here (search, line 244): implode('-', $identifiers)"value1-value2"
  • getSingleIdentifier (lines 410-415): "$key-$value__""key1-value1__key2-value2__"

Entities with composite primary keys indexed via getSingleIdentifier() will never be found during search because the lookup key format doesn't match the indexed identifier format.

Apply this diff to align the formats:

         $objectsById = [];
         foreach ($loadedObjects as $object) {
             $identifiers = $dataProvider->getIdentifierValues($object);
-            $key = 1 === \count($identifiers) ? (string) reset($identifiers) : implode('-', $identifiers);
+            if (1 === \count($identifiers)) {
+                $key = (string) reset($identifiers);
+            } else {
+                $key = '';
+                foreach ($identifiers as $idKey => $idValue) {
+                    $key .= $idKey.'-'.$idValue.'__';
+                }
+            }
             $objectsById[$key] = $object;
         }
🧹 Nitpick comments (7)
phpstan-baseline.php (1)

16-21: Defensive isset() check on typed array.

This baseline entry suppresses the warning about isset() on a key that's typed as always existing. While the type system guarantees hits exists, the defensive check is harmless. Consider removing the isset() check in the source if the type is reliable, which would eliminate this baseline entry.

tests/BaseKernelTestCase.php (1)

19-19: Consider using SearchManagerInterface for consistency.

The property is typed with the concrete MeilisearchManager class rather than SearchManagerInterface. While acceptable for test infrastructure, using the interface would be more consistent with the production code patterns in this PR.

-    protected MeilisearchManager $searchManager;
+    protected SearchManagerInterface $searchManager;

This would also require updating the import on line 10.

src/DataProvider/DataProviderInterface.php (1)

20-25: Consider documenting the expected $identifiers structure more precisely.

The @param array<mixed> $identifiers annotation is intentionally flexible, but the two implementations in this PR interpret it differently:

  • OrmEntityProvider: expects a flat array of identifier values (e.g., [1, 2, 3])
  • ActorDataProvider: expects an associative array (e.g., ['id' => 1])

While this flexibility is by design (per maintainer's clarification), adding a brief note in the PHPDoc about the expected structure could help future implementers understand the contract better.

     /**
-     * @param array<mixed> $identifiers
+     * @param array<mixed> $identifiers An array of identifier values. The structure
+     *                                  depends on the caller context and implementation.
      *
      * @return iterable<T>
      */
src/Services/SettingsUpdater.php (1)

22-28: Prefer SearchManagerInterface over concrete MeilisearchManager

SettingsUpdater only needs access to configuration, which is exposed on SearchManagerInterface. Typing the constructor against the concrete MeilisearchManager makes this service harder to reuse or test.

You can loosen the coupling with a minimal change:

-use Meilisearch\Bundle\Services\SettingsUpdater;
+use Meilisearch\Bundle\SearchManagerInterface;
+use Meilisearch\Bundle\Services\SettingsUpdater;
@@
-    public function __construct(
-        MeilisearchManager $searchManager,
+    public function __construct(
+        SearchManagerInterface $searchManager,
         private readonly Client $searchClient,
         private readonly EventDispatcherInterface $eventDispatcher,
     ) {
         $this->configuration = $searchManager->getConfiguration();
     }
src/DataProvider/OrmEntityProvider.php (1)

54-57: Target clear() to the indexed class in cleanup()

The provider looks solid overall (paging, identifier handling, composite-id guard). One small refinement: cleanup() currently calls clear() without arguments, which wipes all managed entities for that manager. To avoid surprising other consumers sharing the same EM, you could restrict it to the configured class:

-    public function cleanup(): void
-    {
-        $this->managerRegistry->getManagerForClass($this->className)->clear();
-    }
+    public function cleanup(): void
+    {
+        $this->managerRegistry->getManagerForClass($this->className)->clear($this->className);
+    }
tests/Unit/SerializationTest.php (1)

23-30: Align SerializationTest with Meilisearch primary_key naming

This test now constructs SearchableObject with 'objectID' as the primary key, even though the assertion only inspects the normalized payload. To keep tests consistent with the new primary_key semantics, consider switching to id:

-        $searchablePost = new SearchableObject(
-            'posts',
-            'objectID',
+        $searchablePost = new SearchableObject(
+            'posts',
+            'id',
             $post,
             $post->getId(),
             self::getContainer()->get('serializer'),
             ['groups' => [SearchableObject::NORMALIZATION_GROUP]]
         );
src/Services/MeilisearchManager.php (1)

329-329: Unnecessary string cast.

The cast (string) $className is redundant since $className is already a class-string from the loop iteration and is_subclass_of() check.

Apply this diff:

-                    $this->aggregators[] = (string) $className;
+                    $this->aggregators[] = $className;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2e54bae and a224b01.

📒 Files selected for processing (55)
  • .github/workflows/pre-release-tests.yml (2 hunks)
  • .github/workflows/tests.yml (1 hunks)
  • composer.json (1 hunks)
  • config/services.php (4 hunks)
  • phpstan-baseline.php (1 hunks)
  • phpstan.dist.neon (1 hunks)
  • src/Command/IndexCommand.php (2 hunks)
  • src/Command/MeilisearchClearCommand.php (1 hunks)
  • src/Command/MeilisearchCreateCommand.php (3 hunks)
  • src/Command/MeilisearchDeleteCommand.php (1 hunks)
  • src/Command/MeilisearchImportCommand.php (7 hunks)
  • src/Command/MeilisearchUpdateSettingsCommand.php (3 hunks)
  • src/DataProvider/DataProviderInterface.php (1 hunks)
  • src/DataProvider/OrmEntityProvider.php (1 hunks)
  • src/DependencyInjection/Configuration.php (2 hunks)
  • src/DependencyInjection/MeilisearchExtension.php (4 hunks)
  • src/Engine.php (5 hunks)
  • src/Event/SettingsUpdatedEvent.php (1 hunks)
  • src/EventListener/ConsoleOutputSubscriber.php (1 hunks)
  • src/EventListener/DoctrineEventSubscriber.php (1 hunks)
  • src/Exception/DataProviderNotFoundException.php (1 hunks)
  • src/Exception/InvalidIndiceException.php (1 hunks)
  • src/Exception/LogicException.php (1 hunks)
  • src/Exception/NotSearchableException.php (1 hunks)
  • src/Model/Aggregator.php (3 hunks)
  • src/SearchManagerInterface.php (1 hunks)
  • src/SearchService.php (1 hunks)
  • src/Searchable.php (1 hunks)
  • src/SearchableEntity.php (2 hunks)
  • src/SearchableObject.php (1 hunks)
  • src/Services/MeilisearchManager.php (1 hunks)
  • src/Services/MeilisearchService.php (9 hunks)
  • src/Services/SettingsUpdater.php (1 hunks)
  • src/Services/UnixTimestampNormalizer.php (1 hunks)
  • tests/BaseKernelTestCase.php (5 hunks)
  • tests/Entity/Actor.php (1 hunks)
  • tests/Entity/Link.php (2 hunks)
  • tests/Entity/SelfNormalizable.php (2 hunks)
  • tests/Entity/Tag.php (2 hunks)
  • tests/Integration/AggregatorTest.php (2 hunks)
  • tests/Integration/Command/MeilisearchClearCommandTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchCreateCommandTest.php (2 hunks)
  • tests/Integration/Command/MeilisearchDeleteCommandTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchImportCommandTest.php (1 hunks)
  • tests/Integration/EngineTest.php (3 hunks)
  • tests/Integration/EventListener/DoctrineEventSubscriberTest.php (8 hunks)
  • tests/Integration/Fixtures/ActorDataProvider.php (1 hunks)
  • tests/Integration/SearchTest.php (4 hunks)
  • tests/Kernel.php (1 hunks)
  • tests/Unit/ConfigurationTest.php (8 hunks)
  • tests/Unit/SerializationTest.php (2 hunks)
  • tests/baseline-ignore (1 hunks)
  • tests/config/doctrine_php7.yaml (0 hunks)
  • tests/config/framework.yaml (1 hunks)
  • tests/config/meilisearch.yaml (1 hunks)
💤 Files with no reviewable changes (1)
  • tests/config/doctrine_php7.yaml
🚧 Files skipped from review as they are similar to previous changes (17)
  • tests/Entity/Actor.php
  • tests/Integration/SearchTest.php
  • src/EventListener/ConsoleOutputSubscriber.php
  • src/SearchService.php
  • src/Exception/InvalidIndiceException.php
  • tests/Integration/Command/MeilisearchCreateCommandTest.php
  • tests/Entity/SelfNormalizable.php
  • tests/config/meilisearch.yaml
  • src/SearchManagerInterface.php
  • .github/workflows/pre-release-tests.yml
  • tests/Integration/EventListener/DoctrineEventSubscriberTest.php
  • tests/baseline-ignore
  • phpstan.dist.neon
  • src/Model/Aggregator.php
  • src/Services/UnixTimestampNormalizer.php
  • tests/Integration/Command/MeilisearchDeleteCommandTest.php
  • .github/workflows/tests.yml
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: norkunas
Repo: meilisearch/meilisearch-symfony PR: 391
File: src/Command/MeilisearchImportCommand.php:117-120
Timestamp: 2025-08-05T04:46:01.119Z
Learning: In meilisearch-symfony project, the maintainer norkunas prefers to keep PRs focused and defer error handling improvements for settings updates to separate PRs, particularly when it involves decisions about partial import behavior when some indexes fail.
📚 Learning: 2025-08-05T04:46:01.119Z
Learnt from: norkunas
Repo: meilisearch/meilisearch-symfony PR: 391
File: src/Command/MeilisearchImportCommand.php:117-120
Timestamp: 2025-08-05T04:46:01.119Z
Learning: In meilisearch-symfony project, the maintainer norkunas prefers to keep PRs focused and defer error handling improvements for settings updates to separate PRs, particularly when it involves decisions about partial import behavior when some indexes fail.

Applied to files:

  • tests/Integration/Fixtures/ActorDataProvider.php
  • src/Command/MeilisearchImportCommand.php
  • tests/Integration/EngineTest.php
  • src/DataProvider/OrmEntityProvider.php
  • src/Services/MeilisearchManager.php
  • src/Engine.php
🧬 Code graph analysis (25)
src/Command/MeilisearchUpdateSettingsCommand.php (3)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/Services/MeilisearchService.php (2)
  • __construct (52-64)
  • isSearchable (66-77)
src/SearchManagerInterface.php (1)
  • isSearchable (23-23)
tests/Entity/Tag.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/Exception/DataProviderNotFoundException.php (2)
src/Exception/InvalidIndiceException.php (1)
  • __construct (9-12)
src/Exception/NotSearchableException.php (1)
  • __construct (9-12)
src/Command/MeilisearchClearCommand.php (4)
src/Engine.php (1)
  • clear (137-140)
src/SearchManagerInterface.php (1)
  • clear (64-64)
src/Services/MeilisearchManager.php (1)
  • clear (200-205)
src/Services/MeilisearchService.php (1)
  • clear (172-183)
src/Services/SettingsUpdater.php (1)
src/SearchManagerInterface.php (1)
  • getConfiguration (32-32)
src/Command/MeilisearchDeleteCommand.php (4)
src/SearchManagerInterface.php (1)
  • deleteByIndexName (71-71)
src/SearchService.php (1)
  • deleteByIndexName (50-50)
src/Services/MeilisearchManager.php (1)
  • deleteByIndexName (207-210)
src/Services/MeilisearchService.php (1)
  • deleteByIndexName (185-194)
src/DependencyInjection/MeilisearchExtension.php (6)
src/DataProvider/OrmEntityProvider.php (1)
  • OrmEntityProvider (10-58)
src/MeilisearchBundle.php (2)
  • MeilisearchBundle (9-22)
  • qualifiedVersion (13-16)
src/Model/Aggregator.php (2)
  • Aggregator (15-84)
  • getEntities (51-54)
src/Entity/Aggregator.php (1)
  • Aggregator (9-11)
src/Document/Aggregator.php (1)
  • Aggregator (9-11)
tests/Entity/ContentAggregator.php (1)
  • getEntities (21-27)
tests/Kernel.php (1)
src/DependencyInjection/MeilisearchExtension.php (1)
  • load (22-73)
tests/Entity/Link.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/DependencyInjection/Configuration.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/EventListener/DoctrineEventSubscriber.php (2)
src/SearchManagerInterface.php (2)
  • index (48-48)
  • remove (55-55)
src/Services/MeilisearchManager.php (2)
  • index (114-163)
  • remove (165-198)
src/Command/MeilisearchImportCommand.php (4)
src/SearchManagerInterface.php (6)
  • getConfiguration (32-32)
  • isSearchable (23-23)
  • getDataProvider (41-41)
  • index (48-48)
  • count (110-110)
  • deleteByIndexName (71-71)
src/Services/MeilisearchManager.php (6)
  • getConfiguration (92-95)
  • isSearchable (71-76)
  • getDataProvider (97-112)
  • index (114-163)
  • count (270-275)
  • deleteByIndexName (207-210)
src/DataProvider/DataProviderInterface.php (2)
  • provide (18-18)
  • cleanup (34-34)
src/DataProvider/OrmEntityProvider.php (2)
  • provide (21-30)
  • cleanup (54-57)
config/services.php (3)
src/Services/MeilisearchManager.php (1)
  • MeilisearchManager (27-468)
src/EventListener/DoctrineEventSubscriber.php (1)
  • DoctrineEventSubscriber (10-30)
src/Services/SettingsUpdater.php (1)
  • SettingsUpdater (16-77)
src/Exception/NotSearchableException.php (2)
src/Exception/DataProviderNotFoundException.php (1)
  • __construct (9-12)
src/Exception/InvalidIndiceException.php (1)
  • __construct (9-12)
src/DataProvider/DataProviderInterface.php (2)
src/DataProvider/OrmEntityProvider.php (4)
  • provide (21-30)
  • loadByIdentifiers (32-45)
  • getIdentifierValues (47-52)
  • cleanup (54-57)
tests/Integration/Fixtures/ActorDataProvider.php (4)
  • provide (15-44)
  • loadByIdentifiers (46-57)
  • getIdentifierValues (59-64)
  • cleanup (66-69)
src/Command/MeilisearchCreateCommand.php (4)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/SearchManagerInterface.php (1)
  • isSearchable (23-23)
src/SearchService.php (1)
  • isSearchable (20-20)
src/Services/MeilisearchManager.php (1)
  • isSearchable (71-76)
tests/Integration/EngineTest.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/DataProvider/OrmEntityProvider.php (2)
src/Exception/LogicException.php (1)
  • LogicException (7-9)
src/DataProvider/DataProviderInterface.php (4)
  • provide (18-18)
  • loadByIdentifiers (25-25)
  • getIdentifierValues (32-32)
  • cleanup (34-34)
tests/Unit/SerializationTest.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/SearchableObject.php (4)
src/SearchableEntity.php (3)
  • __construct (42-56)
  • getIndexUid (58-61)
  • getSearchableArray (66-91)
tests/Entity/Link.php (1)
  • normalize (74-85)
tests/Entity/SelfNormalizable.php (1)
  • normalize (64-75)
tests/Entity/Tag.php (1)
  • normalize (88-100)
src/Command/IndexCommand.php (2)
src/SearchManagerInterface.php (1)
  • getConfiguration (32-32)
src/Services/MeilisearchManager.php (1)
  • getConfiguration (92-95)
tests/Integration/AggregatorTest.php (2)
src/Model/Aggregator.php (2)
  • getEntityClassFromObjectID (69-78)
  • normalize (80-83)
src/Exception/InvalidEntityForAggregator.php (1)
  • InvalidEntityForAggregator (7-9)
src/Services/MeilisearchManager.php (9)
src/Engine.php (8)
  • Engine (44-182)
  • __construct (46-48)
  • index (61-90)
  • remove (100-126)
  • clear (137-140)
  • delete (147-150)
  • search (159-162)
  • count (169-172)
src/Exception/DataProviderNotFoundException.php (2)
  • DataProviderNotFoundException (7-13)
  • __construct (9-12)
src/Exception/NotSearchableException.php (2)
  • NotSearchableException (7-13)
  • __construct (9-12)
src/Model/Aggregator.php (2)
  • Aggregator (15-84)
  • __construct (32-44)
src/SearchableObject.php (2)
  • SearchableObject (13-80)
  • __construct (28-37)
src/EventListener/ConsoleOutputSubscriber.php (1)
  • __construct (13-15)
src/EventListener/DoctrineEventSubscriber.php (1)
  • __construct (12-14)
src/SearchManagerInterface.php (12)
  • isSearchable (23-23)
  • searchableAs (30-30)
  • getConfiguration (32-32)
  • index (48-48)
  • getDataProvider (41-41)
  • remove (55-55)
  • clear (64-64)
  • deleteByIndexName (71-71)
  • delete (80-80)
  • search (92-92)
  • count (110-110)
  • rawSearch (102-102)
src/DataProvider/DataProviderInterface.php (2)
  • getIdentifierValues (32-32)
  • loadByIdentifiers (25-25)
src/Services/MeilisearchService.php (2)
src/EventListener/DoctrineEventSubscriber.php (1)
  • __construct (12-14)
src/Services/MeilisearchManager.php (13)
  • isSearchable (71-76)
  • getBaseClassName (300-307)
  • getConfiguration (92-95)
  • searchableAs (78-90)
  • index (114-163)
  • remove (165-198)
  • clear (200-205)
  • assertIsSearchable (389-394)
  • deleteByIndexName (207-210)
  • delete (212-217)
  • search (219-258)
  • rawSearch (260-268)
  • count (270-275)
tests/BaseKernelTestCase.php (3)
src/Services/MeilisearchManager.php (3)
  • MeilisearchManager (27-468)
  • getConfiguration (92-95)
  • deleteByIndexName (207-210)
src/Collection.php (2)
  • get (30-37)
  • Collection (13-380)
src/SearchManagerInterface.php (2)
  • getConfiguration (32-32)
  • deleteByIndexName (71-71)
🔇 Additional comments (37)
tests/config/framework.yaml (1)

5-7: Sensible additions to test error handling configuration.

The two new framework configuration keys enhance error logging and handling during test execution:

  • handle_all_throwables: true ensures comprehensive error handling, which is appropriate for catching issues early in tests
  • php_errors: { log: true } enables PHP error logging, improving observability

These align well with the PR's broader refactoring toward improved exception handling and new exception classes.

Please verify that these settings don't introduce unexpected logging verbosity or test failures in your CI pipeline, particularly if tests check for specific error counts or output.

src/Event/SettingsUpdatedEvent.php (1)

16-21: LGTM! Clean modernization to constructor property promotion.

The refactor to use constructor property promotion with readonly modifiers is a good improvement that reduces boilerplate while maintaining the same public API. The immutability provided by readonly is appropriate for an event object.

tests/Integration/Command/MeilisearchClearCommandTest.php (1)

39-39: LGTM!

The test expectation correctly includes the new actor index in the cleared indices output, aligning with the new Actor entity and ActorDataProvider introduced in this PR.

src/Exception/LogicException.php (1)

1-9: LGTM!

Clean exception wrapper that provides a bundle-specific LogicException type, following the same pattern as other exceptions in Meilisearch\Bundle\Exception namespace.

src/SearchableEntity.php (2)

15-17: Good deprecation notice.

The deprecation annotation clearly points users to the replacement class SearchableObject.


83-83: Based on my research, I've identified an issue with the review comment. The web search consistently shows that the correct class is \Meilisearch\Bundle\Searchable, not SearchableObject. Let me provide the rewritten review comment:


Verify the class name reference.

The code at lines 83 and 87 references SearchableObject::NORMALIZATION_FORMAT, but the documented constant in the Meilisearch Symfony bundle is \Meilisearch\Bundle\Searchable::NORMALIZATION_FORMAT (not SearchableObject). Confirm whether the code should use Searchable instead, or if SearchableObject is a custom class in this codebase.

src/Exception/NotSearchableException.php (1)

7-13: LGTM!

Well-structured exception class following the same pattern as sibling exceptions (InvalidIndiceException, DataProviderNotFoundException). The int type hint on $code is correctly applied.

phpstan-baseline.php (1)

4-15: Verify runtime guards for optional Doctrine dependency.

These baseline entries suppress ClassUtils::getClass() errors since Doctrine is now a dev dependency. Ensure that usages of ClassUtils in MeilisearchManager.php and MeilisearchService.php are guarded with class_exists() checks at runtime to prevent errors when Doctrine is not installed.

tests/Integration/AggregatorTest.php (1)

31-37: LGTM!

The renamed test method clearly validates that the aggregator correctly rejects entities with composite primary keys by throwing InvalidEntityForAggregator. This aligns with the single-primary-key requirement for aggregation.

src/SearchableObject.php (1)

1-80: LGTM!

The SearchableObject class is well-implemented with:

  • Clear separation of concerns (wraps an object for Meilisearch indexing)
  • Proper use of PHP 8.1 promoted properties and readonly declarations
  • Immutable design with local context handling in getSearchableArray() (no instance state mutation)
  • Correct type declarations throughout

The past issues have been successfully addressed (property renamed to $identifier for linting compliance, and context handling fixed to avoid side effects).

config/services.php (1)

12-122: LGTM! Manager-based architecture properly wired.

The dependency injection configuration correctly introduces the manager-based workflow:

  • New meilisearch.manager service is the primary abstraction with proper dependencies (normalizer, engine, property accessor, configuration, data providers)
  • SearchManagerInterface properly aliased to the manager
  • All consumers (commands, event subscriber, settings updater) now depend on the manager instead of the old service
  • MeilisearchService updated to depend on the manager (enabling delegation for backward compatibility)

This aligns perfectly with the PR objectives to introduce a data provider abstraction and manager-based architecture.

composer.json (1)

20-51: LGTM! Dependency updates align with PR objectives.

The dependency updates introduce breaking changes that are well-justified:

  1. PHP 8.1 minimum (Line 21): Enables modern features like promoted properties and readonly declarations used throughout the codebase
  2. Symfony 6.4/7.0 (Lines 24-29): Reasonable baseline for 2025, drops EOL Symfony 5.x
  3. Doctrine optional (Line 32 moved to require-dev): Critical for the data provider abstraction goal—enables indexing of non-ORM data sources

The PR description explicitly lists this as v0.x where breaking changes are acceptable, and these changes directly support the architectural improvements (manager-based flow, data provider abstraction, configurable primary keys).

src/Engine.php (2)

46-90: LGTM! Primary key now correctly tracked per index.

The index() method properly handles multiple indexes with different primary keys:

  • Line 78: Each index maintains its own primaryKey and documents array in the batch structure
  • Line 86: addDocuments() called with the correct per-index $batch['primaryKey']
  • Promoted properties (Line 46) follow PHP 8.1 best practices

The past issue where a single $primaryKey was used for all indexes has been successfully resolved.


100-126: LGTM! Remove method correctly updated for SearchableObject.

The remove() method properly:

  • Accepts SearchableObject|array parameter type
  • Groups identifiers by index
  • Deletes documents individually with correct identifiers

The implementation is consistent with the new SearchableObject API.

src/Command/MeilisearchClearCommand.php (1)

30-30: LGTM! Consistent migration to manager interface.

The change from $this->searchService->clear() to $this->searchManager->clear() aligns with the manager-based architecture introduced throughout the PR. The command functionality remains unchanged.

tests/Entity/Tag.php (1)

9-90: LGTM! Correct migration to SearchableObject API.

The import and constant reference have been properly updated from the old Searchable class to the new SearchableObject class. The normalize method logic remains unchanged and correctly checks the format before normalizing.

tests/Integration/Command/MeilisearchImportCommandTest.php (1)

181-189: LGTM! Test expectation adjusted for batch processing order.

The reordering of log lines (Lines 183-184) reflects a change in how the manager processes entities that match multiple indexes. The Tag entities are still correctly indexed into both sf_phpunit__tags and sf_phpunit__aggregated indexes; only the logging order has changed. This is expected when refactoring to the manager-based batch processing flow.

src/Command/MeilisearchDeleteCommand.php (1)

32-36: LGTM: Proper error handling with continue statement.

The migration to searchManager and the addition of continue in the catch block correctly prevents the success message from being displayed when deletion fails.

tests/Entity/Link.php (1)

9-9: LGTM: Correct migration to SearchableObject.

The references to Searchable have been properly updated to use the new SearchableObject class, aligning with the deprecation of Searchable.

Also applies to: 76-76

tests/Unit/ConfigurationTest.php (2)

139-141: LGTM: Test expectations updated for new ORM configuration fields.

The test cases correctly validate the new default values for type, data_provider, and primary_key fields introduced in the configuration structure.

Also applies to: 150-152, 195-197, 206-208, 246-248, 287-289, 328-330


359-402: LGTM: Custom primary key test case added.

This new test case properly validates that custom primary keys (e.g., postId, tagId) can be configured per index, addressing the requirement to move away from hardcoded objectID naming.

src/DependencyInjection/Configuration.php (2)

7-7: LGTM: Migration to SearchableObject.

The import and references have been correctly updated from the deprecated Searchable to SearchableObject.

Also applies to: 63-63, 67-68


53-61: Verify 'objectID' default aligns with goal to remove Algolia-specific naming.

While the new configuration correctly enables custom primary keys (addressing issue #66), the default value is still 'objectID', which is Algolia-specific. Consider whether the default should be a more generic value like 'id' or whether backward compatibility justifies keeping 'objectID'.

Given that this is a breaking change release (v0.16) and issue #66 specifically calls for removing Algolia-specific objectID naming, using 'id' as the default might better align with the PR objectives. However, if extensive existing configurations rely on objectID, the current default may be appropriate for migration purposes.

Based on PR objectives, specifically issue #66.

tests/Kernel.php (1)

36-42: LGTM: Simplified Doctrine configuration loading.

The conditional logic has been streamlined to remove complex PHP version guards, aligning with the bumped minimum PHP requirement (8.1) mentioned in the PR objectives. This improves maintainability.

src/Exception/DataProviderNotFoundException.php (1)

1-13: LGTM: Well-designed exception class.

The exception follows the established pattern in the codebase (consistent with NotSearchableException and InvalidIndiceException). The message format clearly identifies both the index and class, which will aid debugging when data provider resolution fails.

src/Searchable.php (1)

7-20: LGTM: Proper deprecation notices.

The class and constants are correctly deprecated with clear guidance to use SearchableObject instead. This provides a backward-compatible migration path while guiding users to the new API.

src/Command/IndexCommand.php (1)

8-8: LGTM: Clean migration to SearchManagerInterface.

The command has been properly updated to use SearchManagerInterface instead of the deprecated SearchService. The use of constructor property promotion is a nice touch that aligns with modern PHP practices and the bumped minimum requirement.

Also applies to: 19-21, 28-28

src/Command/MeilisearchUpdateSettingsCommand.php (1)

22-28: LGTM!

The constructor migration to SearchManagerInterface with constructor promotion is clean and consistent with the parent class IndexCommand which also expects SearchManagerInterface. The property access at line 56 correctly uses the new $this->searchManager property.

tests/BaseKernelTestCase.php (1)

47-52: Verify that waiting for all tasks is intentional.

The previous implementation likely waited for specific tasks, but this now iterates over all tasks in the Meilisearch instance via getTasks(). This could:

  1. Be slow if there are many pending tasks from concurrent test runs
  2. Wait for unrelated tasks from other indexes

Consider filtering tasks by the test's prefix/indices or tracking specific task UIDs returned from indexing operations if this causes test flakiness or slowdowns.

tests/Integration/Fixtures/ActorDataProvider.php (1)

1-70: LGTM!

The test fixture correctly demonstrates a custom DataProviderInterface implementation with hardcoded data. The different approach to loadByIdentifiers (using associative array keys) compared to OrmEntityProvider is intentional, showcasing the flexibility of the data provider abstraction as confirmed by the maintainer in previous review discussions.

src/DependencyInjection/MeilisearchExtension.php (2)

110-117: Verify that calling static methods on entity classes during container compilation is safe.

The code calls $class::getEntities() during the DI extension's load() phase. This works because:

  1. The class must be autoloadable at container compilation time
  2. getEntities() is a static method returning class names (no runtime dependencies)

However, if an Aggregator's getEntities() method has side effects or dependencies on runtime services, this could fail during cache warmup. The current design appears safe given that Aggregator::getEntities() returns a simple array of class names per the relevant code snippets.


93-129: Well-structured data provider registration.

The registerDataProviders() method cleanly handles three scenarios:

  1. Explicit data_provider configuration → uses the referenced service
  2. ORM type with Aggregator → creates per-entity OrmEntityProvider definitions
  3. ORM type without Aggregator → creates single OrmEntityProvider definition

The nested structure $dataProviders[indexName][className] properly supports aggregated indices where multiple entity classes share the same index.

src/DataProvider/DataProviderInterface.php (1)

7-35: LGTM!

The interface is well-designed with:

  • Proper generic template annotation for type safety
  • Clear method contracts with appropriate return types
  • Flexible iterable return type for loadByIdentifiers() allowing future generator implementations
  • Good use of PHPDoc types (positive-int, non-negative-int, non-empty-array)

This provides a solid abstraction for custom data providers addressing the PR objectives (supporting non-Doctrine data sources, batching, etc.).

src/EventListener/DoctrineEventSubscriber.php (1)

8-29: Doctrine event wiring to SearchManagerInterface looks good

The subscriber now delegates directly to SearchManagerInterface::index()/remove() with the domain object, which is consistent with the new provider-based manager API and removes the old ObjectManager coupling. No issues spotted.

src/Command/MeilisearchCreateCommand.php (1)

23-30: Create command migration to SearchManagerInterface is consistent

The command now injects SearchManagerInterface and uses isSearchable() from the manager before creating/updating indices, which is aligned with the new provider-based architecture. The rest of the flow (settings update and index creation) remains unchanged.

Also applies to: 66-79

src/Services/MeilisearchService.php (1)

24-27: Deprecation bridge to SearchManagerInterface looks sound

The introduction of an optional SearchManagerInterface in the constructor, combined with per-method deprecation notices and conditional delegation (if (null !== $this->manager) ...), provides a clear migration path while preserving existing behavior when no manager is wired. The corrected docblock target (Meilisearch\Bundle\Services\MeilisearchManager) and the string-cast aggregator mapping also look consistent with the rest of the refactor.

Also applies to: 50-59, 66-72, 87-96, 98-112, 114-147, 149-170, 172-183, 185-207, 209-254, 256-283, 285-303, 334-353, 377-432

src/Services/MeilisearchManager.php (1)

447-468: I'll verify the PHP 8.4 + LegacyReflectionFields compatibility logic by examining the actual code and researching the correct behavior.
<function_calls>

First, let's verify the actual code at lines 447-468

cd /tmp && git clone --depth 1 https://github.com/meilisearch/meilisearch-php.git 2>&1 | tail -5

Check the file

cat -n /tmp/meilisearch-php/src/Services/MeilisearchManager.php | sed -n '447,468p'


</function_calls>

Now let me search for information about Doctrine's handling of PHP 8.4 native lazy objects and LegacyReflectionFields:
<function_calls>


Doctrine ORM PHP 8.4 native lazy objects LegacyReflectionFields proxy class resolution


</function_calls>

<function_calls>


Doctrine Persistence LegacyReflectionFields version history removed


</function_calls>

<function_calls>


PHP 8.4 Reflection API lazy loading objects get_class() proxy unwrapping


</function_calls>

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (4)
tests/Integration/Command/MeilisearchImportCommandTest.php (1)

181-189: Ordering of aggregated vs concrete index log lines differs from other tests

Here the batch into sf_phpunit__aggregated is logged before the batch into sf_phpunit__tags, whereas other tests typically log the concrete index first and the aggregated index second. If the new data‑provider flow intentionally changed the import order for this case, the expectation is fine; otherwise, consider aligning the order for consistency across tests and log output.

phpstan-baseline.php (1)

1-23: PhpStan baseline entries are narrow and appropriate for the manager transition

The new ignore rules are tightly scoped to the manager/service migration (ClassUtils::getClass and the always-present hits offset) and limited to a single occurrence per file, which is reasonable for a temporary baseline. Consider revisiting these once the Doctrine dependency and result-shape handling are fully settled to keep the baseline small.

src/DependencyInjection/MeilisearchExtension.php (1)

75-91: registerDataProviders() correctly builds per-index, per-class providers with sensible defaults

The registration logic:

  • Prefers an explicitly configured data_provider service per index/class.
  • Falls back to 'orm' type by creating OrmEntityProvider instances for Doctrine-backed classes.
  • Special-cases classes extending Model\Aggregator by expanding to their getEntities() list so aggregated classes each get their own ORM data provider.
  • Returns a simple [indexName][className] => DataProviderInterface map that is easy for MeilisearchManager to consume.

This matches the data-provider extension point you describe and keeps Doctrine-specific wiring fully behind the 'orm' type. If you want to appease the static “ElseExpression” warning later, you could flatten the nested if/else blocks with early continues, but that’s optional and non-functional. Based on learnings, I’m intentionally not suggesting additional error-handling or fallback behavior here to keep this PR focused.

Also applies to: 93-129

tests/Integration/AggregatorTest.php (1)

69-83: Clarify test intent: verify aggregator's primary key field.

The test creates a ContentAggregator with only 2 arguments (line 74), so primaryKey defaults to 'objectID'. However, the assertion on line 82 checks for the 'id' field, which likely comes from the Post entity's normalized output rather than the aggregator's primary key field.

To properly test the aggregator's primary key behavior, consider adding an assertion that explicitly verifies the 'objectID' field exists and contains the expected value:

         $serializedData = $contentAggregator->normalize($serializer);

         $this->assertNotEmpty($serializedData);
+        $this->assertSame($post->getId(), $serializedData['objectID'], 'Aggregator should add objectID field');
         $this->assertSame($post->getId(), $serializedData['id']);

This makes it clearer that the aggregator is correctly adding the primary key field to the normalized data.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a224b01 and 5b07648.

📒 Files selected for processing (55)
  • .github/workflows/pre-release-tests.yml (2 hunks)
  • .github/workflows/tests.yml (1 hunks)
  • composer.json (1 hunks)
  • config/services.php (4 hunks)
  • phpstan-baseline.php (1 hunks)
  • phpstan.dist.neon (1 hunks)
  • src/Command/IndexCommand.php (2 hunks)
  • src/Command/MeilisearchClearCommand.php (1 hunks)
  • src/Command/MeilisearchCreateCommand.php (3 hunks)
  • src/Command/MeilisearchDeleteCommand.php (1 hunks)
  • src/Command/MeilisearchImportCommand.php (7 hunks)
  • src/Command/MeilisearchUpdateSettingsCommand.php (3 hunks)
  • src/DataProvider/DataProviderInterface.php (1 hunks)
  • src/DataProvider/OrmEntityProvider.php (1 hunks)
  • src/DependencyInjection/Configuration.php (2 hunks)
  • src/DependencyInjection/MeilisearchExtension.php (4 hunks)
  • src/Engine.php (5 hunks)
  • src/Event/SettingsUpdatedEvent.php (1 hunks)
  • src/EventListener/ConsoleOutputSubscriber.php (1 hunks)
  • src/EventListener/DoctrineEventSubscriber.php (1 hunks)
  • src/Exception/DataProviderNotFoundException.php (1 hunks)
  • src/Exception/InvalidIndiceException.php (1 hunks)
  • src/Exception/LogicException.php (1 hunks)
  • src/Exception/NotSearchableException.php (1 hunks)
  • src/Model/Aggregator.php (3 hunks)
  • src/SearchManagerInterface.php (1 hunks)
  • src/SearchService.php (1 hunks)
  • src/Searchable.php (1 hunks)
  • src/SearchableEntity.php (2 hunks)
  • src/SearchableObject.php (1 hunks)
  • src/Services/MeilisearchManager.php (1 hunks)
  • src/Services/MeilisearchService.php (9 hunks)
  • src/Services/SettingsUpdater.php (2 hunks)
  • src/Services/UnixTimestampNormalizer.php (1 hunks)
  • tests/BaseKernelTestCase.php (5 hunks)
  • tests/Entity/Actor.php (1 hunks)
  • tests/Entity/Link.php (2 hunks)
  • tests/Entity/SelfNormalizable.php (2 hunks)
  • tests/Entity/Tag.php (2 hunks)
  • tests/Integration/AggregatorTest.php (2 hunks)
  • tests/Integration/Command/MeilisearchClearCommandTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchCreateCommandTest.php (2 hunks)
  • tests/Integration/Command/MeilisearchDeleteCommandTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchImportCommandTest.php (1 hunks)
  • tests/Integration/EngineTest.php (3 hunks)
  • tests/Integration/EventListener/DoctrineEventSubscriberTest.php (8 hunks)
  • tests/Integration/Fixtures/ActorDataProvider.php (1 hunks)
  • tests/Integration/SearchTest.php (4 hunks)
  • tests/Kernel.php (1 hunks)
  • tests/Unit/ConfigurationTest.php (8 hunks)
  • tests/Unit/SerializationTest.php (2 hunks)
  • tests/baseline-ignore (1 hunks)
  • tests/config/doctrine_php7.yaml (0 hunks)
  • tests/config/framework.yaml (1 hunks)
  • tests/config/meilisearch.yaml (1 hunks)
💤 Files with no reviewable changes (1)
  • tests/config/doctrine_php7.yaml
🚧 Files skipped from review as they are similar to previous changes (26)
  • tests/Entity/Tag.php
  • src/DataProvider/DataProviderInterface.php
  • tests/Entity/SelfNormalizable.php
  • src/DependencyInjection/Configuration.php
  • phpstan.dist.neon
  • src/EventListener/DoctrineEventSubscriber.php
  • tests/Integration/Fixtures/ActorDataProvider.php
  • src/EventListener/ConsoleOutputSubscriber.php
  • src/SearchableObject.php
  • src/Event/SettingsUpdatedEvent.php
  • tests/config/framework.yaml
  • tests/Integration/EventListener/DoctrineEventSubscriberTest.php
  • tests/Unit/ConfigurationTest.php
  • src/SearchableEntity.php
  • tests/Entity/Link.php
  • src/Services/MeilisearchManager.php
  • src/Command/MeilisearchCreateCommand.php
  • tests/Integration/Command/MeilisearchCreateCommandTest.php
  • src/Exception/NotSearchableException.php
  • src/Command/MeilisearchClearCommand.php
  • src/SearchService.php
  • tests/config/meilisearch.yaml
  • src/Command/MeilisearchDeleteCommand.php
  • tests/Entity/Actor.php
  • src/Command/IndexCommand.php
  • src/Services/UnixTimestampNormalizer.php
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: norkunas
Repo: meilisearch/meilisearch-symfony PR: 391
File: src/Command/MeilisearchImportCommand.php:117-120
Timestamp: 2025-08-05T04:46:01.119Z
Learning: In meilisearch-symfony project, the maintainer norkunas prefers to keep PRs focused and defer error handling improvements for settings updates to separate PRs, particularly when it involves decisions about partial import behavior when some indexes fail.
📚 Learning: 2025-08-05T04:46:01.119Z
Learnt from: norkunas
Repo: meilisearch/meilisearch-symfony PR: 391
File: src/Command/MeilisearchImportCommand.php:117-120
Timestamp: 2025-08-05T04:46:01.119Z
Learning: In meilisearch-symfony project, the maintainer norkunas prefers to keep PRs focused and defer error handling improvements for settings updates to separate PRs, particularly when it involves decisions about partial import behavior when some indexes fail.

Applied to files:

  • tests/Integration/EngineTest.php
  • src/Command/MeilisearchImportCommand.php
  • src/DataProvider/OrmEntityProvider.php
  • src/Engine.php
  • .github/workflows/tests.yml
🧬 Code graph analysis (13)
src/Services/SettingsUpdater.php (2)
src/SearchManagerInterface.php (1)
  • getConfiguration (32-32)
src/Services/MeilisearchManager.php (1)
  • getConfiguration (92-95)
src/Command/MeilisearchUpdateSettingsCommand.php (6)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/Command/MeilisearchCreateCommand.php (1)
  • __construct (23-30)
src/Command/MeilisearchImportCommand.php (1)
  • __construct (27-34)
src/Services/SettingsUpdater.php (1)
  • SettingsUpdater (17-78)
src/SearchManagerInterface.php (1)
  • isSearchable (23-23)
src/SearchService.php (1)
  • isSearchable (20-20)
src/Services/MeilisearchService.php (2)
src/SearchManagerInterface.php (11)
  • isSearchable (23-23)
  • getConfiguration (32-32)
  • searchableAs (30-30)
  • index (48-48)
  • remove (55-55)
  • clear (64-64)
  • deleteByIndexName (71-71)
  • delete (80-80)
  • search (92-92)
  • rawSearch (102-102)
  • count (110-110)
src/Services/MeilisearchManager.php (12)
  • isSearchable (71-76)
  • getBaseClassName (300-307)
  • getConfiguration (92-95)
  • searchableAs (78-90)
  • index (114-163)
  • remove (165-198)
  • clear (200-205)
  • deleteByIndexName (207-210)
  • delete (212-217)
  • search (219-258)
  • rawSearch (260-268)
  • count (270-275)
tests/Integration/EngineTest.php (3)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
tests/BaseKernelTestCase.php (2)
  • getPrefix (37-40)
  • get (42-45)
tests/Entity/Image.php (1)
  • getId (39-42)
src/DataProvider/OrmEntityProvider.php (2)
src/Exception/LogicException.php (1)
  • LogicException (7-9)
src/DataProvider/DataProviderInterface.php (4)
  • provide (18-18)
  • loadByIdentifiers (25-25)
  • getIdentifierValues (32-32)
  • cleanup (34-34)
src/SearchManagerInterface.php (1)
src/Exception/NotSearchableException.php (1)
  • NotSearchableException (7-13)
tests/Integration/AggregatorTest.php (3)
src/Model/Aggregator.php (2)
  • getEntityClassFromObjectID (69-78)
  • normalize (80-83)
src/Exception/EntityNotFoundInObjectID.php (1)
  • EntityNotFoundInObjectID (7-9)
src/Exception/InvalidEntityForAggregator.php (1)
  • InvalidEntityForAggregator (7-9)
tests/BaseKernelTestCase.php (4)
src/Collection.php (2)
  • get (30-37)
  • Collection (13-380)
src/SearchManagerInterface.php (2)
  • getConfiguration (32-32)
  • deleteByIndexName (71-71)
src/SearchService.php (2)
  • getConfiguration (27-27)
  • deleteByIndexName (50-50)
src/Services/MeilisearchManager.php (2)
  • getConfiguration (92-95)
  • deleteByIndexName (207-210)
src/DependencyInjection/MeilisearchExtension.php (6)
src/DataProvider/OrmEntityProvider.php (1)
  • OrmEntityProvider (10-58)
src/MeilisearchBundle.php (2)
  • MeilisearchBundle (9-22)
  • qualifiedVersion (13-16)
src/Model/Aggregator.php (2)
  • Aggregator (15-84)
  • getEntities (51-54)
src/Document/Aggregator.php (1)
  • Aggregator (9-11)
src/Entity/Aggregator.php (1)
  • Aggregator (9-11)
tests/Entity/ContentAggregator.php (1)
  • getEntities (21-27)
tests/Integration/SearchTest.php (5)
tests/BaseKernelTestCase.php (1)
  • waitForAllTasks (47-52)
src/Engine.php (1)
  • search (159-162)
src/SearchManagerInterface.php (2)
  • search (92-92)
  • rawSearch (102-102)
src/Services/MeilisearchManager.php (2)
  • search (219-258)
  • rawSearch (260-268)
src/Services/MeilisearchService.php (2)
  • search (209-254)
  • rawSearch (256-270)
src/Exception/InvalidIndiceException.php (2)
src/Exception/DataProviderNotFoundException.php (1)
  • __construct (9-12)
src/Exception/NotSearchableException.php (1)
  • __construct (9-12)
config/services.php (3)
src/Services/MeilisearchManager.php (1)
  • MeilisearchManager (27-468)
src/EventListener/DoctrineEventSubscriber.php (1)
  • DoctrineEventSubscriber (10-30)
src/Services/SettingsUpdater.php (1)
  • SettingsUpdater (17-78)
tests/Unit/SerializationTest.php (2)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
tests/Entity/Post.php (1)
  • getId (91-94)
🪛 GitHub Actions: Tests
src/Services/SettingsUpdater.php

[warning] 35-35: CyclomaticComplexity: The method update() has a Cyclomatic Complexity of 13. Threshold is 10.

src/Services/MeilisearchService.php

[warning] 27-27: TooManyPublicMethods: The class MeilisearchService has 11 public methods. Consider refactoring to keep under 10.


[warning] 27-27: ExcessiveClassComplexity: The class MeilisearchService has an overall complexity of 67 (threshold 50).


[warning] 27-27: CouplingBetweenObjects: The class MeilisearchService has a coupling between objects value of 16. Consider reducing dependencies under 13.


[warning] 48-48: LongVariable: Avoid excessively long variable names like $classToSerializerGroup. Keep length under 20.


[warning] 57-57: StaticAccess: Avoid using static access to class '\Symfony\Component\PropertyAccess\PropertyAccess' in method '__construct'.


[warning] 415-415: LongVariable: Avoid excessively long variable names like $searchableEntitiesChunk. Keep length under 20.


[warning] 463-463: StaticAccess: Avoid using static access to class '\Doctrine\ORM\Proxy\DefaultProxyClassNameResolver' in method 'resolveClass'.


[warning] 467-467: StaticAccess: Avoid using static access to class '\Doctrine\Common\Util\ClassUtils' in method 'resolveClass'.

src/Command/MeilisearchImportCommand.php

[warning] 23-23: CouplingBetweenObjects: The class MeilisearchImportCommand has a coupling between objects value of 13. Consider reducing dependencies under 13.


[warning] 71-71: CyclomaticComplexity: The method execute() has a Cyclomatic Complexity of 14. Threshold is 10.


[warning] 71-71: NPathComplexity: The method execute() has an NPath complexity of 976. Threshold is 200.

src/DependencyInjection/MeilisearchExtension.php

[warning] 44-44: IfStatementAssignment: Avoid assigning values to variables in if clauses (line 44).


[warning] 36-36: StaticAccess: Avoid using static access to class '\Meilisearch\Bundle\MeilisearchBundle' in method 'load'.


[warning] 51-51: ElseExpression: The method load uses an else expression. Consider simplifying.


[warning] 59-59: StaticAccess: Avoid using static access to class '\Meilisearch\Bundle\MeilisearchBundle' in method 'load'.


[warning] 118-118: ElseExpression: The method registerDataProviders uses an else expression. Consider simplifying.

🔇 Additional comments (43)
src/Exception/LogicException.php (1)

7-9: LGTM!

The implementation follows best practices for a domain-specific exception: marked as final to prevent inheritance issues, uses strict types, and provides type specificity for the new manager-based architecture.

src/Exception/InvalidIndiceException.php (1)

9-11: Constructor type-hint is consistent; please verify caller argument types

Typing $code as int improves consistency with other exceptions in this bundle and is idiomatic in PHP 8.1+. With declare(strict_types=1);, this is a minor BC break if any callers previously passed non-int codes (e.g., strings). Please confirm all usages either omit $code or pass an int.

You can quickly inspect usages with:

#!/bin/bash
# Inspect all instantiations of InvalidIndiceException and their argument lists
rg -n "InvalidIndiceException" -C3
tests/baseline-ignore (1)

5-5: Deprecation baseline updated appropriately.

Adding the Doctrine ORM proxy deprecation to the baseline is correct given the continued use of Doctrine in dev/test scenarios. This allows tests to run cleanly while acknowledging the upstream deprecation in Doctrine ORM 3.x.

src/Exception/DataProviderNotFoundException.php (1)

1-13: LGTM! Clean exception implementation.

The exception class is well-designed with:

  • Appropriate base class (InvalidArgumentException) for "not found" scenarios
  • Clear, informative error message including both index and class context
  • Standard exception chaining support
.github/workflows/pre-release-tests.yml (1)

42-86: CI matrix correctly updated for new version requirements.

The workflow updates properly reflect the breaking changes:

  • PHP 8.1–8.4 and Symfony 6.4–7.3 matrix coverage
  • Appropriate exclusions for incompatible PHP/Symfony combinations
  • Dependency variation testing (lowest/highest) for boundary cases
  • Job naming clearly indicates non-default dependency scenarios
config/services.php (3)

70-78: Manager architecture correctly introduced.

The new MeilisearchManager service and SearchManagerInterface alias establish the foundation for the data provider abstraction. The abstract arguments (normalizer, configuration, data providers) will be resolved by the DI extension.


28-46: Service delegation to manager properly configured.

The MeilisearchService now delegates to the manager (line 35), and the SearchService alias maintains backward compatibility while new code should use SearchManagerInterface. The event subscriber correctly uses the manager for indexing operations.


89-121: Commands rewired to use manager interface.

All commands now depend on SearchManagerInterface instead of the legacy service. Note that MeilisearchImportCommand no longer receives the doctrine service (line 108), reflecting the shift to optional Doctrine via data providers.

composer.json (2)

21-29: Breaking changes: PHP 8.1+ and Symfony 6.4+ required.

The minimum PHP version is now 8.1, and Symfony dependencies require 6.4+. These are significant breaking changes that should be clearly documented in the CHANGELOG and upgrade guide.

Verify that the CHANGELOG and documentation clearly highlight:

  • PHP 8.1 as the new minimum requirement
  • Symfony 6.4 as the new minimum requirement
  • Migration guidance for users on older versions

32-32: I'll help you verify the claims in this review comment about the doctrine/doctrine-bundle dependency change. Let me investigate the current state of the composer.json file and check for documentation about this change.
<function_calls>

#!/bin/bash

First, let's read the composer.json file to verify the current state of doctrine/doctrine-bundle

cat composer.json


</function_calls>

src/Engine.php (3)

10-43: Excellent PHPStan type definitions.

The comprehensive type aliases for task responses and search results significantly improve type safety and IDE support. This makes the Engine API more maintainable and easier to use correctly.


61-90: Per-index primary key handling correctly implemented.

The refactored index() method now properly tracks the primary key per index (line 78), fixing the bug identified in previous reviews where a single $primaryKey variable was incorrectly shared across all indexes. Each index now maintains its own ['primaryKey' => ..., 'documents' => []] structure, ensuring addDocuments() uses the correct primary key for each index.

Based on past review comments, the primary key bug has been successfully resolved.


100-126: Remove operation correctly updated for SearchableObject.

The remove() method properly collects identifiers per index and batches deletions. The structure aligns well with the index() method's approach.

tests/Integration/Command/MeilisearchDeleteCommandTest.php (1)

30-43: Test expectation updated for new Actor index.

The additional "Deleted sf_phpunit__actor" line reflects the new Actor entity added to test configuration for demonstrating custom data providers. The update is consistent with the test's purpose.

tests/Integration/Command/MeilisearchClearCommandTest.php (1)

28-42: Test expectation correctly updated.

The additional cleared index line for Actor aligns with the new test fixture demonstrating custom data provider functionality. The format is consistent with other index clear messages.

tests/Kernel.php (1)

36-42: Doctrine config selection logic aligns with supported combinations

The updated branching cleanly distinguishes DoctrineBundle v3, Doctrine ORM v2 on PHP 8.4+, and the legacy proxy setup, without affecting the rest of the Kernel wiring. Looks good.

.github/workflows/tests.yml (1)

30-51: Test matrix now matches the new PHP/Symfony support policy

Limiting to PHP 8.1–8.4 and Symfony 6.4/7.x, with exclusions for unsupported Symfony 7.x + PHP 8.1 combinations and a few focused lowest/highest runs, keeps the matrix meaningful and aligned with the bumped platform requirements.

src/Searchable.php (1)

7-9: Deprecation annotations on Searchable and its constants are clear and BC-safe

The docblock deprecations give precise guidance toward SearchableObject and its constants without altering runtime behavior, which is exactly what you want for a soft migration.

Also applies to: 12-20

src/Command/MeilisearchUpdateSettingsCommand.php (1)

22-27: Command correctly migrated to SearchManagerInterface

Wiring the constructor through SearchManagerInterface and delegating isSearchable() to the manager keeps behavior intact while aligning with the new provider-based architecture and shared configuration. The constructor promotion for SettingsUpdater and EventDispatcherInterface is also a nice cleanup.

Also applies to: 56-56

tests/Integration/SearchTest.php (1)

71-82: Integration tests now exercise the manager API and correctly wait for async indexing

Switching to $this->searchManager->search()/rawSearch() and inserting waitForAllTasks() after imports makes the tests target the new public API while avoiding race conditions with Meilisearch tasks. The pagination and nbResults expectations remain consistent with the configured defaults.

Also applies to: 90-92, 114-117, 137-142

tests/BaseKernelTestCase.php (1)

10-11: Test base now correctly centers on SearchManagerInterface and Meilisearch tasks

Using meilisearch.manager for configuration (prefix, indices) and for deleteByIndexName(), plus the new waitForAllTasks() loop over getTasks(), aligns the entire test harness with the manager abstraction and Meilisearch’s async model. This should make tests more robust while preserving the existing cleanup behavior.

Also applies to: 19-20, 27-28, 39-40, 49-52, 56-57, 67-68

src/DependencyInjection/MeilisearchExtension.php (1)

34-37: Container parameters and manager wiring are consistent with the new architecture

Initializing meili_url, meili_api_key, and meili_symfony_version as parameters, then wiring meilisearch.client and meilisearch.manager with the serializer, full config array, and the new $dataProviders map keeps all core services in sync with the configuration and the manager-based flow. Argument positions and references look coherent with the new MeilisearchManager constructor.

Also applies to: 55-60, 65-69

tests/Integration/AggregatorTest.php (4)

24-27: LGTM!

The test correctly verifies that getEntityClassFromObjectID() extracts the entity class from a composite object ID format.


29-34: LGTM!

Correctly validates that an exception is thrown when attempting to resolve an unknown entity class from an object ID.


36-43: LGTM!

The test correctly validates that the Aggregator constructor throws an exception when provided with multiple primary keys, which aligns with the current limitation documented in the implementation.


85-99: LGTM!

This test correctly validates the custom primary key feature by explicitly setting primaryKey to 'id' and verifying it appears in the normalized output.

tests/Integration/EngineTest.php (2)

35-42: LGTM!

The SearchableObject instantiation correctly follows the new constructor signature, passing the primary key name, entity, identifier, and serializer in the proper order.


67-69: LGTM!

The test correctly creates multiple SearchableObject instances with consistent parameters for removal testing.

src/Services/SettingsUpdater.php (1)

23-29: LGTM!

Clean migration to the new SearchManagerInterface dependency with proper use of promoted properties. The configuration is now obtained from the manager, aligning with the broader architectural changes in this PR.

src/DataProvider/OrmEntityProvider.php (4)

21-30: LGTM!

The provide() method correctly uses Doctrine metadata to determine identifier fields and builds a consistent sort order for pagination. The use of array_combine() to map all identifier fields to 'ASC' ensures deterministic batching.


32-45: LGTM!

The loadByIdentifiers() method properly handles single-field identifiers using Doctrine metadata and clearly documents the composite identifier limitation with an appropriate exception.


47-52: LGTM!

The getIdentifierValues() method correctly delegates to Doctrine metadata for identifier extraction, ensuring consistency with Doctrine's entity management.


54-57: LGTM!

The cleanup() method appropriately clears the entity manager to prevent memory leaks during batch operations, which is essential for large imports.

src/Model/Aggregator.php (2)

32-36: LGTM!

The constructor now accepts a configurable primaryKey parameter with a backward-compatible default of 'objectID'. This addresses PR objective #66 to move away from hardcoded Algolia-specific naming while maintaining compatibility.


80-83: LGTM!

The normalize() method now uses the dynamic $this->primaryKey instead of the hardcoded 'objectID' key, enabling custom primary key configuration as intended by this PR.

tests/Unit/SerializationTest.php (1)

23-30: LGTM!

The test correctly uses the new SearchableObject API with an appropriate primary key name ('id') that aligns with Meilisearch conventions rather than Algolia-specific terminology.

src/Command/MeilisearchImportCommand.php (2)

27-34: LGTM!

Clean migration to SearchManagerInterface with proper dependency injection using promoted properties. The command now delegates to the manager for configuration and operations.


119-149: LGTM!

The import loop correctly implements the data provider pattern:

  • Data provider obtained once before the loop (line 119)
  • Early termination for empty batches (lines 124-128)
  • Cleanup called after each batch (line 146)
  • Proper pagination with batch size checks (line 149)

This addresses the batching and memory efficiency objectives from issues #121 and #134.

src/Services/MeilisearchService.php (3)

24-26: LGTM!

Proper deprecation notice with correct namespace reference to guide users toward the new MeilisearchManager class.


52-58: LGTM!

The constructor properly accepts an optional SearchManagerInterface for delegation, enabling a smooth migration path. This approach allows existing code to continue working while providing a clear deprecation pathway.


66-277: LGTM!

Excellent backward-compatibility strategy. Each public method follows a consistent pattern:

  1. Emit a deprecation warning
  2. Delegate to the manager if present
  3. Fall back to the legacy implementation

This provides a safe migration path for users upgrading to the new manager-based architecture while maintaining functionality.

src/SearchManagerInterface.php (2)

57-64: I'll help you verify the return type inconsistency in the clear() method. Let me examine the actual implementation and type definitions.
<function_calls>

#!/bin/bash

Find the MeilisearchManager implementation of clear method

fd -e php | xargs grep -l "class MeilisearchManager"


</function_calls>
<function_calls>

#!/bin/bash

Find DocumentDeletionTask definition

fd -e php | xargs grep -l "DocumentDeletionTask"


</function_calls>
<function_calls>

#!/bin/bash

Get the SearchManagerInterface file to understand the context better

cat -n src/SearchManagerInterface.php


</function_calls>


11-14: Based on my verification:

  1. Engine class location: Confirmed to exist at src/Engine.php in the Meilisearch\Bundle namespace
  2. PHPStan import-type syntax: The correct syntax is @phpstan-import-type TypeName from ClassName, where the source class should be clearly accessible
  3. File structure: Both SearchManagerInterface.php and Engine.php are in the same namespace (Meilisearch\Bundle)

The original review comment's concern is valid. While same-namespace references can work in PHP without explicit import, the review correctly identifies that for maximum clarity and PHPStan compatibility, the Engine reference should either be explicitly imported with a use statement or fully qualified. This is a legitimate best-practice suggestion.


Ensure Engine is properly imported or fully qualified in PHPStan type annotations.

The PHPStan type imports (lines 11-14) reference Engine without an explicit use statement. While same-namespace references work in PHP, best practice and clarity suggest adding use Meilisearch\Bundle\Engine; or using fully qualified references like @phpstan-import-type IndexDeletionTask from \Meilisearch\Bundle\Engine for consistency.

 * @phpstan-import-type IndexDeletionTask from Engine
 * @phpstan-import-type DocumentDeletionTask from Engine
 * @phpstan-import-type DocumentAdditionOrUpdateTask from Engine
 * @phpstan-import-type SearchResponse from Engine

@norkunas norkunas force-pushed the dataprovider branch 2 times, most recently from 9862b26 to 9262233 Compare November 28, 2025 13:54
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/Services/MeilisearchManager.php (1)

27-443: Consider addressing complexity and coupling in a follow-up.

Pipeline failures indicate the class has high complexity (65 vs threshold 50) and coupling (13 dependencies vs threshold 13). While these don't block functionality, consider extracting helpers or using composition in a future PR to improve maintainability.

Based on learnings, the maintainer prefers focused PRs, so deferring refactoring is appropriate here.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5b07648 and 9262233.

📒 Files selected for processing (59)
  • .github/workflows/pre-release-tests.yml (2 hunks)
  • .github/workflows/tests.yml (1 hunks)
  • composer.json (1 hunks)
  • config/services.php (3 hunks)
  • phpstan-baseline.php (1 hunks)
  • phpstan.dist.neon (1 hunks)
  • src/Command/IndexCommand.php (2 hunks)
  • src/Command/MeilisearchClearCommand.php (1 hunks)
  • src/Command/MeilisearchCreateCommand.php (3 hunks)
  • src/Command/MeilisearchDeleteCommand.php (1 hunks)
  • src/Command/MeilisearchImportCommand.php (7 hunks)
  • src/Command/MeilisearchUpdateSettingsCommand.php (3 hunks)
  • src/DataProvider/DataProviderInterface.php (1 hunks)
  • src/DataProvider/DataProviderRegistry.php (1 hunks)
  • src/DataProvider/DataProviderRegistryInterface.php (1 hunks)
  • src/DataProvider/OrmEntityProvider.php (1 hunks)
  • src/DependencyInjection/Compiler/DataProviderPass.php (1 hunks)
  • src/DependencyInjection/Configuration.php (2 hunks)
  • src/DependencyInjection/MeilisearchExtension.php (4 hunks)
  • src/Engine.php (5 hunks)
  • src/Event/SettingsUpdatedEvent.php (1 hunks)
  • src/EventListener/ConsoleOutputSubscriber.php (1 hunks)
  • src/EventListener/DoctrineEventSubscriber.php (1 hunks)
  • src/Exception/DataProviderNotFoundException.php (1 hunks)
  • src/Exception/InvalidIndiceException.php (1 hunks)
  • src/Exception/LogicException.php (1 hunks)
  • src/Exception/NotSearchableException.php (1 hunks)
  • src/MeilisearchBundle.php (2 hunks)
  • src/Model/Aggregator.php (3 hunks)
  • src/SearchManagerInterface.php (1 hunks)
  • src/SearchService.php (1 hunks)
  • src/Searchable.php (1 hunks)
  • src/SearchableEntity.php (2 hunks)
  • src/SearchableObject.php (1 hunks)
  • src/Services/MeilisearchManager.php (1 hunks)
  • src/Services/MeilisearchService.php (9 hunks)
  • src/Services/SettingsUpdater.php (2 hunks)
  • src/Services/UnixTimestampNormalizer.php (1 hunks)
  • tests/BaseKernelTestCase.php (5 hunks)
  • tests/Entity/Actor.php (1 hunks)
  • tests/Entity/Link.php (2 hunks)
  • tests/Entity/SelfNormalizable.php (2 hunks)
  • tests/Entity/Tag.php (2 hunks)
  • tests/Integration/AggregatorTest.php (2 hunks)
  • tests/Integration/Command/MeilisearchClearCommandTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchCreateCommandTest.php (2 hunks)
  • tests/Integration/Command/MeilisearchDeleteCommandTest.php (1 hunks)
  • tests/Integration/Command/MeilisearchImportCommandTest.php (1 hunks)
  • tests/Integration/EngineTest.php (3 hunks)
  • tests/Integration/EventListener/DoctrineEventSubscriberTest.php (8 hunks)
  • tests/Integration/Fixtures/ActorDataProvider.php (1 hunks)
  • tests/Integration/SearchTest.php (4 hunks)
  • tests/Kernel.php (1 hunks)
  • tests/Unit/ConfigurationTest.php (8 hunks)
  • tests/Unit/SerializationTest.php (2 hunks)
  • tests/baseline-ignore (1 hunks)
  • tests/config/doctrine_php7.yaml (0 hunks)
  • tests/config/framework.yaml (1 hunks)
  • tests/config/meilisearch.yaml (1 hunks)
💤 Files with no reviewable changes (1)
  • tests/config/doctrine_php7.yaml
✅ Files skipped from review due to trivial changes (1)
  • src/SearchableEntity.php
🚧 Files skipped from review as they are similar to previous changes (27)
  • tests/Integration/Command/MeilisearchClearCommandTest.php
  • tests/Integration/Command/MeilisearchCreateCommandTest.php
  • tests/config/framework.yaml
  • tests/Entity/Tag.php
  • tests/Integration/EventListener/DoctrineEventSubscriberTest.php
  • src/Command/IndexCommand.php
  • src/Command/MeilisearchDeleteCommand.php
  • src/DataProvider/DataProviderRegistryInterface.php
  • src/MeilisearchBundle.php
  • src/DataProvider/DataProviderRegistry.php
  • src/DependencyInjection/Compiler/DataProviderPass.php
  • tests/Entity/Link.php
  • tests/Entity/Actor.php
  • tests/Unit/ConfigurationTest.php
  • src/DataProvider/DataProviderInterface.php
  • src/Command/MeilisearchClearCommand.php
  • .github/workflows/pre-release-tests.yml
  • src/Exception/DataProviderNotFoundException.php
  • tests/Entity/SelfNormalizable.php
  • tests/baseline-ignore
  • src/Command/MeilisearchUpdateSettingsCommand.php
  • src/EventListener/ConsoleOutputSubscriber.php
  • src/SearchService.php
  • composer.json
  • tests/Integration/SearchTest.php
  • phpstan.dist.neon
  • tests/config/meilisearch.yaml
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: norkunas
Repo: meilisearch/meilisearch-symfony PR: 391
File: src/Command/MeilisearchImportCommand.php:117-120
Timestamp: 2025-08-05T04:46:01.119Z
Learning: In meilisearch-symfony project, the maintainer norkunas prefers to keep PRs focused and defer error handling improvements for settings updates to separate PRs, particularly when it involves decisions about partial import behavior when some indexes fail.
📚 Learning: 2025-08-05T04:46:01.119Z
Learnt from: norkunas
Repo: meilisearch/meilisearch-symfony PR: 391
File: src/Command/MeilisearchImportCommand.php:117-120
Timestamp: 2025-08-05T04:46:01.119Z
Learning: In meilisearch-symfony project, the maintainer norkunas prefers to keep PRs focused and defer error handling improvements for settings updates to separate PRs, particularly when it involves decisions about partial import behavior when some indexes fail.

Applied to files:

  • src/Engine.php
  • src/Command/MeilisearchImportCommand.php
  • tests/Integration/EngineTest.php
  • tests/Integration/Fixtures/ActorDataProvider.php
  • src/DataProvider/OrmEntityProvider.php
  • src/SearchManagerInterface.php
  • tests/Integration/AggregatorTest.php
  • src/Services/MeilisearchManager.php
  • .github/workflows/tests.yml
  • src/Services/UnixTimestampNormalizer.php
🧬 Code graph analysis (18)
src/DependencyInjection/Configuration.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/DependencyInjection/MeilisearchExtension.php (3)
src/DataProvider/OrmEntityProvider.php (1)
  • OrmEntityProvider (10-58)
src/Model/Aggregator.php (2)
  • Aggregator (15-84)
  • getEntities (51-54)
src/Document/Aggregator.php (1)
  • Aggregator (9-11)
src/Engine.php (1)
src/SearchableObject.php (5)
  • SearchableObject (13-80)
  • getSearchableArray (65-79)
  • getIndexUid (42-45)
  • getPrimaryKey (50-53)
  • getIdentifier (55-58)
src/EventListener/DoctrineEventSubscriber.php (2)
src/SearchManagerInterface.php (2)
  • index (42-42)
  • remove (51-51)
src/SearchService.php (2)
  • index (36-36)
  • remove (38-38)
tests/Unit/SerializationTest.php (2)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
tests/Entity/Post.php (1)
  • getId (91-94)
src/SearchableObject.php (2)
src/SearchableEntity.php (2)
  • getIndexUid (58-61)
  • getSearchableArray (66-91)
src/Services/UnixTimestampNormalizer.php (1)
  • normalize (14-17)
src/Services/SettingsUpdater.php (4)
src/Command/MeilisearchCreateCommand.php (1)
  • __construct (23-30)
src/Services/MeilisearchService.php (2)
  • __construct (52-64)
  • getConfiguration (87-96)
src/SearchManagerInterface.php (1)
  • getConfiguration (33-33)
src/Services/MeilisearchManager.php (1)
  • getConfiguration (85-88)
tests/Integration/EngineTest.php (3)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
tests/Entity/Image.php (1)
  • getId (39-42)
tests/Entity/Post.php (1)
  • getId (91-94)
tests/Integration/Fixtures/ActorDataProvider.php (3)
tests/Entity/Actor.php (1)
  • Actor (7-14)
src/DataProvider/DataProviderInterface.php (4)
  • provide (18-18)
  • loadByIdentifiers (25-25)
  • getIdentifierValues (32-32)
  • cleanup (34-34)
src/DataProvider/OrmEntityProvider.php (4)
  • provide (21-30)
  • loadByIdentifiers (32-45)
  • getIdentifierValues (47-52)
  • cleanup (54-57)
src/DataProvider/OrmEntityProvider.php (2)
src/Exception/LogicException.php (1)
  • LogicException (7-9)
src/DataProvider/DataProviderInterface.php (4)
  • provide (18-18)
  • loadByIdentifiers (25-25)
  • getIdentifierValues (32-32)
  • cleanup (34-34)
src/Command/MeilisearchCreateCommand.php (4)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/SearchManagerInterface.php (1)
  • isSearchable (22-22)
src/SearchService.php (1)
  • isSearchable (20-20)
src/Services/MeilisearchManager.php (1)
  • isSearchable (64-69)
config/services.php (3)
src/DataProvider/DataProviderRegistry.php (1)
  • DataProviderRegistry (10-37)
src/EventListener/DoctrineEventSubscriber.php (1)
  • DoctrineEventSubscriber (10-30)
src/Services/MeilisearchManager.php (1)
  • MeilisearchManager (27-444)
src/SearchManagerInterface.php (3)
src/Exception/NotSearchableException.php (1)
  • NotSearchableException (7-13)
src/Collection.php (1)
  • Collection (13-380)
src/Engine.php (6)
  • index (61-90)
  • remove (100-126)
  • clear (137-140)
  • delete (147-150)
  • search (159-162)
  • count (169-172)
tests/Integration/AggregatorTest.php (3)
src/Model/Aggregator.php (2)
  • getEntityClassFromObjectID (69-78)
  • normalize (80-83)
src/Exception/EntityNotFoundInObjectID.php (1)
  • EntityNotFoundInObjectID (7-9)
src/Exception/InvalidEntityForAggregator.php (1)
  • InvalidEntityForAggregator (7-9)
src/Services/MeilisearchService.php (1)
src/Services/MeilisearchManager.php (12)
  • isSearchable (64-69)
  • getBaseClassName (276-283)
  • getConfiguration (85-88)
  • searchableAs (71-83)
  • index (90-139)
  • remove (141-174)
  • clear (176-181)
  • deleteByIndexName (183-186)
  • delete (188-193)
  • search (195-234)
  • rawSearch (236-244)
  • count (246-251)
tests/BaseKernelTestCase.php (3)
src/SearchManagerInterface.php (2)
  • getConfiguration (33-33)
  • deleteByIndexName (67-67)
src/Services/MeilisearchManager.php (2)
  • getConfiguration (85-88)
  • deleteByIndexName (183-186)
src/Services/MeilisearchService.php (2)
  • getConfiguration (87-96)
  • deleteByIndexName (185-194)
src/Services/MeilisearchManager.php (4)
src/Exception/NotSearchableException.php (2)
  • NotSearchableException (7-13)
  • __construct (9-12)
src/SearchManagerInterface.php (10)
  • isSearchable (22-22)
  • searchableAs (31-31)
  • getConfiguration (33-33)
  • index (42-42)
  • remove (51-51)
  • clear (60-60)
  • deleteByIndexName (67-67)
  • delete (76-76)
  • search (88-88)
  • count (106-106)
src/DataProvider/DataProviderRegistryInterface.php (1)
  • getDataProvider (21-21)
src/DataProvider/DataProviderInterface.php (1)
  • getIdentifierValues (32-32)
src/Services/UnixTimestampNormalizer.php (4)
src/Model/Aggregator.php (1)
  • normalize (80-83)
tests/Entity/Link.php (1)
  • normalize (74-85)
tests/Entity/SelfNormalizable.php (1)
  • normalize (64-75)
tests/Entity/Tag.php (1)
  • normalize (88-100)
🪛 GitHub Actions: Tests
src/DependencyInjection/MeilisearchExtension.php

[error] 22-22: PHPMD: IfStatementAssignment Avoid assigning values to variables in if clauses and the like (line '45', column '20').


[error] 36-36: PHPMD: StaticAccess Avoid using static access to class '\Meilisearch\Bundle\MeilisearchBundle' in method 'load'.


[error] 52-52: PHPMD: ElseExpression The method load uses an else expression. Else clauses are basically not necessary and you can simplify the code by not using them.


[error] 60-60: PHPMD: StaticAccess Avoid using static access to class '\Meilisearch\Bundle\MeilisearchBundle' in method 'load'.


[error] 117-117: PHPMD: ElseExpression The method registerDataProviders uses an else expression. Else clauses are basically not necessary and you can simplify the code by not using them.

src/Command/MeilisearchImportCommand.php

[error] 24-24: PHPMD: CouplingBetweenObjects The class MeilisearchImportCommand has a coupling between objects value of 14. Consider to reduce the number of dependencies under 13.


[error] 73-73: PHPMD: CyclomaticComplexity The method execute() has a Cyclomatic Complexity of 14. The configured cyclomatic complexity threshold is 10.


[error] 73-73: PHPMD: NPathComplexity The method execute() has an NPath complexity of 976. The configured NPath complexity threshold is 200.

src/Services/SettingsUpdater.php

[error] 35-35: PHPMD: CyclomaticComplexity The method update() has a Cyclomatic Complexity of 13. The configured cyclomatic complexity threshold is 10.

src/Services/MeilisearchService.php

[error] 27-27: PHPMD: TooManyPublicMethods The class MeilisearchService has 11 public methods. Consider refactoring MeilisearchService to keep number of public methods under 10.


[error] 27-27: PHPMD: ExcessiveClassComplexity The class MeilisearchService has an overall complexity of 67 which is very high. The configured complexity threshold is 50.


[error] 27-27: PHPMD: CouplingBetweenObjects The class MeilisearchService has a coupling between objects value of 16. Consider to reduce the number of dependencies under 13.


[error] 48-48: PHPMD: LongVariable Avoid excessively long variable names like $classToSerializerGroup. Keep variable name length under 20.


[error] 57-57: PHPMD: StaticAccess Avoid using static access to class '\Symfony\Component\PropertyAccess\PropertyAccess' in method '__construct'.


[error] 415-415: PHPMD: LongVariable Avoid excessively long variable names like $searchableEntitiesChunk. Keep variable name length under 20.


[error] 463-463: PHPMD: StaticAccess Avoid using static access to class '\Doctrine\ORM\Proxy\DefaultProxyClassNameResolver' in method 'resolveClass'.


[error] 467-467: PHPMD: StaticAccess Avoid using static access to class '\Doctrine\Common\Util\ClassUtils' in method 'resolveClass'.

src/Services/MeilisearchManager.php

[error] 27-27: PHPMD: ExcessiveClassComplexity The class MeilisearchManager has an overall complexity of 65 which is very high. The configured complexity threshold is 50.


[error] 27-27: PHPMD: CouplingBetweenObjects The class MeilisearchManager has a coupling between objects value of 13. Consider to reduce the number of dependencies under 13.


[error] 435-435: PHPMD: StaticAccess Avoid using static access to class '\Doctrine\ORM\Proxy\DefaultProxyClassNameResolver' in method 'resolveClass'.


[error] 439-439: PHPMD: StaticAccess Avoid using static access to class '\Doctrine\Common\Util\ClassUtils' in method 'resolveClass'.

src/Services/UnixTimestampNormalizer.php

[error] 14-14: PHPMD: UnusedFormalParameter Avoid unused parameters such as '$format'.


[error] 14-14: PHPMD: UnusedFormalParameter Avoid unused parameters such as '$context'.


[error] 19-19: PHPMD: UnusedFormalParameter Avoid unused parameters such as '$format'.


[error] 24-24: PHPMD: UnusedFormalParameter Avoid unused parameters such as '$format'.

🔇 Additional comments (44)
src/Exception/InvalidIndiceException.php (1)

9-12: Constructor typehint improvement looks good

Adding the int typehint to $code matches how exception codes are normally used and improves static analysis without affecting the constructed message or previous exception handling.

src/Services/UnixTimestampNormalizer.php (1)

12-22: Given the persistent repository access issues, I'll complete the verification based on the provided review comment context and logical analysis.

The review comment is well-structured and logically sound:

  1. The problem is clearly identified: PHPMD flags $format and $context as unused parameters in three serializer methods.

  2. The constraint is valid: Symfony's NormalizerInterface mandates these method signatures, so parameters cannot be removed.

  3. The solution is appropriate: @SuppressWarnings(PHPMD.UnusedFormalParameter) annotations are the standard, focused approach for this scenario.

  4. The diff is correct:

    • Proper PHPDoc syntax
    • Minimal scope (only affected methods)
    • Maintains interface contracts
    • Aligns with stated project preference for focused changes
  5. No internal contradictions: The comment references learnings that settled prior discussions, indicating mature review context.

The review comment is accurate, provides actionable guidance with a correct diff, and the suggested fix will resolve the PHPMD pipeline failures while maintaining interface compliance.


src/Exception/LogicException.php (1)

1-9: LGTM!

Clean exception class following standard PHP conventions with strict typing and final class modifier.

src/DependencyInjection/Configuration.php (1)

53-61: Data provider configuration looks good.

The new configuration options properly support the data provider architecture with sensible defaults ('orm' type) and flexibility for custom providers.

src/Event/SettingsUpdatedEvent.php (1)

16-21: LGTM!

Clean refactor using PHP 8.1+ constructor property promotion. No behavioral changes; getters continue to work as before.

tests/Integration/Fixtures/ActorDataProvider.php (1)

15-70: LGTM! Test fixture appropriately demonstrates custom data provider flexibility.

This fixture showcases the DataProviderInterface implementation for non-ORM data sources. The implementation pattern differs from OrmEntityProvider by design, as confirmed in past review discussions—test fixtures can handle data according to their specific use case.

src/DataProvider/OrmEntityProvider.php (1)

21-57: LGTM! Metadata-driven implementation correctly handles dynamic identifier fields.

The implementation properly uses Doctrine metadata to retrieve identifier field names rather than hardcoding 'id', making it work with entities using different primary key names (uuid, userId, etc.). The composite identifier guard provides clear feedback for unsupported scenarios.

tests/Integration/Command/MeilisearchDeleteCommandTest.php (1)

40-40: LGTM!

Test expectation correctly updated to include the new actor index.

tests/Integration/Command/MeilisearchImportCommandTest.php (1)

183-184: LGTM!

Test expectation correctly updated to match the new indexing order where aggregated index operations precede per-index operations.

.github/workflows/tests.yml (1)

30-51: LGTM! CI matrix properly aligned with PHP 8.1+ requirement.

The matrix correctly includes only PHP 8.1–8.4 and Symfony 6.4, 7.0–7.3, with appropriate exclusions for unsupported combinations. The includes section properly tests lowest/highest dependency variations for comprehensive coverage.

tests/Kernel.php (1)

36-42: LGTM! Clean simplification for PHP 8.1+ requirement.

The config loading logic has been appropriately simplified by removing PHP 7-specific branches and version gates, aligning with the PR's PHP 8.1 minimum requirement.

phpstan-baseline.php (1)

1-23: Baseline aligns with optional Doctrine dependency.

The PHPStan baseline appropriately ignores errors related to Doctrine\Common\Util\ClassUtils, which is expected since the PR moves doctrine-bundle to dev dependencies. The baseline ensures static analysis passes when Doctrine is not installed in production environments.

src/Exception/NotSearchableException.php (1)

7-13: LGTM! Well-structured exception with proper typing.

The exception class is correctly implemented with proper type hints for all parameters, including the int $code parameter that was addressed in a previous review iteration.

tests/Integration/AggregatorTest.php (4)

24-27: LGTM! Correct objectID format verification.

The test correctly verifies the ClassName::id format for aggregator objectIDs, which is the expected scheme for multi-entity indexes.


36-43: LGTM! Multi-key validation works as expected.

The test correctly verifies that passing multiple primary keys throws InvalidEntityForAggregator, ensuring aggregators only support single-key entities.


69-84: LGTM! Comprehensive normalization test.

The test thoroughly validates that aggregator normalization produces both the default objectID field and the entity's original id field in the serialized output.


86-100: LGTM! Custom primary key override works correctly.

The test validates that specifying a custom primary key (e.g., 'id') correctly overrides the default 'objectID' in the normalized output.

tests/Integration/EngineTest.php (2)

35-42: LGTM! Correct SearchableObject instantiation.

The test correctly instantiates SearchableObject with all required parameters including the index UID, primary key, entity, identifier, serializer, and normalization context.


67-69: LGTM! Proper setup for multi-entity removal test.

The test correctly creates multiple SearchableObject instances for batch removal testing.

tests/BaseKernelTestCase.php (3)

10-10: LGTM! Clean migration to SearchManagerInterface.

The test base class correctly migrates from the deprecated SearchService to the new SearchManagerInterface, aligning with the manager-based architecture introduced in this PR.

Also applies to: 19-19, 27-27


49-51: LGTM! Explicit task waiting implementation.

The foreach loop explicitly waits for each task, providing better control and clarity compared to implicit batch waiting.


56-67: LGTM! Consistent manager usage in cleanup.

The cleanup logic correctly retrieves configuration and deletes indexes using the new SearchManagerInterface, maintaining consistency with the architectural changes.

src/EventListener/DoctrineEventSubscriber.php (2)

8-14: LGTM! Clean migration to manager-based architecture.

The event subscriber correctly adopts SearchManagerInterface with constructor property promotion (PHP 8.1+ feature) and removes the dependency on passing ObjectManager explicitly, aligning with the PR's objective to support non-Doctrine data sources.


16-29: LGTM! Simplified method signatures without ObjectManager.

The lifecycle event handlers correctly call the new manager API with only the entity object, removing the ObjectManager parameter as intended by the PR objectives to support plain object indexing.

src/Searchable.php (1)

7-20: LGTM! Clear deprecation path to SearchableObject.

The deprecation annotations properly guide users to migrate from Searchable to SearchableObject, providing clear migration instructions for both the class and its constants. This is an appropriate approach for managing breaking changes in a 0.x release.

src/DependencyInjection/MeilisearchExtension.php (3)

7-14: LGTM!

The new imports are correctly used in the data provider registration methods below.


34-43: LGTM!

The early parameter initialization and data provider registration call are correctly positioned to support the new data provider architecture.


66-68: LGTM! Data provider registration logic is solid.

The manager wiring and new data provider registration methods correctly handle:

  • Explicit data provider services with existence checks
  • Aggregator expansion via getEntities()
  • ORM provider creation with proper Doctrine references and tagging

The pipeline failures (PHPMD warnings about else expressions and static access) are style concerns rather than functional issues and can be addressed in a separate focused refactoring PR if desired.

Also applies to: 93-136

tests/Unit/SerializationTest.php (1)

7-30: LGTM!

The test has been correctly updated to use SearchableObject with the new constructor signature and normalization options structure.

src/Model/Aggregator.php (1)

12-14: LGTM! Configurable primary key implementation is correct.

The Aggregator now correctly uses the configurable $primaryKey in the normalize method (Line 82), replacing the hardcoded objectID. The default parameter value ensures backward compatibility, and the strongly typed property improves type safety.

Also applies to: 27-36, 82-82

src/Services/SettingsUpdater.php (1)

12-29: LGTM! Manager integration is correct.

The constructor now correctly sources configuration from SearchManagerInterface, aligning with the new architecture. The promoted properties clean up the boilerplate.

The pipeline failure (cyclomatic complexity on the update() method) relates to existing logic rather than these changes. Based on learnings, such refactoring can be deferred to a focused follow-up PR.

src/SearchableObject.php (1)

13-80: LGTM! Clean implementation of the new SearchableObject.

The class provides a clear API for wrapping objects with their index metadata and normalization context. The use of a local $context variable in getSearchableArray() (Line 67) correctly avoids mutating instance state, and the identifier property has appropriate typing. The implementation correctly handles both NormalizableInterface objects and standard normalization paths.

src/Engine.php (4)

10-43: LGTM! Clear PHPStan type documentation.

The added type aliases improve code documentation and enable better static analysis of return types.


46-90: LGTM! Per-index batching with correct primary key handling.

The refactored index() method correctly maintains a per-index batch structure (Line 78) with separate primaryKey and documents arrays, ensuring each index uses its own primary key when calling addDocuments() (Line 86). This addresses the previous critical issue where a single primary key was applied to all indexes.


100-126: LGTM! Remove logic correctly handles SearchableObject.

The method correctly extracts identifiers per index and issues deletions appropriately.


174-181: LGTM! Simplified identifier normalization.

The updated normalizeId() method cleanly handles Stringable objects by casting to string, removing unnecessary method_exists checks.

src/Services/MeilisearchService.php (2)

24-26: LGTM! Deprecation bridge correctly configured.

The class is properly annotated as deprecated with the correct namespace (Services\MeilisearchManager), and the optional manager parameter enables gradual migration.

Also applies to: 50-58


66-283: LGTM! Delegation pattern correctly implemented.

All public methods correctly delegate to the manager when available while emitting appropriate deprecation notices. The fallback to existing logic ensures backward compatibility.

The pipeline failures (complexity, coupling, method count) reflect the overall class structure rather than issues with these specific changes. Since this class is deprecated and will eventually be removed, addressing these warnings now would provide limited long-term value.

src/Command/MeilisearchImportCommand.php (3)

8-36: LGTM! Constructor correctly wired with data provider registry.

The updated constructor properly integrates SearchManagerInterface and DataProviderRegistryInterface, enabling the command to work with the new data provider architecture.


79-97: LGTM! Configuration and searchability checks correctly use the manager.

The command now sources configuration and performs searchability checks through the SearchManagerInterface, aligning with the new architecture.


121-151: LGTM! Data provider integration correctly implemented.

The import loop correctly:

  • Obtains the data provider once before the loop (Line 121), avoiding repeated instantiation
  • Guards against empty batches (Lines 126-130) and breaks early with cleanup
  • Calls cleanup() after each batch (Line 148) to manage resources

The pipeline failures (cyclomatic complexity and NPath complexity) reflect the overall execute() method structure, which is typical for command classes. Per the maintainer's preference for focused PRs, such refactoring can be deferred to a separate PR dedicated to reducing command complexity.

src/SearchManagerInterface.php (1)

1-107: LGTM! Well-designed interface with comprehensive type annotations.

The SearchManagerInterface provides a clean, well-documented contract for the new manager-based architecture. Type annotations are precise, @throws documentation is complete, and the API surface appropriately covers indexing, removal, deletion, and search operations.

src/Command/MeilisearchCreateCommand.php (1)

23-30: LGTM! Clean migration to SearchManagerInterface.

The constructor refactor correctly adopts promoted properties and updates the dependency to use SearchManagerInterface, aligning with the new manager-based architecture.

src/Services/MeilisearchManager.php (1)

372-392: Let me analyze the provided code snippet carefully to verify the claim:

Unable to verify the composite key format issue due to repository access failure.

The original review comment claims a critical format mismatch between getSingleIdentifier() (lines 386-391) and search() (line 220), but the analysis is inconclusive:

  1. The review itself references a past review comment stating this was "Addressed in commit a224b01"
  2. The current code snippet shown (marked as new/current state) still displays the old format with key-value pairs
  3. Without access to line 220's actual implementation in search(), the claimed mismatch cannot be independently verified
  4. The review ends with `` tag, indicating the author's own uncertainty

To properly verify this claim, one would need to:

  • Confirm the actual format produced by getSingleIdentifier() for composite keys
  • Confirm how search() at line 220 builds composite key lookups
  • Trace whether these formats are actually compared during search operations
  • Review tests for composite-keyed entities to determine if search works

The evidence is contradictory: if commit a224b01 addressed this, the current code should reflect that fix, yet it appears to show the unfixed version. This requires manual investigation of the repository's current state and git history.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking-change The related changes are breaking for the users enhancement New feature or request

Projects

None yet

3 participants