Routing
Options
Skeleton routes can have the following options
config: required: define the symfony routing config (path, defaults, requirements)template_static: optional: define a template pathtemplate_source: optional: define a property path in the document received from the route query. example [template]query: optional: search a document, if not found 404.index_regex: optional: define an index regex for executing the query
The following route demonstrates the power of skeleton routes. Inside template_static|query|index_regex options we can replace by route params, pattern %param%.
home:
config:
path: '{_locale}/{alias}/page/{id}'
defaults: { _locale: 'nl' }
requirements: { _locale: 'nl|fr', alias: 'snapshot1|snapshot2' }
template_static: template/homepage_%alias%.html.twig
query: '{"query":{"bool":{"must":[{"term":{"_contenttype":{"value":"page"}}},{"term":{"_id":{"value":"%id%"}}}]}},"size":1}'
index_regex: demo_ma_%alias%Config Defaults
| Name | value | Description |
|---|---|---|
| _profiler | true/false | You can disable the profiler for a specific route, by setting _profiler to false. |
| _authenticated | true/false | The AuthenticatedListener will throw an AccessDeniedException if the user is not fully authenticated. See security |
Controllers
Redirect controller
A route can be defined in order to redirect the request to another url. An easy approach is by using the redirect Symfony controller:
favicon_ico:
config:
path: /favicon.ico
controller: 'Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction'
defaults: { permanent: true, path: '/bundles/assets/static/icon64.png' }But if you need logic to specify the redirect url you may use the emsch redirect controller's function:
favicon_ico:
config:
path: /favicon.ico
controller: 'emsch.controller.router::redirect'
template_static: template/ems/redirect_favicon.json.twigAnd in the redirect_favicon.json.twig template:
{% apply spaceless %}
{% do emsch_assets_version('240c99f842c118a733f14420bf40e320bdb500b9') %}
{{ {'url': asset('static/favicon-96x96.png', 'emsch'), 'status': 301 }|json_encode|raw }}
{% endapply %}The template's response should be a JSON containing those optional parameters:
path: path for returning BinaryFileResponseurl: the target url to redirect to, required if path is not definedstatus: the HTTP return's code. Default value: 302message: A 404 message. Default value 'Page not found'headers: array for defining the response headers
If the url parameter is not defined, the controller will throw a 404 with the message parameter.
Instead of redirecting via an HTTP redirect response you can also directly return an assets. To do so, instead of giving a path to redirect to, give a path to a file:
{% extends '@EMSCH/template/variables.twig' %}
{%- block request %}
{% apply spaceless %}
{{ {
path: emsch_asset('img/head/icon.png', {
_config_type: 'image',
_width: 128,
_height: 128,
_quality: 0,
_get_file_path: true,
}),
}|json_encode|raw }}
{% endapply %}
{% endblock request -%}In this previous example we assume that a call to the emsch_assets_version function has been made in the template/variables.twig template.
This controller can also be used to redirected to another controller (as a subrequest). In this example we internally redirect a route into the FileController in order to exploit to range headers for a media file content type.
emsch_media_file:
config:
path: '/media-files{path}'
requirements: { path: .+ }
controller: 'emsch.controller.router::redirect'
query: '{"query":{"bool":{"must":[{"terms":{"_contenttype":["media_file"]}},{"terms":{"media_path":["%path%"]}}]}},"size":1}'{%- block request %}
{% apply spaceless %}
{{ {
controller: 'EMS\\CommonBundle\\Controller\\FileController::resolveAsset',
path: {
fileField: source.media_file,
},
}|json_encode|raw }}
{% endapply %}
{% endblock request -%}This redirect may take 3 parameters:
controller: string identifying the controller where the request must be redirected. This parameter is mandatory.path: associative array containing the named parameters to pass to the controller's method. Default value[].query: associative array containing the non-mandatory parameters (such those passed via the request to the controller). Default value[].
Redirect a host
When you have a domain fqdn.tld you may want that the website answers to those both FQDNs fqdn.tld and www.fqdn.tld. But, for SEO reasons, you also would like to redirect fqdn.tld requests to www.fqdn.tld. Use the following recipe:
In routes.yaml
redirect_hosts:
config:
path: '{path}'
host: 'fqdn.tld'
requirements: { path: '.*' }
controller: 'emsch.controller.router::redirect'
template_static: template/redirects/hosts.json.twigIn template/redirects/hosts.json.twig
{%- block request %}
{% apply spaceless %}
{{ {
url: 'https://www.fqdn.tld/' ~ app.request.get('path', '/'),
status: 301,
}|json_encode|raw }}
{% endapply %}
{% endblock request -%}Search controller
See the search documentation fo more information.
I.e.:
emsch_search:
config:
path: { en: search, fr: chercher, nl: zoeken, de: suche }
defaults: {
search_config:{
"types": ["page", "publication", "slideshow"],
"fields": ["_all"],
"sizes": [10],
"sorts": {
"recent": {"field": "published_date", "order": "desc", "unmapped_type": "date", "missing": "_last"}
}
}
}
controller: 'emsch.controller.search::handle'Pdf controller
For enabling pdf generation use the emsch.controller.pdf controller
{
"path": "/{_locale}/example-pdf",
"controller": "emsch.controller.pdf",
"requirements": {
"_locale": "fr|nl"
}
}In Twig you can set/override the pdf options with custom meta tags in the head section
{%- set siteHashAssets = include('@EMSCH/template/asset_hash.twig')|trim -%}
<head>
<title>Title</title>
<meta name="pdf:filename" content="example.pdf" />
<meta name="pdf:attachment" content="true" />
<meta name="pdf:compress" content="true" />
<meta name="pdf:html5Parsing" content="true" />
<meta name="pdf:orientation" content="portrait" />
<meta name="pdf:size" content="a4" />
<body>
<h1>Example export</h1>
<img src="{{ ems_file_from_archive(siteHashAssets, "img/logo.svg") }}" width="150" alt="{{ 'site.name'|trans }}">
</body>
</head>!> For images, you should use ems_file_from_archive, especially when assets are loaded without a saveDir, which will be the default behaviour in version 7.x.
Spreadsheet controller
For enabling spreadsheet generation use the emsch.controller.spreadsheet controller
test_xlsx:
config:
path: /example-spreadsheet
controller: 'emsch.controller.spreadsheet'
template_static: template/test/spreadsheet.json.twig
order: 4Add style on Cell are available See on EMSCommonBundle documentation
Example writer xlsx
{% set config = {
"filename": "example",
"disposition": "attachment",
"writer": "xlsx",
"sheets": [
{ "name": "Sheet 1", "rows": [ ["A1", "A2"], ["B1", "B2"] ] },
{ "name": "Sheet 2", "rows": [ ["A1", "A2"], ["B1", "B2"] ] },
]
} %}
{{- config|json_encode|raw -}}Example writer csv
{% set config = {
"filename": "example",
"disposition": "attachment",
"writer": "csv",
"csv_separator": ",",
"sheets": [
{ "rows": [ ["A1", "A2"], ["B1", "B2"], ["C1", "C2"] ] }
]
} %}
{{- config|json_encode|raw -}}Asset controller
Routes can also return an assets, generated by a template containing json.
example_asset:
config:
path: /example-asset/{filename}
controller: 'emsch.controller.router::asset'
template_static: template/example_asset.json.twig{% set assetConfig = {
"hash": "c3499c2729730a7f807efb8676a92dcb6f8a3f8f",
"config": {
"_mime_type": "application/pdf",
"_disposition": "inline"
},
"filename": "demo.pdf",
"headers": {
"X-Robots-Tag": "noindex"
},
"immutable": false
} %}
{{- assetConfig|json_encode|raw -}}hash: Asset's hashconfig: Config's hash or config array (see common's processor config)filename: File nameheaders: Associative with response headers.immutable: optional for defining if the asset is immutable [devault value = false]
ElasticSearch Controller
This controller is for proxy an elasticSearch index and scrolling. Ideal for using as source argument in the ems:indexes:synchronize command.
api_index:
config:
path: '/api/{index}/{path}'
method: [GET]
requirements: { path: .*, index: 'demo_preview|demo_live' }
controller: 'emsch.controller.elasticsearch::index'
template_static: template/api.twig
api_scroll:
config:
path: '/api/_search/scroll'
method: [GET, DELETE]
requirements: { path: .* }
controller: 'emsch.controller.elasticsearch::scroll'
template_static: template/api.twigDefining a template_static is optional, but can be used for handling authentication. The two methods index and scroll will first try to render the block named preRequest. In the following example we secure the api by checking if the authentication token exists in the APP_API_TOKENS env variable.
{%- block preRequest -%}
{%- set tokens = app.request.server.all['APP_API_TOKENS']|default('[]')|ems_json_decode -%}
{%- set authToken = app.request.headers.get('Authorization', '')|u.trimPrefix('Bearer ')|format -%}
{%- if authToken not in tokens -%}{%- do emsch_http_error(403) -%}{%- endif -%}
{%- endblock preRequest -%}EMSCH cache (sub-request)
For routes that not return a streamable response we can enable caching that is generated in a subRequest. The pdf controller already has support for streams and can fallback to response when using _emsch_cache.
pdf_example:
config:
path: '/my-pdf-example/{_locale}/{id}/{timestamp}'
requirements: { _locale: fr|nl, id: .+, timestamp: .+ }
defaults: { _emsch_cache: { key: 'pdf_example_%_locale%_%id%_%timestamp%', limit: 300 } }
controller: emsch.controller.pdf
query: '{"query":{"bool":{"must":[{"term":{"_contenttype":{"value":"page"}}},{"term":{"id":{"value":"%id%"}}}]}}}'
template_static: template/my-pdf-example.html.twigReturn HTTP codes:
- 201: On the first request when nothing is cached, this means the sub-request is started
- 202: If the sub-request is still running
- 200: The sub-request was finished and the response comes from the cache
- 500: An exception has occurred and this is now in cache. Check the error logs.
- Max memory limit reached?
- Max execution limit reached, you can increase this on the route.
For now everything is cached using the symfony cache, this means if we restart the server the cache is cleared. The timestamp in the route can be the max _finalization time of your content types, this way the cache will not be used if the content has changed.
This setup only works with php-fpm (no windows) because we continue the process after the response is finished (onKernelTerminate).
Internally, the HttpKernel makes uses of the fastcgi_finish_request PHP function. This means that for now, only the PHP FPM server API can send a response to the client while the server's PHP process still performs some tasks.
With all other server APIs, listeners to
kernel.terminateare still executed, but the response is not sent to the client until they are all completed.
Route to assets in archive
With this controller you can specify routes to files in zip archives.
If the route is not immutable (does not contain the archive hash) you must specify the maxAge argument (by default it's set to one week).
emsch_demo_asset_in_archive:
config:
path: '/assets_in_archive/{path}'
requirements: { path: .* }
defaults: { hash: 253b903b1fb3ac30975ae9844a0352a65cdcfa3d, maxAge: 3600 }
controller: 'EMS\CommonBundle\Controller\FileController::assetInArchive'HTTP Cache Controller
This controller allows you to interact with the configured reverse proxy (e.g. Varnish) that serve your skeleton.
Handle ElasticMS webhooks event
This controller's method will handle regular ElasticMS webhooks event to invalidate caches base on the event. Depending the event, part of all of the caches will be purged.
Example of route:
emsch_admin_webhook:
config:
path: '/_admin_webhook'
method: [POST]
host: 'localhost'
controller: 'emsch.controller.http_cache::adminWebhook'In order to avoid unnecessary exposition you should defined a host in that route that is available from the admin but not from the Internet.
