Lorsque vous devez insérer un champ sur une entité mais que cette donnée est calculée et n'est pas saisie par l'utilisateur, vous avez le réflexe de penser à un extra field. C'est un bon début mais pour appliquer un formateur de champ, vous serez limités car cela n'est pas possible ! Fort heureusement, une solution a été introduite dans Drupal 8, il s'agit des computed fields.
Comment cela fonctionne-t-il ?
Au lieu d'utiliser le hook_entity_extra_field_info()
, vous allez cette fois déclarer un hook_entity_bundle_field_info()
et allez déclarer un base field auquel vous direz qu'il est calculé et indiquerez la classe qui fournie ses données. Avec du code c'est plus simple :
/**
* Implements hook_entity_bundle_field_info().
*/
function hc_core_entity_bundle_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
$fields = [];
if ($entity_type->id() == 'node') {
if ($bundle == 'blog') {
$fields['blog_related_posts'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('Related posts'))
->setComputed(TRUE)
->setClass('\Drupal\hc_core\BlogRelatedPostsComputed')
->setDisplayConfigurable('view', TRUE);
}
}
return $fields;
}
Avec ce code, si vous videz les caches, vous verrez apparaître dans la gestion de l'affichage de votre entité ce nouveau champ auquel vous pourrez appliquer les formateurs pertinents. Cela dépendra du type de données que vous aurez sélectionné sur lequel il se basera.
Jetons maintenant un œil à la classe qui fournit les données sources du champ.
Pour faire les choses simplement, je vous conseille d'étendre la classe de liste du type de données de votre champ (dans mon exemple je m'appuierai sur EntityReferenceFieldItemList
) et nous utiliserons le trait dédié aux champs calculés (computed fields). La classe retourne les NIDs des nœuds qui partagent les mêmes catégories que le nœud actuellement consulté.
<?php
namespace Drupal\hc_core;
use Drupal\Core\Field\EntityReferenceFieldItemList;
use Drupal\Core\TypedData\ComputedItemListTrait;
class BlogRelatedPostsComputed extends EntityReferenceFieldItemList {
use ComputedItemListTrait;
/**
* Computed related blog posts.
*/
protected function computeValue() {
$delta = 0;
// It's a bit tricky, authors are not UIDs but NIDs because we are looking
// for humans and humans are nodes, not users.
$blog_categories = $this->getParent()->getValue()->get('field_category')->getValue();
$blog_nid = $this->getParent()->getValue()->id();
if (count($blog_categories) > 0) {
foreach ($blog_categories as $category) {
$category_ids[] = $category['target_id'];
}
$q = \Drupal::entityQuery('node')
->condition('type', 'blog', '=')
->condition('field_category', $category_ids, 'IN')
->condition('status', NODE_PUBLISHED, '=')
->condition('nid', $blog_nid, '<>')
->range(0, 5)
->sort('created', 'DESC')
->execute();
if ($q) {
foreach ($q as $rev => $r) {
$this->list[$delta] = $this->createItem($delta, $r);
$delta++;
}
}
}
}
}
Grâce au trait, on se concentre uniquement sur la récupération des valeurs qui nous intéressent et on utilise $this->createItem()
pour remplir la collection de valeurs de $this->list
.
Vous pourrez ajouter à cela quelques tags pour optimiser le rendu de vos données et vous voilà prêts à exploiter la puissance des champs calculés et rendus grâce à des formateurs de champs ! Plutôt simple, non ?