kaliop/ezfindsearchenginebundle
KaliopEzFindSearchEngineBundle integrates the eZ Find (Solr) search engine with eZ Platform, providing Symfony services and configuration to index and query content through eZ Find instead of the default search backend.
This bundle bridges the eZ Publish / eZ Platform SearchService API onto a legacy eZ Find (Solr) backend, so you can write repository-style Query objects and have them translated into eZ Find queries instead of hand-rolling Solr PHP client calls.
Minimal setup:
composer require kaliop/ez-find-search-engine-bundleapp/AppKernel.php (legacy) or config/bundles.php (Symfony 4+ style), after the eZ Find bundle.ez_find / ezpublish.solr.* config) are already in place — the bundle does not own the Solr connection, it reuses eZ Find's.eZ\Publish\API\Repository\SearchService (or the bundle's API repository) and run a LocationQuery / ContentQuery as you normally would.Where to look first: the API/Repository/Values/Content/Query/Criterion and .../SortClause folders — that is the surface area the bundle actually provides. If a criterion isn't there, it isn't supported and you'll need a filter workaround.
First use case — a full-text content search with relevance sorting:
use eZ\Publish\API\Repository\Values\Content\Query;
use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
use Kaliop\EzFindSearchEngineBundle\API\Repository\Values\Content\Query\Criterion\EzFindText;
use Kaliop\EzFindSearchEngineBundle\API\Repository\Values\Content\Query\SortClause\Score;
$query = new Query();
$query->query = new EzFindText('hello world'); // full-text across all indexed fields
$query->filter = new Criterion\ContentTypeIdentifier('article');
$query->sortClauses = [new Score('descending')];
$result = $searchService->findContent($query);
Two ways to express full-text. Criterion\FullText maps to matching the ezf_df_text field specifically. EzFindText (new in 1.4.0) emulates a legacy ezfind search: it runs the text across all indexed content fields. Use EzFindText when you want eZ-Find-parity relevancy; use FullText when you deliberately want to constrain to the data-text field. Because EzFindText fans out across many fields, relevancy scoring is generally better and closer to what editors remember from the legacy search box.
Keep the query member singular. EzFindText must be the only member of $query->query. Everything else goes into $query->filter. This is the key workflow change for people migrating legacy search code:
$query->query = new EzFindText($term); // exactly one
$query->filter = new Criterion\LogicalAnd([
new Criterion\ContentTypeIdentifier(['article', 'news']),
new Criterion\Visibility(Criterion\Visibility::VISIBLE),
]);
Pair with Score sort. Score('descending') is what makes the improved relevancy actually visible — without it Solr returns document order and the benefit of EzFindText is masked.
Build a reusable search factory. Wrap query construction in a small service so controllers stay thin and so you can swap FullText ↔ EzFindText in one place when tuning relevancy:
class ArticleSearch
{
public function __construct(private SearchService $search) {}
public function search(string $term, int $limit = 20): ContentSearchResult
{
$q = new Query();
$q->query = new EzFindText($term);
$q->filter = new Criterion\ContentTypeIdentifier('article');
$q->sortClauses = [new Score('descending'), new Query\SortClause\ContentName()];
$q->limit = $limit;
return $this->search->findContent($q);
}
}
Add a secondary sort clause after Score. Score ties are extremely common on short queries; appending a ContentName or DatePublished sort keeps pagination stable.
Faceting and highlighting. These come from Solr/eZ Find, not from the Query object. Drive them through the eZ Find layer (facet config, SolrFacetFieldQuery-style helpers) while using this bundle for the criterion/sort half. Keep them in the same service so the filter set used for facets and the filter set used for the main query don't drift.
Integration tip for legacy migrations. If you're porting an ezfind controller action, the mechanical translation is: old SearchText → EzFindText, old filter array → $query->filter with LogicalAnd, old sort-by-relevance → Score. Anything that was an "extended attribute filter" becomes a standard Criterion in filter.
EzFindText is exclusive to the query member. Putting a second criterion alongside it, or nesting it inside LogicalAnd in filter, does not give you the same behaviour — the legacy emulation assumes the text criterion drives the Solr q parameter. Move all constraints to filter. This is the single most likely source of "relevancy looks wrong" bug reports after the upgrade.
EzFindText is not a drop-in for FullText in all cases. It searches all indexed fields, which can include metadata, tags, and author strings you may not want matchable. If users report spurious hits on things they didn't type into a body field, fall back to FullText for that specific search, or narrow with field-level boosting in the eZ Find config.
Empty / whitespace terms. Solr will error or return everything depending on config. Guard before constructing the criterion:
if (trim($term) === '') {
return $this->search->findContent($this->emptyQuery());
}
Wildcards and special characters. Solr query syntax (*, ~, :, unescaped quotes) passes through in varying degrees depending on the eZ Find tokenizer. Sanitise user input or explicitly document that advanced syntax is accepted — don't leave it ambiguous.
Score ordering requires the criterion that produces a score. Score alongside a filter-only query (no query/EzFindText) will either error or return a meaningless order. Only add Score when you've supplied a text criterion.
Debugging translation. When a query behaves unexpectedly, dump the generated Solr query from the eZ Find layer rather than guessing from the PHP side. The mismatch between what you wrote and what Solr received is almost always in criterion-to-field mapping, not in your code.
Pagination + Score = unstable pages. As above, add a deterministic tiebreaker sort or you'll see duplicate/missing items between pages. Always verify with limit/offset on a high-cardinality result set after upgrading.
Extension points. New criteria are added by implementing the criterion handler and registering it with the bundle's handler/visitor. If you need a criterion this bundle doesn't ship, check whether it can be expressed as a filter composition first — that avoids a custom handler entirely and keeps you upgrade-safe.
Version pinning. EzFindText only exists from 1.4.0. If your code may run against older installs (multi-project shared library, staged deploys), either constrain "kaliop/ez-find-search-engine-bundle": "^1.4" or feature-detect before use, since the class simply won't autoload on 1.3.x.
How can I help you explore Laravel packages today?