nramos/search-indexer
A Laravel package for adding search indexing to Eloquent models. Define indexable data, keep indexes synchronized with model events, and run flexible search queries through a simple, configurable API.
The package is a plain Composer library (not a Laravel package), so the first step is simply:
composer require nramos/search-indexer
Because it targets Doctrine entities rather than Eloquent models, it drops naturally into a Symfony app or a Laravel app using laravel-doctrine/orm. If you are on stock Eloquent you'll get little from it — use Laravel Scout instead.
Where to look first, in order:
src/ top level and README.md — with 3 stars and 0 dependents, the source is the documentation. Read the interfaces (IndexerInterface, driver/engine contracts) before the README examples; the extension contract tells you more about intent than the usage snippet does.Minimal first use case: index a single, small, read-mostly entity (a Product or Article) with only scalar fields, run the package's indexing command, and hit Meilisearch directly (curl or the Meilisearch dashboard) to confirm documents exist before wiring up search UI. Verify the round-trip end to end on one entity before touching a second.
Attribute/interface marking per entity. Expect to opt entities in individually — either by PHP attribute or by implementing an indexer contract. Keep the marked entity a plain data object; anything requiring joins should be denormalized into the indexed document rather than traversed at index time.
Doctrine lifecycle wiring. The core pattern is a Doctrine event subscriber on postPersist/postUpdate/postRemove (or postFlush for batched writes) that dispatches to the indexer. Confirm from source whether the package registers this automatically or whether you must hook it yourself — this is the single biggest determinant of how much glue you write.
Indexer-per-entity-type. Define one indexer per entity rather than a generic one. Each indexer owns:
toSearchableArray-style method),Custom engine via the driver contract. If you need Typesense/Elasticsearch/OpenSearch, implement the driver interface and register it. This is the package's main selling point — lean on it rather than forking. Keep the driver thin: transform Indexer → document[] into engine calls, and let the indexer own the mapping.
Batch/backfill command. For anything beyond a demo, run a console command that pages through entities in chunks (->toIterable() / ->batch()) and pushes to Meilisearch. Full reindex after every schema change is the normal workflow; treat the index as disposable.
Search → hydrate pattern. The engine returns IDs + highlights; hydrate through Doctrine by ID. Do not attempt to reconstruct entities from Meilisearch documents — you lose referential integrity and get stale data.
postPersist vs postFlush. postPersist fires per entity inside the unit of work; a loop inserting 1,000 rows will fire 1,000 index calls. For bulk work either switch to a postFlush collector or use the batch command and disable auto-indexing for that operation.filterableAttributes, sortableAttributes, and searchableAttributes are index settings, not per-document hints. If filtering/sorting silently does nothing, you almost certainly didn't declare the attribute before indexing.composer.json, and wrap calls behind your own thin service so you can swap engines without touching domain code. Read the changelog before composer update.config/ publishing conventions. Budget time for a provider, a config wrapper, and queueing index writes — don't index synchronously in request handlers.taskUid that you never awaited? Meilisearch indexing is asynchronous; the HTTP 200 means “queued,” not “indexed.”NullIndexer in tests and in local env. Indexing during PHPUnit runs is a common source of flaky, slow tests.How can I help you explore Laravel packages today?