You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Drupal 11:通过hook添加自定义双列字段报错求解决方案

问题:自定义双列字段通过hook_entity_base_field_info添加到用户实体时触发QueryException错误

我开发了一个包含c1、c2双列的自定义字段模块,手动将其附加到用户实体并通过Drupal常规配置界面保存设置时可正常使用。但在demo2c.module中通过demo2c_entity_base_field_info()钩子添加该字段到user实体时,触发如下错误:

Drupal\Core\Entity\Query\QueryException: 'my_2c' not found in Drupal\Core\Entity\Query\Sql\Tables->ensureEntityTable() (line 373 of core/lib/Drupal/Core/Entity/Query/Sql/Tables.php).

模块完整代码

demo2c.info.yml

name: Demo With Two Column Field Type
type: module
description: Fails with hook_entity_base_field_info().
core_version_requirement: ^11.0
package: Demo

src/Plugin/Field/FieldType/Demo2CType.php

<?php

namespace Drupal\demo2c\Plugin\Field\FieldType ;

use Drupal\Core\Field\FieldItemBase ;
use Drupal\Core\Field\FieldStorageDefinitionInterface ;
use Drupal\Core\TypedData\DataDefinition ;

/**
 * A field type with two integer columns.
 *
 * @FieldType(
 *   id = "demo2c_field_type",
 *   label = @Translation( "Demo Two Column Field Type" ),
 *   module = "demo2c",
 *   description = @Translation( "Fails when added with hook_entity_base_field_info()." ),
 *   default_widget = "demo2c_field_widget",
 *   default_formatter = "demo2c_field_formatter"
 * )
 */
class Demo2CType extends FieldItemBase {

    /**
     * {@inheritdoc}
     */
    public static function schema( FieldStorageDefinitionInterface $field_definition ) {
        return [ 'columns' => [ 'c1' => [ 'type' => 'int',
                                          'size' => 'normal',
                                          'not null' => FALSE ],
                                'c2' => [ 'type' => 'int',
                                          'size' => 'normal',
                                          'not null' => FALSE ] ] ] ;
    }

    /**
     * {@inheritdoc}
     */
    public function isEmpty() {
        return $this->get( 'c1' )->getValue() === NULL
            && $this->get( 'c2' )->getValue() === NULL ;
    }

    /**
     * {@inheritdoc}
     */
    public static function propertyDefinitions( FieldStorageDefinitionInterface $field_definition ) {
        $prop[ 'c1' ] = DataDefinition::create( 'integer' )->setLabel( t( 'Column One' ) ) ;
        $prop[ 'c2' ] = DataDefinition::create( 'integer' )->setLabel( t( 'Column Two' ) ) ;
        return $prop ;
    }

}

src/Plugin/Field/FieldWidget/Demo2CWidget.php

<?php

namespace Drupal\demo2c\Plugin\Field\FieldWidget ;

use Drupal\Core\Field\FieldItemListInterface ;
use Drupal\Core\Field\WidgetBase ;
use Drupal\Core\Form\FormStateInterface ;

/**
 * A field widget with two integer columns.
 *
 * @FieldWidget(
 *   id = "demo2c_field_widget",
 *   label = @Translation( "Demo Two Column Field Widget" ),
 *   module = "demo2c",
 *   field_types = { "demo2c_field_type" }
 * )
 */
class Demo2CWidget extends WidgetBase {

    /**
     * {@inheritdoc}
     */
    public function formElement( FieldItemListInterface $items,
                                 $delta,
                                 array $element,
                                 array &$form,
                                 FormStateInterface $form_state ) {

        $element += [ '#type' => 'fieldset' ] ;

        $element[ 'c1' ] = [ '#type' => 'number',
                             '#default_value' => $items->get( $delta )->get( 'c1' )->getValue() ?? 0,
                             '#title' => $this->t( "Column One" ) ] ;
        $element[ 'c2' ] = [ '#type' => 'number',
                             '#default_value' => $items->get( $delta )->get( 'c2' )->getValue() ?? 0,
                             '#title' => $this->t( "Column Two" ) ] ;

        return $element ;
    }
}

src/Plugin/Field/FieldFormatter/Demo2CFormatter.php

<?php

namespace Drupal\demo2c\Plugin\Field\FieldFormatter ;

use Drupal\Core\Field\FormatterBase ;
use Drupal\Core\Field\FieldItemListInterface ;

/**
 * A field formatter with two integer columns.
 *
 * @FieldFormatter(
 *   id = "demo2c_field_formatter",
 *   module = "demo2c",
 *   label = @Translation( "Demo Two Column Field Formatter" ),
 *   field_types = { "demo2c_field_type" }
 * )
 */
class Demo2CFormatter extends FormatterBase {

    /**
     * {@inheritdoc}
     */
    public function viewElements( FieldItemListInterface $items, $langcade ) {
        $elements = [] ;

        foreach( $items as $delta => $item ) {
            $c1 = $item->get( 'c1' )->getValue() ;
            $c2 = $item->get( 'c2' )->getValue() ;

            $elements[ $delta ] = [ '#markup' => "Column one: $c1, column two: $c2." ] ;
        }

        return $elements ;
    }
}

触发错误的demo2c.module代码

<?php

use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;

function demo2c_entity_base_field_info( EntityTypeInterface $entity_type ) {
    if ( $entity_type->id() === 'user' ) {

        $fields[ 'my_2c' ] = BaseFieldDefinition::create( 'demo2c_field_type' )
            ->setLabel( t( 'My Demo Two Column Field' ) )
            ->setDescription( t( "Doesn't work." ) )
            ->addConstraint( 'UniqueField' )
            ->setDisplayOptions( 'form', [ 'weight' => -1 ] ) ;

        return $fields ;
    }
}

解决方案

需要修改以下内容来解决问题:

1. 修复hook_entity_base_field_info的返回逻辑

当前钩子仅在匹配user实体时返回字段数组,其他实体类型无返回值,会干扰Drupal的字段收集流程。必须始终返回数组(即使为空):

修改后的demo2c.module代码:

<?php

use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;

function demo2c_entity_base_field_info(EntityTypeInterface $entity_type) {
  $fields = [];
  if ($entity_type->id() === 'user') {
    $fields['my_2c'] = BaseFieldDefinition::create('demo2c_field_type')
      ->setLabel(t('My Demo Two Column Field'))
      ->setDescription(t("Now works."))
      ->addConstraint('UniqueField')
      ->setDisplayOptions('form', ['weight' => -1])
      // 补充显示配置,确保字段在表单和视图中可配置
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);
  }
  return $fields;
}

2. 完善自定义字段类型的属性定义初始化

在Demo2CType.php的propertyDefinitions方法中,需要先初始化空数组,避免潜在的未定义变量问题:

修改后的代码片段:

public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
  $prop = []; // 初始化空数组
  $prop['c1'] = DataDefinition::create('integer')->setLabel(t('Column One'));
  $prop['c2'] = DataDefinition::create('integer')->setLabel(t('Column Two'));
  return $prop;
}

3. 执行数据库更新

修改代码后,运行Drupal数据库更新命令,确保新的基础字段结构被添加到用户表:

drush updatedb

或者通过后台的“模块更新”页面执行更新操作。

原因说明

  • 钩子函数必须始终返回数组,否则会导致Drupal处理其他实体时丢失字段定义,引发查询错误。
  • 补充显示配置选项确保字段在表单和视图中能被正确初始化和处理。
  • 数据库更新会触发Drupal重新生成实体表结构,确保新字段的数据库列被正确创建。

内容的提问来源于stack exchange,提问作者The Real Kwizats Haderach

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 18:35:54