Do you want to know how to Drupal?

Let's Drupal

Field

How to extract entity id from autocomplete string in Drupal?

When we use autocomplete field, the submitted value will look something like this "Title of the entity (10)". To extract the current entity id from this string we can use helper function extractEntityIdFromAutocompleteInput  from EntityAutocomplete

$autocomplete_data = 'Title of the entity (10)';
$entity_id = EntityAutocomplete::extractEntityIdFromAutocompleteInput($autocomplete_data);

 


How to change the type of the field in Drupal 9?

There are many cases when we need to switch a type of a field from one to another based on a new business requirement. How to do it and keep the old data.

First you HAVE TO make a back up :) 

I will provide the full code and then step by step we will break it down. To perform this operation we will use hook_update.

Steps to change the type of the field

  1. Load field storage
  2. Fetch the data for this field
  3. Configure a new field storage
  4. Delete the old storage
  5. Purge the data
  6. Create a new storage configuration
  7. Add field to bundles
  8. Insert the data back to db
<?php
use \Drupal\field\Entity\FieldStorageConfig;
use \Drupal\field\Entity\FieldConfig;

/**
 * Change node__field_quote from string_long to string type.
 */
function yourmodule_update_8XXX() {
  $database = \Drupal::database();
  $table = 'node__field_quote ';
  $entity_type = 'node';
  $field_name = 'field_quote';

  // #1 Load storage configuration.
  $field_storage = FieldStorageConfig::loadByName($entity_type, $field_name);

  // if storage config is not found, there is nothing to do.
  if (is_null($field_storage)) {
    return;
  }

  $rows = [];

  // #2 Fetch the data for the old field.
  if ($database->schema()->tableExists($table)) {
    // The table data to restore after the update is completed.
    $rows = $database->select($table, 'n')
      ->fields('n')
      ->execute()
      ->fetchAll();
  }

  $new_fields = [];

  // Use existing field config for new field.
  foreach ($field_storage->getBundles() as $bundle => $label) {
    $field = FieldConfig::loadByName($entity_type, $bundle, $field_name);
    $new_field = $field->toArray();
    $new_field['field_type'] = 'string';
    $new_field['settings'] = [];

    $new_fields[] = $new_field;
  }

  // #3 Take the old storage configuration present it as an array and update it's settings.
  $new_field_storage = $field_storage->toArray();
  $new_field_storage['type'] = 'string';
  $new_field_storage['settings'] = [
    'max_length' => 255,
    'is_ascii' => FALSE,
    'case_sensitive' => FALSE,
  ];

  // #4 Deleting field storage which will also delete bundles(fields).
  $field_storage->delete();

  // #5 Purge field data now to allow new field and field_storage with same name
  // to be created. You may need to increase batch size.
  field_purge_batch(100);

  // #6 Create new field storage, create a new storage based on configuration above.
  $new_field_storage = FieldStorageConfig::create($new_field_storage);
  $new_field_storage->save();

  // #7 Create new fields.
  foreach ($new_fields as $new_field) {
    $new_field = FieldConfig::create($new_field);
    $new_field->save();
  }

  // #8 Restore existing data in the same table.
  if (!empty($rows)) {
    foreach ($rows as $row) {
	  // We can do some manipulation with data here.
      $database->insert($table)
        ->fields((array) $row)
        ->execute();
    }
  }

}

 

Step 1 - Load the old field storage

First of all of course we need to load the old field storage configuration. A field storage always attached to entity type: node, media, paragraph and etc.

  $field_storage = FieldStorageConfig::loadByName($entity_type, $field_name);

Step 2 - Fetch the data for this field

We fetch all the data for the old field, for any fields and for any bundles. For example this fiels used in articles (node content type) and pages (node content type). We want to keep the data for all of the fields. Later we can perform some transformation of the data. Keep in mind if you have many items in this table, you will need to use batch operations.

 

// #2 Fetch the data for the old field.
  if ($database->schema()->tableExists($table)) {
    // The table data to restore after the update is completed.
    $rows = $database->select($table, 'n')
      ->fields('n')
      ->execute()
      ->fetchAll();
  }

 

Step 3  -  Configure a new field storage

In this example we actually use the old storage configuration and just update few settings.

// #3 Take the old storage configuration present it as an array and update it's settings.
  $new_field_storage = $field_storage->toArray();
  $new_field_storage['type'] = 'string';
  $new_field_storage['settings'] = [
    'max_length' => 255,
    'is_ascii' => FALSE,
    'case_sensitive' => FALSE,
  ];

But in some cases we can get it from configuration files or just pass an array with configuration directly. For example
 

// Get field storage from config file.
$install_storage = new ExtensionInstallStorage(
    \Drupal::service('config.storage'),
    InstallStorage::CONFIG_INSTALL_DIRECTORY
);
$field_storage_config = $install_storage->read("field.storage.node.field_quote");

Step 4  -  Delete the old storage

// #4 Deleting field storage which will also delete bundles(fields).
  $field_storage->delete();

 

Step 5 - Purge the data

Before recreating the new field we need to delete all the data for these fields. If you have a lot of items consider to change the value.

// #5 Purge field data now to allow new field and field_storage with same name
  // to be created. You may need to increase batch size.
  field_purge_batch(100);

 

Step 6 - Create a new storage configuration

Here we just pass a field storage configuration to create method as an array

// #6 Create new field storage, create a new storage based on configuration above.
$new_field_storage = FieldStorageConfig::create($new_field_storage);
$new_field_storage->save();

 

Step 7 - Add field to bundles

We recreate field instances here. We again use the previous configuration of the field and update the settings.
 

// #7 Create new fields.
foreach ($new_fields as $new_field) {
    $new_field = FieldConfig::create($new_field);
    $new_field->save();
}

and then we can also recreate the field completely from scratch, something like this

FieldConfig::create([
      'field_storage' => FieldStorageConfig::loadByName($entity_type, $field_name),
      'bundle' => $bundle,
      'label' => t('Our field label'),
      'required' => TRUE,
      'default_value' => [[
        'value' => 1,
      ],
      ],
])->save();

 

Step 8 - Insert the data back to the field table.

Here we can convert data or just insert as it is.

  // #8 Restore existing data in the same table.
  if (!empty($rows)) {
    foreach ($rows as $row) {
      // We can do some manipulation with data here.
      $database->insert($table)
        ->fields((array) $row)
        ->execute();
    }
  }

 


How to update entity field in Drupal 8?

Sometimes we need to update some settings for a field. In our case we will be update drop down field
function your_module_update_8001() {
  // First we need to get update manager.
  $definition_update_manager = \Drupal::entityDefinitionUpdateManager();

  // Load the storage definition of the field.
  $field_storage = $definition_update_manager->getFieldStorageDefinition('field_name',
    'node');
  // Set a new list of options for the list field.
  $field_storage->setSettings([
    'allowed_values' => [
      'link' => 'Link',
      'posts' => 'Posts',
      'events' => 'Events',
      'page' => 'Page',
    ],
  ]);

  // Update the field storage definition.
  $definition_update_manager->updateFieldStorageDefinition($field_storage);
}

 


How to load entity by a specific field value in Drupal 9?

Quite often it is required to get data by a field value. In our example I will use nodes, but it can be any entity type.

In first example we load entities by based fields. For example vid, type, language and etc.

I want to get all the nodes of the type post  for my custom block. For this we will use EntityTypeManager class

// Load a storage for node entity type
$items = \Drupal::entityTypeManager() 
      ->getStorage('node') 
      ->loadByProperties(['type' => 'post']); // load all nodes with the node type

After we can run through the items and apply our custom logic here.

In the second example, we will get entities based on custom fields. For example we have field "field_type"

$query = $this->entityTypeManager->getStorage('node')->getQuery();
$query->condition('status', 1)
    ->condition('changed', REQUEST_TIME, '<')
    ->condition('field_type', 'post');

$nids = $query->execute();

More examples can be found here: EntityFieldQuery