Drupal:如何在_node_update钩子中调用自定义CustomizedSerializer?
你遇到的核心问题是:CustomizedSerializer是Views的样式插件,而非Drupal核心序列化系统的独立服务,所以直接调用\Drupal::service('serializer')只会触发核心序列化逻辑,不会用到你自定义的插件。
这个插件设计用于处理Views查询结果的输出渲染,原本的JSON API端点应该是基于Views构建的,所以访问时会触发插件的render方法;而你的钩子代码直接调用核心序列化服务,自然走不通同一路径。
推荐优先采用逻辑抽离的方式,保证Views和钩子代码复用同一套序列化逻辑,避免重复维护;如果临时需要快速复用插件,也可以尝试实例化插件的方式(不推荐长期使用)。
方案一:抽离核心序列化逻辑(推荐)
把CustomizedSerializer里的序列化逻辑单独封装成一个独立服务,让Views插件和钩子代码都调用这个服务,确保输出一致。
1. 定义自定义服务
在modules/custom/my_module/my_module.services.yml中添加服务配置:
services: my_module.product_serializer: class: Drupal\my_module\Serializer\ProductSerializer arguments: ['@serializer', '@logger.factory']
2. 创建序列化服务类
新建modules/custom/my_module/src/Serializer/ProductSerializer.php:
<?php namespace Drupal\my_module\Serializer; use Drupal\Core\Logger\LoggerChannelFactoryInterface; use Drupal\Core\Serializer\Serializer; use Drupal\node\NodeInterface; class ProductSerializer { protected $serializer; protected $logger; public function __construct(Serializer $serializer, LoggerChannelFactoryInterface $loggerFactory) { $this->serializer = $serializer; $this->logger = $loggerFactory->get('php'); } /** * 序列化产品节点数组为JSON */ public function serializeProducts(array $nodes): string { $this->logger->notice('ProductSerializer::serializeProducts'); // 这里复制原CustomizedSerializer::render里的逻辑,处理节点数据 $processedData = []; foreach ($nodes as $node) { if ($node instanceof NodeInterface && $node->bundle() === 'product') { $processedData[] = [ 'id' => $node->id(), 'title' => $node->getTitle(), 'created' => $node->getCreatedTime(), // 其他需要的字段处理逻辑,和原插件保持一致 ]; } } return $this->serializer->serialize($processedData, 'json'); } }
3. 修改Views插件调用新服务
更新modules/custom/my_module/src/Plugin/views/style/CustomizedSerializer.php:
<?php namespace Drupal\my_module\Plugin\views\style; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Serializer\Serializer; use Drupal\my_module\Serializer\ProductSerializer; use Drupal\views\Plugin\views\style\Serializer; use Symfony\Component\DependencyInjection\ContainerInterface; class CustomizedSerializer extends Serializer implements ContainerFactoryPluginInterface { protected $productSerializer; public function __construct( array $configuration, $plugin_id, $plugin_definition, Serializer $serializer, ProductSerializer $productSerializer ) { parent::__construct($configuration, $plugin_id, $plugin_definition, $serializer); $this->productSerializer = $productSerializer; } public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { return new static( $configuration, $plugin_id, $plugin_definition, $container->get('serializer'), $container->get('my_module.product_serializer') ); } public function render($unserialized) { // 从Views结果中提取节点对象 $nodes = []; foreach ($unserialized as $row) { if (isset($row->_entity) && $row->_entity instanceof NodeInterface) { $nodes[] = $row->_entity; } } return $this->productSerializer->serializeProducts($nodes); } }
4. 在钩子中调用新服务
修改modules/custom/my_module/my_module.module:
<?php function my_module_node_update($node) { if ($node->getType() == 'product') { $nids = \Drupal::entityQuery('node') ->condition('type', 'product') ->execute(); $nodes = \Drupal\node\Entity\Node::loadMultiple($nids); // 调用自定义序列化服务 $serializer = \Drupal::service('my_module.product_serializer'); $result = $serializer->serializeProducts($nodes); // 使用Drupal文件系统API更安全 $filePath = \Drupal::service('file_system')->realpath('public://products.json'); file_put_contents($filePath, $result); } }
方案二:直接实例化Views插件(临时方案)
如果不想重构代码,可以尝试直接实例化CustomizedSerializer,但需要模拟Views的运行环境(可能存在隐藏依赖,不推荐长期使用):
<?php function my_module_node_update($node) { if ($node->getType() == 'product') { $nids = \Drupal::entityQuery('node') ->condition('type', 'product') ->execute(); $nodes = \Drupal\node\Entity\Node::loadMultiple($nids); // 模拟Views行数据结构(插件render方法接收的是Views结果格式) $unserialized = []; foreach ($nodes as $node) { $unserialized[] = (object) ['_entity' => $node]; } // 实例化Views样式插件 $pluginManager = \Drupal::service('plugin.manager.views.style'); $serializerPlugin = $pluginManager->createInstance('customized_serializer', []); // 调用插件的render方法 $result = $serializerPlugin->render($unserialized); $filePath = \Drupal::service('file_system')->realpath('public://products.json'); file_put_contents($filePath, $result); } }
内容的提问来源于stack exchange,提问作者RobertAKARobin

