Templating
==========
Site API objects are used directly in the templates. Below you will find examples for the most
common use cases. Objects are documented in more detail on :doc:`Objects reference ` documentation page.
.. note::
If you are using PHPStorm, you can type hint Site API Content and Location objects by adding the
following snippet to your template:
.. code-block:: twig
{# content \Netgen\IbexaSiteApi\API\Values\Content #}
{# location \Netgen\IbexaSiteApi\API\Values\Location #}
With that, you will get autocompletion and Cmd/Ctrl-click navigation through the Twig code.
**Content on this page:**
.. contents::
:depth: 1
:local:
Content rendering
-----------------
Site API provides four Twig functions for content rendering:
- ``ng_view_content`` and ``ng_ibexa_view_content``
These two functions provide a way to render Content view without executing a subrequest. Because
of profiling that is active in debug mode, having a lots of subrequests on a page can
significantly affect performance and memory consumption. Since for a large part of use cases
it's not necessary to render Content views through a subrequest, having an alternative way to
render them can improve performance and hence developer's experience.
Both functions support custom controllers. ``ng_view_content`` can be used for views defined in
Site API view configuration under ``ng_content_view`` configuration node, and
``ng_ibexa_view_content`` can be used for views defined in Ibexa CMS view configuration under
``content_view`` configuration node.
.. note::
The functions are not a complete replacement for rendering Content views, since it does not
dispatch MVC events from Ibexa CMS Core, like ``MVCEvents::PRE_CONTENT_VIEW``. For that
reason it's safe to use only for those views that don't depend on them. However, that should
be the case for most of them.
Depending on the use case, you might be able to replace usage of MVC events with
``ViewEvents`` from Ibexa CMS Core, which **are** dispatched by the functions.
The functions accept four parameters, similar as parameters available for ``ibexa_content::viewAction``
controller action:
1. **required** Content or Location object
2. **required** string view identifier (e.g. ``line``, ``block``)
3. **optional** array of parameters, with keys as parameter names and corresponding values as parameter values
Parameters defined through this array will be available as Request attributes and can be
passed as arguments to the controller action if defined there. Also, parameter with name
``params`` is recognized as an array of custom view parameters and will be available in the
view object and in the rendered template.
4. **optional** boolean value indicating whether to render the template in the configured
layout, by default ``false``
Example usage of ``ng_view_content``:
.. code-block:: twig
{{ ng_view_content(
content,
'line',
{
'foo': 'bar',
'params': {
'custom': 'view param'
}
},
false
) }}
The example above is intended to replace the following Content view render through the subrequest:
.. code-block:: twig
{{ render(
controller(
'ng_content::viewAction', {
'contentId': content.id,
'viewType': 'line',
'layout': false,
'foo': 'bar',
'params': {
'custom': 'view param'
}
}
)
) }}
Example usage of ``ng_ibexa_view_content``:
.. code-block:: twig
{{ ng_ibexa_view_content(
content,
'line',
{
'foo': 'bar',
'params': {
'custom': 'view param'
}
},
false
) }}
The example above is intended to replace the following Content view render through the subrequest:
.. code-block:: twig
{{ render(
controller(
'ibexa_content::viewAction', {
'contentId': content.id,
'viewType': 'line',
'layout': false,
'foo': 'bar',
'params': {
'custom': 'view param'
}
}
)
) }}
- ``ng_render_field``
Similar to ``ibexa_render_field`` from Ibexa CMS, this function is used to render the Content's
field using the configured template:
.. code-block:: twig
{{ ng_render_field( content.field.body ) }}
- ``ng_image_alias``
Similar to ``ibexa_image_alias`` from Ibexa CMS, this function provides access to the image
variation of a ``ezimage`` type field:
.. code-block:: twig
``ng_render_field`` and ``ng_image_alias`` are shown in more detail in the examples below. There are
four other Twig functions, ``ng_query``, ``ng_sudo_query``, ``ng_raw_query`` and ``ng_sudo_raw_query``. These are used
with Query Types and are documented separately on :doc:`Query Types reference` documentation page.
Basic usage
-----------
- **Accessing Location's Content object**
Content is available in the Location's property ``content``:
.. code-block:: twig
{{ set content = location.content }}
- **Displaying the name of a Content**
Content's name is available in the ``name`` property:
.. code-block:: twig
Content's name: {{ content.name }}
- **Linking to a Location**
Linking can be done using the ``ibexa_path()`` Twig function, same as before.
.. code-block:: twig
{{ location.content.name }}
:ref:`Location` object also contains :ref:`Url` and :ref:`Path` objects,
which enable generating links without using helper functions
.. code-block:: twig
{{ location.content.name }}
{{ location.content.name }}
- **Linking to a Content**
Linking to Content will create a link to Content's main Location.
It can be done using the ``ibexa_path()`` Twig function, same as before.
.. code-block:: twig
{{ content.name }}
:ref:`Content` object also contains :ref:`Url` and :ref:`Path` objects,
which enable generating links without using helper functions
.. code-block:: twig
{{ content.name }}
{{ content.name }}
Working with Content fields
---------------------------
- **Accessing a Content Field**
.. note::
Content's fields are lazy-loaded, which means they will be transparently loaded only at the
point you access them.
The most convenient way to access a Content field in Twig is from the ``fields`` property on the
Content object, using the dot notation:
.. code-block:: twig
{% set title_field = content.fields.title %}
Alternatively, you can do the same using the array notation:
.. code-block:: twig
{% set title_field = content.fields['title'] %}
Or by calling ``getField()`` method on the Content object, also available as ``field()`` in Twig,
which requires Field identifier as the argument:
.. code-block:: twig
{% set title_field = content.field('title') %}
- **Checking if the Field exists**
Checking if the field exists can be done with ``hasField()`` method on the Content object:
.. code-block:: twig
{% if content.hasField('title') %}
Content has a 'title' field
{% endif %}
- **Choosing first existing and non-empty Field**
You can choose first existing and non-empty Field from the multiple Field identifiers with
``getFirstNonEmptyField()`` method on the Content object, also available as ``getFirstNonEmptyField``
in Twig:
.. code-block:: twig
{{ ng_render_field(content.getFirstNonEmptyField('title', 'short_title', 'name')) }}
.. note::
If no Fields are found on the Content object, a :ref:`surrogate type field`
will be returned. If all found Fields are empty, the first found Field will be returned.
.. note::
If returned Field can be of one of multiple FieldTypes (if identifiers for multiple FieldTypes
are given), accessing the value directly would be ambiguous. In that case it's best to use this
method together with ``ng_render_field`` Twig function, as is shown in the example above.
.. note::
At least one Field identifier must be given to this method, but any number of additional
identifiers can be provided.
- **Displaying Field's metadata**
Field object aggregates some data from the FieldDefinition:
.. code-block:: twig
{% set title_field = content.fields.title %}
Field name: {{ title_field.name }}
Field description: {{ title_field.description }}
FieldType identifier: {{ title_field.fieldTypeIdentifier }}
- **Rendering the field using the configured template**
To render a field in vanilla Ibexa CMS you would use
`ibexa_render_field `_ function, which
does that using the configured template block. For the same purpose and using the same templates, Site API provides
its own function ``ng_render_field``. It has two parameters:
1. **required** Field object
2. **optional** hash of parameters, by default an empty array ``[]``
This parameter is exactly the same as you would use with ``ibexa_render_field``. The only
exception is the ``lang`` parameter, used to override the language of the rendered field, which
is not used by the ``ng_render_field``.
Basic usage:
.. code-block:: twig
{{ ng_render_field( content.fields.title ) }}
Using the second parameter to override the default template block:
.. code-block:: twig
{{
ng_render_field(
content.fields.title,
{ 'template': '@AcmeTest/field/my_field_template.html.twig' }
)
}}
- **Checking if the Field's value is empty**
This is done by calling ``isEmpty()`` method on the Field object, also available as
``empty()`` or just ``empty`` in Twig:
.. code-block:: twig
{% if content.fields.title.empty %}
Title is empty
{% else %}
{{ ng_render_field( content.fields.title ) }}
{% endif %}
- **Accessing the Field's value**
Typically you would render the field using ``ng_render_field`` Twig function, but if needed you
can also access field's value directly. Value format varies by the FieldType, so you'll need to
know about the type of the Field whose value you're accessing. You can find out more about that on
the official `FieldType reference page `_
or even looking at the value's code.
Here we'll assume ``title`` field is of the FieldType ``ezstring``. Latest code for that
FieldType's value can be found `here `_.
.. code-block:: twig
Value of the title field is: '{{ content.fields.title.value.text }}'
- **Rendering the image field**
Typically for this you would use the built-in template through ``ng_render_field`` function, but
you can also do it manually if needed:
.. code-block:: twig
{% set image = content.fields.image %}
{% if not image.empty %}
{% endif %}
Traversing the Content model
----------------------------
Content Locations
~~~~~~~~~~~~~~~~~
- **Accessing the main Location of a Content**
.. code-block:: twig
{% set main_location = content.mainLocation %}
- **Listing Content's Locations**
This is done by calling the method ``getLocations()``, also available as ``locations()`` in
Twig. It returns an array of Locations sorted by the path string (e.g. ``/1/2/191/300/``) and
optionally accepts maximum number of items returned (by default ``25``).
.. code-block:: twig
{% set locations = content.locations(10) %}
First 10 Content's Locations:
- **Paginating through Content's Locations**
This is done by calling the method ``filterLocations()``, which returns a ``Pagerfanta``
instance with Locations sorted by the path string (e.g. ``/1/2/191/300/``) and accepts two
optional parameters:
1. **optional** maximum number of items per page, by default ``25``
2. **optional** current page, by default ``1``
.. code-block:: twig
{% set locations = content.filterLocations(10, 2) %}
Content's Location, page {{ locations.currentPage }}
Total: {{ locations.nbResults }} items
{{ pagerfanta( locations, 'twitter_bootstrap' ) }}
Content Field relations
~~~~~~~~~~~~~~~~~~~~~~~
- **Accessing a single field relation**
This is done by calling the method ``getFieldRelation()``, also available as
``fieldRelation()`` in Twig. It has one required parameter, which is the identifier of the
relation field. In our example, the relation field's identifier is ``related_article``.
.. code-block:: twig
{% set related_content = content.fieldRelation('related_article') %}
{% if related_content is not empty %}
{{ related_content.name }}
{% else %}
There are two possibilities:
- Relation field 'related_article' is empty
- You don't have a permission to read the related Content
In any case, you can't render the related Content!
{% endif %}
.. note::
If relation field contains multiple relations, the first one will be returned. If it doesn't
contain relations or you don't have the access to read the related Content, the method will
return ``null``. Make sure to check if that's the case.
- **Accessing all field relations**
This is done by calling the method ``getFieldRelations()``, also available as
``fieldRelations()`` in Twig. It returns an array of Content items and has two parameters:
1. **required** identifier of the relation field
2. **optional** maximum number of items returned, by default ``25``
.. code-block:: twig
{% set related_articles = content.fieldRelations('related_articles', 10) %}
- **Filtering through field relations**
This is done by calling the method ``filterFieldRelations()``, which returns a Pagerfanta
instance and has four parameters:
1. **required** identifier of the relation field
2. **optional** array of ContentType identifiers that will be used to filter the result, by
default an empty array ``[]``
3. **optional** maximum number of items per page, by default ``25``
4. **optional** current page, by default ``1``
.. code-block:: twig
{% set articles = content.filterFieldRelations('related_items', ['article'], 10, 1) %}
{{ pagerfanta( events, 'twitter_bootstrap' ) }}
Content Field reverse relations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- **Accessing all field relations**
This is done by calling the method ``getReverseFieldRelations()``, also available as
``reverseFieldRelations()`` in Twig. It returns an array of Content items and has two parameters:
1. **required** identifier of the reverse relation field
2. **optional** maximum number of items returned, by default ``25``
.. code-block:: twig
{% set reverse_related_articles = content.reverseFieldRelations('related_articles', 10) %}
- **Filtering through reverse field relations**
This is done by calling the method ``filterReverseFieldRelations()``, which returns a Pagerfanta
instance and has four parameters:
1. **required** identifier of the reverse relation field
2. **optional** array of ContentType identifiers that will be used to filter the result, by
default an empty array ``[]``
3. **optional** maximum number of items per page, by default ``25``
4. **optional** current page, by default ``1``
.. code-block:: twig
{% set articles = content.filterReverseFieldRelations('related_items', ['article'], 10, 1) %}
{{ pagerfanta( events, 'twitter_bootstrap' ) }}
Location children
~~~~~~~~~~~~~~~~~
- **Listing Location's children**
This is done by calling the method ``getChildren()``, also available as ``children()`` in
Twig. It returns an array of children Locations and optionally accepts maximum number of items
returned (by default ``25``).
.. code-block:: twig
{% set children = location.children(10) %}
List of 10 Location's children, sorted as is defined on the Location
- **Accessing the first child of a Location**
This is done by calling the method ``getFirstChild()``, also available as ``firstChild()`` in
Twig. It has one optional parameter, which is a ContentType identifier that returned Location must
match. In our example, the ContentType identifier is ``blog_post``. Returned Location will be
the first one from the children Locations sorted as is defined by their parent Location, which is
the Location the method is called on.
.. code-block:: twig
{% set first_child = location.firstChild('blog_post') %}
{% if first_child is not null %}
First blog post, as sorted by the parent Location:
{{ first_child.content.name }}
{% else %}
There are no blog posts under this Location
{% endif %}
.. note::
If the Location doesn't contain any children, optionally limited by the given ContentType,
the method will return ``null``. Make sure to check if that's the case.
- **Filtering through Location's children**
This is done by calling the method ``filterChildren()``, which returns a Pagerfanta instance
and has three parameters:
1. **optional** array of ContentType identifiers that will be used to filter the result, by default
an empty array ``[]``
2. **optional** maximum number of items per page, by default ``25``
3. **optional** current page, by default ``1``
.. code-block:: twig
{% set documents = location.filterChildren(['document'], 10, 1) %}
Children documents, page {{ documents.currentPage }}
Total: {{ documents.nbResults }} items
{{ pagerfanta( documents, 'twitter_bootstrap' ) }}
Location siblings
~~~~~~~~~~~~~~~~~
- **Listing Location's siblings**
This is done by calling the method ``getSiblings()``, also available as ``siblings()`` in
Twig. It returns an array of children Locations and optionally accepts maximum number of items
returned (by default ``25``).
.. code-block:: twig
{% set siblings = location.siblings(10) %}
List of 10 Location's siblings, sorted as is defined on the parent Location
- **Filtering through Location's siblings**
This is done by calling the method ``filterSiblings()``, which returns a Pagerfanta instance
and has three parameters:
1. **optional** array of ContentType identifiers that will be used to filter the result, by default
an empty array ``[]``
2. **optional** maximum number of items per page, by default ``25``
3. **optional** current page, by default ``1``
.. code-block:: twig
{% set articles = location.filterSiblings(['article'], 10, 1) %}
Sibling articles, page {{ articles.currentPage }}
Total: {{ articles.nbResults }} items
{{ pagerfanta( articles, 'twitter_bootstrap' ) }}
.. _named_object_template:
Working with Named Objects
--------------------------
Named objects feature provides a way to configure specific objects (``Content``, ``Location`` and
``Tag``) by name and ID, and a way to access them by name from PHP, Twig and Query Type
configuration. Site API NamedObjectProvider service is available as ``namedObject``. Its purpose is
providing access to configured named objects.
.. note::
Configuration of named objects is documented in more detail :ref:`on the Configuration page`.
Usage of named objects from PHP is :ref:`documented on the Services page`.
A following named object configuration is given:
.. code-block:: yaml
ibexa:
system:
frontend_group:
ng_site_api:
named_objects:
content:
certificate: 3
locations:
homepage: 2
tags:
colors: 4
Three functions for accessing named objects are available, one for each object type:
- ``ng_named_content``
Provides access to named Content object. Example usage:
.. code-block:: twig
{% set certificate = ng_named_content('certificate') %}
- ``ng_named_location``
Provides access to named Location object. Example usage:
.. code-block:: twig
{% set homepage = ng_named_location('homepage') %}
- ``ng_named_tag``
Provides access to named Tag object. Example usage:
.. code-block:: twig
{% set colors = ng_named_tag('colors') %}