Дано — два типа нод pаrent
и child
, Parent ссылается на Child с помощью поля field_child
.
Задача — создать у нод типа Child вычисляемое поле, по которому можно получить родительскую ноду.
1. Создаём класс вычисляемого поля:
// src/ComputedParentNodeFieldItemList.php
namespace Drupal\MODULENAME;
use Drupal\Core\Field\FieldItemList;
use Drupal\Core\TypedData\ComputedItemListTrait;
use Drupal\node\NodeInterface;
class ComputedParentNodeFieldItemList extends FieldItemList {
use ComputedItemListTrait;
/**
* {@inheritdoc}
*/
protected function computeValue() {
$child_node = $this->getEntity(); /** @var NodeInterface $child_node */
if ($parent_node_id = $this->getParentNodeId($child_node->id())) {
$this->list[0] = $this->createItem(0, $parent_node_id);
}
}
/**
* Return parent node id by child node.
*/
protected function getParentNodeId(int $child_node_id): ?int {
$result = \Drupal::entityQuery('node')
->condition('type', 'parent')
->condition('field_child', $child_node_id)
->execute();
return $result ? (int)current($result) : NULL;
}
}
2. Добавляем нодам Child новое поле:
// MODULENAME.module
use Drupal\MODULENAME\ComputedParentNodeFieldItemList;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
/**
* Implements hook_entity_bundle_field_info().
*/
function MODULENAME_entity_bundle_field_info(EntityTypeInterface $entity_type, string $bundle, array $base_field_definitions): ?array {
if ($entity_type->id() == 'node' && $bundle == 'child') {
$fields['parent'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('Parent node'))
->setComputed(TRUE)
->setClass(ComputedParentNodeFieldItemList::class)
->setReadOnly(TRUE)
->setSetting('target_type', 'node');
return $fields;
}
return NULL;
}
В отличии от предыдущего примера вместо hook_entity_base_field_info()
здесь используется хук hook_entity_bundle_field_info()
, который позволяет добавлять поля для конкретного бандла.
3. По желанию интегрируем новое поле и модуль Token:
// MODULENAME.tokens.inc
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\node\NodeInterface;
/**
* Implements hook_token_info().
*/
function MODULENAME_token_info(): array {
$token_info['tokens']['node']['parent'] = [
'name' => t('Parent node'),
'type' => 'node',
];
return $token_info;
}
/**
* Implements hook_tokens().
*/
function MODULENAME_tokens(string $type, array $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata): array {
$replacements = [];
if ($type == 'node' && !empty($data['node'])) {
$node = $data['node']; /** @var NodeInterface $node */
$token_service = \Drupal::token();
if ($parent_tokens = $token_service->findWithPrefix($tokens, 'parent')) {
/** @var NodeInterface $parent_node */
if ($parent_node = $node->get('parent')->entity) {
$replacements += $token_service->generate('node', $parent_tokens, ['node' => $parent_node], $options, $bubbleable_metadata);
}
}
}
return $replacements;
}
После этого можно пользоваться кодом:
$child_node = Node::load(123);
$parent_node = $child_node->get('parent')->entity;
$text = \Drupal::token()->replace('Parent node title = [node:parent:title]', ['node' => $child_node]);
Подробнее про вычисляемые поля на drupal.org
Код в виде модуля на github
Написанное актуально для
Drupal 8+
Добавить комментарий