lochmueller / seal
SEAL Search - Flexible integration of the Search Engine Abstraction Layer project
Requires
- php: ^8.3
- cmsig/seal: ^0.12
- lochmueller/index: ^2.3
- typo3/cms-core: ^13.4 || ^14.2
- typo3/cms-extbase: ^13.4 || ^14.2
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.84
- phpstan/phpstan: ^2.1
- typo3/testing-framework: ^9.3
Suggests
- cmsig/seal-algolia-adapter: An adapter to support algolia in cmsig/seal search abstraction.
- cmsig/seal-elasticsearch-adapter: An adapter to support elasticsearch in cmsig/seal search abstraction.
- cmsig/seal-loupe-adapter: An adapter to support loupe in cmsig/seal search abstraction.
- cmsig/seal-meilisearch-adapter: An adapter to support meilisearch in cmsig/seal search abstraction.
- cmsig/seal-memory-adapter: An adapter to support to write into memory for cmsig/seal search abstraction.
- cmsig/seal-multi-adapter: An adapter to support to write into multiple other adapters cmsig/seal package.
- cmsig/seal-opensearch-adapter: An adapter to support opensearch in cmsig/seal search abstraction.
- cmsig/seal-read-write-adapter: An adapter to support to split read and write operations for the cmsig/seal package.
- cmsig/seal-redisearch-adapter: An adapter to support RediSearch in cmsig/seal search abstraction.
- cmsig/seal-solr-adapter: An adapter to support apache solr in cmsig/seal search abstraction.
- cmsig/seal-typesense-adapter: An adapter to support typesense in cmsig/seal search abstraction.
- typo3/cms-dashboard: Enables SEAL dashboard widgets for search statistics and index monitoring
Provides
None
Conflicts
None
Replaces
None
README
SEAL Search - Flexible integration of the Search Engine Abstraction Layer (SEAL) project into TYPO3. Different index configurations and multiple possibilities. Use it with TYPO3 or for example with Meilisearch, Solr, Loupe, Typesense - without changing the integration. Have fun.
This extension was funded by the TYPO3 Association: community ideas I / community idea II & first blogpost / second blogpost
Requirements
- PHP 8.3+
- TYPO3 v13.4 or v14.0
- EXT:index
Installation
- Install and configure the EXT:index extension
- Run
composer require lochmueller/seal(note: This extension only works in composer mode) - Optional: Install the right adapter if you want to use a specific engine (see below)
- Load the SiteSet
lochmueller/sealin your site configuration - Configure the search adapter via site configuration (DSN)
- Configure individual search options
Configuration
The extension is configured via the TYPO3 site configuration. The following options are available:
| Option | Default | Description |
|---|---|---|
sealSearchDsn |
typo3:// |
DSN for the search adapter |
sealAutocompleteMinCharacters |
3 |
Minimum characters for autocomplete |
sealItemsPerPage |
10 |
Items per page in search results |
sealHighlighting |
1 |
Highlight the search words in the result list |
sealHighlightingFields |
title,content |
Comma separated index fields that should be highlighted |
Keyword Highlighting
If sealHighlighting is enabled, the configured fields are requested with the SEAL highlighting API
and rendered with the <seal:highlight> ViewHelper:
<seal:highlight item="{item}" field="title"/> <seal:highlight item="{item}" field="content" crop="300"/>
The ViewHelper escapes the indexed value and renders the matches as <mark> elements, so no
unescaped HTML from the index ends up in the frontend. With crop the text is shortened around the
first match. Every field without a match falls back to the raw value of the document.
Search Form, URLs and Caching
The search form is submitted via GET, so the complete search state (search word, tag filters, geo coordinates, page) lives in the URL. Results are bookmarkable and shareable, the back button works without a "resend form?" dialog, and the pagination is able to reproduce the search in the first place.
Three things follow from that and are worth knowing when you override the templates:
-
The form is plain HTML, not
<f:form>.f:formalways renders five__referrer[...]fields plus__trustedProperties; withmethod="get"that HMAC blob would end up in the query string of every result URL. Instead the field names carry the plugin namespace explicitly, e.g.name="{pluginNamespace}[search]".actionandcontrollerare hidden fields, because a browser discards the query string of the action URL on a GET submit. -
The plugin namespace is excluded from the cacheHash in
ext_localconf.php($GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = '^tx_seal_search'). Without it a form submit would hit a missing/invalid cHash. Thesearchaction itself is already registered as non-cacheable. -
Links use
<seal:uri.search>, notaddQueryString.addQueryString="1"only keeps the route arguments of a request (PageLinkBuilder::getQueryArguments()), so it would silently drop the search from page 2 onwards, and"untrusted"would forward every unrelated query parameter.Lochmueller\Seal\Uri\SearchUriBuilderrebuilds the search state instead:<a href="{seal:uri.search(page: 2, section: 'seal-12-results')}">Page 2</a>
Page 1 is omitted from the URI, and empty fields the browser submitted anyway are stripped, so one search always has one canonical URL. Use the same ViewHelper for your own links (sorting, facets) so they keep the current search.
Search statistics are only written for the first result page, otherwise paging through the results would log the same term over and over.
Accessibility (WCAG 2.2)
The shipped Fluid templates are built to pass a WCAG 2.2 AA audit out of the box:
- Search landmark – the form is rendered with
role="search"and anaria-label. - Grouped filters – tag and geo filters use
<fieldset>/<legend>instead of an unassociated<label>(1.3.1). - Unique DOM ids – every id is prefixed with a
sealIdderived from the content element uid, so multiple search plugins on one page keep theirforandaria-describedbyreferences intact (1.3.1 / 4.1.2). - Result semantics – results are an ordered list of
<article>elements, numbered continuously across pages, with a machine readable<time datetime="…">for the index date. - No redundant links – the preview image link is removed from the accessibility tree (technique H2) and the "open" button carries the result title for assistive technology (2.4.4).
- Pagination –
aria-current="page",rel="prev"/rel="next", previous/next links, page numbers with a hidden "Page" prefix and an ellipsis for windowed paginations. - Status messages – the result count and the geolocation status are live regions (4.1.3).
- Redundant entry – search word, radius and determined coordinates are restored from the URL after every submit and survive pagination (WCAG 2.2 – 3.3.7).
- Focus handling – submitting the form and following a pagination link jumps to the
focusable result headline (2.4.3), which has
scroll-margin-topso it is not obscured by sticky headers (WCAG 2.2 – 2.4.11). - Target size –
Resources/Public/Css/Seal.cssguarantees the 24x24 px minimum for pagination and filter controls (WCAG 2.2 – 2.5.8) and ships the.seal-visually-hiddenhelper, so the templates stay accessible without Bootstrap.
All labels are translatable via Resources/Private/Language/locallang.xlf; there is no
hard coded English left in the templates.
DSN Examples
typo3:// # Local TYPO3 database adapter
loupe://var/loupe # Loupe file-based search
meilisearch://127.0.0.1:7700 # Meilisearch server
elasticsearch://127.0.0.1:9200 # Elasticsearch server
typesense://127.0.0.1:8108 # Typesense server
algolia://APP_ID:API_KEY # Algolia cloud service
solr://127.0.0.1:8983 # Apache Solr server
opensearch://127.0.0.1:9200 # OpenSearch server
redisearch://127.0.0.1:6379 # RediSearch
Available Adapters
Install the adapter you need via composer:
| Adapter | Package |
|---|---|
| Memory (Testing) | composer require cmsig/seal-memory-adapter |
| Loupe | composer require cmsig/seal-loupe-adapter (Recommended) |
| Meilisearch | composer require cmsig/seal-meilisearch-adapter |
| Elasticsearch | composer require cmsig/seal-elasticsearch-adapter |
| Typesense | composer require cmsig/seal-typesense-adapter |
| Algolia | composer require cmsig/seal-algolia-adapter |
| Apache Solr | composer require cmsig/seal-solr-adapter |
| OpenSearch | composer require cmsig/seal-opensearch-adapter |
| RediSearch | composer require cmsig/seal-redisearch-adapter |
| Read/Write Split | composer require cmsig/seal-read-write-adapter |
| Multi Adapter | composer require cmsig/seal-multi-adapter |
CLI Commands
# Update the schema in all configured SEAL adapters
vendor/bin/typo3 seal:schema
Extension Structure
| Directory | Description |
|---|---|
Adapter/ |
The SEAL TYPO3 Adapter for local database |
Command/ |
CLI tools for schema management |
Controller/ |
Frontend plugins |
Engine/ |
Factory for engine creation |
Event/ |
PSR-14 events of EXT:seal |
EventListener/ |
Event listeners and connection to EXT:index |
Exception/ |
Custom exceptions |
Filter/ |
Search filter implementations |
Handler/ |
Request handlers (e.g., autocomplete) |
Highlight/ |
Rendering of the keyword highlighting |
Middleware/ |
PSR-15 middleware stack |
Pagination/ |
Fluid pagination based on SEAL Generator |
Schema/ |
Schema structure management |
Uri/ |
URI building that keeps the search state |
ViewHelpers/ |
Fluid ViewHelpers of EXT:seal |
Development
# Install dependencies composer install # Fix code style composer code-fix # Run static analysis (PHPStan level 8) composer code-check # Run unit tests composer code-test
Credits
Thanks to SEAL for the nice idea and TYPO3 Association & TYPO3 Community for sponsoring the extension.