kunstmaan/node-search-bundle
KunstmaanNodeSearchBundle adds Elasticsearch-powered search to Kunstmaan CMS nodes, letting you index and query page content with configurable search services and Symfony integration.
This is a Symfony bundle, not a Laravel package, so "getting started" means getting it booting inside a Symfony kernel first — or, more realistically, reading it as a reference implementation you'll port. It wires kunstmaan/search-bundle (an Elastica/Elasticsearch wrapper) into kunstmaan/node-bundle (a CMS page-tree bundle), so nothing here drops into a Laravel app without adaptation.
Minimal path if you're actually installing it:
composer require kunstmaan/node-search-bundle
Then in app/AppKernel.php:
new Kunstmaan\NodeSearchBundle\KunstmaanNodeSearchBundle(),
new Kunstmaan\SearchBundle\KunstmaanSearchBundle(),
new Kunstmaan\NodeBundle\KunstmaanNodeBundle(),
Point it at an Elasticsearch instance in config.yml (host/port, index name) and confirm the bundle's DependencyInjection/Configuration.php picks up your node-search settings.
Where to look first:
DependencyInjection/ — the config tree tells you every knob that exists; there is no published config file, so this is your source of truth.Elastica client usage) — this is the document-mapping code you'll actually care about.Command/ directory — a reindex console command is usually how you bootstrap existing content.First use case: run the full reindex command against your existing nodes, then call the search service from a controller and render results. That two-step (seed the index → query the index) is the whole package in miniature.
Event-driven incremental indexing. The bundle's core pattern is hooking NodeBundle's node lifecycle events so a save/update/delete re-indexes the corresponding document. In Laravel terms this is Eloquent observers + a queued index write. If you port it, that's the shape: Node::saved → push document, Node::deleted → remove document, wrapped in DB::afterCommit() so you never index a row that rolls back.
Full reindex as a chunked console command. Expect a command that iterates nodes and bulk-indexes them. The portable lesson: never loop index() one document at a time — batch into the Elasticsearch bulk API in chunks of a few hundred, and use an alias swap so you can rebuild without downtime:
// pattern worth stealing regardless of framework
$newIndex = "nodes_{$locale}_" . time();
createIndex($newIndex, $mapping);
bulkIndexInChunks($nodes, $newIndex);
swapAlias("nodes_{$locale}", $newIndex); // atomic
deleteOldIndices("nodes_{$locale}_*");
Per-locale indices. Kunstmaan CMS content is multilingual, so the natural pattern is one index (or at least one document type/field) per locale, with a locale filter applied at query time. Don't try to serve all languages from a single unmapped document — analyzers differ per language.
Query composition. The bundle exposes a search service that builds Elastica queries. Day-to-day use is: build a query with a full-text clause on the title/teaser fields, then add filters (not scored queries) for nodeType, locale, online/published status, and parent/section. Keep filters in filtered/bool.filter so they don't pollute relevance scores.
Integration tip for Laravel devs: if you're evaluating this because you have a Kunstmaan site you're migrating, the reusable asset is the document mapping (which node fields become which ES fields, how the page tree flattens into a searchable document). Copy that mapping into a Searchable trait or a Scout index config, and leave the Symfony plumbing behind.
Dead since 2014. Last release April 2014, 8 stars, zero dependents. Assume PHP 5.4-era syntax, Symfony 2.x service definitions (XML/YAML, not attributes), and an Elastica client pinned to Elasticsearch 0.90/1.x. It will not run against Elasticsearch 6/7/8 without rewriting the query DSL, and modern Symfony won't autoload the bundle metadata.
NOASSERTION license. The repo has no recognizable SPDX license. Before you copy any mapping or indexing code into a commercial Laravel project, resolve the licensing question — this is a real blocker, not a formality.
Hard coupling to KunstmaanNodeBundle's entity model. The indexer assumes specific node/translation entities and their getters. There's no interface to swap in your own model, so "using it standalone" isn't a thing.
Unpublished content leakage. The single easiest bug: forgetting to filter on online/published state, so drafts appear in public search results. Because indexing happens on save (including drafts), the index will contain unpublished nodes. Always filter at query time — and consider a separate index for the admin/editor search.
Stale index on failed writes. If the event listener indexes synchronously and Elasticsearch is down, the save fails or the document silently drifts. Wrap indexing in try/catch with a queue/log, and keep the reindex command idempotent so you can always recover.
Debugging workflow. Log the serialized query JSON before dispatch (it's the only practical way to debug Elastica query-building), then replay it with curl against _search?explain=true. Inspect indices with _cat/indices and _cat/aliases — if search "returns nothing," it's almost always a missing/misnamed index or an alias pointing at a deleted one.
Extension points that do exist: a custom event subscriber on node events (to add/remove fields before indexing), overriding the search service definition in your container, and decorators on the search service to post-process results. If you fork it for modernization, those three seams are where you'd insert an adapter layer for Elasticsearch 7+.
Bottom line for a Laravel team: score it as documentation, not a dependency. Read the mapping and the event-driven indexing flow, then implement it with Scout or a thin Elastica client of your own — you'll spend less time than fighting a decade-old Symfony 2 bundle.
How can I help you explore Laravel packages today?