Adding and overriding mapping targets

Last updated on
15 March 2026

hook_feeds_targets_alter() lets you change which mapping targets are available for a feed type. This document explains two common use cases: overriding the plugin for an existing field (Example 1) and exposing the same field under a different target name (Example 2).

Example 1: Override plugin for an existing field

You can replace the default target plugin for a field with your own plugin. The target key stays the field name (for example field_my_text). The processor still passes that field name to the plugin, so your plugin receives the real field name as usual and does not need any extra logic.

In the alter hook you overwrite the existing target definition for that field with a \Drupal\feeds\FieldTargetDefinition that references your plugin via setPluginId().

Implementing the alter hook

use Drupal\feeds\FeedTypeInterface;
use Drupal\feeds\FieldTargetDefinition;
use Drupal\feeds\Plugin\Type\Processor\EntityProcessorInterface;

/**
 * Implements hook_feeds_targets_alter().
 */
function my_module_feeds_targets_alter(array &$targets, FeedTypeInterface $feed_type) {
  if ($feed_type->id() !== 'my_feed_type') {
    return;
  }

  $processor = $feed_type->getProcessor();
  if (!$processor instanceof EntityProcessorInterface) {
    return;
  }

  $field_definitions = \Drupal::service('entity_field.manager')->getFieldDefinitions(
    $processor->entityType(),
    $processor->bundle()
  );

  if (isset($field_definitions['field_my_text'])) {
    $targets['field_my_text'] = FieldTargetDefinition::createFromFieldDefinition(
      $field_definitions['field_my_text']
    )->setPluginId('my_module_custom_text');
  }
}

Here, any mapping to field_my_text will use the plugin my_module_custom_text instead of the default text plugin. Your plugin's setTarget(), isEmpty(), and clearTarget() are called with field_my_text as the target/field name, so you can use the parent implementation as usual.

Example 2: Expose the same field under a different target name

You can add a new target key (for example my_custom_target) that still maps to an existing entity field (for example field_my_text). While you can define the target in an implementation of hook_feeds_targets_alter() too, it may make more sense to define it in your plugin's targets() method instead. The processor will pass the target key (my_custom_target) to your plugin, not the field name. Your plugin must therefore obtain the actual field name from the target definition in setTarget(), isEmpty(), and clearTarget().

In the static targets() method, add an entry with your custom key and a FieldTargetDefinition created from the real field. Set the plugin id so this plugin is used when that target is mapped.

use Drupal\Core\Entity\EntityInterface;
use Drupal\feeds\FeedTypeInterface;
use Drupal\feeds\FieldTargetDefinition;
use Drupal\feeds\FeedInterface;
use Drupal\feeds\Feeds\Target\Text;
use Drupal\feeds\Plugin\Type\Processor\EntityProcessorInterface;

/**
 * Custom text target that exposes a field under a different target name.
 *
 * @FeedsTarget(
 *   id = "my_module_custom_text",
 *   field_types = {"text", "text_long", "text_with_summary"}
 * )
 */
class CustomTextTarget extends Text {

  /**
   * {@inheritdoc}
   */
  public static function targets(array &$targets, FeedTypeInterface $feed_type, array $definition) {
    $processor = $feed_type->getProcessor();
    if (!$processor instanceof EntityProcessorInterface) {
      return;
    }

    $field_definitions = \Drupal::service('entity_field.manager')->getFieldDefinitions(
      $processor->entityType(),
      $processor->bundle()
    );

    if (isset($field_definitions['field_my_text'])) {
      $targets['my_custom_target'] = FieldTargetDefinition::createFromFieldDefinition(
        $field_definitions['field_my_text']
      )
        ->setPluginId($definition['id'])
        ->addProperty('value', t('Value'));
    }
  }

  /**
   * Returns the field name for the target.
   *
   * @param string $target
   *   The target name.
   *
   * @return string
   *   The field name.
   */
  protected function getFieldNameForTarget(string $target): string {
    if ($this->targetDefinition instanceof FieldTargetDefinition) {
      return $this->targetDefinition->getFieldDefinition()->getName();
    }
    return $target;
  }

  /**
   * {@inheritdoc}
   */
  public function setTarget(FeedInterface $feed, EntityInterface $entity, $target, array $values) {
    return parent::setTarget($feed, $entity, $this->getFieldNameForTarget($target), $values);
  }

  /**
   * {@inheritdoc}
   */
  public function clearTarget(FeedInterface $feed, EntityInterface $entity, string $target) {
    return parent::clearTarget($feed, $entity, $this->getFieldNameForTarget($target));
  }

  /**
   * {@inheritdoc}
   */
  public function isEmpty(FeedInterface $feed, EntityInterface $entity, $target) {
    return parent::isEmpty($feed, $entity, $this->getFieldNameForTarget($target));
  }

}

In the mapping UI, users will see my_custom_target as an option. When the processor runs, it calls your plugin with $field_name or $target equal to 'my_custom_target'. The entity has no such field; the real field is field_my_text. The overrides of setTarget(), isEmpty(), and clearTarget() read the field name from the target definition and pass it to the parent.

With this in place, the processor can call your plugin with the custom target name; your plugin translates it to the real field name and then behaves like a normal field target.

Summary

  • Example 1 (override): Keep the target key equal to the field name. Replace the target definition with one that uses your plugin. No special handling is needed in the plugin.
  • Example 2 (alternative name): Define the target in the plugin's targets() method with a custom key and a FieldTargetDefinition for the real field. Override setTarget(), isEmpty(), and clearTarget() to resolve the target name to the field name via $this->targetDefinition->getFieldDefinition()->getName().

Help improve this page

Page status: No known problems

You can: