3 namespace Drupal\Tests\entity_reference_revisions\Kernel;
5 use Drupal\entity_composite_relationship_test\Entity\EntityTestCompositeRelationship;
6 use Drupal\field\Entity\FieldConfig;
7 use Drupal\field\Entity\FieldStorageConfig;
8 use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
9 use Drupal\language\Entity\ConfigurableLanguage;
10 use Drupal\node\Entity\Node;
11 use Drupal\node\Entity\NodeType;
12 use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
13 use Drupal\Tests\node\Traits\NodeCreationTrait;
16 * Tests entity_reference_revisions composites with a translatable field.
18 * @group entity_reference_revisions
20 class EntityReferenceRevisionsCompositeTranslatableFieldTest extends EntityKernelTestBase {
22 use ContentTypeCreationTrait;
23 use NodeCreationTrait;
30 public static $modules = array(
33 'entity_reference_revisions',
34 'entity_composite_relationship_test',
40 * The current database connection.
42 * @var \Drupal\Core\Database\Connection
47 * The entity type manager.
49 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
52 protected $entityTypeManager;
57 protected function setUp() {
60 ConfigurableLanguage::createFromLangcode('de')->save();
61 ConfigurableLanguage::createFromLangcode('fr')->save();
63 $this->installEntitySchema('entity_test_composite');
64 $this->installSchema('node', ['node_access']);
66 // Create article content type.
67 NodeType::create(['type' => 'article', 'name' => 'Article'])->save();
69 // Create the reference to the composite entity test.
70 $field_storage = FieldStorageConfig::create(array(
71 'field_name' => 'composite_reference',
72 'entity_type' => 'node',
73 'type' => 'entity_reference_revisions',
75 'target_type' => 'entity_test_composite'
78 $field_storage->save();
79 $field = FieldConfig::create(array(
80 'field_storage' => $field_storage,
81 'bundle' => 'article',
82 'translatable' => TRUE,
86 // Inject database connection and entity type manager for the tests.
87 $this->database = \Drupal::database();
88 $this->entityTypeManager = \Drupal::entityTypeManager();
90 // @todo content_translation should not be needed for a storage test, but
91 // \Drupal\Core\Entity\ContentEntityBase::isTranslatable() only returns
92 // TRUE if the bundle is explicitly translatable.
93 \Drupal::service('content_translation.manager')->setEnabled('node', 'article', TRUE);
94 \Drupal::service('content_translation.manager')->setEnabled('entity_test_composite', 'entity_test_composite', TRUE);
95 \Drupal::service('content_translation.manager')->setBundleTranslationSettings('node', 'article', [
96 'untranslatable_fields_hide' => TRUE,
98 \Drupal::service('entity_type.bundle.info')->clearCachedBundles();
102 * Test the storage for handling pending revisions with translations.
104 public function testCompositePendingRevisionTranslation() {
105 /** @var \Drupal\node\NodeStorageInterface $node_storage */
106 $node_storage = \Drupal::entityTypeManager()->getStorage('node');
108 // Create the test composite entity.
109 $composite = EntityTestCompositeRelationship::create([
111 'name' => 'Initial Source Composite',
115 // Create a node with a reference to the test composite entity.
116 $node = Node::create([
118 'title' => 'Initial Source Node',
120 'composite_reference' => $composite,
124 /** @var \Drupal\node\NodeInterface $node */
125 $node = $node_storage->load($node->id());
127 // Assert the revision count.
128 $this->assertRevisionCount(1, 'node', $node->id());
129 $this->assertRevisionCount(1, 'entity_test_composite', $composite->id());
131 // Create a translation as a pending revision for both the composite and the
132 // node. While technically, the referenced composite could be the same
133 // entity, for translatable fields, it makes more sense if each translation
134 // points to a separate entity, each only with a single language.
135 $composite_de = $node->get('composite_reference')->entity->createDuplicate();
136 $composite_de->set('langcode', 'de');
137 $composite_de->set('name', 'Pending Revision Composite #1 DE');
138 /** @var \Drupal\node\NodeInterface $node_de */
139 $node_de = $node->addTranslation('de', ['title' => 'Pending Revision Node #1 DE', 'composite_reference' => $composite_de] + $node->toArray());
140 $node_de->setNewRevision(TRUE);
141 $node_de->isDefaultRevision(FALSE);
144 // Assert the revision count.
145 $this->assertRevisionCount(2, 'node', $node->id());
146 $this->assertRevisionCount(1, 'entity_test_composite', $composite->id());
147 $this->assertRevisionCount(1, 'entity_test_composite', $composite_de->id());
149 // The DE translation will now reference to a pending revision of the
150 // composite entity but the en translation will reference the existing,
151 // unchanged revision.
152 /** @var \Drupal\node\NodeInterface $node_revision */
153 $node_revision = $node_storage->loadRevision($node_de->getRevisionId());
154 $this->assertFalse($node_revision->isDefaultRevision());
155 $this->assertFalse((bool) $node_revision->isRevisionTranslationAffected());
156 $this->assertEquals('Initial Source Node', $node_revision->label());
157 $this->assertTrue($node_revision->get('composite_reference')->entity->isDefaultRevision());
158 $this->assertEquals('Initial Source Composite', $node_revision->get('composite_reference')->entity->label());
159 $this->assertFalse($node_revision->get('composite_reference')->entity->hasTranslation('de'));
160 $this->assertEquals($node->get('composite_reference')->target_revision_id, $node_revision->get('composite_reference')->target_revision_id);
162 $node_de = $node_revision->getTranslation('de');
163 $this->assertTrue((bool) $node_de->isRevisionTranslationAffected());
164 $this->assertEquals('Pending Revision Node #1 DE', $node_de->label());
165 // The composite is the default revision because it is a new entity.
166 $this->assertTrue($node_de->get('composite_reference')->entity->isDefaultRevision());
167 $this->assertEquals('Pending Revision Composite #1 DE', $node_de->get('composite_reference')->entity->label());
168 $this->assertNotEquals($node->get('composite_reference')->target_revision_id, $node_de->get('composite_reference')->target_revision_id);
170 // Reload the default revision of the node, make sure that the composite
171 // there is unchanged.
172 $node = $node_storage->load($node->id());
173 $this->assertFalse($node->hasTranslation('de'));
174 $this->assertEquals('Initial Source Node', $node->label());
175 $this->assertFalse($node->get('composite_reference')->entity->hasTranslation('de'));
176 $this->assertEquals('Initial Source Composite', $node->get('composite_reference')->entity->label());
178 // Create a second translation revision for FR.
179 $composite_fr = $node->get('composite_reference')->entity->createDuplicate();
180 $composite_fr->set('langcode', 'fr');
181 $composite_fr->set('name', 'Pending Revision Composite #1 FR');
182 $node_fr = $node->addTranslation('fr', ['title' => 'Pending Revision Node #1 FR', 'composite_reference' => $composite_fr] + $node->toArray());
183 $node_fr->setNewRevision(TRUE);
184 $node_fr->isDefaultRevision(FALSE);
187 // Assert the revision count.
188 $this->assertRevisionCount(3, 'node', $node->id());
189 $this->assertRevisionCount(1, 'entity_test_composite', $composite->id());
190 $this->assertRevisionCount(1, 'entity_test_composite', $composite_de->id());
191 $this->assertRevisionCount(1, 'entity_test_composite', $composite_fr->id());
193 // Now assert that all 3 revisions exist as expected. Two translation
194 // pending revisions, each has the original revision as parent without
195 // any existing translation.
196 /** @var \Drupal\node\NodeInterface $node_fr */
197 $node_revision = $node_storage->loadRevision($node_fr->getRevisionId());
198 $this->assertFalse($node_revision->isDefaultRevision());
199 $this->assertFalse((bool) $node_revision->isRevisionTranslationAffected());
200 $this->assertEquals('Initial Source Node', $node_revision->label());
201 $this->assertTrue($node_revision->get('composite_reference')->entity->isDefaultRevision());
202 $this->assertEquals('Initial Source Composite', $node_revision->get('composite_reference')->entity->label());
203 $this->assertFalse($node_revision->get('composite_reference')->entity->hasTranslation('de'));
204 $this->assertEquals($node->get('composite_reference')->target_revision_id, $node_revision->get('composite_reference')->target_revision_id);
206 $node_fr = $node_revision->getTranslation('fr');
207 $this->assertTrue((bool) $node_fr->isRevisionTranslationAffected());
208 $this->assertEquals('Pending Revision Node #1 FR', $node_fr->label());
209 $this->assertTrue($node_fr->get('composite_reference')->entity->isDefaultRevision());
210 $this->assertEquals('Pending Revision Composite #1 FR', $node_fr->get('composite_reference')->entity->label());
211 $this->assertNotEquals($node->get('composite_reference')->target_revision_id, $node_fr->get('composite_reference')->target_revision_id);
213 $node_de = $node_storage->loadRevision($node_de->getRevisionId())->getTranslation('de');
214 $this->assertTrue((bool) $node_de->isRevisionTranslationAffected());
215 $this->assertEquals('Pending Revision Node #1 DE', $node_de->label());
216 $this->assertTrue($node_de->get('composite_reference')->entity->isDefaultRevision());
217 $this->assertEquals('Pending Revision Composite #1 DE', $node_de->get('composite_reference')->entity->label());
218 $this->assertNotEquals($node->get('composite_reference')->target_revision_id, $node_de->get('composite_reference')->target_revision_id);
220 // Reload the default revision of the node, make sure that the composite
221 // there is unchanged.
222 $node = $node_storage->load($node->id());
223 $this->assertFalse($node->hasTranslation('de'));
224 $this->assertEquals('Initial Source Node', $node->label());
225 $this->assertFalse($node->get('composite_reference')->entity->hasTranslation('de'));
226 $this->assertEquals('Initial Source Composite', $node->get('composite_reference')->entity->label());
228 // Now make a change to the initial source revision, save as a new default
230 $initial_revision_id = $node->getRevisionId();
231 $node->get('composite_reference')->entity->set('name', 'Updated Source Composite');
232 $node->setTitle('Updated Source Node');
233 $node->setNewRevision(TRUE);
236 // Assert the revision count.
237 $this->assertRevisionCount(4, 'node', $node->id());
238 $this->assertRevisionCount(2, 'entity_test_composite', $composite->id());
239 $this->assertRevisionCount(1, 'entity_test_composite', $composite_de->id());
240 $this->assertRevisionCount(1, 'entity_test_composite', $composite_fr->id());
242 // Assert the two english revisions.
243 // Reload the default revision of the node, make sure that the composite
244 // there is unchanged.
245 $node = $node_storage->load($node->id());
246 $this->assertTrue($node->isDefaultRevision());
247 $this->assertFalse($node->hasTranslation('de'));
248 $this->assertFalse($node->hasTranslation('fr'));
249 $this->assertTrue((bool) $node->isRevisionTranslationAffected());
250 $this->assertEquals('Updated Source Node', $node->label());
251 $this->assertTrue($node->get('composite_reference')->entity->isDefaultRevision());
252 $this->assertFalse($node->get('composite_reference')->entity->hasTranslation('de'));
253 $this->assertEquals('Updated Source Composite', $node->get('composite_reference')->entity->label());
255 $node_initial = $node_storage->loadRevision($initial_revision_id);
256 $this->assertFalse($node_initial->isDefaultRevision());
257 $this->assertFalse($node_initial->hasTranslation('de'));
258 $this->assertFalse($node_initial->hasTranslation('fr'));
259 $this->assertEquals('Initial Source Node', $node_initial->label());
260 $this->assertFalse($node_initial->get('composite_reference')->entity->isDefaultRevision());
261 $this->assertFalse($node_initial->get('composite_reference')->entity->hasTranslation('de'));
262 $this->assertEquals('Initial Source Composite', $node_initial->get('composite_reference')->entity->label());
264 // Now publish the FR pending revision.
265 $node_storage->createRevision($node_fr->getTranslation('fr'))->save();
267 // Assert the revision count.
268 $this->assertRevisionCount(5, 'node', $node->id());
269 $this->assertRevisionCount(2, 'entity_test_composite', $composite->id());
270 $this->assertRevisionCount(1, 'entity_test_composite', $composite_de->id());
271 $this->assertRevisionCount(1, 'entity_test_composite', $composite_fr->id());
273 // The new default revision should now have the updated english source and
274 // the french pending revision.
275 $node = $node_storage->load($node->id());
276 $this->assertTrue($node->isDefaultRevision());
277 $this->assertFalse($node->hasTranslation('de'));
278 $this->assertTrue($node->hasTranslation('fr'));
279 $node_fr = $node->getTranslation('fr');
280 $this->assertFalse((bool) $node->isRevisionTranslationAffected());
281 $this->assertTrue((bool) $node->getTranslation('fr')->isRevisionTranslationAffected());
282 $this->assertEquals('Updated Source Node', $node->label());
283 $this->assertTrue($node->get('composite_reference')->entity->isDefaultRevision());
284 $this->assertFalse($node->get('composite_reference')->entity->hasTranslation('de'));
285 $this->assertTrue($node_fr->get('composite_reference')->entity->hasTranslation('fr'));
286 $this->assertEquals('Pending Revision Node #1 FR', $node_fr->label());
287 $this->assertEquals('Pending Revision Composite #1 FR', $node_fr->get('composite_reference')->entity->getTranslation('fr')->label());
288 $this->assertEquals('Updated Source Composite', $node->get('composite_reference')->entity->label());
290 // Now publish the DE pending revision as well.
291 $node_storage->createRevision($node_de->getTranslation('de'))->save();
293 // Assert the revision count.
294 $this->assertRevisionCount(6, 'node', $node->id());
295 $this->assertRevisionCount(2, 'entity_test_composite', $composite->id());
296 $this->assertRevisionCount(1, 'entity_test_composite', $composite_de->id());
297 $this->assertRevisionCount(1, 'entity_test_composite', $composite_fr->id());
299 // The new default revision should now have the updated source and both
301 $node = $node_storage->load($node->id());
302 $this->assertTrue($node->isDefaultRevision());
303 $this->assertTrue($node->hasTranslation('de'));
304 $this->assertTrue($node->hasTranslation('fr'));
305 $node_fr = $node->getTranslation('fr');
306 $node_de = $node->getTranslation('de');
307 $this->assertFalse((bool) $node->isRevisionTranslationAffected());
308 $this->assertFalse((bool) $node->getTranslation('fr')->isRevisionTranslationAffected());
309 $this->assertTrue((bool) $node->getTranslation('de')->isRevisionTranslationAffected());
310 $this->assertEquals('Updated Source Node', $node->label());
312 // Each translation only has the composite in its translation.
313 $this->assertTrue($node->get('composite_reference')->entity->hasTranslation('en'));
314 $this->assertFalse($node->get('composite_reference')->entity->hasTranslation('de'));
315 $this->assertFalse($node->get('composite_reference')->entity->hasTranslation('fr'));
316 $this->assertFalse($node_fr->get('composite_reference')->entity->hasTranslation('en'));
317 $this->assertTrue($node_fr->get('composite_reference')->entity->hasTranslation('fr'));
318 $this->assertFalse($node_fr->get('composite_reference')->entity->hasTranslation('de'));
319 $this->assertFalse($node_de->get('composite_reference')->entity->hasTranslation('en'));
320 $this->assertTrue($node_de->get('composite_reference')->entity->hasTranslation('de'));
321 $this->assertFalse($node_de->get('composite_reference')->entity->hasTranslation('fr'));
323 $this->assertEquals('Pending Revision Node #1 FR', $node_fr->label());
324 $this->assertEquals('Pending Revision Composite #1 FR', $node_fr->get('composite_reference')->entity->getTranslation('fr')->label());
325 $this->assertEquals('Pending Revision Node #1 DE', $node_de->label());
326 $this->assertEquals('Pending Revision Composite #1 DE', $node_de->get('composite_reference')->entity->getTranslation('de')->label());
327 $this->assertEquals('Updated Source Composite', $node->get('composite_reference')->entity->label());
331 * Asserts the revision count of a certain entity.
333 * @param int $expected
334 * The expected count.
335 * @param string $entity_type_id
336 * The entity type ID, e.g. node.
337 * @param int $entity_id
340 protected function assertRevisionCount($expected, $entity_type_id, $entity_id) {
341 $id_field = \Drupal::entityTypeManager()->getDefinition($entity_type_id)->getKey('id');
343 $revision_count = \Drupal::entityQuery($entity_type_id)
344 ->condition($id_field, $entity_id)
348 $this->assertEquals($expected, $revision_count);