Ibexa DXP : afficher un type de contenu avec les vues et Twig
Configurer une vue de contenu, écrire le template Twig et ajouter une liste d'articles avec un contrôleur personnalisé et le SearchService.
Dans Ibexa DXP, les contributeurs créent les contenus dans le back-office, et c'est à nous, développeurs, de décider comment ils s'affichent. Le mécanisme central pour ça, ce sont les vues de contenu (content views). Prenons un exemple concret : un type de contenu article, avec les champs title, intro, image et body.
1. Déclarer la vue
Dans config/packages/ibexa.yaml, on associe un template à un type de contenu grâce à un matcher :
ibexa:
system:
site_group:
content_view:
full:
article:
template: '@ibexadesign/full/article.html.twig'
match:
Identifier\ContentType: article
full est le type de vue utilisé pour une page complète. On peut définir d'autres types, par exemple line pour un élément dans une liste, avec le même principe.
2. Écrire le template
Ibexa fournit des fonctions Twig pour afficher les champs proprement :
{% extends '@ibexadesign/pagelayout.html.twig' %}
{% block content %}
<article>
<h1>{{ ibexa_content_name(content) }}</h1>
{% if not ibexa_field_is_empty(content, 'image') %}
{{ ibexa_render_field(content, 'image') }}
{% endif %}
{{ ibexa_render_field(content, 'intro') }}
{{ ibexa_render_field(content, 'body') }}
</article>
{% endblock %}
ibexa_render_field utilise le bon rendu pour chaque type de champ : texte riche, image avec ses variations, etc. Il vaut mieux l'utiliser plutôt que d'afficher la valeur brute.
3. Ajouter une liste avec un contrôleur
Pour une page « Blog » qui liste ses articles enfants, on branche un contrôleur sur la vue du dossier parent :
folder_blog:
template: '@ibexadesign/full/blog.html.twig'
controller: App\Controller\BlogController::list
match:
Identifier\ContentType: folder
Identifier\Section: blog
Le contrôleur reçoit la vue, interroge le moteur de recherche et ajoute les résultats aux paramètres du template :
namespace App\Controller;
use Ibexa\Contracts\Core\Repository\SearchService;
use Ibexa\Contracts\Core\Repository\Values\Content\Query;
use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion;
use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause;
use Ibexa\Core\MVC\Symfony\View\ContentView;
final class BlogController
{
public function __construct(private readonly SearchService $searchService) {}
public function list(ContentView $view): ContentView
{
$query = new Query([
'filter' => new Criterion\LogicalAnd([
new Criterion\ParentLocationId($view->getLocation()->id),
new Criterion\ContentTypeIdentifier('article'),
new Criterion\Visibility(Criterion\Visibility::VISIBLE),
]),
'sortClauses' => [new SortClause\DatePublished(Query::SORT_DESC)],
'limit' => 10,
]);
$articles = array_map(
static fn ($hit) => $hit->valueObject,
$this->searchService->findContent($query)->searchHits
);
$view->addParameters(['articles' => $articles]);
return $view;
}
}
Dans le template, chaque article peut ensuite s'afficher avec sa vue line :
{% for article in articles %}
{{ render(controller('ibexa_content::viewAction', {
contentId: article.id,
viewType: 'line'
})) }}
{% endfor %}
Bonnes pratiques
- Utiliser
filterplutôt quequeryquand on ne fait pas de recherche plein texte : c'est plus rapide et cela ne calcule pas de score de pertinence. - Penser à la pagination dès qu'une liste peut grossir, par exemple avec Pagerfanta et l'adaptateur fourni par Ibexa.
- Laisser faire le cache HTTP : les vues de contenu sont automatiquement étiquetées, et le cache est invalidé à la publication.
- Pour des listes réutilisables, les Query Types permettent de déclarer une requête une fois et de la configurer directement en YAML.
Avec ces trois briques — vue, template, contrôleur — on couvre déjà la majorité des besoins d'affichage d'un site Ibexa.