ff3f0071c7264d92bcd171480c2caaa10db7e984
[yaffs-website] / destination / EntityRevision.php
1 <?php
2
3 namespace Drupal\migrate\Plugin\migrate\destination;
4
5 use Drupal\Core\Entity\ContentEntityInterface;
6 use Drupal\Core\Entity\EntityManagerInterface;
7 use Drupal\Core\Entity\EntityStorageInterface;
8 use Drupal\Core\Field\FieldTypePluginManagerInterface;
9 use Drupal\Core\StringTranslation\TranslatableMarkup;
10 use Drupal\migrate\MigrateException;
11 use Drupal\migrate\Plugin\MigrationInterface;
12 use Drupal\migrate\Row;
13
14 /**
15  * Provides entity revision destination plugin.
16  *
17  * Refer to the parent class for configuration keys:
18  * \Drupal\migrate\Plugin\migrate\destination\EntityContentBase
19  *
20  * Entity revisions can only be migrated after the entity to which the revisions
21  * belong has been migrated. For example, revisions of a given content type can
22  * be migrated only after the nodes of that content type have been migrated.
23  *
24  * In order to avoid revision ID conflicts, make sure that the entity migration
25  * also includes the revision ID. If the entity migration did not include the
26  * revision ID, the entity would get the next available revision ID (1 when
27  * migrating to a clean database). Then, when revisions are migrated after the
28  * entities, the revision IDs would almost certainly collide.
29  *
30  * The examples below contain simple node and node revision migrations. The
31  * examples use the EmbeddedDataSource source plugin for the sake of
32  * simplicity. The important part of both examples is the 'vid' property, which
33  * is the revision ID for nodes.
34  *
35  * Example of 'article' node migration, which must be executed before the
36  * 'article' revisions.
37  * @code
38  * id: custom_article_migration
39  * label: 'Custom article migration'
40  * source:
41  *   plugin: embedded_data
42  *   data_rows:
43  *     -
44  *       nid: 1
45  *       vid: 2
46  *       revision_timestamp: 1514661000
47  *       revision_log: 'Second revision'
48  *       title: 'Current title'
49  *       content: '<p>Current content</p>'
50  *   ids:
51  *     nid:
52  *       type: integer
53  * process:
54  *   nid: nid
55  *   vid: vid
56  *   revision_timestamp: revision_timestamp
57  *   revision_log: revision_log
58  *   title: title
59  *   'body/0/value': content
60  *   'body/0/format':
61  *      plugin: default_value
62  *      default_value: basic_html
63  * destination:
64  *   plugin: entity:node
65  *   default_bundle: article
66  * @endcode
67  *
68  * Example of the corresponding node revision migration, which must be executed
69  * after the above migration.
70  * @code
71  * id: custom_article_revision_migration
72  * label: 'Custom article revision migration'
73  * source:
74  *   plugin: embedded_data
75  *   data_rows:
76  *     -
77  *       nid: 1
78  *       vid: 1
79  *       revision_timestamp: 1514660000
80  *       revision_log: 'First revision'
81  *       title: 'Previous title'
82  *       content: '<p>Previous content</p>'
83  *   ids:
84  *     nid:
85  *       type: integer
86  * process:
87  *   nid:
88  *     plugin: migration_lookup
89  *     migration: custom_article_migration
90  *     source: nid
91  *   vid: vid
92  *   revision_timestamp: revision_timestamp
93  *   revision_log: revision_log
94  *   title: title
95  *   'body/0/value': content
96  *   'body/0/format':
97  *      plugin: default_value
98  *      default_value: basic_html
99  * destination:
100  *   plugin: entity_revision:node
101  *   default_bundle: article
102  * migration_dependencies:
103  *   required:
104  *     - custom_article_migration
105  * @endcode
106  *
107  * @MigrateDestination(
108  *   id = "entity_revision",
109  *   deriver = "Drupal\migrate\Plugin\Derivative\MigrateEntityRevision"
110  * )
111  */
112 class EntityRevision extends EntityContentBase {
113
114   /**
115    * {@inheritdoc}
116    */
117   public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, FieldTypePluginManagerInterface $field_type_manager) {
118     $plugin_definition += [
119       'label' => new TranslatableMarkup('@entity_type revisions', ['@entity_type' => $storage->getEntityType()->getSingularLabel()]),
120     ];
121     parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_manager, $field_type_manager);
122   }
123
124   /**
125    * {@inheritdoc}
126    */
127   protected static function getEntityTypeId($plugin_id) {
128     // Remove entity_revision:
129     return substr($plugin_id, 16);
130   }
131
132   /**
133    * Gets the entity.
134    *
135    * @param \Drupal\migrate\Row $row
136    *   The row object.
137    * @param array $old_destination_id_values
138    *   The old destination IDs.
139    *
140    * @return \Drupal\Core\Entity\EntityInterface|false
141    *   The entity or false if it can not be created.
142    */
143   protected function getEntity(Row $row, array $old_destination_id_values) {
144     $revision_id = $old_destination_id_values ?
145       reset($old_destination_id_values) :
146       $row->getDestinationProperty($this->getKey('revision'));
147     if (!empty($revision_id) && ($entity = $this->storage->loadRevision($revision_id))) {
148       $entity->setNewRevision(FALSE);
149     }
150     else {
151       $entity_id = $row->getDestinationProperty($this->getKey('id'));
152       $entity = $this->storage->load($entity_id);
153
154       // If we fail to load the original entity something is wrong and we need
155       // to return immediately.
156       if (!$entity) {
157         return FALSE;
158       }
159
160       $entity->enforceIsNew(FALSE);
161       $entity->setNewRevision(TRUE);
162     }
163     // We need to update the entity, so that the destination row IDs are
164     // correct.
165     $entity = $this->updateEntity($entity, $row);
166     $entity->isDefaultRevision(FALSE);
167     return $entity;
168   }
169
170   /**
171    * {@inheritdoc}
172    */
173   protected function save(ContentEntityInterface $entity, array $old_destination_id_values = []) {
174     $entity->save();
175     return [$entity->getRevisionId()];
176   }
177
178   /**
179    * {@inheritdoc}
180    */
181   public function getIds() {
182     $ids = [];
183
184     $revision_key = $this->getKey('revision');
185     if (!$revision_key) {
186       throw new MigrateException(sprintf('The "%s" entity type does not support revisions.', $this->storage->getEntityTypeId()));
187     }
188     $ids[$revision_key] = $this->getDefinitionFromEntity($revision_key);
189
190     if ($this->isTranslationDestination()) {
191       $langcode_key = $this->getKey('langcode');
192       if (!$langcode_key) {
193         throw new MigrateException(sprintf('The "%s" entity type does not support translations.', $this->storage->getEntityTypeId()));
194       }
195       $ids[$langcode_key] = $this->getDefinitionFromEntity($langcode_key);
196     }
197
198     return $ids;
199   }
200
201   /**
202    * {@inheritdoc}
203    */
204   public function getHighestId() {
205     $values = $this->storage->getQuery()
206       ->accessCheck(FALSE)
207       ->allRevisions()
208       ->sort($this->getKey('revision'), 'DESC')
209       ->range(0, 1)
210       ->execute();
211     // The array keys are the revision IDs.
212     // The array contains only one entry, so we can use key().
213     return (int) key($values);
214   }
215
216 }