D9中如何在Hook函数内用实体引用字段值设置另一实体引用字段
问题
创建PL类型节点时,通过hook函数自动创建Commerce Product,现有两个实体引用字段:
- Name of Project(对应字段
field_name_of_project):引用内容类型 - Type of Use(对应字段
field_type_of_use):引用分类术语
目前标题字段能正常设置,但这两个实体引用字段的值无法正确赋值,以下是我的代码:
<?php /** * @file * file for the PL Product Creation module, which creates a product and product variations when a Pl node is created. * */ use Drupal\node\Entity\Node; use Drupal\node\NodeInterface; use Drupal\commerce_product\Entity\ProductVariation; use Drupal\commerce_product\Entity\Product; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; /** * Implementation of hook_entity_insert(). */ function pl_product_creation_entity_insert(Drupal\Core\Entity\EntityInterface $entity) { if ($entity->getEntityTypeId() == 'node' && $entity->bundle() == 'pl_node') { // Get the pl node properties $name_of_project = $entity->field_name_of_project->entity->getTitle(); $type_of_use = $entity->field_type_of_use->entity->getName(); // Load the product $pl_product_title = 'Products at ' . $name_of_project; // Load the product storage. $entity_type_manager = \Drupal::service('entity_type.manager'); $product_storage = $entity_type_manager->getStorage('commerce_product'); // Load the product by title. $pl_product = $product_storage->loadByProperties(['title' => $pl_product_title]); // Check if the Pl product exists if (!empty($pl_product)) { // Load the Pl product $pl_product = reset($pl_product); } else { // Create a new Pl product $pl_product = Product::create([ 'type' => 'pl', ]); // Set the title field. $pl_product->setTitle($pl_product_title); // Set the values of the custom fields. $pl_product->set('field_name_of_project', $name_of_project); $pl_product->set('field_type_of_use', $type_of_use); // Save the product entity. $pl_product->save(); } } }
问题分析与解决
实体引用字段不能直接赋值实体的标题或名称,必须传入实体ID或者实体对象,你的代码错误地使用了标题/名称字符串,这是核心问题。
错误点拆解
$name_of_project = $entity->field_name_of_project->entity->getTitle();- 获取的是被引用内容类型节点的标题,但实体引用字段需要的是该节点的ID,不是标题文本。
$type_of_use = $entity->field_type_of_use->entity->getName();- 同理,分类术语的实体引用字段需要的是术语的ID,不是术语名称。
- 赋值时直接传入字符串:
$pl_product->set('field_name_of_project', $name_of_project);- 实体引用字段不接受纯字符串,Drupal无法识别这个字符串对应的实体,导致赋值失败。
修正后的代码
<?php /** * @file * file for the PL Product Creation module, which creates a product and product variations when a Pl node is created. * */ use Drupal\node\Entity\Node; use Drupal\node\NodeInterface; use Drupal\commerce_product\Entity\ProductVariation; use Drupal\commerce_product\Entity\Product; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; /** * Implementation of hook_entity_insert(). */ function pl_product_creation_entity_insert(Drupal\Core\Entity\EntityInterface $entity) { if ($entity->getEntityTypeId() == 'node' && $entity->bundle() == 'pl_node') { // 获取被引用实体的ID(而非标题/名称) $project_node = $entity->field_name_of_project->entity; $use_term = $entity->field_type_of_use->entity; // 先检查引用是否存在,避免空指针错误 if (!$project_node || !$use_term) { return; } $project_id = $project_node->id(); $use_term_id = $use_term->id(); $pl_product_title = 'Products at ' . $project_node->getTitle(); // 加载产品存储 $entity_type_manager = \Drupal::service('entity_type.manager'); $product_storage = $entity_type_manager->getStorage('commerce_product'); // 按标题查找产品 $pl_product = $product_storage->loadByProperties(['title' => $pl_product_title]); // 检查产品是否存在 if (!empty($pl_product)) { $pl_product = reset($pl_product); } else { // 创建新产品 $pl_product = Product::create([ 'type' => 'pl', ]); $pl_product->setTitle($pl_product_title); // 使用实体ID赋值实体引用字段 $pl_product->set('field_name_of_project', $project_id); $pl_product->set('field_type_of_use', $use_term_id); $pl_product->save(); } } }
额外优化说明
- 增加了引用实体的空值检查:如果原PL节点的两个引用字段未填写,直接返回,避免后续代码抛出空指针异常。
- 也可以直接传入实体对象赋值,比如
$pl_product->set('field_name_of_project', $project_node);,效果和传ID一致,两种方式都可行。
内容的提问来源于stack exchange,提问作者cnario
相关产品推荐
相关产品推荐

