Score:2

How can I properly create separate storage for a base field?

cn flag

What is the correct and supported way to create a separate table storage for a base field on a custom entity? I tried the answer here, using setCustomStorage(TRUE), but it didn't work, and I don't think that I need to actually create custom storage, I just wanted a separate table in the database for the field.

I was able to get a separate storage table with BundleFieldDefinition::create('address') but I was not sure if it is ok to create a bundle field within the baseFieldDefinitions?

From what I understand, the BundleFieldDefinition class just extends the base field class, and returns FALSE on isBaseField().

The snippet below works fine, but is this a supported way of doing it?

/**
 * {@inheritdoc}
 */
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
  $fields = parent::baseFieldDefinitions($entity_type);
  $fields['shipping_address'] = BundleFieldDefinition::create('address')
    ->setLabel(t('Shipping Address'))
    ->setDescription(t('The shipping address.'))
    ->setCardinality(1)
    ->setSetting('field_overrides', [AddressField::ADDITIONAL_NAME => ['override' => FieldOverride::HIDDEN]])
    ->setDisplayOptions('form', [
      'type' => 'address_default',
      'weight' => 4,
    ])
    ->setDisplayConfigurable('form', TRUE)
    ->setDisplayConfigurable('view', TRUE);

   return $fields;
}
Score:0
us flag

To store the field data in a separate database table, you need to override ContentEntityBase::bundleFieldDefinitions(), which is the method for adding bundle fields or overriding base fields. That does not allow to decide the database table name, but only to avoid using a shared database table that otherwise Drupal would use.

For an entity that depends from the Entity API module, Defining bundle fields in code shows how to define bundle fields for an entity. In particular, it shows how to define the storage for the added bundle fields.

Supposing that the entity class is MyEntity and that the machine name of the module that implements that class is mymodule, the code to use in your case would be the following one.

/**
 * Entity class.
 */
class MyEntity /* extends … */ {

  /**
   * Class properties and other methods
   */

  /**
   * {@inheritdoc}
   */
  public static function bundleFieldDefinitions(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
    if ($bundle == 'mybundle') {
      $fields = parent::bundleFieldDefinitions($entity_type, $bundle, array $base_field_definitions);
      $fields['shipping_address'] = BundleFieldDefinition::create('address')
        ->setLabel(t('Shipping Address'))
        ->setDescription(t('The shipping address.'))
        ->setCardinality(1)
        ->setSetting('field_overrides', [AddressField::ADDITIONAL_NAME => ['override' => FieldOverride::HIDDEN]])
        ->setDisplayOptions('form', ['type' => 'address_default', 'weight' => 4])
        ->setDisplayConfigurable('form', TRUE)
        ->setDisplayConfigurable('view', TRUE);

      return $fields;
    }
  }

  /**
   * Other methods
   */

}
/**
 * mymodule.module
 */

/**
 * Implements hook_entity_field_storage_info().
 */
function mymodule_entity_field_storage_info(EntityTypeInterface $entity_type) {
  if ($entity_type->id() == 'myentity') {
    return MyEntity::bundleFieldDefinitions($entity_type, 'mybundle', []);
  }
}
tonytheferg avatar
cn flag
There's no "custom" database table, but the field gets it's own table, as it would if it was multi-valued. So is your answer saying create the bundle field under bundle field definitions? I've not found any modules that create bundle fields that way as an example.
apaderno avatar
us flag
That's correct: Drupal only allows fields to get their own database table, but not which database table is used for them. Field types decide the schema for their database table, though, whenever that is shared or not.
apaderno avatar
us flag
As for entities that use `bundleFieldDefinitions()`, there are two entities implemented by Drupal core: the *Comment* entity with [`Comment::bundleFieldDefinitions()`](https://api.drupal.org/api/drupal/core%21modules%21comment%21src%21Entity%21Comment.php/function/Comment%3A%3AbundleFieldDefinitions/9), and the *Taxonomy term* entity with [`Term::bundleFieldDefinitions()`](https://api.drupal.org/api/drupal/core%21modules%21taxonomy%21src%21Entity%21Term.php/function/Term%3A%3AbundleFieldDefinitions/9).
apaderno avatar
us flag
I extended the answer to explain how entity field items define their database schema. That still does not allow to choose a database table name for the data of a specific entity field used for a specific entity. Changing the entity field item plugin can change the database schema, but that is all Drupal core allows, at the moment.
tonytheferg avatar
cn flag
I was speaking about examples that use BundleFieldDefinition::create()
tonytheferg avatar
cn flag
I don't think there are any examples in drupal core where ```BundleFieldDefinition::create()``` is called in an entity.
apaderno avatar
us flag
Drupal core does not use a class defined by a contributed module. Drupal core should define its own `BundleFieldDefinition`, and there is an issue for that, but so far it has not been implemented. (Actually, Drupal core has a field definition class for bundle fields, but it has a different name and its instances are created differently from what done with the contributed `BundleFieldDefinition` class.)
tonytheferg avatar
cn flag
Read this multiple times, and it all still seems somewhat convoluted. My module depends upon commerce, which depends upon entity module.
apaderno avatar
us flag
Are you defining a custom entity in a module that depends on the Commerce Core module?
apaderno avatar
us flag
In this case, what is the name of the entity class? What is the module machine name?
tonytheferg avatar
cn flag
Yup. CommercePurchaseOrder. commerce_purchasing. But I decided instead to allow users to install their own address field.
tonytheferg avatar
cn flag
Looking at your updated answer, what if I want the field available for all bundles? Can I not specify the bundle in the definition, and in the field storage?
apaderno avatar
us flag
If a field is available for all the bundles, then it is not anymore a bundle field. In that case, the `baseFieldDefinitions()` method or the `hook_entity_base_field_info()` implementation can return data about that field.
tonytheferg avatar
cn flag
Well, I guess back to the original question of separate storage for base field.
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.