asconsulting / contao-zyppy-search
Live page search with previews and teasers from Zyppy Page.
Package info
github.com/asconsulting/contao-zyppy-search
Type:contao-bundle
pkg:composer/asconsulting/contao-zyppy-search
Requires
- php: ^8.1
- asconsulting/contao-zyppy-page: ^5.0
- contao/core-bundle: >=5.3
- symfony/rate-limiter: >=6.4
Requires (Dev)
- contao/manager-plugin: ^2.0
- contao/test-case: >=5.3
- phpunit/phpunit: ^9.6 || ^10.5 || ^11.0 || ^12.0
Suggests
- asconsulting/contao-zyppy-popup: Adds the popupClear option that clears the results container each time a popup opens.
- contao/news-bundle: Enables the per-page News Reader override (tl_page.zyppy_news), which swaps the page teaser/image for the matching news article's.
Provides
None
Conflicts
- contao/manager-plugin: <2.0 || >=3.0
Replaces
None
README
Live page search for Contao 5. A visitor types into the search box and the results appear underneath as they type, without a page reload — each one with the page's image and teaser from Zyppy Page alongside the usual highlighted excerpt. A page flagged as a news reader shows the matching news article's teaser and image instead.
Underneath it is Contao's own site search: the same tl_search index, the
same query options and the same per-visitor access rules. What this bundle
adds is the live transport and the richer result.
Requirements
- PHP 8.1 or newer
- Contao 5.3 or newer, with no upper bound. The Zyppy Suite supports the LTS releases and is tested against the current non-LTS release as well: the test suite passes on core-bundle 5.3.0, 5.7.13 and 6.0.2 (last run 2026-10-07).
asconsulting/contao-zyppy-page5.x, installed automatically as a dependency. It provides thepage_imageandpage_teaserfields the results are built from.- Optional:
contao/news-bundlefor the News Reader override, andasconsulting/contao-zyppy-popupfor the Clear on Popup option. - No jQuery. The client script is plain JavaScript.
Installation
composer require asconsulting/contao-zyppy-search:^5.0
Then run the database migration. Contao Manager does this on install and on
update; on the command line it is vendor/bin/contao-console contao:migrate.
It adds the module settings to tl_module and the News Reader flag to
tl_page.
The module searches Contao's search index; it does not build it. Index the site as usual (crawl it, or let the front end indexer run) before expecting results.
Usage
Creating the module
Under Themes → Front end modules, create a module of type Zyppy Search (in the Applications group) and place it on a page like any other module. Its settings are the ones Contao's own search module has, plus three formatting options:
| Setting | Effect |
|---|---|
Query type (queryType), fuzzy search (fuzzy), context range (contextLength), minimum keyword length (minKeywordLength), items per page (perPage) |
As in Contao's search module. The minimum keyword length also decides how many characters the visitor has to type before the live search fires (3 if unset). |
Form layout (searchType) |
Advanced adds the "match all / match any" choice to the form; the live search honours it too. |
Reference pages (pages) |
Restricts the search to these pages and their subtrees. Left empty, the module searches the whole site the request host resolves to. |
Redirect page (jumpTo) |
The page the form submits to when JavaScript is off. Left empty, the form submits back to its own page. |
Search template (searchTpl) |
The template for one result. search_zyppy by default. |
Image size (imgSize) |
The image size used for the result image. |
| Format Page Teaser / Page Teaser Limit | Strip tags from the page teaser and optionally cut it to this many characters (0 for unlimited). |
| Format Page Description / Page Description Limit | The same for the page's meta description. |
| Format News Teaser / News Teaser Limit | The same for the news teaser used by the News Reader override. |
Clear on Popup (popupClear) |
Only present with contao-zyppy-popup installed: empties the results when a pop-up opens. |
What the visitor gets
- Typing. 300 ms after the last keystroke, once the minimum length is reached, the results are fetched and dropped into the results area. A new keystroke cancels a request still in flight; clearing the box clears the results. Two search modules on one page do not interfere with each other.
- Without JavaScript. The form submits as an ordinary GET and the first page of results is rendered on the server. The pager is real links, so it works the same way. Both paths render through one template, so the markup is identical.
- Each result shows the page title linked to the page, its relevance, the
page image (if
page_imageis set, at the chosen image size), the page teaser, and the highlighted context excerpt. - Protected pages appear only for visitors allowed to see them, by the same rule core applies. A module itself restricted to member groups answers nobody else, on either path.
The News Reader override
On a news reader page, tick News Reader (zyppy_news) in the page's
Meta legend; the box is offered on regular pages only. When a result points
at a news article under that page, the article's teaser and image replace the
page's own. Only published articles inside their publication window are used.
Requires contao/news-bundle.
The live endpoint
The script calls GET /_zyppy/search/{moduleId} with keywords, an optional
query_type (and or or) and an optional page, and receives JSON: the
result window (count, from, to, page, totalPages) plus one html
string that is dropped into the results area as is.
- Rate limited: 30 requests per minute per client IP, sliding window. Over
the limit the endpoint answers 429 with a
Retry-Afterheader. - Responses are
private, no-storeand carryX-Robots-Tag: noindex; results are member scoped and must never sit in a shared cache. - 404 for an unknown module id or an out-of-range page, 403 for a module the visitor may not see.
Templates
| Template | Role |
|---|---|
zyppy_search (frontend_module/zyppy_search) |
The module shell: form and results area. The module's default template. |
search_zyppy |
One result. Selectable per module through Search template, which is the one an editor has a reason to swap. |
search_zyppy_results |
Everything inside the results area: header, keyword hint, the result rows, pager. Not selectable in the back end; override it by shipping a project template of the same name. |
mod_search_zyppy |
The legacy shell, used only when it is picked as a module's Custom template. Kept so sites that chose it keep rendering. |
The wrapper class mod_zyppy_search is load bearing: the script selects it,
and contao-zyppy-popup relies on it for Clear on Popup. The script is
published at bundles/search/js/search.js and is emitted by the module
template itself, so nothing has to be added to the page layout.
Migration from legacy version
This package replaces the legacy package asconsulting/zyppy_search (namespace
ZyppySearch\). The old name does not resolve any more.
The four legacy Zyppy packages — page, classes, popup and search — have to be
upgraded together, in one composer update: old and new packages register
the same DCA fields and module types, so a site that briefly has both installed
double-registers. The walkthrough for the whole suite, including the order of
operations and a verification checklist, is UPGRADING.md in the
contao-zyppy-page repository. The part specific to this package:
- Columns.
tl_pageis unchanged (zyppy_news).tl_modulekeeps the six format/limit fields and dropsajaxTplanddisableAjax, which no code ever read. Accept the DROP whencontao:migrateoffers it; nothing to carry over. - The module type key
zyppy_searchis unchanged. Existing module rows keep resolving; there is notl_modulemigration to run. - Templates.
mod_search_zyppyandsearch_zyppymoved from.html5to.html.twigunder the same names;search_zyppy_resultsandzyppy_searchare new. A site override of either old template must be ported to Twig and the.html5copy deleted — on Contao 5 the.html5copy silently keeps winning; on Contao 6 it breaks. - If a module has a Custom template set, confirm it still resolves after the upgrade before calling the site done.
- The transport changed. The live search is a JSON route with a rate
limiter now, instead of a page render that stopped halfway. That is
behaviour, not data — nothing to migrate, but exercise the search box once.
The
customizeSearchhook is no longer called. - Results on news reader pages change visibly. The legacy module computed the news teaser and image on every result and rendered neither; 5.0.0 shows them.
contao-zyppy-pageis now a hard requirement rather than a suggestion.
Development
composer install
vendor/bin/phpunit --no-coverage
Two things to know before changing the code:
PageScopeResolveris a security boundary. The pages a module may search come from its own settings or from the request host — never from a request parameter. Adding one would let any visitor widen their own search scope.ResultRendereris the one renderer for both the server-rendered first page and every live update. Two renderers would make Search template apply to the first paint and silently not to the updates after it.
TODO.md carries the original audit, the modernization steps and what has been
verified where.
Licence
Licensed under the GNU Affero General Public License v3.0 only (AGPL-3.0-only). See LICENSE. Copyright Andrew Stevens.