Added Entity and Entity Reference Revisions which got dropped somewhere along the...
[yaffs-website] / web / modules / contrib / entity_reference_revisions / src / Plugin / migrate / destination / EntityReferenceRevisions.php
1 <?php
2
3 namespace Drupal\entity_reference_revisions\Plugin\migrate\destination;
4
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;
17
18 /**
19  * Provides entity_reference_revisions destination plugin.
20  *
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.
25  *
26  * @MigrateDestination(
27  *   id = "entity_reference_revisions",
28  *   deriver = "Drupal\entity_reference_revisions\Plugin\Derivative\MigrateEntityReferenceRevisions"
29  * )
30  */
31 class EntityReferenceRevisions extends EntityRevision implements ConfigurablePluginInterface {
32
33   /**
34    * {@inheritdoc}
35    */
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);
39   }
40
41   /**
42    * {@inheritdoc}
43    */
44   public function setConfiguration(array $configuration) {
45     $this->configuration = NestedArray::mergeDeep(
46       $this->defaultConfiguration(),
47       $configuration
48     );
49   }
50
51   /**
52    * {@inheritdoc}
53    */
54   public function getConfiguration() {
55     return $this->configuration;
56   }
57
58   /**
59    * {@inheritdoc}
60    */
61   public function defaultConfiguration() {
62     return [
63       'new_revisions' => FALSE,
64     ];
65   }
66
67   /**
68    * {@inheritdoc}
69    */
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);
75   }
76
77   /**
78    * {@inheritdoc}
79    */
80   protected function save(ContentEntityInterface $entity, array $oldDestinationIdValues = []) {
81     $entity->save();
82
83     return [
84       $this->getKey('id') => $entity->id(),
85       $this->getKey('revision') => $entity->getRevisionId(),
86     ];
87   }
88
89   /**
90    * {@inheritdoc}
91    */
92   public function getIds() {
93     if ($revision_key = $this->getKey('revision')) {
94       $id_key = $this->getKey('id');
95       $ids[$id_key]['type'] = 'integer';
96
97       // TODO: Improve after https://www.drupal.org/node/2783715 is finished.
98       $ids[$revision_key]['type'] = 'integer';
99
100       if ($this->isTranslationDestination()) {
101         if ($revision_key = $this->getKey('langcode')) {
102           $ids[$revision_key]['type'] = 'string';
103         }
104         else {
105           throw new MigrateException('This entity type does not support translation.');
106         }
107       }
108
109       return $ids;
110     }
111     throw new MigrateException('This entity type does not support revisions.');
112   }
113
114   /**
115    * {@inheritdoc}
116    */
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'));
124
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");
131       }
132       $entity = $this->updateEntity($entity, $row) ?: $entity;
133     }
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);
140       }
141       $entity = $this->updateEntity($entity, $row) ?: $entity;
142     }
143
144     // Otherwise, create a new (possibly stub) entity.
145     else {
146       // Attempt to ensure we always have a bundle.
147       if ($bundle = $this->getBundle($row)) {
148         $row->setDestinationProperty($this->getKey('bundle'), $bundle);
149       }
150
151       // Stubs might need some required fields filled in.
152       if ($row->isStub()) {
153         $this->processStubRow($row);
154       }
155       $entity = $this->storage->create($row->getDestination())
156         ->enforceIsNew(TRUE);
157       $entity->setNewRevision(TRUE);
158     }
159     $this->rollbackAction = MigrateIdMapInterface::ROLLBACK_DELETE;
160     return $entity;
161   }
162
163   /**
164    * {@inheritdoc}
165    */
166   public function rollback(array $destination_identifiers) {
167     if ($this->isTranslationDestination()) {
168       $this->rollbackTranslation($destination_identifiers);
169     }
170     else {
171       $this->rollbackNonTranslation($destination_identifiers);
172     }
173   }
174
175   /**
176    * Rollback translation destinations.
177    *
178    * @param array $destination_identifiers
179    *   The IDs of the destination object to delete.
180    */
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);
192               $entity->save();
193             }
194           }
195         }
196       }
197     }
198   }
199
200   /**
201    * Rollback non-translation destinations.
202    *
203    * @param array $destination_identifiers
204    *   The IDs of the destination object to delete.
205    */
206   protected function rollbackNonTranslation(array $destination_identifiers) {
207     $revision_id = array_pop($destination_identifiers);
208     $entity = $this->storage->loadRevision($revision_id);
209     if ($entity) {
210       if ($entity->isDefaultRevision()) {
211         $entity->delete();
212       }
213       else {
214         $this->storage->deleteRevision($revision_id);
215       }
216     }
217   }
218
219 }