3 namespace Drupal\entity_reference_revisions\Plugin\migrate\destination;
5 use Drupal\Component\Plugin\ConfigurablePluginInterface;
6 use Drupal\Component\Utility\NestedArray;
7 use Drupal\Core\Entity\ContentEntityInterface;
8 use Drupal\Core\Entity\EntityManagerInterface;
9 use Drupal\Core\Entity\EntityStorageInterface;
10 use Drupal\Core\Field\FieldTypePluginManagerInterface;
11 use Drupal\Core\TypedData\TranslatableInterface;
12 use Drupal\migrate\MigrateException;
13 use Drupal\migrate\Plugin\migrate\destination\EntityRevision;
14 use Drupal\migrate\Plugin\MigrateIdMapInterface;
15 use Drupal\migrate\Plugin\MigrationInterface;
16 use Drupal\migrate\Row;
19 * Provides entity_reference_revisions destination plugin.
21 * Available configuration keys:
22 * - new_revisions: (optional) Flag to indicate if a new revision should be
23 * created instead of updating a previous default record. Only applicable when
24 * providing an entity id without a revision_id.
26 * @MigrateDestination(
27 * id = "entity_reference_revisions",
28 * deriver = "Drupal\entity_reference_revisions\Plugin\Derivative\MigrateEntityReferenceRevisions"
31 class EntityReferenceRevisions extends EntityRevision implements ConfigurablePluginInterface {
36 public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, FieldTypePluginManagerInterface $field_type_manager) {
37 parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_manager, $field_type_manager);
38 $this->setConfiguration($configuration);
44 public function setConfiguration(array $configuration) {
45 $this->configuration = NestedArray::mergeDeep(
46 $this->defaultConfiguration(),
54 public function getConfiguration() {
55 return $this->configuration;
61 public function defaultConfiguration() {
63 'new_revisions' => FALSE,
70 protected static function getEntityTypeId($pluginId) {
71 // Remove "entity_reference_revisions:".
72 // Ideally, we would call getDerivativeId(), but since this is static
73 // that is not possible so we follow the same pattern as core.
74 return substr($pluginId, 27);
80 protected function save(ContentEntityInterface $entity, array $oldDestinationIdValues = []) {
84 $this->getKey('id') => $entity->id(),
85 $this->getKey('revision') => $entity->getRevisionId(),
92 public function getIds() {
93 if ($revision_key = $this->getKey('revision')) {
94 $id_key = $this->getKey('id');
95 $ids[$id_key]['type'] = 'integer';
97 // TODO: Improve after https://www.drupal.org/node/2783715 is finished.
98 $ids[$revision_key]['type'] = 'integer';
100 if ($this->isTranslationDestination()) {
101 if ($revision_key = $this->getKey('langcode')) {
102 $ids[$revision_key]['type'] = 'string';
105 throw new MigrateException('This entity type does not support translation.');
111 throw new MigrateException('This entity type does not support revisions.');
117 protected function getEntity(Row $row, array $oldDestinationIdValues) {
118 $entity_id = $oldDestinationIdValues ?
119 array_shift($oldDestinationIdValues) :
120 $this->getEntityId($row);
121 $revision_id = $oldDestinationIdValues ?
122 array_pop($oldDestinationIdValues) :
123 $row->getDestinationProperty($this->getKey('revision'));
125 // If a specific revision_id is supplied and exists, assert the entity_id
126 // matches (if supplied), and update the revision.
127 /** @var \Drupal\Core\Entity\RevisionableInterface|\Drupal\Core\Entity\EntityInterface $entity */
128 if (!empty($revision_id) && ($entity = $this->storage->loadRevision($revision_id))) {
129 if (!empty($entity_id) && ($entity->id() != $entity_id)) {
130 throw new MigrateException("The revision_id exists for this entity type, but does not belong to the given entity id");
132 $entity = $this->updateEntity($entity, $row) ?: $entity;
134 // If there is no revision_id supplied, but there is an entity_id
135 // supplied that exists, update it.
136 elseif (!empty($entity_id) && ($entity = $this->storage->load($entity_id))) {
137 // If so configured, create a new revision while updating.
138 if ($this->getConfiguration()['new_revisions']) {
139 $entity->setNewRevision(TRUE);
141 $entity = $this->updateEntity($entity, $row) ?: $entity;
144 // Otherwise, create a new (possibly stub) entity.
146 // Attempt to ensure we always have a bundle.
147 if ($bundle = $this->getBundle($row)) {
148 $row->setDestinationProperty($this->getKey('bundle'), $bundle);
151 // Stubs might need some required fields filled in.
152 if ($row->isStub()) {
153 $this->processStubRow($row);
155 $entity = $this->storage->create($row->getDestination())
156 ->enforceIsNew(TRUE);
157 $entity->setNewRevision(TRUE);
159 $this->rollbackAction = MigrateIdMapInterface::ROLLBACK_DELETE;
166 public function rollback(array $destination_identifiers) {
167 if ($this->isTranslationDestination()) {
168 $this->rollbackTranslation($destination_identifiers);
171 $this->rollbackNonTranslation($destination_identifiers);
176 * Rollback translation destinations.
178 * @param array $destination_identifiers
179 * The IDs of the destination object to delete.
181 protected function rollbackTranslation(array $destination_identifiers) {
182 $entity = $this->storage->loadRevision(array_pop($destination_identifiers));
183 if ($entity && $entity instanceof TranslatableInterface) {
184 if ($key = $this->getKey('langcode')) {
185 if (isset($destination_identifier[$key])) {
186 $langcode = $destination_identifier[$key];
187 if ($entity->hasTranslation($langcode)) {
188 // Make sure we don't remove the default translation.
189 $translation = $entity->getTranslation($langcode);
190 if (!$translation->isDefaultTranslation()) {
191 $entity->removeTranslation($langcode);
201 * Rollback non-translation destinations.
203 * @param array $destination_identifiers
204 * The IDs of the destination object to delete.
206 protected function rollbackNonTranslation(array $destination_identifiers) {
207 $revision_id = array_pop($destination_identifiers);
208 $entity = $this->storage->loadRevision($revision_id);
210 if ($entity->isDefaultRevision()) {
214 $this->storage->deleteRevision($revision_id);