drupal

Drupal 11 - programatically create vocabulary with custom field


I have a custom content type. Upon creating a new node for that content type, I'd like to create a new vocabulary with a custom taxonomy reference field. I have created the code below which does it all, except the field is never shown in the UI form when adding new terms into the newly-created taxonomy. Otherwise, everything seems to be in order - the field is created, it has the correct type, name and everything else.

I tried to manually create a field of the same type and it does indeed show in the term adding form. I also tried to clear caches and reposition this field in the UI manually and save it - nothing works.

function mymodule_entity_insert(\Drupal\Core\Entity\EntityInterface $entity) {
  // create top and bottom menu vocabularies if we have
  if ( $entity->bundle() == "section" ) {
    /***
     * Top menu vocabulary
     */
    $vocabulary_id = $entity->id() . '_top_menu';
    $vocabulary_name = t( '@entity_name Top Menu', [ '@entity_name' => $entity->label() ] );

    Vocabulary::create([
      'vid' => $vocabulary_id,
      'name' => $vocabulary_name,
    ])->save();

    // Define the field name and settings
    $field_name = 'field_parent_entity_reference';
    $entity_type = 'taxonomy_term';
    $bundle = $vocabulary_id;

    // Check if the field storage exists
    if (!$field_storage = FieldStorageConfig::loadByName($entity_type, $field_name)) {
      // Create the field storage definition
      $field_storage = FieldStorageConfig::create([
        'field_name' => $field_name,
        'entity_type' => $entity_type,
        'type' => 'entity_reference',
        'settings' => [
          'target_type' => 'node',
        ],
        'cardinality' => 1, // Set to -1 for unlimited.
        'provider' => 'field_create',
      ]);
      $field_storage->save();
    }

    // Check if the field configuration exists
    if (!FieldConfig::loadByName($entity_type, $bundle, $field_name)) {
      // Create the field instance for the vocabulary bundle
      FieldConfig::create([
        'field_name' => $field_name,
        'entity_type' => $entity_type,
        'field_storage' => $field_storage,
        'bundle' => $bundle,
        'label' => t('Content to show'),
        'settings' => [
          'handler' => 'default',
          'handler_settings' => [
            'target_bundles' => [
              // Limit to specific content types
              'announcement' => 'announcement',
              'section' => 'section',
              'page' => 'page',
              'event' => 'event',
              'article' => 'article',
            ],
          ],
        ],
      ])->save();
    }

    // Load the form display for the vocabulary
    $form_display = EntityFormDisplay::load('taxonomy_term.' . $bundle . '.default');
    if (!$form_display) {
      $form_display = EntityFormDisplay::create([
        'targetEntityType' => 'taxonomy_term',
        'bundle' => $bundle,
        'mode' => 'default',
      ]);
    }

    // Add the field to the form display
    $form_display->setComponent($field_name, [
      'type' => 'entity_reference_autocomplete', // Field widget type
      'weight' => 50,
    ])->save();

    // Load the view display for the vocabulary
    $view_display = EntityViewDisplay::load('taxonomy_term.' . $bundle . '.default');
    if (!$view_display) {
      $view_display = EntityViewDisplay::create([
        'targetEntityType' => 'taxonomy_term',
        'bundle' => $bundle,
        'mode' => 'default',
      ]);
    }

    // Add the field to the view display
    $view_display->setComponent($field_name, [
      'label' => 'above', // Show the label above the field
      'type' => 'entity_reference_label', // Field formatter
      'weight' => 50,
    ])->save();

    \Drupal::logger('activit')->info('Form display updated for field: @field', ['@field' => $field_name]);

    \Drupal::service('cache.entity')->invalidateAll();
    drupal_flush_all_caches();

    $vocabulary = Vocabulary::load($vocabulary_id);
    $field = FieldConfig::loadByName('taxonomy_term', $bundle, $field_name);
    \Drupal::logger('activit')->info('Vocabulary: @vocab, Field: @field', [
      '@vocab' => $vocabulary ? $vocabulary->id() : 'Not Found',
      '@field' => $field ? $field->id() : 'Not Found',
    ]);

    $term = \Drupal\taxonomy\Entity\Term::create([
      'vid' => $vocabulary_id,
      'name' => 'Test Term',
    ]);
    $term->save();
    $loaded_term = \Drupal\taxonomy\Entity\Term::load($term->id());
    \Drupal::logger('custom_module')->info('Field Value: @value', [
      '@value' => $loaded_term->get($field_name)->value ?? 'Empty',
    ]);

  }
}

Solution

  • I was missing status part in the EntityFormDisplay::create function. Here is the corrected code part:

    // Load the form display for the vocabulary
        $form_display = EntityFormDisplay::load('taxonomy_term.' . $bundle . '.default');
        if (!$form_display) {
          $form_display = EntityFormDisplay::create([
            'targetEntityType' => 'taxonomy_term',
            'bundle' => $bundle,
            'mode' => 'default',
            'status' => TRUE,
          ]);
        }