Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Data Indexer Laravel Package

fsi/data-indexer

A Laravel package for building searchable data indexes. Define indexes for your models and records, keep them in sync automatically, and run fast filtered queries without complex SQL. A simple, configurable API makes adding search and filtering to any Laravel app straightforward.

View on GitHub
Deep Wiki
Context7

Getting Started

Start by reading src/ rather than the README — the package is tiny and the contract is the whole story. The core abstraction is an indexer that turns a domain object into two things:

  • an index: an associative array of scalar values describing the object (the fields you want to filter/sort/display),
  • an identifier: a stable scalar (usually a string) uniquely identifying the object.

Minimal setup:

composer require fsi/data-indexer:^1.0

Then look for the main interface (DataIndexerInterface / IndexerInterface depending on version) and the Doctrine implementation (DoctrineDataIndexer). The fastest first use case is wiring it into an FSi DataSource/DataGrid so grid rows have stable identifiers and filterable index values:

use FSi\Component\DataIndexer\DoctrineDataIndexer;

$indexer = new DoctrineDataIndexer($entityManager, MyEntity::class);

$index = $indexer->getIndex($entity);      // ['id' => 1, 'name' => '...', ...]
$id    = $indexer->getIdentifier($entity); // '1'

If the shipped Doctrine indexer does not map your fields the way you need, skip it and implement the interface directly — that is usually less work than fighting it.

Implementation Patterns

Custom indexer per aggregate. Implement the interface once per entity/aggregate and keep the mapping explicit:

final class ProductIndexer implements DataIndexerInterface
{
    public function getIndex($product): array
    {
        return [
            'id'       => $product->getId(),
            'sku'      => $product->getSku(),
            'name'     => $product->getName(),
            'price'    => $product->getPrice(),
            'category' => $product->getCategory()?->getId(),
        ];
    }

    public function getIdentifier($product): string
    {
        return (string) $product->getId();
    }
}

Doctrine metadata-driven indexing. Use DoctrineDataIndexer when the identifier is the mapped @Id and you want the component to read it via metadata instead of getters. This avoids proxy issues and works for entities whose getters are expensive or absent. Where you need extra index fields, subclass/decorate and merge your own array with the parent result.

Decoration and composition. Because the interface is small, decorate indexers to add cross-cutting behaviour: prefix keys (product.name), add a __type discriminator for heterogeneous collections, memoize results, or fall back to a default identifier when the primary key is null (unsaved entities). A composite indexer that dispatches on instanceof is a clean way to index mixed collections.

Integration with DataSource/DataGrid. The intended workflow is: build the indexer, hand it to the data source/grid layer, and let the grid use getIdentifier() for row keys and getIndex() for column values and filters. Register it as a service and inject it into the factory/grid definition rather than constructing it inline:

services:
    App\Indexer\ProductIndexer:
        tags: ['app.data_indexer']

There is no bundle autoconfiguration here, so plain service registration is expected. If you are on Symfony, add a small compiler pass or explicit service arguments; do not expect the package to discover your indexers. On Laravel, bind the indexer in a service provider or use contextual binding — there is no package service provider to lean on either.

Testing. The indexer is pure and trivially unit-testable with plain objects or Doctrine mocks. Assert the exact index array shape — downstream grids depend on those keys — and assert identifier stability across hydration states (new, managed, proxy, detached). 1.0.0 adds static analysis to the package itself, which is useful if you fork it, but it does not type your domain objects; keep asserting the concrete array shape in your own tests.

Gotchas and Tips

  • Version and support. 1.0.0 is the current line: it allows PHP 8, adds static analysis, and drops PHP < 7.1, Symfony 2.x and doctrine/common < 2.9. Constraint it as ^1.0 and check that your PHP, Symfony (if used) and Doctrine versions clear those floors before upgrading. It remains a small utility with effectively no dependents, so be prepared to fork — but target 1.0.0 rather than an abandoned dev branch.
  • Upgrading to 1.0.0. The release notes list only platform/compatibility changes, not API changes, so existing indexers should keep working. Re-run your suite anyway, especially if you were previously on an unconstrained dev version that Composer may have resolved differently.
  • Not a bundle, not a Laravel package. There is no DI extension, no autoconfiguration, no recipes, no service provider. You wire services manually.
  • Interface signatures stay untyped. The release adds static analysis but does not appear to add parameter types to the interface. Do not add parameter types to implementations (getIndex(Product $p)) — that narrows the parameter and triggers a declaration-compatibility error. Return types are safe; parameter types are not unless the interface declares them.
  • Identifier must be a scalar. Composite keys must be encoded (e.g. implode('-', $ids)) and decoded consistently; keep the encoding in one place.
  • Doctrine proxies. get_class($object) returns the proxy subclass. Use Doctrine\Common\Util\ClassUtils::getClass() or Doctrine metadata when keying by class name — doctrine/common >= 2.9 still ships ClassUtils, so this remains the right tool on the new floor.
  • Lazy loading. Indexing relations can trigger N+1 queries. Prefer identifiers from associations ($product->getCategory()->getId() still initializes the proxy in some setups) or fetch-join in the query that feeds the grid.
  • Nulls and types. Decide and document how nulls, DateTime (format? timezone?), booleans and enums appear in the index. Downstream filtering/sorting breaks quietly on inconsistent types. PHP 8 enums in particular need an explicit ->value (or ->name) mapping; they are not scalars.
  • Index key order and stability. Use associative keys and treat the array as a map. If anything consumes it positionally, adding a field is a breaking change.
  • Unsaved/new objects. getId() can be null before flush; either exclude them from indexing or use a synthetic identifier, and make that behaviour explicit.
  • Debugging. When a grid shows blank rows or wrong identifiers, dump getIndex() and getIdentifier() for one object first; the failure is almost always a mismatched key name or a non-scalar identifier, not the grid.
  • Extension point. If you need more than mapping, wrap rather than modify: a decorator implementing the same interface keeps you upgradable and lets you swap in your own implementation when the package is eventually abandoned. The 1.0.0 static-analysis setup also makes a fork less painful — you inherit a checked baseline.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings