3 namespace Drupal\migrate\Plugin\migrate\destination;
5 use Drupal\Core\Entity\ContentEntityInterface;
6 use Drupal\Core\Entity\EntityInterface;
7 use Drupal\Core\Entity\EntityManagerInterface;
8 use Drupal\Core\Entity\EntityStorageInterface;
9 use Drupal\Core\Field\FieldTypePluginManagerInterface;
10 use Drupal\Core\TypedData\TranslatableInterface;
11 use Drupal\Core\TypedData\TypedDataInterface;
12 use Drupal\migrate\Plugin\MigrationInterface;
13 use Drupal\migrate\MigrateException;
14 use Drupal\migrate\Plugin\MigrateIdMapInterface;
15 use Drupal\migrate\Row;
16 use Symfony\Component\DependencyInjection\ContainerInterface;
19 * The destination class for all content entities lacking a specific class.
21 class EntityContentBase extends Entity {
26 * @var \Drupal\Core\Entity\EntityManagerInterface
28 protected $entityManager;
31 * Field type plugin manager.
33 * @var \Drupal\Core\Field\FieldTypePluginManagerInterface
35 protected $fieldTypeManager;
38 * Constructs a content entity.
40 * @param array $configuration
41 * A configuration array containing information about the plugin instance.
42 * @param string $plugin_id
43 * The plugin ID for the plugin instance.
44 * @param mixed $plugin_definition
45 * The plugin implementation definition.
46 * @param \Drupal\migrate\Plugin\MigrationInterface $migration
47 * The migration entity.
48 * @param \Drupal\Core\Entity\EntityStorageInterface $storage
49 * The storage for this entity type.
50 * @param array $bundles
51 * The list of bundles this entity type has.
52 * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
53 * The entity manager service.
54 * @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
55 * The field type plugin manager service.
57 public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, FieldTypePluginManagerInterface $field_type_manager) {
58 parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles);
59 $this->entityManager = $entity_manager;
60 $this->fieldTypeManager = $field_type_manager;
66 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
67 $entity_type = static::getEntityTypeId($plugin_id);
73 $container->get('entity.manager')->getStorage($entity_type),
74 array_keys($container->get('entity.manager')->getBundleInfo($entity_type)),
75 $container->get('entity.manager'),
76 $container->get('plugin.manager.field.field_type')
83 public function import(Row $row, array $old_destination_id_values = []) {
84 $this->rollbackAction = MigrateIdMapInterface::ROLLBACK_DELETE;
85 $entity = $this->getEntity($row, $old_destination_id_values);
87 throw new MigrateException('Unable to get entity');
90 $ids = $this->save($entity, $old_destination_id_values);
91 if (!empty($this->configuration['translations'])) {
92 $ids[] = $entity->language()->getId();
100 * @param \Drupal\Core\Entity\ContentEntityInterface $entity
101 * The content entity.
102 * @param array $old_destination_id_values
103 * (optional) An array of destination ID values. Defaults to an empty array.
106 * An array containing the entity ID.
108 protected function save(ContentEntityInterface $entity, array $old_destination_id_values = []) {
110 return [$entity->id()];
114 * Get whether this destination is for translations.
117 * Whether this destination is for translations.
119 protected function isTranslationDestination() {
120 return !empty($this->configuration['translations']);
126 public function getIds() {
127 $id_key = $this->getKey('id');
128 $ids[$id_key] = $this->getDefinitionFromEntity($id_key);
130 if ($this->isTranslationDestination()) {
131 if (!$langcode_key = $this->getKey('langcode')) {
132 throw new MigrateException('This entity type does not support translation.');
134 $ids[$langcode_key] = $this->getDefinitionFromEntity($langcode_key);
141 * Updates an entity with the new values from row.
143 * @param \Drupal\Core\Entity\EntityInterface $entity
144 * The entity to update.
145 * @param \Drupal\migrate\Row $row
146 * The row object to update from.
148 * @return \Drupal\Core\Entity\EntityInterface|null
149 * An updated entity, or NULL if it's the same as the one passed in.
151 protected function updateEntity(EntityInterface $entity, Row $row) {
152 // By default, an update will be preserved.
153 $rollback_action = MigrateIdMapInterface::ROLLBACK_PRESERVE;
155 // Make sure we have the right translation.
156 if ($this->isTranslationDestination()) {
157 $property = $this->storage->getEntityType()->getKey('langcode');
158 if ($row->hasDestinationProperty($property)) {
159 $language = $row->getDestinationProperty($property);
160 if (!$entity->hasTranslation($language)) {
161 $entity->addTranslation($language);
163 // We're adding a translation, so delete it on rollback.
164 $rollback_action = MigrateIdMapInterface::ROLLBACK_DELETE;
166 $entity = $entity->getTranslation($language);
170 // If the migration has specified a list of properties to be overwritten,
171 // clone the row with an empty set of destination values, and re-add only
172 // the specified properties.
173 if (isset($this->configuration['overwrite_properties'])) {
174 $clone = $row->cloneWithoutDestination();
175 foreach ($this->configuration['overwrite_properties'] as $property) {
176 $clone->setDestinationProperty($property, $row->getDestinationProperty($property));
181 foreach ($row->getDestination() as $field_name => $values) {
182 $field = $entity->$field_name;
183 if ($field instanceof TypedDataInterface) {
184 $field->setValue($values);
188 $this->setRollbackAction($row->getIdMap(), $rollback_action);
190 // We might have a different (translated) entity, so return it.
195 * Populates as much of the stub row as possible.
197 * @param \Drupal\migrate\Row $row
200 protected function processStubRow(Row $row) {
201 $bundle_key = $this->getKey('bundle');
202 if ($bundle_key && empty($row->getDestinationProperty($bundle_key))) {
203 if (empty($this->bundles)) {
204 throw new MigrateException('Stubbing failed, no bundles available for entity type: ' . $this->storage->getEntityTypeId());
206 $row->setDestinationProperty($bundle_key, reset($this->bundles));
209 // Populate any required fields not already populated.
210 $fields = $this->entityManager
211 ->getFieldDefinitions($this->storage->getEntityTypeId(), $bundle_key);
212 foreach ($fields as $field_name => $field_definition) {
213 if ($field_definition->isRequired() && is_null($row->getDestinationProperty($field_name))) {
214 // Use the configured default value for this specific field, if any.
215 if ($default_value = $field_definition->getDefaultValueLiteral()) {
216 $values[] = $default_value;
219 // Otherwise, ask the field type to generate a sample value.
220 $field_type = $field_definition->getType();
221 /** @var \Drupal\Core\Field\FieldItemInterface $field_type_class */
222 $field_type_class = $this->fieldTypeManager
223 ->getPluginClass($field_definition->getType());
224 $values = $field_type_class::generateSampleValue($field_definition);
225 if (is_null($values)) {
226 // Handle failure to generate a sample value.
227 throw new MigrateException('Stubbing failed, unable to generate value for field ' . $field_name);
231 $row->setDestinationProperty($field_name, $values);
239 public function rollback(array $destination_identifier) {
240 if ($this->isTranslationDestination()) {
241 // Attempt to remove the translation.
242 $entity = $this->storage->load(reset($destination_identifier));
243 if ($entity && $entity instanceof TranslatableInterface) {
244 if ($key = $this->getKey('langcode')) {
245 if (isset($destination_identifier[$key])) {
246 $langcode = $destination_identifier[$key];
247 if ($entity->hasTranslation($langcode)) {
248 // Make sure we don't remove the default translation.
249 $translation = $entity->getTranslation($langcode);
250 if (!$translation->isDefaultTranslation()) {
251 $entity->removeTranslation($langcode);
260 parent::rollback($destination_identifier);
265 * Gets the field definition from a specific entity base field.
267 * The method takes the field ID as an argument and returns the field storage
268 * definition to be used in getIds() by querying the destination entity base
275 * An associative array with a structure that contains the field type, keyed
276 * as 'type', together with field storage settings as they are returned by
277 * FieldStorageDefinitionInterface::getSettings().
279 * @see \Drupal\Core\Field\FieldStorageDefinitionInterface::getSettings()
281 protected function getDefinitionFromEntity($key) {
282 $entity_type_id = static::getEntityTypeId($this->getPluginId());
283 /** @var \Drupal\Core\Field\FieldStorageDefinitionInterface[] $definitions */
284 $definitions = $this->entityManager->getBaseFieldDefinitions($entity_type_id);
285 $field_definition = $definitions[$key];
288 'type' => $field_definition->getType(),
289 ] + $field_definition->getSettings();