Skip to content

Conversation

@norkunas
Copy link
Collaborator

@norkunas norkunas commented Nov 24, 2025

Pull Request

Early christmas present 🎄

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'
      type: custom
      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 readonly class ActorDataProvider implements DataProviderInterface
{
    public function __construct(
        private IdNormalizerInterface $idNormalizer,
    ) {
    }

    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 normalizeIdentifiers(object $object): string|int
    {
        return $this->idNormalizer->normalize($this->getIdentifierValues($object));
    }

    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 #240
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;
  • Adds possibility to use custom id normalizer;
  • Adds support for composite identifiers;

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

    • Manager-based search API, pluggable data-provider system (including ORM provider), batch import with configurable batch size/timeouts, identifier normalization and custom primary-key support; new searchable wrapper using serializer groups.
  • Chores

    • Minimum PHP bumped to 8.1 and Symfony constraints aligned to 6.4+/7.x; CI matrix and tooling versions updated; dependency and DI wiring updated; bundle registers data-provider compiler pass.
  • Deprecations

    • Legacy search service and prior searchable wrappers marked deprecated; migrate to the manager and new searchable object.
  • Documentation

    • Doctrine integration section added to README.

✏️ 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

Walkthrough

Replaces SearchService with a new SearchManagerInterface + MeilisearchManager, introduces a DataProvider system (registry, ORM provider, compiler pass), adds identifier normalization and SearchableObject with configurable primary keys, updates DI wiring/commands/listeners/tests, and modernizes CI/composer constraints and PHPStan baseline.

Changes

Cohort / File(s) Summary
CI & Composer
\.github/workflows/tests.yml, \.github/workflows/pre-release-tests.yml, composer.json, \.github/workflows/pre-release-tests.yml
Narrow CI matrix (remove PHP 7.4 / Symfony 5.4 combos), add dependencies axis/includes, adjust composer PHP requirement to ^8.1, move doctrine-bundle to require-dev, bump tooling/phpstan/php-cs-fixer versions.
Manager & Service API
src/SearchManagerInterface.php, src/Services/MeilisearchManager.php, src/Services/MeilisearchService.php, src/SearchService.php
Add SearchManagerInterface and MeilisearchManager; deprecate SearchService and delegate to manager when present; migrate public APIs and adapt service to optional manager.
Data Provider System
src/DataProvider/DataProviderInterface.php, src/DataProvider/OrmEntityProvider.php, src/DataProvider/DataProviderRegistry.php, src/DataProvider/DataProviderRegistryInterface.php, src/DependencyInjection/Compiler/DataProviderPass.php
Introduce DataProvider contract, ORM provider implementation, registry + interface, and a compiler pass to collect/tag providers and register a service locator/map.
Identifier Normalization
src/Identifier/IdNormalizerInterface.php, src/Identifier/DefaultIdNormalizer.php
Add IdNormalizerInterface and DefaultIdNormalizer to normalize/denormalize composite identifiers (URL-safe base64 JSON encoding for composites).
Searchable & Aggregation
src/SearchableObject.php, src/Model/Aggregator.php, src/Searchable.php, src/SearchableEntity.php
Add SearchableObject (indexUid, primaryKey, identifier, normalizer), update Aggregator to use dynamic primaryKey, and mark legacy Searchable/SearchableEntity as deprecated.
Engine & Core Services
src/Engine.php, src/Services/SettingsUpdater.php, src/Services/UnixTimestampNormalizer.php, src/Event/SettingsUpdatedEvent.php
Engine now batches per-primaryKey and returns typed tasks; services refactored to depend on SearchManagerInterface; constructor property promotions and docblock/type tightening.
Commands & Event Listeners
src/Command/*, src/EventListener/*, config/doctrine.php
Commands and Doctrine listener switched to SearchManagerInterface; Import command now uses DataProviderRegistry and per-provider batching; services wired to use meilisearch.manager.
Dependency Injection & Bundle
config/services.php, src/DependencyInjection/MeilisearchExtension.php, src/DependencyInjection/Configuration.php, src/MeilisearchBundle.php
Wire meilisearch.manager, data provider registry, default id normalizer and aliases; add registerDataProviders helpers and register DataProviderPass in bundle build(); extend config with per-index type, primary_key, data_provider, id_normalizer.
Exceptions & Small API changes
src/Exception/*
Add DataProviderNotFoundException, NotSearchableException, LogicException; tighten InvalidIndiceException constructor signature.
PHPStan & Tests
phpstan-baseline.php, phpstan.dist.neon, tests/**, tests/config/**, tests/Integration/Fixtures/ActorDataProvider.php
Add PHPStan baseline file and include it; update tests to use SearchManagerInterface and SearchableObject; add Actor/Car test entities and ActorDataProvider fixture; adjust kernel/test configs and baselines; update assertions/log outputs.
Docs & README
README.md
Add Doctrine Integration section and remove a line about Doctrine ORM support in Requirements.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant CLI as Command (Import/Create/Update)
    participant Registry as DataProviderRegistry
    participant Provider as DataProvider (ORM/custom)
    participant Manager as MeilisearchManager
    participant Engine as Engine
    participant Client as MeiliClient

    CLI->>Registry: request provider for index/class
    Registry-->>Provider: return provider instance
    CLI->>Provider: provide(limit, offset)
    Provider-->>CLI: return batch of domain objects
    CLI->>Manager: index(batch objects)
    Manager->>Engine: create SearchableObject(s) and group per index
    Engine->>Client: addDocuments(index, documents, primaryKey)
    Client-->>Engine: task/result
    Engine-->>Manager: return tasks/results
    Manager-->>CLI: aggregated responses
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Focus areas:
    • src/Services/MeilisearchManager.php — batching, aggregator resolution, provider lookup and ordering.
    • src/DataProvider/OrmEntityProvider.php — composite identifier queries and query-builder correctness.
    • src/DependencyInjection/MeilisearchExtension.php & src/DependencyInjection/Compiler/DataProviderPass.php — DI wiring, generated service definitions and tag processing.
    • src/Identifier/DefaultIdNormalizer.php — encoding/decoding edge cases and error handling.
    • Backwards compatibility in src/Services/MeilisearchService.php and DI aliases.

Possibly related PRs

Suggested reviewers

  • curquiza

Poem

🐇 I nibbled through code in the hush of night,

New manager hops in, setting IDs right,
Providers lined up, each batch on parade,
Composite keys wrapped in a neat little braid,
Tests twinkle green — I twitch, then delight.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.72% 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
Title check ✅ Passed Title 'Add Data providers extension point' is clear, specific, and accurately summarizes the main change—introducing a data provider abstraction for extensible data sources.
Linked Issues check ✅ Passed PR successfully addresses all linked issues: #66 removes objectID assumptions via configurable primary keys; #121 introduces DataProvider interfaces and registry for external sources; #134 implements batch loading to reduce DB queries; #240 adds IdNormalizer for custom ID formatting; #302 enables plain object indexing without EntityManager.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the stated objectives: dependency updates (PHP 8.1, Symfony 6.4), new interfaces/classes for data providers and ID normalization, deprecation of old services, and comprehensive test coverage for new functionality.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 85.12035% with 68 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.38%. Comparing base (b857d59) to head (62d5729).
⚠️ Report is 11 commits behind head on main.

Files with missing lines Patch % Lines
src/Services/MeilisearchService.php 0.00% 38 Missing ⚠️
src/Services/MeilisearchManager.php 94.50% 10 Missing ⚠️
src/DependencyInjection/MeilisearchExtension.php 83.67% 8 Missing ⚠️
src/DataProvider/OrmEntityProvider.php 94.87% 2 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 ⚠️
.../DependencyInjection/Compiler/DataProviderPass.php 93.33% 1 Missing ⚠️
src/Exception/InvalidIndiceException.php 0.00% 1 Missing ⚠️
... and 1 more
Additional details and impacted files
@@             Coverage Diff             @@
##             main     #410       +/-   ##
===========================================
- Coverage   88.38%   72.38%   -16.00%     
===========================================
  Files          20       28        +8     
  Lines         878     1224      +346     
===========================================
+ Hits          776      886      +110     
- Misses        102      338      +236     

☔ 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.

@norkunas norkunas force-pushed the dataprovider branch 2 times, most recently from 2df2b03 to b7abc5d Compare December 13, 2025 16:07
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: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
src/Services/MeilisearchService.php (1)

24-64: CI blocker: PHPMD violations on deprecated MeilisearchService should be suppressed (or CI will keep failing).
Given this class is intentionally “legacy + compatibility”, adding targeted suppressions is the smallest change to unblock the pipeline.

 /**
  * @deprecated Since 0.16, use `Meilisearch\Bundle\Services\MeilisearchManager` instead.
+ *
+ * @SuppressWarnings(PHPMD.TooManyPublicMethods)
+ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ * @SuppressWarnings(PHPMD.LongVariable)
+ * @SuppressWarnings(PHPMD.StaticAccess)
  */
 final class MeilisearchService implements SearchService
 {
config/services.php (1)

30-49: Potential BC pitfall: consider making SearchService::class alias public.
If users fetch by id ($container->get(SearchService::class)), a private alias can break even though the target service is public.

-    $services->alias(SearchService::class, 'meilisearch.service');
+    $services->alias(SearchService::class, 'meilisearch.service')->public();
src/Engine.php (1)

61-90: Bug risk: array union (+) won’t overwrite an existing primary key field.
If the normalizer already outputs the primary key field (even null/wrong type), the Engine-injected identifier is ignored. Prefer explicit overwrite.

         foreach ($searchableObjects as $object) {
             $searchableArray = $object->getSearchableArray();

             if ([] === $searchableArray) {
                 continue;
             }

             $indexUid = $object->getIndexUid();

             $data[$indexUid] ??= ['primaryKey' => $object->getPrimaryKey(), 'documents' => []];
-            $data[$indexUid]['documents'][] = $searchableArray + [$object->getPrimaryKey() => $this->normalizeId($object->getIdentifier())];
+            $pk = $object->getPrimaryKey();
+            $searchableArray[$pk] = $this->normalizeId($object->getIdentifier());
+            $data[$indexUid]['documents'][] = $searchableArray;
         }
src/Command/MeilisearchImportCommand.php (2)

7-16: Fix PHPMD CouplingBetweenObjects by removing a PHPDoc-only import (minimal change).
use Meilisearch\Exceptions\TimeOutException; appears to be used only in the @throws PHPDoc, but it still counts towards PHPMD coupling.

-use Meilisearch\Exceptions\TimeOutException;
-    /**
-     * @throws TimeOutException
-     */
+    /**
+     * @throws \Meilisearch\Exceptions\TimeOutException
+     */
     private function formatIndexingResponse(array $batch, int $responseTimeout): array

Also applies to: 165-168


73-163: CI will stay red: execute() must be decomposed to pass PHPMD Cyclomatic/NPath thresholds.
This is now a merge blocker (Cyclomatic=14, NPath=976). A low-risk fix is extracting cohesive blocks (option parsing, per-index import, optional swap) into private methods without changing logic.

Example direction (sketch):

 protected function execute(InputInterface $input, OutputInterface $output): int
 {
-    $this->eventDispatcher->addSubscriber(...);
-    $indexes = ...;
-    ...
-    foreach ($entitiesToIndex as $index) {
-        ...
-    }
-    if ($swapIndices) { ... }
-    ...
+    $this->bootstrapConsole($input, $output);
+    [$indexes, $entitiesToIndex] = $this->resolveIndexes($input, $output);
+    $options = $this->parseOptions($input);
+    $this->importAll($entitiesToIndex, $options, $output);
+    $this->maybeSwap($options, $indexes, $output);
+    $output->writeln('<info>Done!</info>');
+    return 0;
 }

If you want, I can propose a concrete refactor diff that targets the PHPMD thresholds directly while keeping the PR focused (no behavioral changes). Based on learnings, I’m intentionally not expanding this into broader error-handling semantics.

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

51-67: Proxy test still asserts a “self-referential” objectId value (likely masking real behavior).
This repeats a previously raised concern: the identifier values passed to ContentAggregator and the assertion both use the literal string 'objectId', not the persisted entity identifier.

🧹 Nitpick comments (8)
tests/Unit/ConfigurationTest.php (1)

359-402: Consider adding a type=custom coverage case (and expected failure/success).
Right now tests validate defaults + primary_key override, but not the “custom provider” branch.

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

44-86: Potentially fragile exact-output assertion due to ordering sensitivity.
If index iteration order changes, this test will fail even if behavior is correct (Line 44+). Consider asserting key lines/segments (or sorting) instead of full heredoc equality.

src/DataProvider/DataProviderInterface.php (1)

20-25: Consider clarifying $identifiers parameter documentation.

The @param array<mixed> $identifiers annotation is intentionally flexible to allow different implementations. However, adding a comment explaining that implementations may interpret this parameter differently (e.g., flat array of IDs vs. associative array with field names) could reduce confusion for implementers.

Example enhancement:

     /**
+     * Loads objects by their identifiers. The structure of $identifiers is implementation-specific
+     * (e.g., flat array of ID values, or associative array with field names as keys).
+     *
      * @param array<mixed> $identifiers
      *
      * @return iterable<T>
      */
     public function loadByIdentifiers(array $identifiers): iterable;
src/DependencyInjection/MeilisearchExtension.php (1)

93-122: Data provider registration logic is sound but consider adding diagnostics.

The registration flow correctly handles three cases:

  1. Explicit custom data provider (tagged if service exists)
  2. ORM Aggregator classes (expanded to register providers for each aggregated entity)
  3. Regular ORM entities (single provider registration)

However, when a custom data_provider service is specified but doesn't exist (lines 99-110), the code silently skips registration. Consider logging a warning or throwing an exception to help developers catch configuration errors.

             if (null !== $indice['data_provider']) {
-                if ($container->hasDefinition($indice['data_provider'])) {
+                if (!$container->hasDefinition($indice['data_provider'])) {
+                    throw new \InvalidArgumentException(
+                        sprintf('Data provider service "%s" for index "%s" does not exist.', $indice['data_provider'], $indexName)
+                    );
+                }
+
                     $container
                         ->findDefinition($indice['data_provider'])
                         ->addTag('meilisearch.data_provider', [
                             'index' => $indexName,
                             'class' => $class,
                         ]);
-                }
 
                 continue;
             }
src/Services/SettingsUpdater.php (1)

35-77: Pre-existing complexity issue flagged by pipeline.

The PHPMD warning about cyclomatic complexity (13 vs threshold 10) on update() is a pre-existing condition—this PR only changes the constructor wiring, not the method logic. The complexity stems from multiple conditionals handling different setting types and error states.

Based on learnings, you prefer to keep PRs focused, so this can be addressed in a separate refactor if desired (e.g., extracting setting value resolution and task handling into private methods).

src/Engine.php (1)

100-126: Consider batching deletions per index using deleteDocuments().

The current code calls deleteDocument() in a loop for each document ID, resulting in N network requests per index. The meilisearch-php client supports bulk deletion via deleteDocuments(array $options): array, which accepts ['ids' => [...]]. You can batch the IDs for each index and make a single call instead:

foreach ($data as $indexUid => $objects) {
    $result[$indexUid] = $this->client
        ->index($indexUid)
        ->deleteDocuments(['ids' => $objects]);
}

This reduces network overhead significantly and is simpler.

src/Services/MeilisearchManager.php (2)

90-139: Consider caching providers per (indexName, baseClass) within a call to avoid repeated registry lookups.
Right now DataProviderRegistryInterface::getDataProvider() is called per entity per matching index; if providers are non-trivial (or stateful), this can add overhead.

A simple local cache (array key: "$indexName|$baseClass") inside index() would keep behavior while reducing churn.

Also applies to: 313-363


195-234: Minor: avoid reusing $identifiers for different meanings inside search().
$identifiers = array_column(...) is later reused for per-object identifiers (Line 219), which is easy to misread. Rename to $hitIds and $objectIds (no behavior change).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

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

📒 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)
💤 Files with no reviewable changes (1)
  • tests/config/doctrine_php7.yaml
🚧 Files skipped from review as they are similar to previous changes (20)
  • src/Exception/InvalidIndiceException.php
  • src/Exception/DataProviderNotFoundException.php
  • tests/Kernel.php
  • src/EventListener/ConsoleOutputSubscriber.php
  • tests/config/framework.yaml
  • src/Event/SettingsUpdatedEvent.php
  • src/DataProvider/DataProviderRegistryInterface.php
  • .github/workflows/tests.yml
  • phpstan.dist.neon
  • src/SearchService.php
  • src/DependencyInjection/Compiler/DataProviderPass.php
  • src/EventListener/DoctrineEventSubscriber.php
  • src/Services/UnixTimestampNormalizer.php
  • tests/Entity/Tag.php
  • src/DataProvider/OrmEntityProvider.php
  • tests/Integration/Command/MeilisearchDeleteCommandTest.php
  • src/Command/MeilisearchDeleteCommand.php
  • src/DataProvider/DataProviderRegistry.php
  • tests/baseline-ignore
  • .github/workflows/pre-release-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:

  • src/DependencyInjection/Configuration.php
  • tests/Integration/Fixtures/ActorDataProvider.php
  • config/services.php
  • tests/Integration/EngineTest.php
  • src/Engine.php
  • tests/Integration/AggregatorTest.php
  • src/Command/MeilisearchImportCommand.php
  • src/Services/MeilisearchManager.php
  • src/SearchManagerInterface.php
🧬 Code graph analysis (24)
src/SearchableEntity.php (5)
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)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/Command/MeilisearchClearCommand.php (5)
src/Engine.php (1)
  • clear (137-140)
src/SearchManagerInterface.php (1)
  • clear (60-60)
src/SearchService.php (1)
  • clear (43-43)
src/Services/MeilisearchManager.php (1)
  • clear (176-181)
src/Services/MeilisearchService.php (1)
  • clear (172-183)
tests/Entity/Link.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
tests/Entity/SelfNormalizable.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/DependencyInjection/Configuration.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/Command/IndexCommand.php (3)
src/Command/MeilisearchUpdateSettingsCommand.php (1)
  • __construct (22-28)
src/SearchManagerInterface.php (1)
  • getConfiguration (33-33)
src/Services/MeilisearchManager.php (1)
  • getConfiguration (85-88)
src/Services/MeilisearchService.php (2)
src/EventListener/DoctrineEventSubscriber.php (1)
  • __construct (12-14)
src/Services/MeilisearchManager.php (13)
  • isSearchable (64-69)
  • getBaseClassName (276-283)
  • getConfiguration (85-88)
  • searchableAs (71-83)
  • index (90-139)
  • remove (141-174)
  • clear (176-181)
  • assertIsSearchable (365-370)
  • deleteByIndexName (183-186)
  • delete (188-193)
  • search (195-234)
  • rawSearch (236-244)
  • count (246-251)
src/Command/MeilisearchCreateCommand.php (5)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/Command/MeilisearchUpdateSettingsCommand.php (1)
  • __construct (22-28)
src/Services/MeilisearchService.php (2)
  • __construct (52-64)
  • isSearchable (66-77)
src/Services/SettingsUpdater.php (2)
  • __construct (23-29)
  • SettingsUpdater (17-78)
src/SearchManagerInterface.php (1)
  • isSearchable (22-22)
tests/Integration/EventListener/DoctrineEventSubscriberTest.php (2)
src/SearchManagerInterface.php (1)
  • search (88-88)
src/Services/MeilisearchManager.php (1)
  • search (195-234)
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/Exception/NotSearchableException.php (2)
src/Exception/DataProviderNotFoundException.php (1)
  • __construct (9-12)
src/Exception/InvalidIndiceException.php (1)
  • __construct (9-12)
src/Services/SettingsUpdater.php (3)
src/SearchManagerInterface.php (1)
  • getConfiguration (33-33)
src/SearchService.php (1)
  • getConfiguration (27-27)
src/Services/MeilisearchManager.php (1)
  • getConfiguration (85-88)
tests/BaseKernelTestCase.php (2)
src/SearchManagerInterface.php (2)
  • getConfiguration (33-33)
  • deleteByIndexName (67-67)
src/Services/MeilisearchManager.php (2)
  • getConfiguration (85-88)
  • deleteByIndexName (183-186)
tests/Integration/SearchTest.php (8)
tests/BaseKernelTestCase.php (1)
  • waitForAllTasks (47-52)
src/Engine.php (1)
  • search (159-162)
src/SearchManagerInterface.php (2)
  • search (88-88)
  • rawSearch (98-98)
src/Services/MeilisearchManager.php (2)
  • search (195-234)
  • rawSearch (236-244)
src/Services/MeilisearchService.php (2)
  • search (209-254)
  • rawSearch (256-270)
tests/Entity/ContentItem.php (1)
  • getTitle (53-56)
tests/Entity/Page.php (1)
  • getTitle (68-71)
tests/Entity/Post.php (1)
  • getTitle (104-107)
tests/Unit/SerializationTest.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
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/Engine.php (1)
src/SearchManagerInterface.php (5)
  • index (42-42)
  • remove (51-51)
  • clear (60-60)
  • count (106-106)
  • search (88-88)
src/SearchableObject.php (4)
src/SearchableEntity.php (1)
  • getSearchableArray (66-91)
src/Services/UnixTimestampNormalizer.php (1)
  • normalize (14-17)
tests/Entity/Link.php (1)
  • normalize (74-85)
tests/Entity/SelfNormalizable.php (1)
  • normalize (64-75)
tests/Entity/Actor.php (4)
src/Command/MeilisearchCreateCommand.php (1)
  • __construct (23-30)
src/DataProvider/DataProviderRegistry.php (1)
  • __construct (15-19)
src/DataProvider/OrmEntityProvider.php (1)
  • __construct (15-19)
src/SearchableObject.php (1)
  • __construct (28-37)
src/Command/MeilisearchUpdateSettingsCommand.php (3)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/Services/MeilisearchManager.php (2)
  • __construct (51-62)
  • isSearchable (64-69)
src/SearchManagerInterface.php (1)
  • isSearchable (22-22)
src/Command/MeilisearchImportCommand.php (3)
src/DataProvider/DataProviderRegistryInterface.php (1)
  • getDataProvider (21-21)
src/DataProvider/DataProviderInterface.php (2)
  • provide (18-18)
  • cleanup (34-34)
src/DataProvider/OrmEntityProvider.php (2)
  • provide (21-30)
  • cleanup (54-57)
src/Services/MeilisearchManager.php (5)
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/Model/Aggregator.php (3)
  • Aggregator (15-84)
  • __construct (32-44)
  • getEntities (51-54)
src/SearchableObject.php (2)
  • SearchableObject (13-80)
  • __construct (28-37)
src/DataProvider/DataProviderRegistryInterface.php (1)
  • getDataProvider (21-21)
src/DataProvider/DataProviderInterface.php (1)
  • getIdentifierValues (32-32)
src/MeilisearchBundle.php (1)
src/DependencyInjection/Compiler/DataProviderPass.php (1)
  • DataProviderPass (12-40)
src/SearchManagerInterface.php (3)
src/Exception/NotSearchableException.php (1)
  • NotSearchableException (7-13)
src/SearchService.php (11)
  • isSearchable (20-20)
  • searchableAs (34-34)
  • getConfiguration (27-27)
  • index (36-36)
  • remove (38-38)
  • clear (43-43)
  • deleteByIndexName (50-50)
  • delete (48-48)
  • search (59-64)
  • rawSearch (73-77)
  • count (84-84)
src/Collection.php (1)
  • Collection (13-380)
🪛 GitHub Actions: Tests
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/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/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/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/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'.

🔇 Additional comments (49)
composer.json (3)

21-21: PHP 8.1 and Symfony 6.4/7.0 requirements are consistent.

The PHP version bump to 8.1 and Symfony constraints to ^6.4 || ^7.0 align with the stated PR objectives. The removal of symfony/polyfill-php80 (referenced in the summary) is correct given the new PHP 8.1 floor.

Also applies to: 24-29


32-32: The review comment cannot be verified as written. The current state of composer.json shows doctrine/doctrine-bundle is already in require-dev (line 32), not in the require section. The comment treats this as a breaking change that requires documentation updates, but the final state alone does not establish that this is a new change in this PR. Without additional context confirming that doctrine-bundle was previously in require and moved to require-dev, the premise of the review cannot be substantiated.

Additionally, the README.md mentions "Support Doctrine ORM" in the requirements section but provides no configuration details for the ORM provider or doctrine-bundle setup. If the intent is to ensure documentation exists for using the Doctrine ORM integration, that concern should be stated directly rather than framed as documenting a migration.

Likely an incorrect or invalid review comment.


49-49: The symfony/http-client patch versions are security-critical and justified.

The constraints ^6.4.15 || ^7.1.8 address CVE-2024-50342, which allows internal IP/port enumeration via NoPrivateNetworkHttpClient hostname resolution. These specific patch versions are the first releases that fixed this security vulnerability in their respective minor version branches. The constraints are necessary and should be maintained.

tests/Entity/Actor.php (1)

7-14: LGTM!

The Actor entity is well-structured as a simple immutable test fixture using PHP 8.1 constructor property promotion with readonly properties. The implementation is clean and idiomatic.

src/Searchable.php (1)

7-20: LGTM!

The deprecation notices properly guide users to the new SearchableObject class and its constants, providing a clear migration path. The notices are well-documented and consistent with the broader refactoring introduced in this PR.

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

181-189: LGTM!

The test expectation update correctly reflects the new batch processing order introduced by the manager-driven import flow. The aggregated index now processes before the primary index for the Tag entity, which aligns with the refactored batch aggregation logic.

src/Command/MeilisearchClearCommand.php (1)

30-30: LGTM!

The migration from searchService to searchManager is clean and maintains the same method signature. This aligns with the PR's objective of introducing the SearchManagerInterface and deprecating the old SearchService.

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

39-39: LGTM!

The test expectation correctly includes the new Actor index introduced in this PR. The output format is consistent with the other cleared indices.

tests/Entity/SelfNormalizable.php (2)

9-9: LGTM!

The import migration from the deprecated Searchable class to the new SearchableObject is appropriate and aligns with the broader refactoring in this PR.


66-66: LGTM!

The constant reference is correctly updated to use SearchableObject::NORMALIZATION_FORMAT, maintaining consistency with the import change and the deprecation of the Searchable class.

tests/config/meilisearch.yaml (1)

57-60: LGTM!

The new Actor index configuration demonstrates the custom data provider functionality introduced in this PR. The configuration is well-structured with explicit type: 'custom' and references to the ActorDataProvider, serving as a good example of the new DataProvider extension point.

tests/Entity/Link.php (1)

9-10: Good update to SearchableObject normalization constants.
Keeps the test entity aligned with the new normalization entry point.

Also applies to: 74-84

tests/Unit/ConfigurationTest.php (1)

108-156: Test expectations updated consistently for new index metadata fields.
The added “custom primary key” case is a useful regression test for the new primary_key option (Line 359+).

Also applies to: 158-217, 219-257, 259-339, 359-402

src/Exception/LogicException.php (1)

1-9: Simple, fine addition for a bundle-specific exception type.

src/MeilisearchBundle.php (1)

7-25: Compiler pass registration in build() looks correct.
parent::build() is called and DataProviderPass is registered (Line 20-25).

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

14-134: Test updates correctly match new search(string $className, ...) signature.

src/Command/IndexCommand.php (1)

8-28: LGTM! Clean migration to SearchManagerInterface.

The refactoring from SearchService to SearchManagerInterface is well-executed with constructor property promotion (PHP 8.1+) and consistent configuration access throughout the class.

src/Exception/NotSearchableException.php (1)

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

The exception follows best practices: marked final, extends appropriate base class, has proper type hints (including int $code), and provides a clear, formatted message.

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

15-70: LGTM! Test fixture appropriately demonstrates custom provider flexibility.

This fixture showcases how DataProviderInterface can be implemented with custom logic. The implementation is internally consistent and serves its purpose as a test demonstration.

tests/Integration/EngineTest.php (2)

35-42: Tests updated to use SearchableObject with new signature.

The migration from SearchableEntity to SearchableObject correctly reflects the new API with explicit primaryKey, identifier, serializer, and normalizationContext parameters.


67-69: Consistent usage of SearchableObject across multiple entity tests.

Both test cases properly instantiate SearchableObject with the required parameters and validate removal/indexing behavior.

src/DataProvider/DataProviderInterface.php (1)

12-34: Well-designed interface with strong type annotations.

The interface uses PHP 8.1+ generics appropriately, has clear method signatures, and detailed PHPDoc annotations including positive-int, non-negative-int, and non-empty-array constraints.

src/Model/Aggregator.php (2)

32-36: Well-structured constructor with configurable primary key.

The addition of the $primaryKey parameter with a default of 'objectID' maintains backward compatibility while enabling the flexibility required by PR objective #66. Constructor property promotion is clean and idiomatic for PHP 8.1+.


80-83: Dynamic primary key in normalization - behavior is correct.

The normalization correctly uses $this->primaryKey instead of hardcoded 'objectID'. The array_merge([$this->primaryKey => $this->objectID], $normalizer->normalize(...)) call means if the normalized entity contains a field matching $this->primaryKey, the entity's value takes precedence. This is the intended behavior—entity data takes priority—as demonstrated by test cases in AggregatorTest.php (lines 86-100), which verify that when primaryKey='id', the entity's normalized id field is preserved in the final output. Since the aggregator's objectID is always derived from the entity's identifier, values are consistent and there are no conflicts.

tests/BaseKernelTestCase.php (3)

10-27: LGTM! Clean migration to SearchManagerInterface in test base.

The refactoring consistently replaces SearchService with SearchManagerInterface throughout the test base class, properly updating service retrieval and configuration access.


47-52: Improved task waiting logic.

The new implementation properly iterates through all tasks and waits for each one individually, which is more robust than the previous approach.


54-72: Consistent usage of SearchManagerInterface in cleanup methods.

Both cleanUp() and cleanupIndex() correctly use the new manager interface for configuration access and index deletion operations.

src/DependencyInjection/MeilisearchExtension.php (2)

124-136: ORM provider registration is well-implemented.

The method correctly:

  • Generates unique service IDs using xxh32 hash to avoid collisions
  • Creates provider definitions with Doctrine reference and target class
  • Tags providers with index and class metadata for registry lookup

34-68: Service wiring updated for manager-based architecture.

The early parameter setting and manager argument configuration align well with the new SearchManagerInterface and data provider infrastructure introduced in this PR.

tests/Integration/SearchTest.php (3)

71-92: LGTM! Proper migration to manager-based API with task synchronization.

The addition of waitForAllTasks() before search operations ensures async indexing completes before assertions. The API migration from searchService to searchManager correctly removes the ObjectManager parameter, aligning with the new SearchManagerInterface signature.


114-121: LGTM! Pagination test correctly updated.

Task synchronization added and search call properly migrated to the new manager API.


137-142: LGTM! Results count test properly migrated.

Follows the same pattern as other tests with waitForAllTasks() before search operations.

src/Command/MeilisearchUpdateSettingsCommand.php (2)

22-28: LGTM! Clean migration to SearchManagerInterface with constructor promotion.

The constructor now uses PHP 8 promoted properties consistently with other commands in this PR, and correctly delegates to the parent IndexCommand constructor.


56-58: LGTM! Correct usage of the new manager interface.

The isSearchable check properly uses $this->searchManager which is inherited from IndexCommand.

tests/Unit/SerializationTest.php (1)

23-30: LGTM! Constructor updated to match new SearchableObject signature.

The migration correctly:

  • Uses 'id' as the primary key field name (replacing the objectID concept)
  • Passes the identifier directly via $post->getId() instead of relying on Doctrine metadata
  • Uses the standard Symfony Serializer 'groups' context key

This aligns with the PR objective to support non-Doctrine/plain objects by removing metadata dependencies.

src/Command/MeilisearchCreateCommand.php (2)

23-30: LGTM! Consistent migration to SearchManagerInterface.

Constructor uses promoted properties and follows the same pattern as other commands in this PR.


66-68: LGTM! Correct usage of manager's isSearchable method.

src/Services/SettingsUpdater.php (1)

23-29: LGTM! Clean migration to SearchManagerInterface.

Constructor correctly obtains configuration from the new manager interface and uses promoted properties consistently with other classes in this PR.

tests/Integration/AggregatorTest.php (3)

24-34: Good coverage for objectID parsing + unknown-entity failure paths.
These two tests are straightforward and help lock down error handling for getEntityClassFromObjectID().


36-43: Constructor exception test looks correct for “multiple primary keys” guard.
The expectation aligns with the new stricter constructor contract.


69-100: Normalization tests nicely cover default primary key vs custom primary key.
Verifying both 'objectID' and custom 'id' output is valuable for the new primary_key capability.

src/SearchableObject.php (2)

13-38: Clean, minimal value object for “indexUid/primaryKey/identifier + normalization context”.
The constructor forcing meilisearch=true in context is a good, predictable hook for custom normalizers.


65-79: Normalization flow is side-effect free and Symfony-version guarded.
Copying $this->normalizationContext into a local $context avoids surprising reuse issues.

src/Services/MeilisearchService.php (1)

66-283: Deprecation + delegation pattern is consistent.
Triggering the deprecation and delegating when $this->manager is available preserves BC while nudging users to the manager.

config/services.php (1)

72-81: Manager + DataProviderRegistry wiring is cohesive and matches the new extension point.
Good: commands/subscriber/updater are all pointed at the manager, and the registry is a dedicated service.

Also applies to: 82-125, 129-135

src/Command/MeilisearchImportCommand.php (1)

121-151: Batch import loop looks correct (provider reuse + empty-batch break + cleanup).
Provider is resolved once per index and cleanup() is called both on the final empty batch and on normal iterations.

src/SearchManagerInterface.php (1)

15-107: Interface contract is consistent with the batch/chunk return shape used by commands.
In particular, index() returning a list<...> matches the “list of chunks” iteration pattern in MeilisearchImportCommand::formatIndexingResponse().

src/Services/MeilisearchManager.php (2)

95-112: Verify primary_key is always present in index config (or add a default).
Multiple paths assume $indexConfig['primary_key'] / $index['primary_key'] exists. If YAML config makes it optional, this should defensively default (e.g., 'objectID' or 'id') at config normalization time or here.

Also applies to: 155-162, 202-227


7-20: Remove LegacyReflectionFields import and use string literal in class_exists() check to reduce coupling.

The Doctrine\ORM\Mapping\LegacyReflectionFields class is correctly used as the marker for PHP 8.4 native lazy objects compatibility. Since it's only used once in the class_exists() check at line 431, removing the import and converting to a string literal will drop CouplingBetweenObjects from 13 to 12 while maintaining identical behavior.

Remove the import at line 8:

-use Doctrine\ORM\Mapping\LegacyReflectionFields;

Update the check at line 431:

-            if (PHP_VERSION_ID >= 80400 && class_exists(LegacyReflectionFields::class)) {
+            if (PHP_VERSION_ID >= 80400 && class_exists('Doctrine\\ORM\\Mapping\\LegacyReflectionFields')) {

Apply the same change at the second occurrence in lines 423-443.

Likely an incorrect or invalid review comment.

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/Model/Aggregator.php (1)

30-44: Ensure aggregator’s primary key value is not overridden by the entity’s normalized data.

Right now:

return array_merge(
    [$this->primaryKey => $this->objectID],
    $normalizer->normalize($this->entity, $format, $context),
);

If the normalized entity already has a field with the same name as $this->primaryKey (e.g. primary key 'id'), its value will override $this->objectID, defeating the purpose of using the aggregator’s combined identifier as the actual primary key.

To keep the aggregator’s ID authoritative, flip the merge order:

-        return array_merge([$this->primaryKey => $this->objectID], $normalizer->normalize($this->entity, $format, $context));
+        return array_merge(
+            $normalizer->normalize($this->entity, $format, $context),
+            [$this->primaryKey => $this->objectID],
+        );

This preserves all entity fields while guaranteeing that the configured primary key field contains the aggregator’s normalized identifier.

Also applies to: 80-83

♻️ Duplicate comments (2)
.php-cs-fixer.dist.php (1)

12-15: Remove redundant setUnsupportedPhpVersionAllowed(true) call.

The method is already invoked on Line 12; the second call on Line 15 is a no-op and just adds noise. Suggest deleting the duplicate line.

     ->setRiskyAllowed(true)
     ->setFinder($finder)
-    ->setUnsupportedPhpVersionAllowed(true)
     ->setRules([
src/DependencyInjection/Configuration.php (1)

53-62: Add config validation: type=custom should require data_provider.

As previously flagged, setting type: custom without configuring data_provider will likely cause a runtime error. Adding validation here provides a clear error at config time.

                         ->end()
+                        ->validate()
+                            ->ifTrue(static fn (array $v): bool => 'custom' === ($v['type'] ?? null) && null === ($v['data_provider'] ?? null))
+                            ->thenInvalid('When index "type" is set to "custom", "data_provider" must be configured.')
+                        ->end()
                     ->end()
                 ->end()
🧹 Nitpick comments (5)
src/Identifier/IdNormalizerInterface.php (1)

7-12: Interface shape is reasonable; consider tightening the PHPDoc only if needed.

The contract matches ConcatenatingIdNormalizer and the data‑provider usage. If you ever need stronger static guarantees, you could narrow the PHPDoc to something like non-empty-array<string, scalar|\Stringable> to reflect current usage, but that’s purely optional at this point.

src/DependencyInjection/Compiler/DataProviderPass.php (1)

20-39: Data provider wiring looks correct; consider how you want to handle the static-access warning.

The pass correctly builds a ServiceLocator plus an index => class => locatorKey map from meilisearch.data_provider tags and injects them into the registry; resolution logic in MeilisearchManager/DataProviderRegistry should work fine with this shape.

The pipeline’s StaticAccess warning on ServiceLocatorTagPass::register() is essentially about style: that method is designed to be called statically, so there isn’t a non‑static alternative. If you care about a clean report, it’s probably better to tune or suppress this specific rule for ServiceLocatorTagPass::class than to try to change the code here.

src/DataProvider/DataProviderInterface.php (1)

7-36: Interface contract is solid; you might want to clarify identifier array shape.

The generic DataProviderInterface<T> and the split between provide(), loadByIdentifiers(), getIdentifierValues() and normalizeIdentifiers() fit the new provider model well.

One minor improvement would be to document the expected shape of $identifiers in loadByIdentifiers() (e.g., list of scalar IDs vs. associative field => value), so custom providers implement it consistently with OrmEntityProvider and the registry.

src/Identifier/ConcatenatingIdNormalizer.php (1)

21-26: Consider using implode() for cleaner concatenation.

The loop with manual string concatenation and rtrim() can be simplified:

-        $objectID = '';
-        foreach ($identifiers as $value) {
-            $objectID .= $value.'-';
-        }
-
-        return rtrim($objectID, '-');
+        return implode('-', $identifiers);
src/Services/MeilisearchManager.php (1)

195-234: Use normalizeIdentifiers() in search() to mirror indexing ID formatting.

Right now search() re-derives lookup keys from getIdentifierValues() and implode('-'…), while indexing uses DataProviderInterface::normalizeIdentifiers() in batchProcess(). That duplication risks subtle mismatches for custom data providers and future changes to identifier formatting.

You can centralize the contract and guarantee symmetry between indexing and hydration by keying objectsById with normalizeIdentifiers() directly:

-        $dataProvider = $this->dataProviderRegistry->getDataProvider($index['name'], $baseClassName);
-        $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;
-        }
+        $dataProvider = $this->dataProviderRegistry->getDataProvider($index['name'], $baseClassName);
+        $loadedObjects = $dataProvider->loadByIdentifiers($identifiers);
+        $objectsById = [];
+        foreach ($loadedObjects as $object) {
+            $identifier = $dataProvider->normalizeIdentifiers($object);
+            $objectsById[$identifier] = $object;
+        }

This keeps search() aligned with the provider contract and the ID representation actually sent to Meilisearch, while simplifying the manager code. Please double-check that all current DataProviderInterface implementations already treat normalizeIdentifiers() as the canonical ID encoding (which they appear to, based on the existing usage in batchProcess() and OrmEntityProvider).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b7abc5d and dc67a8f.

📒 Files selected for processing (62)
  • .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/Identifier/ConcatenatingIdNormalizer.php (1 hunks)
  • src/Identifier/IdNormalizerInterface.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 (10 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/Command/IndexCommand.php
  • tests/Kernel.php
  • tests/Entity/SelfNormalizable.php
  • tests/Entity/Link.php
  • tests/Entity/Actor.php
  • src/MeilisearchBundle.php
  • src/DependencyInjection/MeilisearchExtension.php
  • phpstan-baseline.php
  • src/DataProvider/OrmEntityProvider.php
  • src/SearchService.php
  • src/DataProvider/DataProviderRegistryInterface.php
  • src/Searchable.php
  • src/EventListener/DoctrineEventSubscriber.php
  • src/SearchableEntity.php
  • tests/Integration/Command/MeilisearchImportCommandTest.php
  • tests/Integration/Command/MeilisearchClearCommandTest.php
  • phpstan.dist.neon
  • src/SearchManagerInterface.php
  • tests/config/framework.yaml
  • tests/config/meilisearch.yaml
  • src/Exception/DataProviderNotFoundException.php
  • src/Exception/LogicException.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/Services/UnixTimestampNormalizer.php
  • tests/Integration/AggregatorTest.php
  • src/Command/MeilisearchImportCommand.php
  • tests/Integration/Fixtures/ActorDataProvider.php
  • src/DependencyInjection/Configuration.php
  • src/Engine.php
  • config/services.php
  • src/Services/MeilisearchManager.php
  • .github/workflows/tests.yml
🧬 Code graph analysis (26)
tests/Entity/Tag.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/Identifier/IdNormalizerInterface.php (6)
src/Identifier/ConcatenatingIdNormalizer.php (1)
  • normalize (9-27)
src/Model/Aggregator.php (1)
  • normalize (80-83)
src/Services/UnixTimestampNormalizer.php (1)
  • normalize (14-17)
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/Identifier/ConcatenatingIdNormalizer.php (1)
src/Identifier/IdNormalizerInterface.php (1)
  • normalize (12-12)
tests/BaseKernelTestCase.php (3)
src/SearchManagerInterface.php (2)
  • getConfiguration (33-33)
  • deleteByIndexName (67-67)
src/SearchService.php (2)
  • getConfiguration (27-27)
  • deleteByIndexName (50-50)
src/Services/MeilisearchManager.php (2)
  • getConfiguration (85-88)
  • deleteByIndexName (183-186)
src/DependencyInjection/Compiler/DataProviderPass.php (1)
src/Services/MeilisearchManager.php (1)
  • index (90-139)
tests/Integration/SearchTest.php (3)
tests/BaseKernelTestCase.php (1)
  • waitForAllTasks (47-52)
src/SearchManagerInterface.php (2)
  • search (88-88)
  • rawSearch (98-98)
src/Services/MeilisearchManager.php (2)
  • search (195-234)
  • rawSearch (236-244)
src/Command/MeilisearchClearCommand.php (5)
src/Engine.php (1)
  • clear (137-140)
src/SearchManagerInterface.php (1)
  • clear (60-60)
src/SearchService.php (1)
  • clear (43-43)
src/Services/MeilisearchManager.php (1)
  • clear (176-181)
src/Services/MeilisearchService.php (1)
  • clear (172-183)
tests/Integration/AggregatorTest.php (5)
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/Identifier/ConcatenatingIdNormalizer.php (1)
  • normalize (9-27)
src/Identifier/IdNormalizerInterface.php (1)
  • normalize (12-12)
src/Exception/NotSearchableException.php (2)
src/Exception/DataProviderNotFoundException.php (1)
  • __construct (9-12)
src/Exception/InvalidIndiceException.php (1)
  • __construct (9-12)
tests/Integration/EventListener/DoctrineEventSubscriberTest.php (3)
src/Engine.php (1)
  • search (159-162)
src/SearchManagerInterface.php (1)
  • search (88-88)
src/Services/MeilisearchManager.php (1)
  • search (195-234)
src/Command/MeilisearchCreateCommand.php (3)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/SearchManagerInterface.php (1)
  • isSearchable (22-22)
src/Services/MeilisearchManager.php (1)
  • isSearchable (64-69)
src/Command/MeilisearchImportCommand.php (5)
src/Engine.php (2)
  • __construct (46-48)
  • index (61-90)
src/SearchManagerInterface.php (2)
  • getConfiguration (33-33)
  • index (42-42)
src/DataProvider/DataProviderRegistry.php (1)
  • getDataProvider (21-36)
src/DataProvider/DataProviderRegistryInterface.php (1)
  • getDataProvider (21-21)
src/DataProvider/DataProviderInterface.php (2)
  • provide (18-18)
  • cleanup (36-36)
tests/Integration/Fixtures/ActorDataProvider.php (3)
tests/Entity/Actor.php (1)
  • Actor (7-14)
src/DataProvider/DataProviderInterface.php (5)
  • provide (18-18)
  • loadByIdentifiers (25-25)
  • getIdentifierValues (32-32)
  • normalizeIdentifiers (34-34)
  • cleanup (36-36)
src/Identifier/IdNormalizerInterface.php (1)
  • normalize (12-12)
src/DependencyInjection/Configuration.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/DataProvider/DataProviderRegistry.php (2)
src/Exception/DataProviderNotFoundException.php (2)
  • DataProviderNotFoundException (7-13)
  • __construct (9-12)
src/DataProvider/DataProviderRegistryInterface.php (1)
  • getDataProvider (21-21)
src/Engine.php (1)
src/SearchableObject.php (5)
  • SearchableObject (13-80)
  • getSearchableArray (65-79)
  • getIndexUid (42-45)
  • getPrimaryKey (50-53)
  • getIdentifier (55-58)
config/services.php (7)
src/DataProvider/DataProviderRegistry.php (1)
  • DataProviderRegistry (10-37)
src/Engine.php (1)
  • Engine (44-182)
src/EventListener/DoctrineEventSubscriber.php (1)
  • DoctrineEventSubscriber (10-30)
src/Identifier/ConcatenatingIdNormalizer.php (1)
  • ConcatenatingIdNormalizer (7-28)
src/Services/MeilisearchManager.php (1)
  • MeilisearchManager (27-420)
src/Services/SettingsUpdater.php (1)
  • SettingsUpdater (17-78)
src/Services/UnixTimestampNormalizer.php (1)
  • UnixTimestampNormalizer (9-30)
src/Command/MeilisearchUpdateSettingsCommand.php (4)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/Command/MeilisearchCreateCommand.php (1)
  • __construct (23-30)
src/Services/MeilisearchManager.php (2)
  • __construct (51-62)
  • isSearchable (64-69)
src/SearchManagerInterface.php (1)
  • isSearchable (22-22)
src/Services/MeilisearchManager.php (5)
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/SearchableObject.php (2)
  • SearchableObject (13-80)
  • __construct (28-37)
src/DataProvider/OrmEntityProvider.php (4)
  • __construct (16-21)
  • getIdentifierValues (49-54)
  • normalizeIdentifiers (56-59)
  • loadByIdentifiers (34-47)
src/DataProvider/DataProviderRegistryInterface.php (1)
  • getDataProvider (21-21)
src/DataProvider/DataProviderInterface.php (3)
  • getIdentifierValues (32-32)
  • normalizeIdentifiers (34-34)
  • loadByIdentifiers (25-25)
src/Command/MeilisearchDeleteCommand.php (4)
src/SearchManagerInterface.php (1)
  • deleteByIndexName (67-67)
src/SearchService.php (1)
  • deleteByIndexName (50-50)
src/Services/MeilisearchManager.php (1)
  • deleteByIndexName (183-186)
src/Services/MeilisearchService.php (1)
  • deleteByIndexName (185-194)
src/Services/SettingsUpdater.php (2)
src/SearchManagerInterface.php (1)
  • getConfiguration (33-33)
src/Services/MeilisearchManager.php (1)
  • getConfiguration (85-88)
src/Model/Aggregator.php (2)
src/Identifier/ConcatenatingIdNormalizer.php (1)
  • normalize (9-27)
src/Identifier/IdNormalizerInterface.php (1)
  • normalize (12-12)
tests/Unit/SerializationTest.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/Services/MeilisearchService.php (1)
src/Services/MeilisearchManager.php (11)
  • isSearchable (64-69)
  • 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)
src/DataProvider/DataProviderInterface.php (2)
src/DataProvider/OrmEntityProvider.php (5)
  • provide (23-32)
  • loadByIdentifiers (34-47)
  • getIdentifierValues (49-54)
  • normalizeIdentifiers (56-59)
  • cleanup (61-64)
tests/Integration/Fixtures/ActorDataProvider.php (5)
  • provide (21-50)
  • loadByIdentifiers (52-63)
  • getIdentifierValues (65-70)
  • normalizeIdentifiers (72-75)
  • cleanup (77-80)
src/SearchableObject.php (4)
src/SearchableEntity.php (3)
  • __construct (42-56)
  • getIndexUid (58-61)
  • getSearchableArray (66-91)
src/Identifier/ConcatenatingIdNormalizer.php (1)
  • normalize (9-27)
src/Identifier/IdNormalizerInterface.php (1)
  • normalize (12-12)
src/Services/UnixTimestampNormalizer.php (1)
  • normalize (14-17)
🪛 GitHub Actions: Tests
src/DependencyInjection/Compiler/DataProviderPass.php

[warning] 37-37: StaticAccess: Avoid using static access to class '\Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass' in method 'process'.

src/Services/UnixTimestampNormalizer.php

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


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


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


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

src/Command/MeilisearchImportCommand.php

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


[warning] 73-73: CyclomaticComplexity: The method execute() has a Cyclomatic Complexity of 14. The configured cyclomatic complexity threshold is 10.


[warning] 73-73: NPathComplexity: The method execute() has an NPath complexity of 976. The configured NPath complexity threshold is 200.

src/Services/MeilisearchManager.php

[warning] 27-27: ExcessiveClassComplexity: The class MeilisearchManager has an overall complexity of 60 which is very high. The configured complexity threshold is 50.


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


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

src/Services/SettingsUpdater.php

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

src/Services/MeilisearchService.php

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


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


[warning] 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] 415-415: LongVariable: Avoid excessively long variable names like $searchableEntitiesChunk. Keep variable name 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'.

🔇 Additional comments (46)
src/EventListener/ConsoleOutputSubscriber.php (1)

13-19: Constructor promotion and $output rename look good

Using a private readonly OutputStyle $output via constructor promotion is clean and idiomatic, and the updated $this->output->writeln() usage is consistent. No behavioral changes; PHPMD short variable concern is resolved.

src/Event/SettingsUpdatedEvent.php (1)

16-20: Constructor property promotion + readonly look correct and BC-safe

Signature for consumers is unchanged, the promoted properties align with the existing docblocks, and readonly matches the previous effectively-immutable behavior of this event. All good from a typing and compatibility standpoint.

tests/baseline-ignore (1)

5-5: LGTM! Consistent with the PR's Doctrine modernization.

Adding this deprecation suppression for DefaultProxyClassNameResolver aligns with the broader move toward native lazy objects in PHP 8.1+ and is consistent with other Doctrine proxy deprecations already being suppressed in this baseline.

src/Services/UnixTimestampNormalizer.php (2)

11-17: Parameter rename improves consistency.

The rename from $object to $data improves naming consistency with the supportsNormalization method and aligns with common normalizer conventions. The explicit mixed type hint correctly reflects the NormalizerInterface contract.

The pipeline warnings about unused $format and $context parameters are unavoidable—they're required by the interface signature even though this specific normalizer doesn't need them.


19-19: LGTM!

Adding the explicit mixed type hint aligns with the NormalizerInterface contract and improves type declaration consistency across the class.

src/Exception/InvalidIndiceException.php (1)

9-12: Constructor type-hint is correct with no backward compatibility impact

Typing $code as int (Line 9) is correct and consistent with \Exception's signature, and matches the file's strict_types=1 usage. The codebase contains only one instantiation of this exception (src/Services/SettingsUpdater.php), which passes only the $indice parameter and relies on the default int $code = 0. No callers pass non-int codes, so the change poses no backward compatibility concerns.

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

44-85: Actor index expectation update looks consistent; keep an eye on output ordering.

Adding Creating index sf_phpunit__actor... to both branches aligns the test with the new actor index configuration. Since these assertions use assertSame on the full output, any future change in creation order (e.g., from config reordering or data‑provider changes) will break the test, so just be sure the sequence here matches the intended deterministic order from the command.

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

42-65: Pre-release matrix and install options look aligned with composer constraints.

The PHP/Symfony matrix (8.1–8.4 / 6.4–7.3 with default/lowest/highest dependencies) plus the ramsey/composer-install options are consistent with the new composer.json requirements and give good coverage of dependency combinations.

Also applies to: 79-86

composer.json (1)

21-52: Platform and Symfony constraints align with the new support policy.

Bumping PHP to ^8.1, narrowing Symfony components to ^6.4 || ^7.0, and moving doctrine/doctrine-bundle into require‑dev are all consistent with the new CI matrices and the “ORM as optional/provider” architecture described in the PR.

.github/workflows/tests.yml (1)

30-31: CI matrix and Symfony pinning look coherent with composer.json.

The integration matrix (PHP 8.1–8.4 / Symfony 6.4–7.3 with dependency variants) matches the supported ranges, and using SYMFONY_REQUIRE: 7.4.* in the code‑style job to exercise the latest allowed Symfony version is a sensible choice as long as that version is available on Packagist.

Also applies to: 100-102

src/Command/MeilisearchClearCommand.php (1)

30-30: LGTM!

The migration from searchService to searchManager is consistent with the PR's architectural shift to the manager-based approach. The method signature remains compatible (clear(string $className): array).

tests/Entity/Tag.php (1)

9-9: LGTM!

The migration from Searchable to SearchableObject is consistent with the broader codebase refactoring. The NORMALIZATION_FORMAT constant retains the same value ('searchableArray'), ensuring backward-compatible behavior.

Also applies to: 90-90

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

40-40: LGTM!

The additional expected output line reflects the new actor index introduced with the data provider architecture. The test correctly validates that all configured indices are deleted.

src/Command/MeilisearchDeleteCommand.php (1)

31-39: LGTM!

The migration to searchManager is consistent with the PR's architectural changes. The exception handling correctly skips the success message when deletion fails, ensuring accurate user feedback.

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

23-23: LGTM!

The test updates correctly reflect the simplified SearchManagerInterface::search() signature that no longer requires an ObjectManager parameter. The manager internally handles entity loading via the data provider infrastructure.

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

tests/Unit/ConfigurationTest.php (2)

362-407: Good test coverage for the new primary_key configuration option.

The test case validates that custom primary keys (postId, tagId) are correctly processed and preserved in the configuration, supporting the PR's objective to allow configurable primary keys instead of relying solely on objectID.


409-441: Good test coverage for the new id_normalizer configuration option.

This test validates that a custom ID normalizer service reference (acme.id_normalizer) is correctly accepted and stored in the processed configuration.

src/DependencyInjection/Configuration.php (2)

7-7: LGTM!

Import correctly updated to use SearchableObject consistent with the codebase-wide migration.


64-69: LGTM!

The serializer groups configuration correctly references SearchableObject::NORMALIZATION_GROUP, maintaining consistency with the new class.

src/Exception/NotSearchableException.php (1)

7-13: LGTM! Clean exception design.

The exception class follows a consistent pattern with other exceptions in the codebase (e.g., InvalidIndiceException, DataProviderNotFoundException), provides a clear message, and correctly types all parameters including the addressed int $code.

tests/Integration/SearchTest.php (3)

71-92: LGTM! Clean migration to manager-based API with proper async handling.

The addition of waitForAllTasks() before search operations ensures asynchronous indexing completes, and the migration from searchService to searchManager consistently removes the ObjectManager dependency across all test methods.


114-121: LGTM! Consistent async handling in pagination test.

The test properly waits for tasks before executing the paginated search with the updated manager API.


137-142: LGTM! Proper async handling added.

The waitForAllTasks() call ensures the import completes before asserting search results.

tests/Unit/SerializationTest.php (1)

23-44: LGTM! Clean migration to SearchableObject with explicit ID provisioning.

The test correctly migrates from SearchableEntity to SearchableObject, replacing Doctrine metadata with explicit primary key and ID values. The unchanged expected array confirms backward compatibility of the serialization output.

tests/BaseKernelTestCase.php (3)

10-27: LGTM! Clean migration to SearchManagerInterface.

The base test case correctly migrates from SearchService to SearchManagerInterface, enabling the new manager-based architecture across all integration tests.


47-52: LGTM! Improved task waiting with per-task iteration.

The updated implementation iterates over all tasks and waits for each individually, ensuring all asynchronous operations complete before proceeding.


54-72: LGTM! Consistent manager usage in cleanup.

The cleanup methods correctly use searchManager for configuration access and index deletion, maintaining consistency with the new architecture.

src/DataProvider/DataProviderRegistry.php (1)

21-36: LGTM! Efficient provider resolution with inheritance support.

The registry uses a two-tier lookup strategy:

  1. Direct O(1) lookup for exact class matches
  2. Fallback iteration with is_a() for polymorphic resolution (parent/child relationships)

This design efficiently handles both explicit and inheritance-based provider registration while throwing a descriptive exception when no provider is found.

src/Command/MeilisearchUpdateSettingsCommand.php (2)

22-28: LGTM! Clean migration with constructor promotion.

The command correctly migrates to SearchManagerInterface using PHP 8.1 constructor promotion, simplifying property declarations while maintaining the same functionality.


56-58: LGTM! Consistent manager usage.

The isSearchable check correctly uses the new searchManager instance.

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

16-80: LGTM! Well-structured test fixture demonstrating custom provider flexibility.

This test fixture appropriately showcases a custom data provider implementation with in-memory data. The $identifiers['id'] access pattern in loadByIdentifiers intentionally differs from OrmEntityProvider to demonstrate the flexibility of the DataProviderInterface, as confirmed in previous review discussions.

src/Command/MeilisearchCreateCommand.php (2)

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

The command correctly migrates to SearchManagerInterface using PHP 8.1 constructor promotion, consistent with other command updates in this PR.


66-68: LGTM! Consistent manager usage.

The isSearchable check correctly uses the new searchManager instance, maintaining consistency across all commands.

src/Services/SettingsUpdater.php (1)

23-29: LGTM! Clean migration to manager-based architecture.

The constructor correctly accepts SearchManagerInterface and retrieves configuration from the manager. The use of promoted constructor properties improves code clarity.

src/SearchableObject.php (2)

28-37: LGTM! Clean constructor with proper context initialization.

The use of promoted constructor properties is idiomatic, and merging ['meilisearch' => true] into the normalization context ensures the custom normalizers (like UnixTimestampNormalizer) can detect Meilisearch-specific processing.


65-79: LGTM! Proper context handling avoids state mutation.

Creating a local $context copy prevents the mutation issue flagged in past reviews. The conditional DateTime normalization for Symfony 7.1+ is appropriate, and the delegation pattern for normalization is standard.

tests/Integration/AggregatorTest.php (2)

69-84: LGTM! Comprehensive test for aggregator normalization.

The test verifies that both objectID and id fields are present in the serialized data, ensuring proper normalization behavior for aggregated entities.


86-100: LGTM! Good coverage for custom primary key.

This test ensures that when a custom primary key ('id') is specified, the aggregator correctly uses it in the normalized output.

src/Services/MeilisearchService.php (2)

50-58: LGTM! Clean deprecation pathway with backward compatibility.

The optional SearchManagerInterface parameter enables a gradual migration path. When the manager is present, methods delegate to it; otherwise, the legacy behavior is preserved. This is a well-designed deprecation strategy.


66-77: LGTM! Consistent deprecation pattern.

The deprecation notice is clear, and the delegation to the manager (when present) ensures users can migrate incrementally.

config/services.php (1)

74-82: LGTM! Manager service wiring is clean.

The MeilisearchManager is correctly configured with all required dependencies: normalizer, engine, property accessor, data provider registry, and configuration. The alias to SearchManagerInterface enables type-hinted autowiring.

src/Engine.php (3)

61-90: LGTM! Per-index batching correctly handles multiple primary keys.

The refactored batching logic properly tracks the primary key per index (line 78), preventing the bug where a single primary key was used for all indexes. Each index's documents are correctly merged with its own primary key field (line 79), and addDocuments is called with the appropriate primary key per index (line 86).


100-126: LGTM! Clean removal logic with per-index grouping.

The removal method correctly groups identifiers by index and deletes each document individually. This ensures proper per-index handling.


174-181: LGTM! Simplified identifier normalization.

The normalizeId helper is cleaner than before—it simply casts objects to strings without checking for __toString explicitly, relying on the type constraint \Stringable|string|int to ensure compatibility.

src/Command/MeilisearchImportCommand.php (2)

28-36: LGTM! Clean migration to manager-based architecture.

The constructor correctly accepts SearchManagerInterface and DataProviderRegistryInterface, enabling the data provider abstraction that was a core objective of this PR (objective #121).


121-151: LGTM! Efficient data provider usage and proper cleanup.

The data provider is obtained once before the loop (line 121), avoiding repeated lookups. The early break on empty batches (lines 126-130) prevents unnecessary indexing calls, and cleanup is called appropriately to release resources.

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

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

35-42: Hardcoded 'objectID' still contradicts PR objective #66.

Despite the previous comment being marked as addressed, 'objectID' is still hardcoded here and on lines 68-69. PR objective #66 aims to remove Algolia-specific "objectID" terminology in favor of MeiliSearch primary key semantics.

Consider using 'id' consistently:

         $searchableImage = new SearchableObject(
             $this->getPrefix().'image',
-            'objectID',
+            'id',
             $image,
             $image->getId(),
             $this->get('serializer'),
             ['groups' => ['force_empty']],
         );
config/services.php (1)

138-142: Alias target for IdNormalizerInterface must reference the registered service id

The service is registered with id 'meilisearch.identifier.concatenating_id_normalizer', but the alias targets ConcatenatingIdNormalizer::class which is not a registered service id. This will cause autowiring of IdNormalizerInterface to fail. Update the alias to target the actual service id:

    $services
        ->set('meilisearch.identifier.concatenating_id_normalizer', ConcatenatingIdNormalizer::class)
-    ->alias(IdNormalizerInterface::class, ConcatenatingIdNormalizer::class)
+    ->alias(IdNormalizerInterface::class, 'meilisearch.identifier.concatenating_id_normalizer')
    ;
🧹 Nitpick comments (3)
src/DependencyInjection/MeilisearchExtension.php (1)

100-111: Silent skip when configured data_provider service doesn't exist.

If a user configures data_provider: some_service but the service doesn't exist in the container, this silently skips tagging and continues without warning. The user would only discover the misconfiguration at runtime when the DataProviderNotFoundException is thrown.

Consider throwing a configuration exception for a clearer fail-fast behavior:

             if (null !== $indice['data_provider']) {
-                if ($container->hasDefinition($indice['data_provider'])) {
-                    $container
-                        ->findDefinition($indice['data_provider'])
-                        ->addTag('meilisearch.data_provider', [
-                            'index' => $indexName,
-                            'class' => $class,
-                        ]);
+                if (!$container->hasDefinition($indice['data_provider'])) {
+                    throw new \InvalidArgumentException(\sprintf(
+                        'Data provider service "%s" configured for index "%s" does not exist.',
+                        $indice['data_provider'],
+                        $indexName
+                    ));
                 }
+
+                $container
+                    ->findDefinition($indice['data_provider'])
+                    ->addTag('meilisearch.data_provider', [
+                        'index' => $indexName,
+                        'class' => $class,
+                    ]);

                 continue;
             }
src/DependencyInjection/Compiler/DataProviderPass.php (1)

25-34: Add validation for required tag attributes.

Lines 27-28 access $attributes['index'] and $attributes['class'] without validation, which could throw undefined array key errors if a service is tagged incorrectly.

Apply this diff to add validation:

         foreach ($container->findTaggedServiceIds('meilisearch.data_provider') as $id => $tags) {
             foreach ($tags as $attributes) {
+                if (!isset($attributes['index'], $attributes['class'])) {
+                    throw new \InvalidArgumentException(\sprintf('Service "%s" tagged as "meilisearch.data_provider" must have "index" and "class" attributes.', $id));
+                }
+
                 $index = $attributes['index'];
                 $class = $attributes['class'];
src/Engine.php (1)

10-42: Engine batching and task typing are consistent with the new SearchableObject flow

Per‑index grouping in index() (primary key + documents) and corresponding aggregation in remove() line up with how SearchableObject exposes index UID, primary key, and identifier. The added phpstan type aliases make the Meilisearch task shapes explicit without changing behavior. If you ever need to optimize deletions, deleteDocuments() could replace the inner per‑id loop in remove(), but that’s purely an optional micro‑optimization.

Also applies to: 61-87, 100-125, 174-181

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dc67a8f and 842be8c.

📒 Files selected for processing (61)
  • .github/workflows/pre-release-tests.yml (2 hunks)
  • .github/workflows/tests.yml (2 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 (3 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/Identifier/ConcatenatingIdNormalizer.php (1 hunks)
  • src/Identifier/IdNormalizerInterface.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 (10 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)
  • phpstan-baseline.php
🚧 Files skipped from review as they are similar to previous changes (23)
  • tests/Entity/Tag.php
  • tests/Entity/SelfNormalizable.php
  • src/DataProvider/DataProviderRegistryInterface.php
  • src/SearchableEntity.php
  • src/Command/IndexCommand.php
  • tests/baseline-ignore
  • src/Identifier/ConcatenatingIdNormalizer.php
  • src/Searchable.php
  • src/SearchManagerInterface.php
  • tests/Kernel.php
  • phpstan.dist.neon
  • src/DataProvider/OrmEntityProvider.php
  • src/Command/MeilisearchDeleteCommand.php
  • tests/Entity/Link.php
  • tests/Unit/ConfigurationTest.php
  • .github/workflows/tests.yml
  • src/Command/MeilisearchClearCommand.php
  • src/Exception/InvalidIndiceException.php
  • src/Exception/NotSearchableException.php
  • tests/Integration/Command/MeilisearchDeleteCommandTest.php
  • tests/BaseKernelTestCase.php
  • src/Services/MeilisearchService.php
  • tests/Integration/Command/MeilisearchImportCommandTest.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/DependencyInjection/Configuration.php
  • src/EventListener/ConsoleOutputSubscriber.php
  • tests/Integration/EngineTest.php
  • src/Services/UnixTimestampNormalizer.php
  • tests/Integration/Fixtures/ActorDataProvider.php
  • src/Engine.php
  • config/services.php
  • src/Command/MeilisearchImportCommand.php
  • src/Services/MeilisearchManager.php
  • tests/Integration/AggregatorTest.php
🧬 Code graph analysis (24)
src/MeilisearchBundle.php (1)
src/DependencyInjection/Compiler/DataProviderPass.php (1)
  • DataProviderPass (12-40)
src/Services/SettingsUpdater.php (3)
src/Command/MeilisearchUpdateSettingsCommand.php (1)
  • __construct (22-28)
src/SearchManagerInterface.php (1)
  • getConfiguration (33-33)
src/Services/MeilisearchManager.php (1)
  • getConfiguration (85-88)
src/Command/MeilisearchUpdateSettingsCommand.php (4)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/Services/MeilisearchManager.php (2)
  • __construct (51-62)
  • isSearchable (64-69)
src/SearchManagerInterface.php (1)
  • isSearchable (22-22)
src/SearchService.php (1)
  • isSearchable (20-20)
src/Identifier/IdNormalizerInterface.php (1)
src/Identifier/ConcatenatingIdNormalizer.php (1)
  • normalize (9-27)
tests/Integration/EventListener/DoctrineEventSubscriberTest.php (2)
src/SearchManagerInterface.php (1)
  • search (88-88)
src/Services/MeilisearchManager.php (1)
  • search (195-234)
src/DependencyInjection/Configuration.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/EventListener/ConsoleOutputSubscriber.php (1)
src/Event/SettingsUpdatedEvent.php (4)
  • __construct (16-21)
  • SettingsUpdatedEvent (9-46)
  • getSetting (42-45)
  • getIndex (34-37)
tests/Integration/SearchTest.php (3)
src/Engine.php (1)
  • search (159-162)
src/SearchManagerInterface.php (2)
  • search (88-88)
  • rawSearch (98-98)
src/Services/MeilisearchManager.php (2)
  • search (195-234)
  • rawSearch (236-244)
tests/Integration/EngineTest.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/DependencyInjection/MeilisearchExtension.php (6)
src/DataProvider/OrmEntityProvider.php (1)
  • OrmEntityProvider (11-65)
src/MeilisearchBundle.php (2)
  • MeilisearchBundle (11-31)
  • qualifiedVersion (15-18)
src/Model/Aggregator.php (2)
  • Aggregator (15-84)
  • getEntities (51-54)
src/Entity/Aggregator.php (1)
  • Aggregator (9-11)
src/Engine.php (1)
  • index (61-90)
src/Services/MeilisearchManager.php (1)
  • index (90-139)
src/DependencyInjection/Compiler/DataProviderPass.php (5)
src/Engine.php (1)
  • index (61-90)
src/SearchManagerInterface.php (1)
  • index (42-42)
src/SearchService.php (1)
  • index (36-36)
src/Services/MeilisearchManager.php (1)
  • index (90-139)
src/Services/MeilisearchService.php (1)
  • index (114-147)
tests/Integration/Fixtures/ActorDataProvider.php (2)
src/DataProvider/DataProviderInterface.php (5)
  • provide (18-18)
  • loadByIdentifiers (25-25)
  • getIdentifierValues (32-32)
  • normalizeIdentifiers (34-34)
  • cleanup (36-36)
src/Identifier/IdNormalizerInterface.php (1)
  • normalize (12-12)
src/SearchableObject.php (1)
src/SearchableEntity.php (3)
  • __construct (42-56)
  • getIndexUid (58-61)
  • getSearchableArray (66-91)
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 (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/DataProvider/DataProviderInterface.php (2)
src/DataProvider/OrmEntityProvider.php (5)
  • provide (23-32)
  • loadByIdentifiers (34-47)
  • getIdentifierValues (49-54)
  • normalizeIdentifiers (56-59)
  • cleanup (61-64)
tests/Integration/Fixtures/ActorDataProvider.php (5)
  • provide (21-50)
  • loadByIdentifiers (52-63)
  • getIdentifierValues (65-70)
  • normalizeIdentifiers (72-75)
  • cleanup (77-80)
src/Model/Aggregator.php (2)
src/Identifier/ConcatenatingIdNormalizer.php (1)
  • normalize (9-27)
src/Identifier/IdNormalizerInterface.php (1)
  • normalize (12-12)
tests/Entity/Actor.php (5)
src/Command/MeilisearchCreateCommand.php (1)
  • __construct (23-30)
src/DataProvider/OrmEntityProvider.php (1)
  • __construct (16-21)
src/Engine.php (1)
  • __construct (46-48)
src/Event/SettingsUpdatedEvent.php (1)
  • __construct (16-21)
src/EventListener/ConsoleOutputSubscriber.php (1)
  • __construct (13-15)
src/Engine.php (2)
src/SearchableEntity.php (3)
  • __construct (42-56)
  • getSearchableArray (66-91)
  • getIndexUid (58-61)
src/SearchableObject.php (5)
  • SearchableObject (13-80)
  • getSearchableArray (65-79)
  • getIndexUid (42-45)
  • getPrimaryKey (50-53)
  • getIdentifier (55-58)
src/Command/MeilisearchImportCommand.php (7)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/Engine.php (3)
  • __construct (46-48)
  • index (61-90)
  • count (169-172)
src/Services/MeilisearchService.php (5)
  • __construct (52-64)
  • getConfiguration (87-96)
  • isSearchable (66-77)
  • index (114-147)
  • count (272-283)
src/SearchManagerInterface.php (4)
  • getConfiguration (33-33)
  • isSearchable (22-22)
  • index (42-42)
  • count (106-106)
src/DataProvider/DataProviderRegistryInterface.php (1)
  • getDataProvider (21-21)
src/DataProvider/DataProviderInterface.php (2)
  • provide (18-18)
  • cleanup (36-36)
src/DataProvider/OrmEntityProvider.php (2)
  • provide (23-32)
  • cleanup (61-64)
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)
src/Exception/DataProviderNotFoundException.php (3)
src/DataProvider/DataProviderRegistry.php (1)
  • __construct (15-19)
src/Exception/InvalidIndiceException.php (1)
  • __construct (9-12)
src/Exception/NotSearchableException.php (1)
  • __construct (9-12)
src/DataProvider/DataProviderRegistry.php (2)
src/Exception/DataProviderNotFoundException.php (2)
  • DataProviderNotFoundException (7-13)
  • __construct (9-12)
src/DataProvider/DataProviderRegistryInterface.php (1)
  • getDataProvider (21-21)
src/Services/MeilisearchManager.php (5)
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/Model/Aggregator.php (2)
  • Aggregator (15-84)
  • __construct (32-44)
src/SearchableObject.php (2)
  • SearchableObject (13-80)
  • __construct (28-37)
src/DataProvider/DataProviderRegistryInterface.php (1)
  • getDataProvider (21-21)
src/DataProvider/DataProviderInterface.php (3)
  • getIdentifierValues (32-32)
  • normalizeIdentifiers (34-34)
  • loadByIdentifiers (25-25)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: integration-tests (PHP 8.1) (Symfony 6.4.*)
🔇 Additional comments (39)
tests/config/framework.yaml (1)

5-7: Test framework error handling looks good.

The addition of handle_all_throwables and php_errors.log improves error visibility in tests, which aligns well with the broader PHP 8.1+ migration and strengthened test rigor.

composer.json (1)

21-21: Dependency constraints align well with PR objectives.

PHP 8.1+ requirement and Symfony 6.4+ constraints are correct. Moving doctrine-bundle to require-dev supports the DataProvider abstraction by decoupling ORM as an optional dependency—a necessary breaking change for enabling custom/non-Doctrine data sources. Removal of symfony/polyfill-php80 is appropriate for PHP 8.1+. All dev dependencies are properly updated to 6.4+.

Also applies to: 24-29, 32-32, 38-38, 41-43, 46-52

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

42-44: CI matrix refactoring is correct and comprehensive.

The removal of PHP 7.4 and Symfony 5.x versions, addition of PHP 8.4, and dependency-aware matrix expansion (lines 54–63 with lowest/highest variants for PHP 8.4) appropriately test edge cases for pre-release Meilisearch. The exclusion logic for PHP 8.1 + Symfony 7.x combinations is reasonable. Job naming (line 65) correctly appends the dependency variant for visibility. The ramsey/composer-install usage with dependency-versions matrix parameter (lines 84–85) is correct.

Also applies to: 54-63, 65-65, 84-85

src/Exception/LogicException.php (1)

7-9: LGTM!

The exception class is correctly implemented as a final marker exception. The use of final prevents unintended subclassing, and extending \LogicException is appropriate for logic errors in the bundle.

src/SearchService.php (1)

9-11: Deprecation notice is clear and aligned with the new API

The @deprecated docblock cleanly communicates both the version (0.16) and the replacement (SearchManagerInterface) without affecting behavior. This is a suitable and non-disruptive way to steer users toward the new manager-based API.

src/EventListener/ConsoleOutputSubscriber.php (2)

13-15: LGTM! Constructor property promotion correctly implemented.

The rename from $io to $output successfully addresses the PHPMD ShortVariable violation while maintaining clarity. The use of private readonly for the promoted property is appropriate for this immutable dependency.


17-20: LGTM! Property usage correctly updated.

The reference to $this->output is consistent with the renamed constructor property. The event handler correctly uses the SettingsUpdatedEvent accessor methods and provides clear console feedback.

src/Identifier/IdNormalizerInterface.php (1)

7-13: LGTM! Clean interface design.

The interface is well-designed with a focused responsibility. The return type string|int correctly aligns with MeiliSearch's primary key requirements, and the non-empty-array constraint in the docblock provides helpful guidance for implementers.

src/Services/UnixTimestampNormalizer.php (1)

11-17: LGTM! Signature updated to match Symfony normalizer contract.

The parameter renaming from $object to $data and the type widening to mixed align with Symfony's normalizer interface. The framework guarantees this method is only called after supportsNormalization() confirms the type, so the \DateTimeInterface docblock serves as helpful documentation of the expected runtime type.

src/Event/SettingsUpdatedEvent.php (1)

16-21: LGTM! Clean constructor promotion refactor.

The migration to constructor property promotion with private readonly is idiomatic for PHP 8.1+ and improves maintainability by eliminating redundant property declarations and assignments. The event remains immutable with no behavioral changes.

src/Model/Aggregator.php (2)

32-36: LGTM! Configurable primary key resolves hardcoded objectID issue.

The addition of the $primaryKey parameter with a default value of 'objectID' provides backward compatibility while enabling custom primary keys per index. This addresses the previous TODO and aligns with the PR's objective to eliminate Algolia-specific objectID hardcoding.


82-82: LGTM! Dynamic primary key correctly applied in normalization.

The normalization logic now uses $this->primaryKey instead of the hardcoded 'objectID' string, correctly respecting the configured primary key for each aggregator instance.

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

39-39: LGTM! Test updated for new Actor index.

The expected output correctly includes the new Actor index clearing log, consistent with the addition of the Actor entity and its corresponding data provider configuration in this PR.

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

63-63: LGTM! Test assertions updated for Actor index creation.

Both test paths correctly include the Actor index creation log in the expected output, maintaining consistency across scenarios with and without settings updates.

Also applies to: 80-80

tests/Entity/Actor.php (1)

7-14: LGTM! Clean test entity for DataProvider demonstration.

The Actor class is a straightforward value object using constructor promotion with public readonly properties. This provides a simple, non-Doctrine entity to demonstrate the new custom data provider functionality introduced in this PR.

tests/config/meilisearch.yaml (1)

57-60: LGTM! Configuration demonstrates custom DataProvider usage.

This configuration entry effectively demonstrates the new DataProvider extension point introduced in this PR. The type: 'custom' and explicit data_provider reference show how users can plug in non-Doctrine data sources for their indices.

src/MeilisearchBundle.php (1)

20-25: LGTM! Standard Symfony bundle pattern for compiler pass registration.

The build() method correctly calls parent::build() before registering the DataProviderPass, following Symfony best practices for bundle extension.

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

23-23: LGTM! Tests correctly updated to use the new SearchManagerInterface API.

The search calls are consistently migrated from searchService->search($entityManager, $class, $query) to searchManager->search($class, $query), aligning with the PR's objective to remove Doctrine ObjectManager from the public search API.

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

src/Exception/DataProviderNotFoundException.php (1)

7-13: LGTM! Exception follows established patterns in the codebase.

The exception class is consistent with other exceptions like NotSearchableException and InvalidIndiceException, providing a clear, actionable error message with both index name and class name for debugging.

tests/Integration/SearchTest.php (3)

71-72: LGTM! Proper synchronization added for async operations.

Adding waitForAllTasks() after the import command ensures all async Meilisearch indexing operations complete before executing search assertions. This prevents flaky tests due to race conditions.

Also applies to: 75-75, 81-81, 90-90


114-116: LGTM! Test properly waits for indexing before pagination test.


137-139: LGTM! Consistent pattern applied for nbResults test.

src/DependencyInjection/Configuration.php (2)

53-62: LGTM! New configuration nodes properly structured.

The new type, primary_key, data_provider, and id_normalizer configuration options are well-defined with appropriate defaults. The objectID default for primary_key maintains backward compatibility as discussed in previous reviews.


104-107: LGTM! Validation rule correctly implemented.

The validation ensures data_provider must be configured when type is set to 'custom', providing a clear config-time error instead of a runtime failure. This addresses the concern from the previous review.

tests/Integration/AggregatorTest.php (2)

25-43: LGTM! Good test coverage for error cases.

The tests properly verify:

  • Entity class extraction from objectID format
  • Exception thrown for unknown entities
  • Exception thrown when aggregators receive multiple primary keys

69-100: LGTM! New normalization tests cover key scenarios.

testAggregatorNormalization verifies the default objectID primary key behavior, while testAggregatorCustomPrimaryKey confirms custom primary key names work correctly. These tests support the PR's objective of configurable primary keys.

src/DependencyInjection/MeilisearchExtension.php (2)

125-141: LGTM! ORM provider registration is well-structured.

The helper method creates deterministic service IDs using a hash of the class name, preventing collisions while keeping IDs debuggable. The OrmEntityProvider is properly wired with doctrine registry, id normalizer, and target class.


66-68: LGTM! Manager service wiring updated for new architecture.

The argument replacements align with MeilisearchManager's constructor expectations for the serializer and configuration.

src/EventListener/DoctrineEventSubscriber.php (1)

8-28: LGTM! Clean migration to SearchManagerInterface.

The refactoring successfully removes the Doctrine ObjectManager dependency from search operations, aligning with the PR's goal of decoupling data retrieval from the search layer. The constructor promotion and readonly properties are idiomatic for PHP 8.1+.

src/DataProvider/DataProviderRegistry.php (1)

21-36: LGTM! Class hierarchy matching is correctly implemented.

The two-phase lookup (exact match followed by inheritance check) correctly handles both direct class registration and polymorphic provider resolution. The is_a($className, $registeredClass, true) call properly checks class relationships using string class names.

tests/Unit/SerializationTest.php (1)

23-30: LGTM! Test correctly migrated to SearchableObject.

The test properly uses the new SearchableObject API with explicit primary key ('id'), identifier value, and serializer injection. The normalization context correctly uses the groups key.

src/Command/MeilisearchUpdateSettingsCommand.php (1)

22-28: LGTM! Clean migration to SearchManagerInterface.

The constructor properly uses property promotion and updates the dependency from SearchService to SearchManagerInterface. The parent constructor call and property references are correctly updated.

src/Services/SettingsUpdater.php (1)

23-29: LGTM! Proper migration to manager-based configuration.

The constructor correctly migrates to SearchManagerInterface and uses property promotion for all dependencies. Configuration is now properly sourced from the manager, maintaining the same functionality while improving the architecture.

src/DataProvider/DataProviderInterface.php (1)

10-37: LGTM! Well-designed interface addresses key PR objectives.

This interface provides a clean abstraction for data providers with:

  • Pagination support via provide() (batch loading to reduce DB queries per PR objective #134)
  • Batch loading via loadByIdentifiers() (further query reduction)
  • Custom identifier handling via getIdentifierValues() and normalizeIdentifiers() (addresses PR objective #240 for custom ID formatting)
  • Resource cleanup hook

The generic template T and return type flexibility (iterable vs array) demonstrate thoughtful API design.

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

16-80: Test fixture DataProvider is consistent and sufficient

Constructor injection of IdNormalizerInterface, in-memory provide() implementation, and normalizeIdentifiers() delegation form a clean, self‑contained example of a custom provider. Given this is an integration fixture, rebuilding the actor list and linear scanning in loadByIdentifiers() is acceptable; no changes needed.

src/SearchableObject.php (1)

28-79: Immutable normalization wrapper looks correct

Constructor/context handling and getSearchableArray() logic are consistent with the existing SearchableEntity behavior while staying side‑effect free (local $context copy, meilisearch flag preserved). This should work well with the new Engine batching.

src/Command/MeilisearchCreateCommand.php (1)

23-30: Create command refactor to SearchManagerInterface looks sound

Constructor wiring to IndexCommand and the switch to $this->searchManager->isSearchable($entityClassName) match the new manager API and keep behavior unchanged while decoupling from the deprecated SearchService.

Also applies to: 66-67

config/services.php (1)

74-83: Manager, registry, and command wiring are coherent

meilisearch.manager arguments (normalizer, engine, property accessor, data provider registry, configuration) match MeilisearchManager’s constructor, and all commands/settings services now consistently depend on SearchManagerInterface. This keeps the public surface aligned with the new architecture without breaking existing aliases like SearchService::classmeilisearch.service.

Also applies to: 84-127

src/Command/MeilisearchImportCommand.php (1)

28-36: Import command’s move to data providers and manager-based indexing looks correct

The command now cleanly delegates data loading to DataProviderRegistryInterface and all indexing/clearing to SearchManagerInterface, with a sensible batchSize derivation and an early exit on empty batches that still guarantees cleanup() is called. The swap/delete flow using deleteByIndexName() keeps engine access centralized in the manager while preserving existing behavior.

Also applies to: 79-85, 121-152, 168-189, 235-238

@norkunas norkunas force-pushed the dataprovider branch 3 times, most recently from 555fb7a to 120c503 Compare December 15, 2025 18:58
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: 5

🧹 Nitpick comments (5)
src/DependencyInjection/MeilisearchExtension.php (1)

125-141: Consider collision risk in hash-based service ID generation.

The service ID uses xxh32 hash (line 127) to deduplicate providers for the same class across indices. While xxh32 is fast, it's a 32-bit hash with potential collision risk in large codebases.

Consider adding a simple check or using the full class name in the ID instead:

-        $definitionId = \sprintf('meilisearch.data_provider.%s_%s', $indexName, hash('xxh32', $class));
+        $definitionId = \sprintf('meilisearch.data_provider.%s_%s', $indexName, hash('xxh128', $class));

Or use the class name directly if collisions are not a concern:

-        $definitionId = \sprintf('meilisearch.data_provider.%s_%s', $indexName, hash('xxh32', $class));
+        $definitionId = \sprintf('meilisearch.data_provider.%s.%s', $indexName, str_replace('\\', '.', $class));
src/DependencyInjection/Compiler/DataProviderPass.php (1)

25-34: Add validation for required tag attributes.

If a service is tagged with meilisearch.data_provider but missing the index or class attributes, this will produce a confusing "undefined array key" error. Providing a clear exception improves developer experience.

         foreach ($container->findTaggedServiceIds('meilisearch.data_provider') as $id => $tags) {
             foreach ($tags as $attributes) {
+                if (!isset($attributes['index'], $attributes['class'])) {
+                    throw new \InvalidArgumentException(\sprintf(
+                        'Service "%s" tagged with "meilisearch.data_provider" must have "index" and "class" attributes.',
+                        $id
+                    ));
+                }
+
                 $index = $attributes['index'];
                 $class = $attributes['class'];
src/Services/SettingsUpdater.php (1)

23-29: Constructor refactor to SearchManagerInterface is sound; update() complexity can be deferred

Using SearchManagerInterface to obtain configuration in the constructor simplifies dependency wiring without changing behavior. The cyclomatic-complexity warning on update() stems from the existing branching; if you want to address it, extracting the per-setting update block (lines 51‑76) into a private method would likely satisfy the linter without altering logic, but this can reasonably be done in a separate, focused PR.

Also applies to: 35-77

src/Command/MeilisearchImportCommand.php (1)

73-163: Consider complexity reduction in a follow-up.

The execute() method has high cyclomatic (14) and NPath (976) complexity. Consider extracting helper methods for:

  • Settings update logic
  • Batch processing flow
  • Index swapping logic

This would improve maintainability and testability. Given the PR's architectural focus, this can be deferred.

src/Services/MeilisearchManager.php (1)

27-422: Consider extracting responsibilities to reduce complexity.

The class has an overall complexity of 59 (threshold: 50). Consider extracting in a follow-up:

  • Search hydration logic (lines 214-233) into a dedicated helper/service
  • Class resolution (lines 401-421) into a separate ClassResolver utility

This would improve maintainability while keeping the core manager focused. Given the PR's architectural scope, this can be deferred.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 842be8c and 555fb7a.

📒 Files selected for processing (62)
  • .github/workflows/pre-release-tests.yml (2 hunks)
  • .github/workflows/tests.yml (2 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 (3 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/Identifier/DefaultIdNormalizer.php (1 hunks)
  • src/Identifier/IdNormalizerInterface.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/Car.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 (5 hunks)
  • tests/Kernel.php (1 hunks)
  • tests/Unit/ConfigurationTest.php (10 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)
  • src/Services/UnixTimestampNormalizer.php
  • tests/Entity/Tag.php
  • src/DataProvider/DataProviderRegistryInterface.php
  • src/EventListener/ConsoleOutputSubscriber.php
  • phpstan.dist.neon
  • src/MeilisearchBundle.php
  • tests/Integration/Command/MeilisearchImportCommandTest.php
  • src/Exception/LogicException.php
  • src/Exception/InvalidIndiceException.php
  • tests/config/framework.yaml
  • src/Model/Aggregator.php
  • src/Command/IndexCommand.php
  • src/SearchService.php
  • src/Event/SettingsUpdatedEvent.php
  • phpstan-baseline.php
  • tests/config/meilisearch.yaml
  • tests/Integration/EngineTest.php
  • tests/Entity/SelfNormalizable.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/AggregatorTest.php
  • tests/Integration/Fixtures/ActorDataProvider.php
  • src/SearchManagerInterface.php
  • .github/workflows/tests.yml
  • src/Engine.php
  • config/services.php
  • src/Command/MeilisearchImportCommand.php
  • src/DataProvider/OrmEntityProvider.php
  • src/DependencyInjection/Configuration.php
  • src/Services/MeilisearchManager.php
🧬 Code graph analysis (28)
src/DependencyInjection/MeilisearchExtension.php (4)
src/DataProvider/OrmEntityProvider.php (1)
  • OrmEntityProvider (10-86)
src/MeilisearchBundle.php (2)
  • MeilisearchBundle (11-31)
  • qualifiedVersion (15-18)
src/Model/Aggregator.php (2)
  • Aggregator (15-84)
  • getEntities (51-54)
src/Entity/Aggregator.php (1)
  • Aggregator (9-11)
tests/Entity/Link.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/Identifier/DefaultIdNormalizer.php (1)
src/Identifier/IdNormalizerInterface.php (2)
  • normalize (12-12)
  • denormalize (21-21)
tests/Kernel.php (1)
src/DependencyInjection/MeilisearchExtension.php (1)
  • load (22-73)
src/Command/MeilisearchUpdateSettingsCommand.php (5)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/Command/MeilisearchCreateCommand.php (1)
  • __construct (23-30)
src/Services/MeilisearchManager.php (2)
  • __construct (51-62)
  • isSearchable (64-69)
src/SearchManagerInterface.php (1)
  • isSearchable (22-22)
src/Services/MeilisearchService.php (1)
  • isSearchable (66-77)
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)
src/DependencyInjection/Compiler/DataProviderPass.php (2)
src/Services/MeilisearchManager.php (1)
  • index (90-139)
src/Services/MeilisearchService.php (1)
  • index (114-147)
src/Services/MeilisearchService.php (3)
src/EventListener/DoctrineEventSubscriber.php (1)
  • __construct (12-14)
src/SearchManagerInterface.php (11)
  • isSearchable (22-22)
  • getConfiguration (33-33)
  • searchableAs (31-31)
  • index (42-42)
  • remove (51-51)
  • clear (60-60)
  • deleteByIndexName (67-67)
  • delete (76-76)
  • search (88-88)
  • rawSearch (98-98)
  • count (106-106)
src/Services/MeilisearchManager.php (13)
  • isSearchable (64-69)
  • getBaseClassName (278-285)
  • getConfiguration (85-88)
  • searchableAs (71-83)
  • index (90-139)
  • remove (141-174)
  • clear (176-181)
  • assertIsSearchable (367-372)
  • deleteByIndexName (183-186)
  • delete (188-193)
  • search (195-236)
  • rawSearch (238-246)
  • count (248-253)
tests/Integration/SearchTest.php (5)
tests/BaseKernelTestCase.php (1)
  • waitForAllTasks (47-52)
src/SearchManagerInterface.php (2)
  • search (88-88)
  • rawSearch (98-98)
src/Services/MeilisearchManager.php (2)
  • search (195-236)
  • rawSearch (238-246)
tests/Entity/ContentItem.php (1)
  • getTitle (53-56)
tests/Entity/Post.php (1)
  • getTitle (104-107)
src/DataProvider/DataProviderRegistry.php (2)
src/Exception/DataProviderNotFoundException.php (2)
  • DataProviderNotFoundException (7-13)
  • __construct (9-12)
src/DataProvider/DataProviderRegistryInterface.php (1)
  • getDataProvider (21-21)
src/Exception/NotSearchableException.php (2)
src/Exception/DataProviderNotFoundException.php (1)
  • __construct (9-12)
src/Exception/InvalidIndiceException.php (1)
  • __construct (9-12)
tests/Integration/AggregatorTest.php (5)
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/Identifier/DefaultIdNormalizer.php (1)
  • normalize (9-31)
src/Identifier/IdNormalizerInterface.php (1)
  • normalize (12-12)
src/Identifier/IdNormalizerInterface.php (1)
src/Identifier/DefaultIdNormalizer.php (2)
  • normalize (9-31)
  • denormalize (33-39)
tests/Integration/EventListener/DoctrineEventSubscriberTest.php (3)
src/SearchManagerInterface.php (1)
  • search (88-88)
src/Services/MeilisearchManager.php (1)
  • search (195-236)
src/Services/MeilisearchService.php (1)
  • search (209-254)
src/Command/MeilisearchCreateCommand.php (4)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/Command/MeilisearchUpdateSettingsCommand.php (1)
  • __construct (22-28)
src/SearchManagerInterface.php (1)
  • isSearchable (22-22)
src/Services/MeilisearchManager.php (1)
  • isSearchable (64-69)
tests/Integration/Fixtures/ActorDataProvider.php (3)
tests/Entity/Actor.php (1)
  • Actor (7-14)
src/DataProvider/DataProviderInterface.php (6)
  • provide (18-18)
  • loadByIdentifiers (25-25)
  • getIdentifierValues (32-32)
  • normalizeIdentifiers (39-39)
  • denormalizeIdentifier (46-46)
  • cleanup (48-48)
src/Identifier/IdNormalizerInterface.php (2)
  • normalize (12-12)
  • denormalize (21-21)
src/SearchManagerInterface.php (6)
src/Exception/NotSearchableException.php (1)
  • NotSearchableException (7-13)
src/SearchService.php (11)
  • isSearchable (20-20)
  • searchableAs (34-34)
  • getConfiguration (27-27)
  • index (36-36)
  • remove (38-38)
  • clear (43-43)
  • deleteByIndexName (50-50)
  • delete (48-48)
  • search (59-64)
  • rawSearch (73-77)
  • count (84-84)
src/Services/MeilisearchManager.php (11)
  • isSearchable (64-69)
  • searchableAs (71-83)
  • getConfiguration (85-88)
  • index (90-139)
  • remove (141-174)
  • clear (176-181)
  • deleteByIndexName (183-186)
  • delete (188-193)
  • search (195-236)
  • rawSearch (238-246)
  • count (248-253)
src/Services/MeilisearchService.php (11)
  • isSearchable (66-77)
  • searchableAs (98-112)
  • getConfiguration (87-96)
  • index (114-147)
  • remove (149-170)
  • clear (172-183)
  • deleteByIndexName (185-194)
  • delete (196-207)
  • search (209-254)
  • rawSearch (256-270)
  • count (272-283)
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)
src/Engine.php (2)
src/SearchableEntity.php (3)
  • __construct (42-56)
  • getSearchableArray (66-91)
  • getIndexUid (58-61)
src/SearchableObject.php (5)
  • SearchableObject (13-80)
  • getSearchableArray (65-79)
  • getIndexUid (42-45)
  • getPrimaryKey (50-53)
  • getIdentifier (55-58)
src/DataProvider/DataProviderInterface.php (1)
src/DataProvider/OrmEntityProvider.php (6)
  • provide (22-31)
  • loadByIdentifiers (33-63)
  • getIdentifierValues (65-70)
  • normalizeIdentifiers (72-75)
  • denormalizeIdentifier (77-80)
  • cleanup (82-85)
tests/Unit/SerializationTest.php (2)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
tests/Entity/Post.php (1)
  • getId (91-94)
config/services.php (7)
src/DataProvider/DataProviderRegistry.php (1)
  • DataProviderRegistry (10-37)
src/Engine.php (1)
  • Engine (44-182)
src/EventListener/DoctrineEventSubscriber.php (1)
  • DoctrineEventSubscriber (10-30)
src/Identifier/DefaultIdNormalizer.php (1)
  • DefaultIdNormalizer (7-40)
src/Services/MeilisearchManager.php (1)
  • MeilisearchManager (27-422)
src/Services/SettingsUpdater.php (1)
  • SettingsUpdater (17-78)
src/Services/UnixTimestampNormalizer.php (1)
  • UnixTimestampNormalizer (9-30)
src/Command/MeilisearchImportCommand.php (4)
src/Services/MeilisearchManager.php (5)
  • getConfiguration (85-88)
  • isSearchable (64-69)
  • index (90-139)
  • count (248-253)
  • deleteByIndexName (183-186)
src/DataProvider/DataProviderRegistryInterface.php (1)
  • getDataProvider (21-21)
src/DataProvider/DataProviderInterface.php (2)
  • provide (18-18)
  • cleanup (48-48)
src/DataProvider/OrmEntityProvider.php (2)
  • provide (22-31)
  • cleanup (82-85)
src/DataProvider/OrmEntityProvider.php (3)
src/DataProvider/DataProviderInterface.php (6)
  • provide (18-18)
  • loadByIdentifiers (25-25)
  • getIdentifierValues (32-32)
  • normalizeIdentifiers (39-39)
  • denormalizeIdentifier (46-46)
  • cleanup (48-48)
src/Identifier/DefaultIdNormalizer.php (2)
  • denormalize (33-39)
  • normalize (9-31)
src/Identifier/IdNormalizerInterface.php (2)
  • denormalize (21-21)
  • normalize (12-12)
src/Command/MeilisearchClearCommand.php (5)
src/Engine.php (1)
  • clear (137-140)
src/SearchManagerInterface.php (1)
  • clear (60-60)
src/SearchService.php (1)
  • clear (43-43)
src/Services/MeilisearchManager.php (1)
  • clear (176-181)
src/Services/MeilisearchService.php (1)
  • clear (172-183)
src/DependencyInjection/Configuration.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
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-80)
src/SearchableObject.php (4)
src/SearchableEntity.php (3)
  • __construct (42-56)
  • getIndexUid (58-61)
  • getSearchableArray (66-91)
src/Identifier/DefaultIdNormalizer.php (1)
  • normalize (9-31)
src/Identifier/IdNormalizerInterface.php (1)
  • normalize (12-12)
tests/Entity/SelfNormalizable.php (1)
  • normalize (64-75)
src/Services/SettingsUpdater.php (3)
src/Command/MeilisearchCreateCommand.php (1)
  • __construct (23-30)
src/SearchManagerInterface.php (1)
  • getConfiguration (33-33)
src/Services/MeilisearchManager.php (1)
  • getConfiguration (85-88)
🪛 GitHub Actions: Tests
src/DependencyInjection/MeilisearchExtension.php

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


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


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


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


[warning] 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/Identifier/DefaultIdNormalizer.php

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

src/DependencyInjection/Compiler/DataProviderPass.php

[warning] 37-37: StaticAccess Avoid using static access to class '\Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass' in method 'process'.

src/Services/MeilisearchService.php

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


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


[warning] 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] 415-415: LongVariable Avoid excessively long variable names like $searchableEntitiesChunk. Keep variable name 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'.

tests/Integration/SearchTest.php

[error] 161-161: Test 'MeilisearchBundle\Tests\Integration\SearchTest::testCars' failed due to an error: Call to undefined method Doctrine\ORM\QueryBuilder::createNamedParameter()

src/Command/MeilisearchImportCommand.php

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


[warning] 73-73: CyclomaticComplexity The method execute() has a Cyclomatic Complexity of 14. The configured cyclomatic complexity threshold is 10.


[warning] 73-73: NPathComplexity The method execute() has an NPath complexity of 976. The configured NPath complexity threshold is 200.

src/DataProvider/OrmEntityProvider.php

[error] 54-54: Call to undefined method Doctrine\ORM\QueryBuilder::createNamedParameter()

src/DependencyInjection/Configuration.php

[warning] 14-14: ExcessiveMethodLength The method getConfigTreeBuilder() has 100 lines of code. Current threshold is set to 100. Avoid really long methods.

src/Services/MeilisearchManager.php

[warning] 27-27: ExcessiveClassComplexity The class MeilisearchManager has an overall complexity of 59 which is very high. The configured complexity threshold is 50.


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


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

src/Services/SettingsUpdater.php

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

🔇 Additional comments (60)
tests/baseline-ignore (1)

5-5: LGTM! Appropriate baseline addition.

Adding this Doctrine ORM deprecation to the baseline is standard practice. The migration to native lazy objects can be addressed in a focused follow-up PR.

src/Searchable.php (1)

7-20: LGTM! Clean deprecation path to SearchableObject.

The deprecation docblocks are clear and provide the correct replacement path. The constants remain functional for backward compatibility while guiding users to the new API.

src/Exception/NotSearchableException.php (1)

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

The exception provides a clear, descriptive message and follows PHP 8.1+ conventions with proper type declarations. The previous review comment about the int type hint has been addressed.

src/DependencyInjection/MeilisearchExtension.php (1)

93-123: Data provider registration logic is well-structured.

The method correctly handles both explicit data provider configuration and automatic ORM provider registration. The aggregator handling (lines 114-117) properly registers providers for each aggregated entity class.

composer.json (1)

21-52: LGTM! Dependency updates align with architectural changes.

The PHP 8.1 requirement and Symfony 6.4/7.0 updates support the new data provider architecture. Moving doctrine-bundle to dev dependencies (line 32) is appropriate given the abstraction layer that now allows non-Doctrine data sources.

src/EventListener/DoctrineEventSubscriber.php (1)

12-29: LGTM! Clean migration to SearchManagerInterface.

The constructor property promotion is idiomatic PHP 8.1+, and removing the ObjectManager parameter from index() and remove() calls correctly reflects the new manager API that delegates to data providers.

src/Services/MeilisearchService.php (3)

24-26: Deprecation notice is clear.

The docblock correctly identifies version 0.16 and points to the appropriate replacement class. Note: The past review comment about the FQN has been addressed.


52-58: Well-designed backward compatibility bridge.

The optional manager parameter enables gradual migration. The delegation pattern throughout the class (checking null !== $this->manager before delegating) maintains full backward compatibility while guiding users to the new API.


68-72: Consistent deprecation pattern.

The trigger_deprecation() call followed by delegation when the manager is available is the correct approach. This pattern is consistently applied across all delegated methods.

config/services.php (3)

74-82: LGTM! Manager service wiring is correct.

The MeilisearchManager is properly configured with all required dependencies: normalizer, engine, property accessor, data provider registry, and configuration. The alias to SearchManagerInterface enables type-hinted dependency injection.


131-136: LGTM! DataProviderRegistry wiring is correct.

The service definition correctly uses abstract arguments for the provider locator and map, which will be replaced by the compiler pass. The chained alias (line 136) is valid Symfony syntax, as confirmed by the maintainer.


138-142: LGTM! DefaultIdNormalizer wiring is correct.

The service is registered with ID 'meilisearch.identifier.default_id_normalizer' (line 139), and the alias correctly references this service ID string (line 140). The chained alias syntax is valid.

src/Command/MeilisearchDeleteCommand.php (1)

32-37: LGTM! Correct manager usage and error handling.

The migration from searchService to searchManager aligns with the architectural changes. The continue statement (line 36) correctly prevents the "Deleted" message (line 39) from being printed after an exception, which would mislead users.

src/Command/MeilisearchClearCommand.php (1)

30-30: LGTM! Clean migration to the new manager interface.

The replacement of searchService with searchManager aligns correctly with the architectural refactoring described in the PR objectives.

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

40-41: LGTM! Test expectations updated for new test entities.

The addition of actor and cars index deletion lines correctly reflects the new test entities introduced in this PR.

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

39-40: LGTM! Test expectations correctly expanded.

The test now accounts for the newly added Actor and Car test entities, ensuring comprehensive coverage.

tests/Entity/Link.php (1)

9-9: LGTM! Consistent migration to SearchableObject.

The import and constant reference have been correctly updated from Searchable to SearchableObject, aligning with the architectural refactoring throughout the codebase.

Also applies to: 76-76

tests/Entity/Actor.php (1)

7-14: LGTM! Clean test entity using PHP 8.1 features.

The Actor class is appropriately simple for a test fixture, leveraging constructor property promotion and readonly properties.

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

63-64: LGTM! Test expectations updated for comprehensive index coverage.

Both test paths (with and without settings updates) now correctly expect the creation of actor and cars indices, maintaining test accuracy with the expanded configuration.

Also applies to: 81-82

.github/workflows/tests.yml (1)

30-31: LGTM! CI matrix correctly updated for minimum version requirements.

The removal of PHP 7.4 and Symfony 5.4 from the test matrix properly aligns with the PR's minimum version bump (PHP 8.1, Symfony 6.4).

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

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

All test methods consistently updated to use searchManager->search(EntityClass::class, $query) without the entity manager parameter, aligning with the new SearchManagerInterface signature.

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

tests/Kernel.php (1)

36-42: LGTM! Bootstrap simplification aligns with PHP 8.1+ requirement.

The simplified three-way conditional for Doctrine configuration loading removes PHP 7 specific paths, consistent with the PR's PHP 8.1 baseline.

tests/Entity/Car.php (1)

1-34: LGTM! Useful test entity for composite primary key scenarios.

The Car entity provides test coverage for composite primary keys (name + year), supporting the PR's objective of customizable identifier normalization (issue #240).

src/SearchableEntity.php (1)

15-17: LGTM! Deprecation pathway is clear.

The deprecation of SearchableEntity in favor of SearchableObject is well-marked, and both normalization format constant references correctly updated to use SearchableObject::NORMALIZATION_FORMAT.

Also applies to: 83-83, 87-87

tests/Unit/ConfigurationTest.php (3)

29-29: LGTM! Method signatures modernized for PHP 8.1+.

The addition of mixed type hints aligns with the PHP 8.1 requirement and improves type safety.

Also applies to: 47-47


135-138: LGTM! Configuration expectations updated for ORM-based data providers.

All existing test scenarios now correctly expect the new ORM metadata fields: type, data_provider, id_normalizer, and primary_key. Defaults are appropriate (type: 'orm', primary_key: 'objectID').

Also applies to: 147-150, 193-196, 205-208, 246-249, 288-291, 330-333


362-407: LGTM! Test coverage for custom primary keys and id normalizers.

The new test scenarios validate:

  • Custom primary key configuration (postId, tagId)
  • Custom id normalizer configuration (acme.id_normalizer)

This coverage aligns with PR objectives for configurable identifiers (issues #66, #240).

Also applies to: 409-441

src/Exception/DataProviderNotFoundException.php (1)

1-13: LGTM! Well-designed exception for data provider lookups.

The DataProviderNotFoundException provides clear context (index name and class name) for debugging failed provider lookups.

tests/Integration/AggregatorTest.php (4)

24-27: LGTM! Test coverage for aggregator entity class resolution.

New tests validate:

  • Successful entity class extraction from objectID
  • Proper exception throwing for unknown entities

Also applies to: 29-34


36-43: LGTM! Constructor validation updated for new signature.

The test correctly validates that aggregators reject multiple primary keys, now using the updated ContentAggregator constructor with the primaryKeyName parameter.


45-67: LGTM! Proxy handling test updated for new constructor.

The test correctly constructs ContentAggregator with the new signature and validates serialization through the Symfony Serializer.


69-84: LGTM! Comprehensive normalization test coverage.

New tests validate:

  • Default normalization behavior with objectID primary key
  • Custom primary key configuration (id)
  • Proper serialization output structure

This supports the PR's objective of configurable primary keys (issue #66).

Also applies to: 86-100

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

42-63: LGTM! Enhanced CI matrix for better compatibility testing.

The expanded matrix now tests:

  • PHP 8.1-8.4 across Symfony 6.4-7.3
  • Dependency variations (lowest/highest) for PHP 8.4

This strengthens compatibility guarantees for the new baseline requirements.


65-65: LGTM! Improved job naming and dependency-driven installation.

The dynamic job naming and matrix-driven dependency-versions parameter improve CI clarity and maintainability.

Also applies to: 84-85

src/DependencyInjection/Configuration.php (2)

53-62: New configuration nodes look well-structured.

The added configuration options for type, primary_key, data_provider, and id_normalizer align with the PR objectives for supporting custom data providers and configurable primary keys. The enum constraint on type and the default values are appropriate.


104-107: Validation correctly enforces data_provider for custom type.

This addresses the earlier review feedback and ensures users receive a clear configuration error rather than a runtime failure.

tests/Integration/SearchTest.php (1)

72-76: Good addition of waitForAllTasks() before assertions.

Adding synchronization after import ensures async indexing operations complete before running search assertions. This prevents flaky tests.

src/DependencyInjection/Compiler/DataProviderPass.php (1)

37-38: Static access to ServiceLocatorTagPass::register() is idiomatic.

The pipeline warning about static access is a false positive here. Using ServiceLocatorTagPass::register() is the standard Symfony pattern for creating service locators in compiler passes.

tests/BaseKernelTestCase.php (1)

10-27: Migration to SearchManagerInterface is consistent.

The property type, import, and service retrieval are all correctly updated to use the new manager interface.

src/DataProvider/DataProviderRegistry.php (1)

21-36: Data provider resolution logic is consistent and type-safe

Exact match, then inheritance/interface-based fallback, and a clear exception on miss make the registry behavior predictable and easy to reason about. The phpdoc on $dataProvidersMap also aligns with the usage.

src/DataProvider/DataProviderInterface.php (1)

7-48: Identifier normalization contract is well-shaped

The added normalizeIdentifiers / denormalizeIdentifier methods and non-empty-array/phpdoc constraints give a clear, symmetric contract for single and composite identifiers while keeping implementations flexible.

src/Command/MeilisearchUpdateSettingsCommand.php (1)

22-27: Migration to SearchManagerInterface looks correct

Constructor promotion and the switch to $this->searchManager->isSearchable() align this command with the new manager-based API and with IndexCommand/MeilisearchCreateCommand.

Also applies to: 56-56

tests/Unit/SerializationTest.php (1)

7-30: Test update to SearchableObject matches the new API

The constructor arguments (index UID, primary key, entity, identifier, serializer, and groups context) align with SearchableObject’s signature and keep the previous serialization expectations intact.

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

14-85: Fixture correctly demonstrates a custom DataProviderInterface implementation

This provider cleanly showcases a non‑ORM data source with its own identifier shape (associative ['id' => ...]) and delegates normalization concerns to IdNormalizerInterface, which is exactly the kind of flexibility the DataProvider abstraction is meant to support.

src/SearchableObject.php (1)

13-79: SearchableObject encapsulation and normalization behavior look good

Immutable constructor state, a non‑mutating getSearchableArray() that adjusts DateTime handling based on Kernel::VERSION_ID, and support for both NormalizableInterface and generic normalization make this a solid replacement for the older entity‑centric wrapper.

src/DataProvider/OrmEntityProvider.php (1)

33-63: Review comment is based on outdated code that does not match the repository

The code snippet shown calls $queryBuilder->createNamedParameter($value), but the actual code in src/DataProvider/OrmEntityProvider.php (lines 33–63) already implements the correct solution using setParameter() with manually-constructed named parameters. The problematic method call is commented out as a future enhancement for Doctrine ORM >= 3.3. No changes are needed.

Likely an incorrect or invalid review comment.

src/SearchManagerInterface.php (1)

1-107: LGTM! Clean interface contract.

The interface is well-designed with comprehensive type hints, proper PHPDoc annotations, and clear method signatures. The use of phpstan type imports for Engine types improves type safety across the codebase.

src/Command/MeilisearchCreateCommand.php (2)

23-30: LGTM! Clean constructor with promoted properties.

The constructor properly uses PHP 8.1+ promoted properties and correctly passes the SearchManagerInterface to the parent class. This aligns well with the new manager-based architecture.


66-66: LGTM! Correct usage of SearchManagerInterface.

The isSearchable() call correctly uses the new SearchManagerInterface method.

src/Engine.php (4)

46-48: LGTM! Clean constructor with promoted property.

The constructor properly uses PHP 8.1+ promoted property syntax, making the code more concise.


61-90: LGTM! Correct per-index batching with primary keys.

The implementation correctly tracks primary keys per index and batches documents accordingly. The fix from the previous review ensures different indexes with different primary keys are handled properly.


100-126: LGTM! Consistent per-index removal logic.

The removal logic correctly batches by index and issues individual deleteDocument calls per identifier, maintaining consistency with the indexing approach.


174-181: LGTM! Simplified and type-safe identifier normalization.

The tightened signature using \Stringable|string|int provides better type safety, and the simplified logic correctly handles all cases.

src/Command/MeilisearchImportCommand.php (2)

28-36: LGTM! Constructor properly wired for data provider architecture.

The constructor correctly injects the new DataProviderRegistryInterface and uses promoted properties effectively. The number of dependencies reflects the command's legitimate responsibilities.


121-151: LGTM! Correct data provider usage with proper cleanup.

The data provider is correctly obtained outside the loop (addressing the previous review), and the empty batch guard ensures robust handling of edge cases. The cleanup calls are appropriately placed.

src/Services/MeilisearchManager.php (5)

51-62: LGTM! Well-structured manager constructor.

The constructor properly injects dependencies and initializes internal state. The immediate setup of searchables and aggregators ensures the manager is ready for use.


90-139: LGTM! Robust indexing with aggregator support.

The implementation correctly handles both direct entities and aggregator-wrapped entities, properly gates indexing with shouldBeIndexed(), and batches operations efficiently.


141-174: LGTM! Consistent removal with proper identifier normalization.

The removal logic correctly uses normalizeIdentifiers() to ensure consistent identifier formatting between indexing and removal operations.


195-236: LGTM! Search with correct identifier normalization and order preservation.

The search implementation correctly uses normalizeIdentifiers() (line 221) to ensure hydration keys match indexed document IDs, addressing the previous review's concern. The order-preserving hydration logic is sound.


401-421: LGTM! Robust class resolution across Doctrine versions.

The implementation correctly handles multiple Doctrine versions and PHP 8.4+ native lazy objects. The static access to Doctrine utilities (lines 413, 417) is intentional and appropriate for this compatibility layer, despite the pipeline warning.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Services/SettingsUpdater.php (1)

35-77: Reduce update() cyclomatic complexity to satisfy CI

The pipeline flags update() with cyclomatic complexity 13 (threshold 10). You can keep behavior identical by extracting the per‑setting logic into a private helper and delegating from update().

@@
-    public function update(string $indice, ?int $responseTimeout = null, ?string $prefixedName = null): void
-    {
-        $index = (new Collection($this->configuration->get('indices')))->firstWhere('prefixed_name', $indice);
-
-        if (!\is_array($index)) {
-            throw new InvalidIndiceException($indice);
-        }
-
-        if (!\is_array($index['settings'] ?? null) || [] === $index['settings']) {
-            return;
-        }
-
-        $indexName = $prefixedName ?? $index['prefixed_name'];
-        $indexInstance = $this->searchClient->index($indexName);
-        $responseTimeout = $responseTimeout ?? self::DEFAULT_RESPONSE_TIMEOUT;
-
-        foreach ($index['settings'] as $variable => $value) {
-            $method = \sprintf('update%s', ucfirst($variable));
-
-            if (!method_exists($indexInstance, $method)) {
-                throw new InvalidSettingName(\sprintf('Invalid setting name: "%s"', $variable));
-            }
-
-            if (isset($value['_service']) && $value['_service'] instanceof SettingsProvider) {
-                $value = $value['_service']();
-            } elseif (('distinctAttribute' === $variable || 'proximityPrecision' === $variable || 'searchCutoffMs' === $variable) && \is_array($value)) {
-                $value = $value[0] ?? null;
-            }
-
-            // Update
-            $task = $indexInstance->{$method}($value);
-
-            // Get task information using uid
-            $indexInstance->waitForTask($task['taskUid'], $responseTimeout);
-            $task = $indexInstance->getTask($task['taskUid']);
-
-            if ('failed' === $task['status']) {
-                throw new TaskException($task['error']['message']);
-            }
-
-            $this->eventDispatcher->dispatch(new SettingsUpdatedEvent($index['class'], $indexName, $variable));
-        }
-    }
+    public function update(string $indice, ?int $responseTimeout = null, ?string $prefixedName = null): void
+    {
+        $index = (new Collection($this->configuration->get('indices')))->firstWhere('prefixed_name', $indice);
+
+        if (!\is_array($index)) {
+            throw new InvalidIndiceException($indice);
+        }
+
+        if (!\is_array($index['settings'] ?? null) || [] === $index['settings']) {
+            return;
+        }
+
+        $indexName = $prefixedName ?? $index['prefixed_name'];
+        $indexInstance = $this->searchClient->index($indexName);
+        $responseTimeout = $responseTimeout ?? self::DEFAULT_RESPONSE_TIMEOUT;
+
+        $this->updateIndexSettings($index, $indexInstance, $responseTimeout, $indexName);
+    }
+
+    /**
+     * @param array<string,mixed> $index
+     */
+    private function updateIndexSettings(array $index, object $indexInstance, int $responseTimeout, string $indexName): void
+    {
+        foreach ($index['settings'] as $variable => $value) {
+            $method = \sprintf('update%s', ucfirst($variable));
+
+            if (!method_exists($indexInstance, $method)) {
+                throw new InvalidSettingName(\sprintf('Invalid setting name: "%s"', $variable));
+            }
+
+            if (isset($value['_service']) && $value['_service'] instanceof SettingsProvider) {
+                $value = $value['_service']();
+            } elseif (('distinctAttribute' === $variable || 'proximityPrecision' === $variable || 'searchCutoffMs' === $variable) && \is_array($value)) {
+                $value = $value[0] ?? null;
+            }
+
+            $task = $indexInstance->{$method}($value);
+
+            $indexInstance->waitForTask($task['taskUid'], $responseTimeout);
+            $task = $indexInstance->getTask($task['taskUid']);
+
+            if ('failed' === $task['status']) {
+                throw new TaskException($task['error']['message']);
+            }
+
+            $this->eventDispatcher->dispatch(new SettingsUpdatedEvent($index['class'], $indexName, $variable));
+        }
+    }

This should bring update() below the cyclomatic complexity threshold while preserving existing error-handling and event behavior. Based on learnings, this keeps the PR focused on structural changes without altering settings error semantics.

♻️ Duplicate comments (3)
.github/workflows/tests.yml (1)

31-31: Consider adding Symfony 7.4 to the integration test matrix.

The integration test matrix on line 31 tests up to Symfony 7.3, while the code-style job on line 101 requires Symfony 7.4.*. This creates an inconsistency where static analysis runs on a Symfony version not covered by integration tests. Since Symfony 7.4 is stable (released December 8, 2025), consider adding '7.4' to the matrix to ensure comprehensive testing coverage.

src/Identifier/DefaultIdNormalizer.php (1)

1-39: Fix identifier type handling and denormalization for simple IDs, and add the missing import

Two coupled issues here:

  1. normalize() can currently return non‑scalar values (e.g. arrays) when a single identifier is not a scalar, despite the string|int return type.
  2. denormalize() always assumes a base64‑encoded JSON payload with JSON_THROW_ON_ERROR, so it will throw for simple scalar identifiers like "5" that normalize() returns directly.

This will both violate the declared return type and break consumers that call denormalize() on simple IDs.

You can address this and the static‑analysis import error with something along these lines:

 <?php
 
 declare(strict_types=1);
 
 namespace Meilisearch\Bundle\Identifier;
 
+use InvalidArgumentException;
+
 final class DefaultIdNormalizer implements IdNormalizerInterface
 {
     public function normalize(array $identifiers): string|int
     {
         if (1 === \count($identifiers)) {
             $identifier = reset($identifiers);
 
             if (\is_object($identifier) && method_exists($identifier, '__toString')) {
                 return (string) $identifier;
             }
 
             if (\is_object($identifier)) {
-                throw new \InvalidArgumentException('Identifier object must implement __toString().');
+                throw new InvalidArgumentException('Identifier object must implement __toString().');
             }
 
-            return $identifier;
+            if (!\is_scalar($identifier)) {
+                throw new InvalidArgumentException('Single identifier must be a scalar value or an object with __toString().');
+            }
+
+            // Preserve ints and strings as‑is; safely cast other scalars (bool/float) to string.
+            if (\is_int($identifier) || \is_string($identifier)) {
+                return $identifier;
+            }
+
+            return (string) $identifier;
         }
 
         ksort($identifiers);
 
         $json = json_encode($identifiers, \JSON_THROW_ON_ERROR);
         $base64 = base64_encode($json);
 
         return rtrim(strtr($base64, '+/', '-_'), '=');
     }
 
     public function denormalize(string $identifier): array
     {
-        $padded = $identifier.str_repeat('=', (4 - \strlen($identifier) % 4) % 4);
-        $base64 = strtr($padded, '-_', '+/');
-
-        return json_decode(base64_decode($base64), true, 512, \JSON_THROW_ON_ERROR);
+        // Attempt to treat the identifier as a composite key; fall back to a simple scalar ID.
+        $padded = $identifier.str_repeat('=', (4 - \strlen($identifier) % 4) % 4);
+        $base64 = strtr($padded, '-_', '+/');
+
+        $decoded = base64_decode($base64, true);
+        if (false === $decoded) {
+            return [$identifier];
+        }
+
+        $result = json_decode($decoded, true);
+        if (!\is_array($result)) {
+            return [$identifier];
+        }
+
+        return $result;
     }
 }

This keeps composite keys working as before while making normalize()/denormalize() safe and type‑correct for simple identifiers.

src/SearchManagerInterface.php (1)

35-42: Ensure index() docblock matches actual return shape

The index() PHPDoc still documents @return list<array<non-empty-string, DocumentAdditionOrUpdateTask>>. If the implementation returns a map of indexName => DocumentAdditionOrUpdateTask (as previously discussed), this should be updated to array<non-empty-string, DocumentAdditionOrUpdateTask> to keep the interface accurate for callers and static analysis.

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

12-14: Dynamic primary key support in Aggregator looks correct

The new object $entity type, configurable $primaryKey (defaulting to 'objectID'), and use of $this->primaryKey in normalize() align with the new tests and per‑index primary_key handling while preserving backward compatibility.

You might optionally update the docblock around $objectID to mention that it now backs a configurable primary key name rather than always being tied to a literal objectID.

Also applies to: 27-36, 80-83

src/Engine.php (1)

96-126: Consider batching deletions per index instead of one deleteDocument() call per id

remove() currently loops identifiers and calls deleteDocument() for each one. This is correct but may be suboptimal for large batches. When/if you need to optimize, you could switch to a single per-index bulk call (e.g., deleteDocuments([...])) while keeping the return shape consistent.

src/Services/MeilisearchService.php (1)

24-63: Delegation to SearchManagerInterface is a good deprecation strategy

The added $manager dependency plus per-method trigger_deprecation() + conditional delegation cleanly support the new API while keeping existing callers working. Given this class is now explicitly deprecated and mainly a compatibility shim, I’d handle the PHPMD complexity/static-access warnings via suppression or tooling config rather than large refactors here.

src/Command/MeilisearchImportCommand.php (1)

73-152: Import loop correctly uses data providers; consider guarding against batch-size <= 0

The new loop that pulls entities from a per-index data provider, skips empty batches, and calls cleanup() per non-empty batch is sound and matches the new provider abstraction. One small hardening you might consider (in a follow-up) is validating --batch-size upfront and rejecting <= 0 values to avoid a non-terminating do/while for misconfigured runs.

src/Services/MeilisearchManager.php (1)

401-421: Class resolution helper is pragmatic; PHPMD warnings can be handled later

The cached resolver that switches between native class resolution, modern proxy resolution, and a legacy fallback gives good coverage across PHP/Doctrine versions. The static-access PHPMD warnings here are mostly stylistic; if they keep CI noisy, consider extracting this into a tiny dedicated helper service or adding a suppression, but it doesn’t need to block this PR.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 555fb7a and 120c503.

📒 Files selected for processing (62)
  • .github/workflows/pre-release-tests.yml (2 hunks)
  • .github/workflows/tests.yml (2 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 (3 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/Identifier/DefaultIdNormalizer.php (1 hunks)
  • src/Identifier/IdNormalizerInterface.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/Car.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 (5 hunks)
  • tests/Kernel.php (1 hunks)
  • tests/Unit/ConfigurationTest.php (10 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 (20)
  • src/DataProvider/DataProviderRegistryInterface.php
  • src/SearchService.php
  • .github/workflows/pre-release-tests.yml
  • src/DependencyInjection/Configuration.php
  • src/SearchableObject.php
  • src/Identifier/IdNormalizerInterface.php
  • src/Exception/InvalidIndiceException.php
  • tests/Integration/Command/MeilisearchCreateCommandTest.php
  • tests/Entity/Car.php
  • tests/Integration/EngineTest.php
  • src/EventListener/ConsoleOutputSubscriber.php
  • tests/Entity/Tag.php
  • src/DataProvider/DataProviderInterface.php
  • tests/Integration/Command/MeilisearchImportCommandTest.php
  • src/Searchable.php
  • src/Services/UnixTimestampNormalizer.php
  • phpstan.dist.neon
  • tests/Entity/SelfNormalizable.php
  • src/DependencyInjection/Compiler/DataProviderPass.php
  • tests/Unit/SerializationTest.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/DataProvider/OrmEntityProvider.php
  • tests/Integration/AggregatorTest.php
  • config/services.php
  • src/Engine.php
  • src/Services/MeilisearchManager.php
  • .github/workflows/tests.yml
  • tests/Integration/Fixtures/ActorDataProvider.php
  • src/Command/MeilisearchImportCommand.php
  • src/SearchManagerInterface.php
🧬 Code graph analysis (23)
src/Command/IndexCommand.php (4)
src/Command/MeilisearchCreateCommand.php (1)
  • __construct (23-30)
src/Command/MeilisearchUpdateSettingsCommand.php (1)
  • __construct (22-28)
src/SearchManagerInterface.php (1)
  • getConfiguration (33-33)
src/Services/MeilisearchManager.php (1)
  • getConfiguration (85-88)
src/Model/Aggregator.php (2)
src/Identifier/DefaultIdNormalizer.php (1)
  • normalize (9-31)
src/Identifier/IdNormalizerInterface.php (1)
  • normalize (12-12)
src/DependencyInjection/MeilisearchExtension.php (7)
src/DataProvider/OrmEntityProvider.php (1)
  • OrmEntityProvider (10-93)
src/MeilisearchBundle.php (2)
  • MeilisearchBundle (11-31)
  • qualifiedVersion (15-18)
src/Model/Aggregator.php (2)
  • Aggregator (15-84)
  • getEntities (51-54)
src/Document/Aggregator.php (1)
  • Aggregator (9-11)
src/Engine.php (1)
  • index (61-90)
src/Services/MeilisearchManager.php (1)
  • index (90-139)
src/Services/MeilisearchService.php (1)
  • index (114-147)
src/MeilisearchBundle.php (1)
src/DependencyInjection/Compiler/DataProviderPass.php (1)
  • DataProviderPass (12-40)
tests/Entity/Link.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/Command/MeilisearchDeleteCommand.php (4)
src/SearchManagerInterface.php (1)
  • deleteByIndexName (67-67)
src/SearchService.php (1)
  • deleteByIndexName (50-50)
src/Services/MeilisearchManager.php (1)
  • deleteByIndexName (183-186)
src/Services/MeilisearchService.php (1)
  • deleteByIndexName (185-194)
src/EventListener/DoctrineEventSubscriber.php (2)
src/SearchManagerInterface.php (2)
  • index (42-42)
  • remove (51-51)
src/Services/MeilisearchManager.php (2)
  • index (90-139)
  • remove (141-174)
tests/Integration/EventListener/DoctrineEventSubscriberTest.php (2)
src/SearchManagerInterface.php (1)
  • search (88-88)
src/Services/MeilisearchManager.php (1)
  • search (195-236)
src/DataProvider/OrmEntityProvider.php (3)
src/DataProvider/DataProviderInterface.php (6)
  • provide (18-18)
  • loadByIdentifiers (25-25)
  • getIdentifierValues (32-32)
  • normalizeIdentifiers (39-39)
  • denormalizeIdentifier (46-46)
  • cleanup (48-48)
src/Identifier/DefaultIdNormalizer.php (2)
  • denormalize (33-39)
  • normalize (9-31)
src/Identifier/IdNormalizerInterface.php (2)
  • denormalize (21-21)
  • normalize (12-12)
tests/Integration/AggregatorTest.php (3)
src/Model/Aggregator.php (1)
  • getEntityClassFromObjectID (69-78)
src/Exception/EntityNotFoundInObjectID.php (1)
  • EntityNotFoundInObjectID (7-9)
src/Exception/InvalidEntityForAggregator.php (1)
  • InvalidEntityForAggregator (7-9)
tests/Entity/Actor.php (14)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/Command/MeilisearchCreateCommand.php (1)
  • __construct (23-30)
src/Command/MeilisearchImportCommand.php (1)
  • __construct (28-36)
src/Command/MeilisearchUpdateSettingsCommand.php (1)
  • __construct (22-28)
src/DataProvider/DataProviderRegistry.php (1)
  • __construct (15-19)
src/DataProvider/OrmEntityProvider.php (1)
  • __construct (15-20)
src/Engine.php (1)
  • __construct (46-48)
src/EventListener/ConsoleOutputSubscriber.php (1)
  • __construct (13-15)
src/EventListener/DoctrineEventSubscriber.php (1)
  • __construct (12-14)
src/Exception/DataProviderNotFoundException.php (1)
  • __construct (9-12)
src/Exception/NotSearchableException.php (1)
  • __construct (9-12)
src/Model/Aggregator.php (1)
  • __construct (32-44)
src/SearchableEntity.php (1)
  • __construct (42-56)
src/SearchableObject.php (1)
  • __construct (28-37)
src/SearchableEntity.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)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
config/services.php (3)
src/DataProvider/DataProviderRegistry.php (1)
  • DataProviderRegistry (10-37)
src/Identifier/DefaultIdNormalizer.php (1)
  • DefaultIdNormalizer (7-40)
src/Services/MeilisearchManager.php (1)
  • MeilisearchManager (27-422)
src/Exception/DataProviderNotFoundException.php (2)
src/Exception/InvalidIndiceException.php (1)
  • __construct (9-12)
src/Exception/NotSearchableException.php (1)
  • __construct (9-12)
tests/Integration/SearchTest.php (4)
tests/BaseKernelTestCase.php (1)
  • waitForAllTasks (47-52)
src/Engine.php (1)
  • search (159-162)
src/SearchManagerInterface.php (2)
  • search (88-88)
  • rawSearch (98-98)
src/Services/MeilisearchService.php (2)
  • search (209-254)
  • rawSearch (256-270)
src/Engine.php (2)
src/Services/MeilisearchManager.php (2)
  • index (90-139)
  • search (195-236)
src/SearchableObject.php (5)
  • SearchableObject (13-80)
  • getSearchableArray (65-79)
  • getIndexUid (42-45)
  • getPrimaryKey (50-53)
  • getIdentifier (55-58)
tests/Integration/Fixtures/ActorDataProvider.php (3)
tests/Entity/Actor.php (1)
  • Actor (7-14)
src/DataProvider/DataProviderInterface.php (6)
  • provide (18-18)
  • loadByIdentifiers (25-25)
  • getIdentifierValues (32-32)
  • normalizeIdentifiers (39-39)
  • denormalizeIdentifier (46-46)
  • cleanup (48-48)
src/Identifier/IdNormalizerInterface.php (2)
  • normalize (12-12)
  • denormalize (21-21)
src/Command/MeilisearchUpdateSettingsCommand.php (4)
src/Services/MeilisearchManager.php (2)
  • __construct (51-62)
  • isSearchable (64-69)
src/SearchManagerInterface.php (1)
  • isSearchable (22-22)
src/SearchService.php (1)
  • isSearchable (20-20)
src/Services/MeilisearchService.php (1)
  • isSearchable (66-77)
src/Command/MeilisearchImportCommand.php (7)
src/Engine.php (3)
  • __construct (46-48)
  • index (61-90)
  • count (169-172)
src/EventListener/DoctrineEventSubscriber.php (1)
  • __construct (12-14)
src/SearchManagerInterface.php (5)
  • getConfiguration (33-33)
  • isSearchable (22-22)
  • index (42-42)
  • count (106-106)
  • deleteByIndexName (67-67)
src/Services/MeilisearchManager.php (5)
  • getConfiguration (85-88)
  • isSearchable (64-69)
  • index (90-139)
  • count (248-253)
  • deleteByIndexName (183-186)
src/DataProvider/DataProviderRegistryInterface.php (1)
  • getDataProvider (21-21)
src/DataProvider/DataProviderInterface.php (2)
  • provide (18-18)
  • cleanup (48-48)
src/DataProvider/OrmEntityProvider.php (2)
  • provide (22-31)
  • cleanup (89-92)
src/Services/SettingsUpdater.php (3)
src/Command/MeilisearchUpdateSettingsCommand.php (1)
  • __construct (22-28)
src/SearchManagerInterface.php (1)
  • getConfiguration (33-33)
src/Services/MeilisearchManager.php (1)
  • getConfiguration (85-88)
src/DataProvider/DataProviderRegistry.php (2)
src/Exception/DataProviderNotFoundException.php (2)
  • DataProviderNotFoundException (7-13)
  • __construct (9-12)
src/DataProvider/DataProviderRegistryInterface.php (1)
  • getDataProvider (21-21)
src/SearchManagerInterface.php (2)
src/Exception/NotSearchableException.php (1)
  • NotSearchableException (7-13)
src/Services/MeilisearchManager.php (5)
  • isSearchable (64-69)
  • searchableAs (71-83)
  • getConfiguration (85-88)
  • index (90-139)
  • search (195-236)
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)
🪛 GitHub Actions: Tests
src/Identifier/DefaultIdNormalizer.php

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

src/DependencyInjection/MeilisearchExtension.php

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


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


[error] 52-52: 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: StaticAccess Avoid using static access to class '\Meilisearch\Bundle\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/DataProvider/OrmEntityProvider.php

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

src/Services/MeilisearchManager.php

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


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


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

src/Command/MeilisearchImportCommand.php

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


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


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

src/Services/SettingsUpdater.php

[error] 35-35: 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/config/framework.yaml (1)

5-7: LGTM! Error handling configuration supports the new architecture.

The addition of handle_all_throwables and php_errors logging enhances error visibility during tests, which is appropriate for validating the new DataProvider and manager-based architecture introduced in this PR.

tests/Entity/Link.php (1)

9-9: LGTM! Migration to SearchableObject is consistent.

The import change from Searchable to SearchableObject aligns with the PR's architecture refactoring, and the corresponding constant update on line 76 maintains consistency across the normalization flow.

tests/baseline-ignore (1)

5-5: LGTM! Appropriate baseline entry for Doctrine ORM deprecation.

Adding the DefaultProxyClassNameResolver deprecation notice is consistent with the existing Doctrine proxy-related deprecations in the baseline and aligns with Doctrine ORM's migration path toward native lazy objects.

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

40-41: LGTM! Test expectations updated for new indices.

The additions correctly reflect the new actor and cars indices introduced in the test configuration, ensuring the delete command test validates all configured indices.

src/Exception/LogicException.php (1)

1-9: LGTM! Clean exception implementation.

The new LogicException provides a bundle-specific exception type following standard PHP conventions. The final class modifier and namespace are appropriate.

src/SearchableEntity.php (1)

15-17: LGTM! Deprecation properly documented.

The deprecation notice clearly directs users to SearchableObject, and the consistent constant reference updates on lines 83 and 87 maintain compatibility during the migration period.

src/Exception/DataProviderNotFoundException.php (1)

1-13: LGTM! Exception follows bundle conventions.

The DataProviderNotFoundException implementation is consistent with other bundle exceptions (e.g., NotSearchableException, InvalidIndiceException), properly extends InvalidArgumentException, and provides a clear error message identifying both the index and class involved.

src/Event/SettingsUpdatedEvent.php (1)

16-20: Constructor promotion is clean and behavior‑preserving

Using promoted private readonly properties keeps the event immutable and matches the existing getters without changing semantics. Looks good.

tests/Integration/AggregatorTest.php (2)

24-43: Good coverage of entity‑class resolution and constructor validation

The tests around getEntityClassFromObjectID() and the multi‑primary‑key constructor guard accurately pin the expected behavior and error conditions for aggregators.


69-100: Normalization tests nicely validate default vs custom primary key behavior

testAggregatorNormalization() and testAggregatorCustomPrimaryKey() clearly capture the expectations for the objectID field and for a custom primary key ('id'), matching the new Aggregator::normalize() semantics.

config/services.php (3)

32-40: Manager‑based wiring and aliases are consistent

MeilisearchService, the Doctrine subscriber, and all commands now depend on SearchManagerInterface via the meilisearch.manager service and its alias, which matches the new manager API and keeps the old service available behind a deprecating facade. The argument order for MeilisearchManager and the updated constructors for commands/settings updater are all respected.

Also applies to: 42-47, 48-52, 84-88, 93-107, 110-117, 120-123


10-11: Data provider registry DI matches the new registry contract

The meilisearch.data_provider.registry service is correctly wired with the container locator and provider map, and aliased to DataProviderRegistryInterface, so data providers can be resolved per index/class via the manager without further config changes.

Also applies to: 74-82, 131-136


14-16: Default ID normalizer registration looks correct

Registering meilisearch.identifier.default_id_normalizer and aliasing IdNormalizerInterface to that service ensures a single, configurable ID normalization strategy is used across ORM and custom data providers, with an explicit service id that fits existing alias patterns.

Also applies to: 128-142

src/Command/MeilisearchDeleteCommand.php (1)

31-33: Delete command correctly migrated to SearchManagerInterface

Using $this->searchManager->deleteByIndexName($indexName) is consistent with the new manager API and keeps the command behavior unchanged aside from routing through the central manager.

src/Command/MeilisearchClearCommand.php (1)

29-31: Clear command now correctly delegates to the manager

Switching to $this->searchManager->clear($className) routes index clearing through the new manager while preserving the existing status handling and output.

tests/Entity/Actor.php (1)

7-13: Test entity Actor is simple and idiomatic

Immutable promoted properties for id and name are a good fit for test data and align with the rest of the codebase’s PHP 8.1 style.

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

28-41: Updated expectations reflect newly added indices

Including the actor and cars indices in the expected clear output keeps the test aligned with the expanded test Meilisearch configuration and the manager‑driven clear behavior.

tests/config/meilisearch.yaml (1)

57-62: Actor/cars indices wiring looks consistent with new data provider model

type: 'custom' with data_provider pointing to the fixtures namespace and the additional cars index both align with the new configuration schema and test setup. No issues from this change.

src/Command/IndexCommand.php (1)

8-29: Migration to SearchManagerInterface in IndexCommand looks correct

Constructor promotion and the switch from searchService to searchManager for reading prefix and indices preserve the previous behavior while aligning with the new API. The rest of getEntitiesFromArgs() remains functionally identical, so no regression is introduced here.

src/MeilisearchBundle.php (1)

7-25: Compiler pass registration for data providers is straightforward and correct

Overriding build() to call parent::build() and then add DataProviderPass is the expected Symfony pattern and cleanly wires the new data provider infrastructure into the bundle.

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

23-132: Tests correctly updated to use SearchManagerInterface::search()

Replacing the old service calls with $this->searchManager->search(<Entity>::class, <query>) keeps the semantics of these integration tests intact while aligning them with the new public API. The expectations on IDs and titles still exercise the subscriber behavior end‑to‑end.

tests/Kernel.php (1)

36-42: Doctrine config selection logic is consistent with the new PHP/Doctrine targets

The simplified branching on $doctrineBundleV3, LegacyReflectionFields and PHP version cleanly covers v3, v2-on-8.4, and older proxy configs, while preserving meilisearch wiring. No functional issues spotted.

tests/Unit/ConfigurationTest.php (2)

29-60: Using mixed in dynamic settings tests is appropriate

Typing $value as mixed for the dynamic settings checker tests matches the data provider contract and the provider data (string|int|bool|array|null). This keeps the tests strict without constraining valid inputs.


135-151: Extended configuration expectations for type, data provider, id normalizer, and primary key look coherent

The added defaults (type: 'orm', data_provider: null, id_normalizer: 'meilisearch.identifier.default_id_normalizer', primary_key: 'objectID') and the new cases for custom primary keys and custom id_normalizer all align with the new configuration schema and the PR goals (custom providers + identifier normalization). The expected trees are internally consistent and should give good coverage of the new options.

Also applies to: 187-209, 244-250, 288-292, 330-334, 362-441

src/Exception/NotSearchableException.php (1)

7-12: NotSearchableException is well‑typed and clear

Exception design and typing (int $code = 0, ?Throwable $previous = null) are solid, and the message clearly describes the error case. No changes needed.

phpstan-baseline.php (1)

1-23: Baseline configuration looks appropriate for optional Doctrine dependency.

The baseline correctly suppresses PHPStan errors for ClassUtils::getClass() calls, which is expected since Doctrine is now a dev dependency. The isset.offset suppression for the hits array is a minor PHPStan false positive.

src/DependencyInjection/MeilisearchExtension.php (1)

66-69: Manager service wiring is consistent with the service pattern.

The wiring correctly injects the serializer reference and configuration, mirroring the pattern used for meilisearch.service.

tests/Integration/SearchTest.php (3)

72-77: Good practice: Adding waitForAllTasks() ensures test reliability.

The addition of waitForAllTasks() after the import command ensures that asynchronous Meilisearch indexing completes before search assertions, preventing flaky tests.


76-92: Migration to SearchManagerInterface is correctly applied.

The test methods now use searchManager->search() and searchManager->rawSearch() without the ObjectManager parameter, aligning with the new SearchManagerInterface signatures. This simplifies the API and removes Doctrine coupling from the search interface.


145-164: Test correctly validates Car entity indexing with proper configuration.

The test follows the established pattern: persist entities, run import, wait for tasks, and verify search results. The 'cars' index is properly configured in tests/config/meilisearch.yaml and correctly mapped to the Car entity.

src/EventListener/DoctrineEventSubscriber.php (2)

8-14: Clean migration to SearchManagerInterface with property promotion.

The constructor now uses PHP 8.1 constructor property promotion with the new SearchManagerInterface, which aligns with the minimum PHP version bump in this PR.


16-29: Event handlers correctly simplified to use new manager interface.

The handlers now pass only the entity object to index() and remove(), as the SearchManagerInterface internally resolves the appropriate data provider. Using preRemove (rather than postRemove) is correct since the entity must still be managed to extract identifiers.

composer.json (2)

21-29: Breaking change: PHP 8.1 and Symfony 6.4 minimum requirements.

This is a significant compatibility change that drops support for PHP 7.4/8.0 and Symfony 5.x. Ensure this is clearly documented in the changelog/upgrade guide since it will affect users on older stacks.


38-43: Dev tooling updates are appropriate.

The PHPStan and PHP-CS-Fixer version bumps are minor maintenance updates that shouldn't affect functionality.

src/DataProvider/DataProviderRegistry.php (1)

21-35: Provider resolution logic looks solid

Direct map lookup with a sane is_a fallback per index is clear and matches the registry contract; throwing DataProviderNotFoundException is appropriate when nothing matches.

tests/BaseKernelTestCase.php (2)

10-40: Migration to SearchManagerInterface is consistent

Using meilisearch.manager in setUp(), getPrefix(), and cleanupIndex() aligns with the new API and keeps tests in sync with production wiring.

Also applies to: 54-68


47-52: Code is correct — getTasks() is directly iterable

Verified: Client::getTasks() returns a TasksResults object that implements IteratorAggregate, making it directly iterable. The foreach loop in waitForAllTasks() correctly yields individual task arrays, each with a uid key. No changes needed.

src/Services/SettingsUpdater.php (1)

23-29: Constructor refactor to SearchManagerInterface is appropriate

Grabbing configuration via $searchManager->getConfiguration() in the constructor keeps this service aligned with the new manager abstraction and avoids touching settings logic.

src/Command/MeilisearchUpdateSettingsCommand.php (1)

22-28: Command wiring to SearchManagerInterface is correct

Injecting SearchManagerInterface into the command, passing it to IndexCommand, and using $this->searchManager->isSearchable() keeps this command consistent with the new manager-based API.

Also applies to: 56-57

src/Command/MeilisearchCreateCommand.php (1)

23-30: Create command migration to SearchManagerInterface looks good

Constructor promotion and the switch to $this->searchManager->isSearchable() are consistent with the new manager API; index creation and optional settings update behavior remain intact.

Also applies to: 66-79

src/Engine.php (1)

55-90: Per-index batching and primary key handling in index() look correct

Using SearchableObject plus the ['primaryKey' => ..., 'documents' => []] structure per $indexUid cleanly scopes primary keys by index and avoids the earlier “first object primary key reused for all indexes” bug. The normalization of identifiers via normalizeId() is also consistent with the declared types.

src/Command/MeilisearchImportCommand.php (3)

28-36: Constructor wiring to the new manager and registry looks consistent

Injecting SearchManagerInterface into the parent plus keeping Client, SettingsUpdater, EventDispatcherInterface, and DataProviderRegistryInterface as readonly promoted properties cleanly aligns this command with the new architecture.


168-188: Task handling and logging in formatIndexingResponse() are solid

Waiting on each task, throwing TaskException on failures, and accumulating indexedDocuments per index gives the import command clear, aggregated feedback while respecting Meilisearch’s async task model.


214-238: Swap and cleanup of temporary indices now properly route through the manager

Using SearchManagerInterface::deleteByIndexName() instead of reaching directly into the client keeps deletion consistent with the rest of the new API while still leveraging the low-level client for swapIndexes().

src/Services/MeilisearchManager.php (3)

90-139: Manager-side batching and aggregator handling in index() look consistent with the new architecture

The index() implementation correctly:

  • Resolves all matching index configs per entity (including aggregators via getIndicesForEntity()),
  • Uses the data provider for identifier values,
  • Wraps entities in aggregator classes when needed, and
  • Defers chunking + SearchableObject creation to batchProcess().

This keeps provider-based ID handling and serializer-group context centralized while preserving the expected “list of per-chunk responses” shape for callers like the import command.


195-236: Search hydration correctly respects provider-based ID normalization and batching

The search() method:

  • Enforces searchability via assertIsSearchable(),
  • Uses the configured nbResults limit,
  • Extracts primary-key values from hits,
  • Loads domain objects via loadByIdentifiers(), and
  • Builds the lookup map using $dataProvider->normalizeIdentifiers($dataProvider->getIdentifierValues($object)).

This keeps hit-to-entity mapping fully aligned with the ID normalization used during indexing and avoids hard-coding any particular identifier format, which is exactly what the new DataProvider/IdNormalizer abstractions are for.


287-313: Aggregator and entities-to-index mapping is well-structured

setSearchables() and setAggregatorsAndEntitiesAggregators() give you:

  • A flat list of searchable classes (index class entries), and
  • A per-entity mapping of which aggregator classes/index names apply.

Combined with getIndicesForEntity(), this lets entities participate in both direct and aggregator-based indices without special-casing at call sites.

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 (1)
src/DependencyInjection/MeilisearchExtension.php (1)

106-114: Silent skip when data_provider service doesn't exist may mask configuration errors.

If a user configures a data_provider service that doesn't exist, this silently continues without the provider being tagged. This could lead to confusing runtime errors when the data provider registry can't find the expected provider.

Consider throwing a clear exception when the service is configured but not found.

🧹 Nitpick comments (6)
src/Identifier/IdNormalizerInterface.php (1)

7-22: Consider adding usage clarification to the interface documentation.

The interface is correctly designed. However, to prevent future confusion about the type asymmetry (as evidenced by past review comments), consider adding a brief class-level docblock explaining that:

  • normalize() returns scalar values for single identifiers and encoded strings for composite identifiers
  • denormalize() is only called for composite identifiers (those encoded by normalize())

Example:

+/**
+ * Normalizes entity identifiers to MeiliSearch-compatible primary keys.
+ * 
+ * Single identifiers are returned as-is (string|int), while composite 
+ * identifiers are encoded as base64 strings. The denormalize() method 
+ * only handles composite identifiers.
+ */
 interface IdNormalizerInterface
 {
src/DependencyInjection/Configuration.php (1)

14-113: Note: Pipeline warning about method length is borderline.

The getConfigTreeBuilder() method is exactly 100 lines, which meets the PHPMD threshold. While this is technically acceptable, if the method grows further in future changes, consider extracting the indices configuration into a separate method for better maintainability.

This is an optional refactor and doesn't need to block this PR.

src/Services/MeilisearchService.php (1)

27-27: PHPMD complexity warnings are acceptable for deprecated code.

The pipeline flags TooManyPublicMethods, ExcessiveClassComplexity, and CouplingBetweenObjects. Since this class is deprecated and will be removed, investing effort to refactor it is not warranted. The focus should be on the new MeilisearchManager instead.

src/Command/MeilisearchImportCommand.php (1)

73-163: Consider extracting methods to reduce complexity.

The execute() method has high cyclomatic (14) and NPath (976) complexity per PHPMD. While the logic is correct, extracting the index creation and import loops into separate private methods would improve readability and reduce complexity below thresholds.

Based on learnings, this can be deferred to a follow-up PR to keep this one focused.

src/Services/MeilisearchManager.php (2)

377-399: Potential duplicate index configs for aggregator entities.

In getIndicesForEntity(), an entity might match both the direct class check (line 385-386) and the aggregator check (lines 389-394), potentially adding the same config twice to $matchingConfigs.

Consider deduplicating:

     private function getIndicesForEntity(object $entity): array
     {
         $className = $this->getBaseClassName($entity);
         $matchingConfigs = [];
+        $seen = [];

         foreach ($this->configuration->get('indices') as $config) {
             $configClass = $config['class'];
+            $configKey = $config['name'] . '::' . $configClass;

             if ($className === $configClass || is_subclass_of($className, $configClass)) {
-                $matchingConfigs[] = $config;
+                if (!isset($seen[$configKey])) {
+                    $matchingConfigs[] = $config;
+                    $seen[$configKey] = true;
+                }
             }

             if (isset($this->entitiesAggregators[$className])) {
                 foreach ($this->entitiesAggregators[$className] as $aggInfo) {
                     if ($aggInfo['class'] === $configClass && $aggInfo['index'] === $config['name']) {
-                        $matchingConfigs[] = $config;
+                        if (!isset($seen[$configKey])) {
+                            $matchingConfigs[] = $config;
+                            $seen[$configKey] = true;
+                        }
                     }
                 }
             }
         }

         return $matchingConfigs;
     }

27-422: Class complexity exceeds threshold but is justified.

PHPMD reports ExcessiveClassComplexity (59 > 50). The manager centralizes index/search/removal logic, making some complexity inherent.

To reduce complexity without major restructuring, consider extracting resolveClass() into a small ClassResolver service and moving the search hydration logic (lines 216-233) into a private helper. This could be addressed in a follow-up PR.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 120c503 and f57906b.

📒 Files selected for processing (65)
  • .github/workflows/pre-release-tests.yml (2 hunks)
  • .github/workflows/tests.yml (2 hunks)
  • README.md (1 hunks)
  • composer.json (1 hunks)
  • config/doctrine.php (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 (3 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/Identifier/DefaultIdNormalizer.php (1 hunks)
  • src/Identifier/IdNormalizerInterface.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/Car.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/DependencyInjectionTest.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 (5 hunks)
  • tests/Kernel.php (1 hunks)
  • tests/Unit/ConfigurationTest.php (10 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 (23)
  • src/SearchService.php
  • src/Exception/NotSearchableException.php
  • tests/Entity/SelfNormalizable.php
  • tests/Entity/Link.php
  • tests/Entity/Actor.php
  • tests/Integration/Command/MeilisearchCreateCommandTest.php
  • .github/workflows/pre-release-tests.yml
  • tests/Integration/Fixtures/ActorDataProvider.php
  • src/EventListener/ConsoleOutputSubscriber.php
  • src/MeilisearchBundle.php
  • src/DependencyInjection/Compiler/DataProviderPass.php
  • tests/Integration/Command/MeilisearchDeleteCommandTest.php
  • src/Searchable.php
  • src/DataProvider/OrmEntityProvider.php
  • tests/baseline-ignore
  • phpstan.dist.neon
  • src/EventListener/DoctrineEventSubscriber.php
  • src/DataProvider/DataProviderInterface.php
  • src/DataProvider/DataProviderRegistry.php
  • tests/config/meilisearch.yaml
  • src/Model/Aggregator.php
  • src/Services/UnixTimestampNormalizer.php
  • tests/Unit/SerializationTest.php
🧰 Additional context used
🧠 Learnings (3)
📓 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-12-15T19:07:07.476Z
Learnt from: norkunas
Repo: meilisearch/meilisearch-symfony PR: 410
File: src/Identifier/DefaultIdNormalizer.php:18-22
Timestamp: 2025-12-15T19:07:07.476Z
Learning: In PHP projects (Symfony style), avoid importing global PHP classes (e.g., \InvalidArgumentException, \LogicException) via use statements inside namespaces. Instead, reference them with a leading backslash (fully qualified name) when used, e.g. \InvalidArgumentException $e = new \InvalidArgumentException('msg'); This preserves clarity about the global namespace and follows Symfony coding conventions for global classes.

Applied to files:

  • src/DataProvider/DataProviderRegistryInterface.php
  • tests/Entity/Tag.php
  • src/Command/IndexCommand.php
  • tests/Integration/DependencyInjectionTest.php
  • tests/Integration/EventListener/DoctrineEventSubscriberTest.php
  • src/Services/SettingsUpdater.php
  • tests/Unit/ConfigurationTest.php
  • config/doctrine.php
  • tests/Kernel.php
  • tests/Integration/SearchTest.php
  • phpstan-baseline.php
  • src/Exception/DataProviderNotFoundException.php
  • src/SearchableObject.php
  • src/Command/MeilisearchDeleteCommand.php
  • src/SearchableEntity.php
  • src/Command/MeilisearchCreateCommand.php
  • src/DependencyInjection/Configuration.php
  • src/Event/SettingsUpdatedEvent.php
  • src/Exception/LogicException.php
  • tests/Integration/AggregatorTest.php
  • tests/Entity/Car.php
  • src/Command/MeilisearchUpdateSettingsCommand.php
  • tests/Integration/Command/MeilisearchClearCommandTest.php
  • src/Identifier/DefaultIdNormalizer.php
  • src/Identifier/IdNormalizerInterface.php
  • config/services.php
  • src/SearchManagerInterface.php
  • tests/Integration/Command/MeilisearchImportCommandTest.php
  • src/Services/MeilisearchService.php
  • tests/Integration/EngineTest.php
  • tests/BaseKernelTestCase.php
  • src/Command/MeilisearchImportCommand.php
  • src/Command/MeilisearchClearCommand.php
  • src/Engine.php
  • src/Services/MeilisearchManager.php
  • src/DependencyInjection/MeilisearchExtension.php
  • src/Exception/InvalidIndiceException.php
📚 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:

  • .github/workflows/tests.yml
  • src/DependencyInjection/Configuration.php
  • tests/Integration/AggregatorTest.php
  • src/Identifier/DefaultIdNormalizer.php
  • config/services.php
  • src/SearchManagerInterface.php
  • tests/Integration/EngineTest.php
  • tests/BaseKernelTestCase.php
  • src/Command/MeilisearchImportCommand.php
  • src/Engine.php
  • src/Services/MeilisearchManager.php
🧬 Code graph analysis (30)
src/DataProvider/DataProviderRegistryInterface.php (2)
src/Exception/DataProviderNotFoundException.php (1)
  • DataProviderNotFoundException (7-13)
src/DataProvider/DataProviderRegistry.php (1)
  • getDataProvider (21-36)
tests/Entity/Tag.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
src/Command/IndexCommand.php (4)
src/Command/MeilisearchUpdateSettingsCommand.php (1)
  • __construct (22-28)
src/EventListener/DoctrineEventSubscriber.php (1)
  • __construct (12-14)
src/SearchManagerInterface.php (1)
  • getConfiguration (33-33)
src/Services/MeilisearchManager.php (1)
  • getConfiguration (85-88)
tests/Integration/EventListener/DoctrineEventSubscriberTest.php (2)
src/SearchManagerInterface.php (1)
  • search (88-88)
src/Services/MeilisearchManager.php (1)
  • search (195-236)
src/Services/SettingsUpdater.php (1)
src/SearchManagerInterface.php (1)
  • getConfiguration (33-33)
config/doctrine.php (1)
src/EventListener/DoctrineEventSubscriber.php (1)
  • DoctrineEventSubscriber (10-30)
tests/Kernel.php (1)
src/DependencyInjection/MeilisearchExtension.php (1)
  • load (22-79)
tests/Integration/SearchTest.php (3)
tests/BaseKernelTestCase.php (1)
  • waitForAllTasks (47-52)
src/SearchManagerInterface.php (2)
  • search (88-88)
  • rawSearch (98-98)
src/Services/MeilisearchManager.php (2)
  • search (195-236)
  • rawSearch (238-246)
src/Exception/DataProviderNotFoundException.php (2)
src/Exception/InvalidIndiceException.php (1)
  • __construct (9-12)
src/Exception/NotSearchableException.php (1)
  • __construct (9-12)
src/SearchableObject.php (4)
src/SearchableEntity.php (1)
  • getSearchableArray (66-91)
src/Identifier/DefaultIdNormalizer.php (1)
  • normalize (9-31)
src/Identifier/IdNormalizerInterface.php (1)
  • normalize (12-12)
src/Services/UnixTimestampNormalizer.php (1)
  • normalize (14-17)
src/Command/MeilisearchDeleteCommand.php (4)
src/SearchManagerInterface.php (1)
  • deleteByIndexName (67-67)
src/SearchService.php (1)
  • deleteByIndexName (50-50)
src/Services/MeilisearchManager.php (1)
  • deleteByIndexName (183-186)
src/Services/MeilisearchService.php (1)
  • deleteByIndexName (185-194)
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-80)
src/Command/MeilisearchCreateCommand.php (3)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/SearchManagerInterface.php (1)
  • isSearchable (22-22)
src/Services/MeilisearchManager.php (1)
  • isSearchable (64-69)
src/DependencyInjection/Configuration.php (1)
src/SearchableObject.php (1)
  • SearchableObject (13-80)
tests/Integration/AggregatorTest.php (4)
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/Identifier/DefaultIdNormalizer.php (1)
  • normalize (9-31)
tests/Entity/Car.php (3)
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/Command/MeilisearchUpdateSettingsCommand.php (4)
src/Command/IndexCommand.php (1)
  • __construct (19-24)
src/Services/MeilisearchManager.php (2)
  • __construct (51-62)
  • isSearchable (64-69)
src/SearchManagerInterface.php (1)
  • isSearchable (22-22)
src/Services/MeilisearchService.php (1)
  • isSearchable (66-77)
src/Identifier/DefaultIdNormalizer.php (1)
src/Identifier/IdNormalizerInterface.php (2)
  • normalize (12-12)
  • denormalize (21-21)
src/Identifier/IdNormalizerInterface.php (1)
src/Identifier/DefaultIdNormalizer.php (2)
  • normalize (9-31)
  • denormalize (33-39)
config/services.php (5)
src/DataProvider/DataProviderRegistry.php (1)
  • DataProviderRegistry (10-37)
src/Engine.php (1)
  • Engine (44-182)
src/Identifier/DefaultIdNormalizer.php (1)
  • DefaultIdNormalizer (7-40)
src/Services/MeilisearchManager.php (1)
  • MeilisearchManager (27-422)
src/Services/SettingsUpdater.php (1)
  • SettingsUpdater (17-78)
src/SearchManagerInterface.php (2)
src/Exception/NotSearchableException.php (1)
  • NotSearchableException (7-13)
src/SearchService.php (2)
  • searchableAs (34-34)
  • search (59-64)
src/Services/MeilisearchService.php (3)
src/EventListener/DoctrineEventSubscriber.php (1)
  • __construct (12-14)
src/Collection.php (4)
  • __construct (20-23)
  • Collection (13-380)
  • get (30-37)
  • count (205-208)
src/Services/MeilisearchManager.php (11)
  • isSearchable (64-69)
  • getConfiguration (85-88)
  • searchableAs (71-83)
  • index (90-139)
  • remove (141-174)
  • clear (176-181)
  • deleteByIndexName (183-186)
  • delete (188-193)
  • search (195-236)
  • rawSearch (238-246)
  • count (248-253)
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/BaseKernelTestCase.php (2)
src/SearchManagerInterface.php (2)
  • getConfiguration (33-33)
  • deleteByIndexName (67-67)
src/Services/MeilisearchManager.php (2)
  • getConfiguration (85-88)
  • deleteByIndexName (183-186)
src/Command/MeilisearchImportCommand.php (6)
src/EventListener/DoctrineEventSubscriber.php (1)
  • __construct (12-14)
src/SearchManagerInterface.php (4)
  • isSearchable (22-22)
  • index (42-42)
  • count (106-106)
  • deleteByIndexName (67-67)
src/DataProvider/DataProviderRegistry.php (1)
  • getDataProvider (21-36)
src/DataProvider/DataProviderRegistryInterface.php (1)
  • getDataProvider (21-21)
src/DataProvider/DataProviderInterface.php (2)
  • provide (18-18)
  • cleanup (48-48)
src/DataProvider/OrmEntityProvider.php (2)
  • provide (22-31)
  • cleanup (89-92)
src/Command/MeilisearchClearCommand.php (5)
src/Engine.php (1)
  • clear (137-140)
src/SearchManagerInterface.php (1)
  • clear (60-60)
src/SearchService.php (1)
  • clear (43-43)
src/Services/MeilisearchManager.php (1)
  • clear (176-181)
src/Services/MeilisearchService.php (1)
  • clear (172-183)
src/Engine.php (2)
src/SearchManagerInterface.php (5)
  • index (42-42)
  • remove (51-51)
  • clear (60-60)
  • count (106-106)
  • search (88-88)
src/SearchableObject.php (5)
  • SearchableObject (13-80)
  • getSearchableArray (65-79)
  • getIndexUid (42-45)
  • getPrimaryKey (50-53)
  • getIdentifier (55-58)
src/Services/MeilisearchManager.php (7)
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/NotSearchableException.php (2)
  • NotSearchableException (7-13)
  • __construct (9-12)
src/SearchableObject.php (2)
  • SearchableObject (13-80)
  • __construct (28-37)
src/DataProvider/OrmEntityProvider.php (4)
  • __construct (15-20)
  • getIdentifierValues (72-77)
  • normalizeIdentifiers (79-82)
  • loadByIdentifiers (33-70)
src/SearchManagerInterface.php (11)
  • 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)
  • rawSearch (98-98)
  • count (106-106)
src/DataProvider/DataProviderRegistryInterface.php (1)
  • getDataProvider (21-21)
src/DataProvider/DataProviderInterface.php (3)
  • getIdentifierValues (32-32)
  • normalizeIdentifiers (39-39)
  • loadByIdentifiers (25-25)
src/DependencyInjection/MeilisearchExtension.php (4)
src/DataProvider/OrmEntityProvider.php (1)
  • OrmEntityProvider (10-93)
src/MeilisearchBundle.php (2)
  • MeilisearchBundle (11-31)
  • qualifiedVersion (15-18)
src/Model/Aggregator.php (2)
  • Aggregator (15-84)
  • getEntities (51-54)
src/Entity/Aggregator.php (1)
  • Aggregator (9-11)
src/Exception/InvalidIndiceException.php (1)
src/Exception/DataProviderNotFoundException.php (1)
  • __construct (9-12)
🪛 GitHub Actions: Tests
src/DependencyInjection/Configuration.php

[error] 14-14: PHPMD: ExcessiveMethodLength - The method getConfigTreeBuilder() has 100 lines of code. Current threshold is set to 100. Avoid really long methods.

src/Identifier/DefaultIdNormalizer.php

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

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/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/MeilisearchManager.php

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


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


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

src/DependencyInjection/MeilisearchExtension.php

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


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


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


[error] 55-55: 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] 66-66: PHPMD: StaticAccess - Avoid using static access to class '\MeilisearchBundle\MeilisearchBundle' in method 'load'.


[error] 121-121: PHPMD: MissingImport - Missing class import via use statement (line '121', column '31').


[error] 128-128: 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.

🔇 Additional comments (63)
src/Exception/LogicException.php (1)

1-9: LGTM! Clean exception class implementation.

The custom LogicException is correctly implemented following Symfony conventions, including the use of a fully qualified name for the base exception class (as per your coding style) and the final keyword for the leaf exception type.

tests/Integration/AggregatorTest.php (5)

24-27: LGTM!

The test correctly validates the objectID parsing logic, ensuring that getEntityClassFromObjectID extracts the entity class from the composite identifier format.


29-34: LGTM!

The test appropriately validates exception handling for unknown entities in objectID strings.


36-43: LGTM!

The test correctly validates that the aggregator rejects entities with composite primary keys, ensuring single-identifier constraints are enforced.


69-84: LGTM!

The test correctly validates normalization with the default primary key, properly passing identifier values and verifying both the objectID (stringified) and id fields in the serialized output.


86-100: LGTM!

The test correctly validates custom primary key configuration, ensuring that specifying a custom primary key name ('id') properly overrides the default and appears in the serialized output.

README.md (1)

56-59: Clear documentation of optional Doctrine integration.

The new section effectively communicates that Doctrine is now an optional dependency and provides straightforward installation instructions.

src/Exception/InvalidIndiceException.php (1)

9-9: LGTM: Type safety improvement.

Explicitly typing $code as int aligns with the parent \InvalidArgumentException signature and improves type safety. This matches the pattern used in other new exceptions like DataProviderNotFoundException.

tests/Kernel.php (1)

36-42: Clean simplification of Doctrine config loading.

The three-way conditional elegantly handles different Doctrine bundle versions (v3, v2 with PHP 8.4+, and legacy) while removing unnecessary complexity from the previous implementation.

src/SearchableEntity.php (2)

15-17: Proper deprecation notice guiding migration to SearchableObject.

The deprecation clearly directs users to the replacement class, supporting a smooth migration path.


83-87: Consistent constant migration to SearchableObject.

Both normalization paths (lines 83 and 87) correctly reference SearchableObject::NORMALIZATION_FORMAT, ensuring consistency across NormalizableInterface and standard normalization flows.

src/Event/SettingsUpdatedEvent.php (1)

16-21: Clean adoption of constructor property promotion.

Using promoted private readonly properties is idiomatic for PHP 8.1+ and reduces boilerplate while maintaining the same public API through the getter methods.

src/Command/MeilisearchDeleteCommand.php (1)

32-32: Correct migration to SearchManagerInterface.

The call to deleteByIndexName() correctly uses $this->searchManager instead of the deprecated searchService, aligning with the broader API migration across all commands.

phpstan-baseline.php (1)

1-23: Baseline appropriately suppresses PHPStan errors for optional Doctrine dependency.

The baseline is acceptable. Verification confirms that MeilisearchExtension conditionally loads Doctrine-specific services only when DoctrineBundle is present (line 43-46), and explicitly throws a LogicException when ORM data providers are misconfigured without Doctrine (line 120-121).

Both MeilisearchService and MeilisearchManager have runtime guards via class_exists(DefaultProxyClassNameResolver::class) checks before falling back to ClassUtils::getClass(), reducing the risk of runtime failures when Doctrine is absent. The @codeCoverageIgnore annotation marks the ClassUtils fallback as an untested code path, which is appropriate given it only executes in legacy Doctrine versions.

src/Command/MeilisearchClearCommand.php (1)

30-30: LGTM! Clean migration to SearchManagerInterface.

The method call has been correctly updated from the deprecated SearchService to the new SearchManagerInterface. The clear() method signature remains unchanged, ensuring backward compatibility.

tests/config/framework.yaml (1)

5-7: LGTM! Improved test error handling and observability.

Adding handle_all_throwables and php_errors configuration enhances error visibility during test execution, which aligns with the broader test suite improvements in this PR.

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

184-184: LGTM! Test expectation updated to reflect new import ordering.

The log line order change reflects the updated batching behavior in the import command after migrating to the DataProvider architecture. Both indices are still correctly populated; only the logging order has changed.

tests/Entity/Tag.php (1)

9-9: LGTM! Clean migration to SearchableObject.

The import and constant reference have been correctly updated from the old Searchable to the new SearchableObject class. The NORMALIZATION_FORMAT constant maintains the same value ('searchableArray'), ensuring consistent behavior.

Also applies to: 90-90

tests/Integration/DependencyInjectionTest.php (1)

13-18: LGTM! Improved test isolation.

The setUp method ensures each test begins with a clean container state by resetting kernel.bundles to an empty array. This prevents test pollution and improves reliability when testing DI configuration in isolation.

src/Command/IndexCommand.php (2)

8-8: LGTM! Clean refactoring to SearchManagerInterface with constructor promotion.

The migration from concrete SearchService to SearchManagerInterface improves testability and follows dependency inversion principles. The use of constructor property promotion is idiomatic for modern PHP 8.1+.

Also applies to: 19-21


28-28: LGTM! Consistent usage of the new SearchManagerInterface.

Configuration retrieval has been correctly updated to use the injected $searchManager, maintaining consistency throughout the class.

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

39-40: LGTM! Test expectations updated for new test indices.

The test assertions have been correctly updated to include the newly added Actor and Car indices in the expected command output.

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

23-23: LGTM! API simplified by removing EntityManager dependency.

All search() calls have been correctly updated to use the new signature that removes the $entityManager parameter. This decouples the search API from Doctrine and aligns with the PR's objective to support non-Doctrine data sources (#302, #121).

The new signature search(string $className, string $query = '', array $searchParams = []): array is cleaner and more flexible.

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

src/DataProvider/DataProviderRegistryInterface.php (1)

1-22: LGTM! Clean interface design with proper generic annotations.

The DataProviderRegistryInterface follows best practices with strict type declarations, clear PHPDoc annotations including generic type parameters, and explicit exception documentation. The interface contract is straightforward and provides a clean abstraction for retrieving data providers by index and class.

config/doctrine.php (1)

1-17: LGTM! Doctrine event subscriber wiring is correct.

The service configuration properly wires the DoctrineEventSubscriber with the new meilisearch.manager dependency, aligning with the migration from SearchService to SearchManagerInterface.

src/Exception/DataProviderNotFoundException.php (1)

1-13: LGTM! Exception follows codebase conventions.

The exception class is consistent with other exceptions in the codebase (NotSearchableException, InvalidIndiceException) and correctly uses the fully qualified \InvalidArgumentException following Symfony coding standards.

tests/Unit/ConfigurationTest.php (3)

29-29: LGTM! Type hints updated appropriately.

The explicit mixed type hints align with PHP 8.1+ requirements and improve code clarity by removing redundant @param annotations.

Also applies to: 47-47


135-150: LGTM! Test expectations updated to validate new configuration options.

The tests now properly validate the new data provider architecture including type, data_provider, id_normalizer, and primary_key configuration options across all index configuration scenarios.

Also applies to: 193-208, 246-249, 288-291, 330-333


362-441: LGTM! Comprehensive test coverage for new configuration options.

The new test scenarios effectively validate:

  1. Custom primary key configuration (different from the default 'objectID')
  2. Custom ID normalizer service configuration

This ensures the new data provider architecture is properly tested.

src/Identifier/DefaultIdNormalizer.php (1)

7-40: LGTM! Implementation correctly handles single and composite identifiers.

The normalization logic is well-designed:

  • Single identifiers are returned as-is (with proper __toString handling)
  • Composite identifiers are ksorted, JSON-encoded, and converted to URL-safe base64

Note on pipeline failure: The PHPMD warning about missing import for \InvalidArgumentException at line 19 is expected. As per Symfony coding standards, global PHP classes should be referenced with a leading backslash rather than imported via use statements.

Based on learnings, Symfony projects prefer fully qualified names for global classes.

src/DependencyInjection/Configuration.php (4)

7-7: LGTM! Import updated to reflect new architecture.

The change from Searchable to SearchableObject aligns with the broader refactoring in this PR.


53-62: LGTM! New configuration options support the data provider architecture.

The new configuration nodes properly support:

  • Index type with sensible default ('orm') and validation
  • Configurable primary_key (defaulting to 'objectID' for BC)
  • Optional custom data_provider
  • Configurable id_normalizer with sensible default

64-69: LGTM! Documentation updated to reference new constant.

The info text correctly references SearchableObject::NORMALIZATION_GROUP after the refactoring.


104-107: LGTM! Validation prevents configuration errors.

The validation rule ensures that when type is set to 'custom', a data_provider must be configured. This provides clear feedback at configuration time rather than runtime failures.

tests/Entity/Car.php (1)

1-34: LGTM! Test entity properly configured for composite primary key testing.

The Car entity provides good test coverage for:

  • Composite primary keys (name + year)
  • PHP 8.1 promoted constructor properties with attributes
  • Property-to-getter name mapping (e.g., $namegetModelName())

Unlike other test entities (Tag, Link, SelfNormalizable), this entity doesn't implement NormalizableInterface, which is appropriate for testing default serialization behavior with composite keys.

tests/Integration/EngineTest.php (2)

35-42: LGTM!

The SearchableObject constructor usage is correct, with all required parameters properly provided. The use of 'objectID' as the primary key is intentional for backward compatibility testing.


67-69: LGTM!

Both SearchableObject instantiations correctly provide all required constructor arguments and properly reuse the serializer instance.

tests/Integration/SearchTest.php (4)

72-92: LGTM!

The addition of waitForAllTasks() ensures indexing completes before search assertions, and the updated search calls correctly use the new SearchManagerInterface API with proper signatures.


115-117: LGTM!

Proper synchronization with waitForAllTasks() and correct pagination parameters in the search call.


138-140: LGTM!

The test properly waits for tasks and correctly invokes the search API.


145-164: LGTM!

The new test properly exercises the cars index with the data provider flow, following the established test pattern with proper synchronization and assertions.

composer.json (1)

21-52: LGTM!

The dependency updates properly align with the PR objectives: PHP 8.1+ requirement, Symfony 6.4+ for LTS support, and moving Doctrine to optional dev dependency to support the new DataProvider abstraction. Dev tooling updates are appropriate maintenance.

tests/BaseKernelTestCase.php (1)

10-67: LGTM!

The consistent migration from SearchService to SearchManagerInterface is correctly implemented throughout the test base class. All method calls properly match the new interface signatures, and the waitForAllTasks() improvement now correctly waits for all tasks instead of just the first one.

src/Services/SettingsUpdater.php (1)

23-29: LGTM!

The constructor properly migrates to SearchManagerInterface dependency and effectively uses PHP 8.1+ property promotion. Configuration retrieval from the manager is correctly implemented.

src/Command/MeilisearchUpdateSettingsCommand.php (1)

22-56: LGTM!

The command properly migrates to SearchManagerInterface with correct property promotion, parent constructor delegation, and proper usage of the isSearchable() interface method.

src/SearchableObject.php (3)

28-37: LGTM!

The constructor properly uses descriptive parameter names (including $identifier instead of the shorter $id), applies appropriate type constraints, and correctly merges the normalization context with the Meilisearch flag.


39-58: LGTM!

The getter methods are straightforward property accessors with appropriate return type annotations.


65-79: LGTM!

The method correctly avoids mutating instance state by using a local context copy, properly handles Symfony version-specific DateTime normalization, and appropriately delegates to either the object's own normalize method or the injected normalizer.

src/Command/MeilisearchCreateCommand.php (2)

23-30: LGTM!

The constructor migration to SearchManagerInterface with readonly property promotion is clean and aligns with the broader refactoring pattern across the codebase.


66-68: LGTM!

The usage update from $this->searchService to $this->searchManager correctly reflects the new dependency.

src/SearchManagerInterface.php (2)

15-17: Well-structured interface with comprehensive PHPDoc.

The interface cleanly defines the search manager contract with proper type annotations and exception documentation.


35-42: Verify return type consistency with implementation.

The return type list<array<non-empty-string, DocumentAdditionOrUpdateTask>> suggests a list of per-index task mappings. Confirm this aligns with how MeilisearchManager::index() returns batched results via array_merge(...$responses).

src/Engine.php (3)

10-43: Comprehensive PHPDoc type definitions.

The PHPStan type definitions for task responses and search response shapes provide excellent type safety and documentation for the Engine's return types.


78-79: Good fix for per-index primary key tracking.

The batching now correctly associates each index with its own primary key using ['primaryKey' => ..., 'documents' => []], addressing the previously identified issue where a single primary key was applied to all indexes.


174-181: Simplified ID normalization.

The normalizeId method correctly handles \Stringable by casting objects to string. Since the type hint is \Stringable|string|int, any object passed here must implement __toString(), making the explicit method check unnecessary.

src/Services/MeilisearchService.php (1)

24-27: Appropriate deprecation strategy.

The deprecation docblock and trigger_deprecation calls throughout the methods provide a clear migration path while maintaining backward compatibility. The delegation to the new manager when available ensures existing code continues to work.

config/services.php (2)

69-77: Manager service correctly wired.

The MeilisearchManager service is properly configured with all required dependencies: normalizer, engine, property_accessor, data_provider.registry, and configuration.


126-137: Data provider and ID normalizer services properly registered.

The DataProviderRegistry and DefaultIdNormalizer services are correctly defined with their interface aliases, enabling dependency injection via interface type-hints.

src/Command/MeilisearchImportCommand.php (2)

28-36: Constructor properly migrated to new dependencies.

The command now correctly depends on SearchManagerInterface and DataProviderRegistryInterface, with readonly property promotion for cleaner code.


121-151: Data provider-based import loop is well-structured.

The loop correctly:

  1. Obtains the data provider once before the loop
  2. Breaks early on empty results with cleanup
  3. Indexes via the manager
  4. Calls cleanup after each batch
  5. Uses proper pagination with $page counter

This addresses the earlier concern about repeated provider instantiation.

src/Services/MeilisearchManager.php (3)

51-62: Well-structured constructor with clear dependencies.

The constructor properly injects all required dependencies and initializes the internal state via dedicated setup methods.


195-236: Search hydration correctly uses provider's normalizeIdentifiers.

The search method now consistently uses $dataProvider->normalizeIdentifiers() for building object keys, ensuring alignment with how IDs are normalized during indexing. This addresses the earlier concern about custom ID normalizer consistency.


401-421: Static access is acceptable for Doctrine proxy resolution.

The PHPMD StaticAccess warnings for DefaultProxyClassNameResolver::getClass() and ClassUtils::getClass() are unavoidable here—these are utility methods provided by Doctrine for resolving proxy class names. The static memoization pattern with $resolver is appropriate for this use case.

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.

I’ve reviewed the discussion and the automated CodeRabbit review. Most points have been addressed and checks are passing. I’m happy to trust your technical judgement here.

Approving this PR, thanks for the work 🙌

@norkunas norkunas enabled auto-merge December 17, 2025 04:38
@Strift Strift disabled auto-merge December 17, 2025 07:53
@Strift Strift added this pull request to the merge queue Dec 17, 2025
Merged via the queue into meilisearch:main with commit 36cfede Dec 17, 2025
23 of 25 checks passed
@norkunas norkunas deleted the dataprovider branch December 17, 2025 08:00
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