class EntityContentBase

Same name in other branches
  1. 8.9.x core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php \Drupal\migrate\Plugin\migrate\destination\EntityContentBase
  2. 10 core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php \Drupal\migrate\Plugin\migrate\destination\EntityContentBase
  3. 11.x core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php \Drupal\migrate\Plugin\migrate\destination\EntityContentBase

Provides destination class for all content entities lacking a specific class.

Available configuration keys:

  • translations: (optional) Boolean, indicates if the entity is translatable, defaults to FALSE.
  • overwrite_properties: (optional) A list of properties that will be overwritten if an entity with the same ID already exists. Any properties that are not listed will not be overwritten.
  • validate: (optional) Boolean, indicates whether an entity should be validated, defaults to FALSE.

Example:

The example below will create a 'node' entity of content type 'article'.

The language of the source will be used because the configuration 'translations: true' was set. Without this configuration option the site's default language would be used.

The example content type has fields 'title', 'body' and 'field_example'. The text format of the body field is defaulted to 'basic_html'. The example uses the EmbeddedDataSource source plugin for the sake of simplicity.

If the migration is executed again in an update mode, any updates done in the destination Drupal site to the 'title' and 'body' fields would be overwritten with the original source values. Updates done to 'field_example' would be preserved because 'field_example' is not included in 'overwrite_properties' configuration.


id: custom_article_migration
label: Custom article migration
source:
  plugin: embedded_data
  data_rows:
    -
      id: 1
      langcode: 'fi'
      title: 'Sivun otsikko'
      field_example: 'Huhuu'
      content: '<p>Hoi maailma</p>'
  ids:
    id:
      type: integer
process:
  nid: id
  langcode: langcode
  title: title
  field_example: field_example
  'body/0/value': content
  'body/0/format':
    plugin: default_value
    default_value: basic_html
destination:
  plugin: entity:node
  default_bundle: article
  translations: true
  overwrite_properties:
    - title
    - body
  # Run entity and fields validation before saving an entity.
  #   validate: true

Hierarchy

Expanded class hierarchy of EntityContentBase

See also

\Drupal\Core\Entity\FieldableEntityInterface::validate()

\Drupal\migrate\Plugin\migrate\destination\Entity

\Drupal\migrate\Plugin\migrate\destination\EntityRevision

8 files declare their use of EntityContentBase
Book.php in core/modules/book/src/Plugin/migrate/destination/Book.php
DestinationCategoryTest.php in core/modules/migrate_drupal/tests/src/Kernel/Plugin/migrate/DestinationCategoryTest.php
EntityComment.php in core/modules/comment/src/Plugin/migrate/destination/EntityComment.php
EntityContentBaseTest.php in core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityContentBaseTest.php
Contains \Drupal\Tests\migrate\Unit\Plugin\migrate\destination\EntityContentBaseTest.
EntityFile.php in core/modules/file/src/Plugin/migrate/destination/EntityFile.php

... See full list

File

core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php, line 94

Namespace

Drupal\migrate\Plugin\migrate\destination
View source
class EntityContentBase extends Entity implements HighestIdInterface, MigrateValidatableEntityInterface {
    
    /**
     * Field type plugin manager.
     *
     * @var \Drupal\Core\Field\FieldTypePluginManagerInterface
     */
    protected $fieldTypeManager;
    
    /**
     * The account switcher service.
     *
     * @var \Drupal\Core\Session\AccountSwitcherInterface
     */
    protected $accountSwitcher;
    
    /**
     * Constructs a content entity.
     *
     * @param array $configuration
     *   A configuration array containing information about the plugin instance.
     * @param string $plugin_id
     *   The plugin ID for the plugin instance.
     * @param mixed $plugin_definition
     *   The plugin implementation definition.
     * @param \Drupal\migrate\Plugin\MigrationInterface $migration
     *   The migration entity.
     * @param \Drupal\Core\Entity\EntityStorageInterface $storage
     *   The storage for this entity type.
     * @param array $bundles
     *   The list of bundles this entity type has.
     * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
     *   The entity field manager.
     * @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
     *   The field type plugin manager service.
     * @param \Drupal\Core\Session\AccountSwitcherInterface $account_switcher
     *   The account switcher service.
     */
    public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityFieldManagerInterface $entity_field_manager, FieldTypePluginManagerInterface $field_type_manager, AccountSwitcherInterface $account_switcher = NULL) {
        parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles);
        $this->entityFieldManager = $entity_field_manager;
        $this->fieldTypeManager = $field_type_manager;
        if ($account_switcher === NULL) {
            @trigger_error('Calling ' . __NAMESPACE__ . '\\EntityContentBase::__construct() without the $account_switcher argument is deprecated in drupal:9.3.0 and will be required in drupal:10.0.0. See https://www.drupal.org/node/3142975', E_USER_DEPRECATED);
            $account_switcher = \Drupal::service('account_switcher');
        }
        $this->accountSwitcher = $account_switcher;
    }
    
    /**
     * {@inheritdoc}
     */
    public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
        $entity_type = static::getEntityTypeId($plugin_id);
        return new static($configuration, $plugin_id, $plugin_definition, $migration, $container->get('entity_type.manager')
            ->getStorage($entity_type), array_keys($container->get('entity_type.bundle.info')
            ->getBundleInfo($entity_type)), $container->get('entity_field.manager'), $container->get('plugin.manager.field.field_type'), $container->get('account_switcher'));
    }
    
    /**
     * {@inheritdoc}
     *
     * @throws \Drupal\migrate\MigrateException
     *   When an entity cannot be looked up.
     * @throws \Drupal\migrate\Exception\EntityValidationException
     *   When an entity validation hasn't been passed.
     */
    public function import(Row $row, array $old_destination_id_values = []) {
        $this->rollbackAction = MigrateIdMapInterface::ROLLBACK_DELETE;
        $entity = $this->getEntity($row, $old_destination_id_values);
        if (!$entity) {
            throw new MigrateException('Unable to get entity');
        }
        assert($entity instanceof ContentEntityInterface);
        if ($this->isEntityValidationRequired($entity)) {
            $this->validateEntity($entity);
        }
        $ids = $this->save($entity, $old_destination_id_values);
        if ($this->isTranslationDestination()) {
            $ids[] = $entity->language()
                ->getId();
        }
        return $ids;
    }
    
    /**
     * {@inheritdoc}
     */
    public function isEntityValidationRequired(FieldableEntityInterface $entity) {
        // Prioritize the entity method over migration config because it won't be
        // possible to save that entity unvalidated.
        
        /* @see \Drupal\Core\Entity\ContentEntityBase::preSave() */
        return $entity->isValidationRequired() || !empty($this->configuration['validate']);
    }
    
    /**
     * {@inheritdoc}
     */
    public function validateEntity(FieldableEntityInterface $entity) {
        // Entity validation can require the user that owns the entity. Switch to
        // use that user during validation.
        // As an example:
        // @see \Drupal\Core\Entity\Plugin\Validation\Constraint\ValidReferenceConstraint
        $account = $entity instanceof EntityOwnerInterface ? $entity->getOwner() : NULL;
        // Validate account exists as the owner reference could be invalid for any
        // number of reasons.
        if ($account) {
            $this->accountSwitcher
                ->switchTo($account);
        }
        // This finally block ensures that the account is always switched back, even
        // if an exception was thrown. Any validation exceptions are intentionally
        // left unhandled. They should be caught and logged by a catch block
        // surrounding the row import and then added to the migration messages table
        // for the current row.
        try {
            $violations = $entity->validate();
        } finally {
            if ($account) {
                $this->accountSwitcher
                    ->switchBack();
            }
        }
        if (count($violations) > 0) {
            throw new EntityValidationException($violations);
        }
    }
    
    /**
     * Saves the entity.
     *
     * @param \Drupal\Core\Entity\ContentEntityInterface $entity
     *   The content entity.
     * @param array $old_destination_id_values
     *   (optional) An array of destination ID values. Defaults to an empty array.
     *
     * @return array
     *   An array containing the entity ID.
     */
    protected function save(ContentEntityInterface $entity, array $old_destination_id_values = []) {
        $entity->save();
        return [
            $entity->id(),
        ];
    }
    
    /**
     * {@inheritdoc}
     */
    public function isTranslationDestination() {
        return !empty($this->configuration['translations']);
    }
    
    /**
     * {@inheritdoc}
     */
    public function getIds() {
        $ids = [];
        $id_key = $this->getKey('id');
        $ids[$id_key] = $this->getDefinitionFromEntity($id_key);
        if ($this->isTranslationDestination()) {
            $langcode_key = $this->getKey('langcode');
            if (!$langcode_key) {
                throw new MigrateException(sprintf('The "%s" entity type does not support translations.', $this->storage
                    ->getEntityTypeId()));
            }
            $ids[$langcode_key] = $this->getDefinitionFromEntity($langcode_key);
        }
        return $ids;
    }
    
    /**
     * Updates an entity with the new values from row.
     *
     * @param \Drupal\Core\Entity\EntityInterface $entity
     *   The entity to update.
     * @param \Drupal\migrate\Row $row
     *   The row object to update from.
     *
     * @return \Drupal\Core\Entity\EntityInterface
     *   An updated entity from row values.
     */
    protected function updateEntity(EntityInterface $entity, Row $row) {
        $empty_destinations = $row->getEmptyDestinationProperties();
        // By default, an update will be preserved.
        $rollback_action = MigrateIdMapInterface::ROLLBACK_PRESERVE;
        // Make sure we have the right translation.
        if ($this->isTranslationDestination()) {
            $property = $this->storage
                ->getEntityType()
                ->getKey('langcode');
            if ($row->hasDestinationProperty($property)) {
                $language = $row->getDestinationProperty($property);
                if (!$entity->hasTranslation($language)) {
                    $entity->addTranslation($language);
                    // We're adding a translation, so delete it on rollback.
                    $rollback_action = MigrateIdMapInterface::ROLLBACK_DELETE;
                }
                $entity = $entity->getTranslation($language);
            }
        }
        // If the migration has specified a list of properties to be overwritten,
        // clone the row with an empty set of destination values, and re-add only
        // the specified properties.
        if (isset($this->configuration['overwrite_properties'])) {
            $empty_destinations = array_intersect($empty_destinations, $this->configuration['overwrite_properties']);
            $clone = $row->cloneWithoutDestination();
            foreach ($this->configuration['overwrite_properties'] as $property) {
                $clone->setDestinationProperty($property, $row->getDestinationProperty($property));
            }
            $row = $clone;
        }
        foreach ($row->getDestination() as $field_name => $values) {
            $field = $entity->{$field_name};
            if ($field instanceof TypedDataInterface) {
                $field->setValue($values);
            }
        }
        foreach ($empty_destinations as $field_name) {
            $entity->{$field_name} = NULL;
        }
        $this->setRollbackAction($row->getIdMap(), $rollback_action);
        // We might have a different (translated) entity, so return it.
        return $entity;
    }
    
    /**
     * Populates as much of the stub row as possible.
     *
     * @param \Drupal\migrate\Row $row
     *   The row of data.
     */
    protected function processStubRow(Row $row) {
        $bundle_key = $this->getKey('bundle');
        if ($bundle_key && empty($row->getDestinationProperty($bundle_key))) {
            if (empty($this->bundles)) {
                throw new MigrateException('Stubbing failed, no bundles available for entity type: ' . $this->storage
                    ->getEntityTypeId());
            }
            $row->setDestinationProperty($bundle_key, reset($this->bundles));
        }
        $bundle = $row->getDestinationProperty($bundle_key) ?? $this->storage
            ->getEntityTypeId();
        // Populate any required fields not already populated.
        $fields = $this->entityFieldManager
            ->getFieldDefinitions($this->storage
            ->getEntityTypeId(), $bundle);
        foreach ($fields as $field_name => $field_definition) {
            if ($field_definition->isRequired() && is_null($row->getDestinationProperty($field_name))) {
                // Use the configured default value for this specific field, if any.
                if ($default_value = $field_definition->getDefaultValueLiteral()) {
                    $values = $default_value;
                }
                else {
                    
                    /** @var \Drupal\Core\Field\FieldItemInterface $field_type_class */
                    $field_type_class = $this->fieldTypeManager
                        ->getPluginClass($field_definition->getType());
                    $values = $field_type_class::generateSampleValue($field_definition);
                    if (is_null($values)) {
                        // Handle failure to generate a sample value.
                        throw new MigrateException('Stubbing failed, unable to generate value for field ' . $field_name);
                    }
                }
                $row->setDestinationProperty($field_name, $values);
            }
        }
    }
    
    /**
     * {@inheritdoc}
     */
    public function rollback(array $destination_identifier) {
        if ($this->isTranslationDestination()) {
            // Attempt to remove the translation.
            $entity = $this->storage
                ->load(reset($destination_identifier));
            if ($entity && $entity instanceof TranslatableInterface) {
                if ($key = $this->getKey('langcode')) {
                    if (isset($destination_identifier[$key])) {
                        $langcode = $destination_identifier[$key];
                        if ($entity->hasTranslation($langcode)) {
                            // Make sure we don't remove the default translation.
                            $translation = $entity->getTranslation($langcode);
                            if (!$translation->isDefaultTranslation()) {
                                $entity->removeTranslation($langcode);
                                $entity->save();
                            }
                        }
                    }
                }
            }
        }
        else {
            parent::rollback($destination_identifier);
        }
    }
    
    /**
     * {@inheritdoc}
     */
    public function getHighestId() {
        $values = $this->storage
            ->getQuery()
            ->accessCheck(FALSE)
            ->sort($this->getKey('id'), 'DESC')
            ->range(0, 1)
            ->execute();
        return (int) current($values);
    }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title Overrides
DependencyTrait::$dependencies protected property The object&#039;s dependencies.
DependencyTrait::addDependencies protected function Adds multiple dependencies.
DependencyTrait::addDependency protected function Adds a dependency.
DestinationBase::$migration protected property The migration.
DestinationBase::$rollbackAction protected property The rollback action to be saved for the last imported item.
DestinationBase::$supportsRollback protected property Indicates whether the destination can be rolled back.
DestinationBase::checkRequirements public function Checks if requirements for this plugin are OK. Overrides RequirementsInterface::checkRequirements
DestinationBase::getDestinationModule public function Gets the destination module handling the destination data. Overrides MigrateDestinationInterface::getDestinationModule 1
DestinationBase::rollbackAction public function The rollback action for the last imported item. Overrides MigrateDestinationInterface::rollbackAction
DestinationBase::setRollbackAction protected function For a destination item being updated, set the appropriate rollback action.
DestinationBase::supportsRollback public function Whether the destination can be rolled back or not. Overrides MigrateDestinationInterface::supportsRollback
Entity::$bundles protected property The list of the bundles of this entity type.
Entity::$entityFieldManager protected property The entity field manager.
Entity::$storage protected property The entity storage.
Entity::calculateDependencies public function Calculates dependencies for the configured plugin. Overrides DependentPluginInterface::calculateDependencies
Entity::fields public function Returns an array of destination fields. Overrides MigrateDestinationInterface::fields
Entity::getBundle public function Gets the bundle for the row taking into account the default.
Entity::getEntity protected function Creates or loads an entity. 5
Entity::getEntityId protected function Gets the entity ID of the row. 2
Entity::getKey protected function Returns a specific entity key.
EntityContentBase::$accountSwitcher protected property The account switcher service.
EntityContentBase::$fieldTypeManager protected property Field type plugin manager.
EntityContentBase::create public static function Creates an instance of the plugin. Overrides Entity::create 2
EntityContentBase::getHighestId public function Returns the highest ID tracked by the implementing plugin. Overrides HighestIdInterface::getHighestId 2
EntityContentBase::getIds public function Gets the destination IDs. Overrides MigrateDestinationInterface::getIds 2
EntityContentBase::import public function Overrides MigrateDestinationInterface::import 2
EntityContentBase::isEntityValidationRequired public function Returns a state of whether an entity needs to be validated before saving. Overrides MigrateValidatableEntityInterface::isEntityValidationRequired
EntityContentBase::isTranslationDestination public function
EntityContentBase::processStubRow protected function Populates as much of the stub row as possible. 3
EntityContentBase::rollback public function Delete the specified destination object from the target Drupal. Overrides Entity::rollback 1
EntityContentBase::save protected function Saves the entity. 3
EntityContentBase::updateEntity protected function Updates an entity with the new values from row. 3
EntityContentBase::validateEntity public function Validates the entity. Overrides MigrateValidatableEntityInterface::validateEntity
EntityContentBase::__construct public function Constructs a content entity. Overrides Entity::__construct 3
EntityFieldDefinitionTrait::getDefinitionFromEntity protected function Gets the field definition from a specific entity base field.
EntityFieldDefinitionTrait::getEntityTypeId protected static function Finds the entity type from configuration or plugin ID. 3
PluginInspectionInterface::getPluginDefinition public function Gets the definition of the plugin implementation. 6
PluginInspectionInterface::getPluginId public function Gets the plugin_id of the plugin instance. 2

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.