3 namespace Drupal\migrate\Plugin\migrate\destination;
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;
15 * Provides entity revision destination plugin.
17 * Refer to the parent class for configuration keys:
18 * \Drupal\migrate\Plugin\migrate\destination\EntityContentBase
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.
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.
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.
35 * Example of 'article' node migration, which must be executed before the
36 * 'article' revisions.
38 * id: custom_article_migration
39 * label: 'Custom article migration'
41 * plugin: embedded_data
46 * revision_timestamp: 1514661000
47 * revision_log: 'Second revision'
48 * title: 'Current title'
49 * content: '<p>Current content</p>'
56 * revision_timestamp: revision_timestamp
57 * revision_log: revision_log
59 * 'body/0/value': content
61 * plugin: default_value
62 * default_value: basic_html
65 * default_bundle: article
68 * Example of the corresponding node revision migration, which must be executed
69 * after the above migration.
71 * id: custom_article_revision_migration
72 * label: 'Custom article revision migration'
74 * plugin: embedded_data
79 * revision_timestamp: 1514660000
80 * revision_log: 'First revision'
81 * title: 'Previous title'
82 * content: '<p>Previous content</p>'
88 * plugin: migration_lookup
89 * migration: custom_article_migration
92 * revision_timestamp: revision_timestamp
93 * revision_log: revision_log
95 * 'body/0/value': content
97 * plugin: default_value
98 * default_value: basic_html
100 * plugin: entity_revision:node
101 * default_bundle: article
102 * migration_dependencies:
104 * - custom_article_migration
107 * @MigrateDestination(
108 * id = "entity_revision",
109 * deriver = "Drupal\migrate\Plugin\Derivative\MigrateEntityRevision"
112 class EntityRevision extends EntityContentBase {
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()]),
121 parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_manager, $field_type_manager);
127 protected static function getEntityTypeId($plugin_id) {
128 // Remove entity_revision:
129 return substr($plugin_id, 16);
135 * @param \Drupal\migrate\Row $row
137 * @param array $old_destination_id_values
138 * The old destination IDs.
140 * @return \Drupal\Core\Entity\EntityInterface|false
141 * The entity or false if it can not be created.
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);
151 $entity_id = $row->getDestinationProperty($this->getKey('id'));
152 $entity = $this->storage->load($entity_id);
154 // If we fail to load the original entity something is wrong and we need
155 // to return immediately.
160 $entity->enforceIsNew(FALSE);
161 $entity->setNewRevision(TRUE);
163 // We need to update the entity, so that the destination row IDs are
165 $entity = $this->updateEntity($entity, $row);
166 $entity->isDefaultRevision(FALSE);
173 protected function save(ContentEntityInterface $entity, array $old_destination_id_values = []) {
175 return [$entity->getRevisionId()];
181 public function getIds() {
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()));
188 $ids[$revision_key] = $this->getDefinitionFromEntity($revision_key);
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()));
195 $ids[$langcode_key] = $this->getDefinitionFromEntity($langcode_key);
204 public function getHighestId() {
205 $values = $this->storage->getQuery()
208 ->sort($this->getKey('revision'), 'DESC')
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);