Added Entity and Entity Reference Revisions which got dropped somewhere along the...
[yaffs-website] / web / modules / contrib / entity_reference_revisions / tests / src / Kernel / EntityReferenceRevisionsCompositeTranslationTest.php
1 <?php
2
3 namespace Drupal\Tests\entity_reference_revisions\Kernel;
4
5 use Drupal\Core\Entity\EntityInterface;
6 use Drupal\Core\Field\FieldStorageDefinitionInterface;
7 use Drupal\entity_composite_relationship_test\Entity\EntityTestCompositeRelationship;
8 use Drupal\field\Entity\FieldConfig;
9 use Drupal\field\Entity\FieldStorageConfig;
10 use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
11 use Drupal\language\Entity\ConfigurableLanguage;
12 use Drupal\node\Entity\Node;
13 use Drupal\node\Entity\NodeType;
14 use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
15 use Drupal\Tests\node\Traits\NodeCreationTrait;
16
17 /**
18  * Tests the entity_reference_revisions composite relationship.
19  *
20  * @group entity_reference_revisions
21  */
22 class EntityReferenceRevisionsCompositeTranslationTest extends EntityKernelTestBase {
23
24   use ContentTypeCreationTrait;
25   use NodeCreationTrait;
26
27   /**
28    * Modules to enable.
29    *
30    * @var array
31    */
32   public static $modules = [
33     'node',
34     'field',
35     'entity_reference_revisions',
36     'entity_composite_relationship_test',
37     'language',
38     'content_translation'
39   ];
40
41   /**
42    * The current database connection.
43    *
44    * @var \Drupal\Core\Database\Connection
45    */
46   protected $database;
47
48   /**
49    * The entity type manager.
50    *
51    * @var \Drupal\Core\Entity\EntityTypeManagerInterface
52    *
53    */
54   protected $entityTypeManager;
55
56   /**
57    * {@inheritdoc}
58    */
59   protected function setUp() {
60     parent::setUp();
61
62     ConfigurableLanguage::createFromLangcode('de')->save();
63     ConfigurableLanguage::createFromLangcode('fr')->save();
64
65     $this->installEntitySchema('entity_test_composite');
66     $this->installSchema('node', ['node_access']);
67
68     // Create article content type.
69     NodeType::create(['type' => 'article', 'name' => 'Article'])->save();
70
71     // Create the reference to the composite entity test.
72     $field_storage = FieldStorageConfig::create([
73       'field_name' => 'composite_reference',
74       'entity_type' => 'node',
75       'type' => 'entity_reference_revisions',
76       'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
77       'settings' => [
78         'target_type' => 'entity_test_composite'
79       ],
80     ]);
81     $field_storage->save();
82     $field = FieldConfig::create([
83       'field_storage' => $field_storage,
84       'bundle' => 'article',
85       'translatable' => FALSE,
86     ]);
87     $field->save();
88
89     // Create an untranslatable field on the composite entity.
90     $text_field_storage = FieldStorageConfig::create([
91       'field_name' => 'field_untranslatable',
92       'entity_type' => 'entity_test_composite',
93       'type' => 'string',
94     ]);
95     $text_field_storage->save();
96     $text_field = FieldConfig::create([
97       'field_storage' => $text_field_storage,
98       'bundle' => 'entity_test_composite',
99       'translatable' => FALSE,
100     ]);
101     $text_field->save();
102
103     // Add a nested composite field.
104     $field_storage = FieldStorageConfig::create([
105       'field_name' => 'composite_reference',
106       'entity_type' => 'entity_test_composite',
107       'type' => 'entity_reference_revisions',
108       'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
109       'settings' => [
110         'target_type' => 'entity_test_composite'
111       ],
112     ]);
113     $field_storage->save();
114     $field = FieldConfig::create([
115       'field_storage' => $field_storage,
116       'bundle' => 'entity_test_composite',
117       'translatable' => FALSE,
118     ]);
119     $field->save();
120
121     // Inject database connection and entity type manager for the tests.
122     $this->database = \Drupal::database();
123     $this->entityTypeManager = \Drupal::entityTypeManager();
124
125     // @todo content_translation should not be needed for a storage test, but
126     //   \Drupal\Core\Entity\ContentEntityBase::isTranslatable() only returns
127     //   TRUE if the bundle is explicitly translatable.
128     \Drupal::service('content_translation.manager')->setEnabled('node', 'article', TRUE);
129     \Drupal::service('content_translation.manager')->setEnabled('entity_test_composite', 'entity_test_composite', TRUE);
130     \Drupal::service('content_translation.manager')->setBundleTranslationSettings('node', 'article', [
131       'untranslatable_fields_hide' => TRUE,
132     ]);
133     \Drupal::service('entity_type.bundle.info')->clearCachedBundles();
134   }
135
136   /**
137    * Test the storage for handling pending revisions with translations.
138    */
139   public function testCompositePendingRevisionTranslation() {
140     /** @var \Drupal\node\NodeStorageInterface $node_storage */
141     $node_storage = \Drupal::entityTypeManager()->getStorage('node');
142
143     // Create a nested composite entity.
144     $nested_composite = EntityTestCompositeRelationship::create([
145       'langcode' => 'en',
146       'name' => 'Initial Nested Source Composite',
147     ]);
148     $nested_composite->save();
149
150     // Create a composite entity.
151     $composite = EntityTestCompositeRelationship::create([
152       'langcode' => 'en',
153       'name' => 'Initial Source Composite',
154       'field_untranslatable' => 'Initial untranslatable field',
155       'composite_reference' => $nested_composite,
156     ]);
157     $composite->save();
158
159     // Create a node with a reference to the test composite entity.
160     $node = Node::create([
161       'langcode' => 'en',
162       'title' => 'Initial Source Node',
163       'type' => 'article',
164       'composite_reference' => $composite,
165     ]);
166     $node->save();
167     $initial_revision_id = $node->getRevisionId();
168
169     /** @var \Drupal\node\NodeInterface $node */
170     $node = $node_storage->load($node->id());
171
172     // Assert that there is only 1 revision when creating a node.
173     $this->assertRevisionCount(1, $node);
174     // Assert there is no new composite revision after creating a host entity.
175     $this->assertRevisionCount(1, $composite);
176     // Assert there is no new composite revision after creating a host entity.
177     $this->assertRevisionCount(1, $nested_composite);
178
179     // Create a second nested composite entity.
180     $second_nested_composite = EntityTestCompositeRelationship::create([
181       'langcode' => 'en',
182       'name' => 'Initial Nested Composite #2',
183     ]);
184
185     // Add a pending revision.
186     $node = $node_storage->createRevision($node, FALSE);
187     $node->get('composite_reference')->entity->get('composite_reference')->appendItem($second_nested_composite);
188     $node->save();
189     $pending_en_revision_id = $node->getRevisionId();
190
191     $this->assertRevisionCount(2, $node);
192     $this->assertRevisionCount(2, $composite);
193     $this->assertRevisionCount(2, $nested_composite);
194     $this->assertRevisionCount(1, $second_nested_composite);
195
196     // Create a DE translation, start as a draft to replicate the behavior of
197     // the UI.
198     $node_de = $node->addTranslation('de', ['title' => 'New Node #1 DE'] + $node->toArray());
199     $node_de = $node_storage->createRevision($node_de, FALSE);
200
201     // Despite starting of the draft revision, creating draft of the translation
202     // uses the paragraphs of the default revision.
203     $this->assertCount(1, $node_de->get('composite_reference')->entity->get('composite_reference'));
204
205     $node_de->get('composite_reference')->entity->getTranslation('de')->set('name', 'New Composite #1 DE');
206     $node_de->get('composite_reference')->entity->get('composite_reference')->entity->getTranslation('de')->set('name', 'New Nested Composite #1 DE');
207     $node_de->isDefaultRevision(TRUE);
208     $violations = $node_de->validate();
209     foreach ($violations as $violation) {
210       $this->fail($violation->getPropertyPath() . ': ' . $violation->getMessage());
211     }
212     $this->assertEquals(0, count($violations));
213     $node_de->save();
214
215     $this->assertRevisionCount(3, $node);
216     $this->assertRevisionCount(3, $composite);
217     $this->assertRevisionCount(3, $nested_composite);
218     $this->assertRevisionCount(1, $second_nested_composite);
219
220     // Update the translation as a pending revision for both the composite and
221     // the node.
222     $node_de->get('composite_reference')->entity->getTranslation('de')->set('name', 'Pending Revision Composite #1 DE');
223     $node_de->get('composite_reference')->entity->get('composite_reference')->entity->getTranslation('de')->set('name', 'Pending Nested Composite #1 DE');
224     $node_de->set('title', 'Pending Revision Node #1 DE');
225     $node_de->setNewRevision(TRUE);
226     $node_de->isDefaultRevision(FALSE);
227     $violations = $node_de->validate();
228     foreach ($violations as $violation) {
229       $this->fail($violation->getMessage());
230     }
231     $this->assertEquals(0, count($violations));
232     $node_de->save();
233
234     $this->assertRevisionCount(4, $node);
235     $this->assertRevisionCount(4, $composite);
236     $this->assertRevisionCount(4, $nested_composite);
237     $this->assertRevisionCount(1, $second_nested_composite);
238
239     /** @var \Drupal\node\NodeInterface $node_de */
240     $node_de = $node_storage->loadRevision($node_de->getRevisionId());
241     $this->assertFalse($node_de->isDefaultRevision());
242     $this->assertFalse((bool) $node_de->isRevisionTranslationAffected());
243     $this->assertTrue((bool) $node_de->getTranslation('de')->isRevisionTranslationAffected());
244     $this->assertEquals('Pending Revision Node #1 DE', $node_de->getTranslation('de')->label());
245     $this->assertEquals('Initial Source Node', $node_de->label());
246     $this->assertFalse($node_de->get('composite_reference')->entity->isDefaultRevision());
247     $this->assertEquals('Pending Revision Composite #1 DE', $node_de->get('composite_reference')->entity->getTranslation('de')->label());
248     $this->assertEquals('Pending Nested Composite #1 DE', $node_de->get('composite_reference')->entity->get('composite_reference')->entity->getTranslation('de')->label());
249     $this->assertEquals('Initial untranslatable field', $node_de->get('composite_reference')->entity->getTranslation('de')->get('field_untranslatable')->value);
250     $this->assertEquals('Initial Source Composite', $node_de->get('composite_reference')->entity->label());
251
252     // Reload the default revision of the node, make sure that the composite
253     // there is unchanged.
254     $node = $node_storage->load($node->id());
255     $this->assertTrue($node->hasTranslation('de'));
256     $this->assertEquals('Initial Source Node', $node->label());
257     $this->assertTrue($node->get('composite_reference')->entity->hasTranslation('de'));
258     $this->assertEquals('Initial Source Composite', $node->get('composite_reference')->entity->label());
259
260     // Create a FR translation, start as a draft to replicate the behavior of
261     // the UI.
262     $node_fr = $node->addTranslation('fr', ['title' => 'Pending Revision Node #1 FR'] + $node->toArray());
263     $node_fr = $node_storage->createRevision($node_fr, FALSE);
264     $node_fr->get('composite_reference')->entity->getTranslation('fr')->set('name', 'Pending Revision Composite #1 FR');
265     $node_fr->get('composite_reference')->entity->get('composite_reference')->entity->getTranslation('fr')->set('name', 'Pending Nested Composite #1 FR');
266     $violations = $node_fr->validate();
267     $this->assertEquals(0, count($violations));
268     $node_fr->save();
269
270     // Now assert that all 3 revisions exist as expected. Two translation
271     // pending revisions, each composite has the original revision as parent
272     // without any existing translation.
273     /** @var \Drupal\node\NodeInterface $node_fr */
274     $node_fr = $node_storage->loadRevision($node_fr->getRevisionId());
275     $this->assertFalse($node_fr->isDefaultRevision());
276     $this->assertTrue($node_fr->hasTranslation('de'));
277     $this->assertFalse((bool) $node_fr->isRevisionTranslationAffected());
278     $this->assertTrue((bool) $node_fr->getTranslation('fr')->isRevisionTranslationAffected());
279     $this->assertEquals('Pending Revision Node #1 FR', $node_fr->getTranslation('fr')->label());
280     $this->assertEquals('Initial Source Node', $node_fr->label());
281     $this->assertFalse($node_fr->get('composite_reference')->entity->isDefaultRevision());
282     $this->assertTrue($node_fr->get('composite_reference')->entity->hasTranslation('de'));
283     $this->assertEquals('Pending Revision Composite #1 FR', $node_fr->get('composite_reference')->entity->getTranslation('fr')->label());
284     $this->assertEquals('Pending Nested Composite #1 FR', $node_fr->get('composite_reference')->entity->get('composite_reference')->entity->getTranslation('fr')->label());
285     $this->assertEquals('Initial untranslatable field', $node_fr->get('composite_reference')->entity->getTranslation('fr')->get('field_untranslatable')->value);
286     $this->assertEquals('Initial Source Composite', $node_fr->get('composite_reference')->entity->label());
287
288     $node_de = $node_storage->loadRevision($node_de->getRevisionId());
289     $this->assertFalse($node_de->isDefaultRevision());
290     $this->assertFalse($node_de->hasTranslation('fr'));
291     $this->assertEquals('Pending Revision Node #1 DE', $node_de->getTranslation('de')->label());
292     $this->assertEquals('Initial Source Node', $node_de->label());
293     $this->assertFalse($node_de->get('composite_reference')->entity->isDefaultRevision());
294     $this->assertFalse($node_de->get('composite_reference')->entity->hasTranslation('fr'));
295     $this->assertEquals('Pending Revision Composite #1 DE', $node_de->get('composite_reference')->entity->getTranslation('de')->label());
296     $this->assertEquals('Pending Nested Composite #1 DE', $node_de->get('composite_reference')->entity->get('composite_reference')->entity->getTranslation('de')->label());
297     $this->assertEquals('Initial untranslatable field', $node_de->get('composite_reference')->entity->getTranslation('de')->get('field_untranslatable')->value);
298     $this->assertEquals('Initial Source Composite', $node_de->get('composite_reference')->entity->label());
299
300     // Reload the default revision of the node, make sure that the composite
301     // there is unchanged.
302     $node = $node_storage->load($node->id());
303     $this->assertTrue($node->hasTranslation('de'));
304     $this->assertEquals('Initial Source Node', $node->label());
305     $this->assertTrue($node->get('composite_reference')->entity->hasTranslation('de'));
306     $this->assertEquals('Initial Source Composite', $node->get('composite_reference')->entity->label());
307
308     // Create another pending EN revision and make that the default.
309     $node = $node_storage->loadRevision($pending_en_revision_id);
310     $new_revision = $node_storage->createRevision($node);
311     $new_revision->get('composite_reference')->entity->set('name', 'Updated Source Composite');
312     $new_revision->get('composite_reference')->entity->set('field_untranslatable', 'Updated untranslatable field');
313     $new_revision->setTitle('Updated Source Node');
314     $new_revision->get('composite_reference')->entity->get('composite_reference')[1]->entity->set('name', 'Draft Nested Source Composite #2');
315     $violations = $new_revision->validate();
316     $this->assertEquals(0, count($violations));
317     $new_revision->save();
318
319     // Assert the two english revisions.
320     // Reload the default revision of the node, make sure that the composite
321     // there is unchanged.
322     $node = $node_storage->load($node->id());
323     $this->assertTrue($node->isDefaultRevision());
324     $this->assertTrue($node->hasTranslation('de'));
325     $this->assertFalse($node->hasTranslation('fr'));
326     $this->assertTrue((bool) $node->isRevisionTranslationAffected());
327     $this->assertEquals('Updated Source Node', $node->label());
328     $this->assertTrue($node->get('composite_reference')->entity->isDefaultRevision());
329     $this->assertTrue($node->get('composite_reference')->entity->hasTranslation('de'));
330     $this->assertFalse($node->get('composite_reference')->entity->hasTranslation('fr'));
331     $this->assertEquals('Updated Source Composite', $node->get('composite_reference')->entity->label());
332     $this->assertEquals('Initial Nested Source Composite', $node->get('composite_reference')->entity->get('composite_reference')->entity->label());
333     $this->assertEquals('Draft Nested Source Composite #2', $node->get('composite_reference')->entity->get('composite_reference')[1]->entity->label());
334     $this->assertEquals('Updated untranslatable field', $node->get('composite_reference')->entity->get('field_untranslatable')->value);
335
336     $node_initial = $node_storage->loadRevision($initial_revision_id);
337     $this->assertFalse($node_initial->isDefaultRevision());
338     $this->assertFalse($node_initial->hasTranslation('de'));
339     $this->assertFalse($node_initial->hasTranslation('fr'));
340     $this->assertEquals('Initial Source Node', $node_initial->label());
341     $this->assertFalse($node_initial->get('composite_reference')->entity->isDefaultRevision());
342     $this->assertFalse($node_initial->get('composite_reference')->entity->hasTranslation('de'));
343     $this->assertEquals('Initial Source Composite', $node_initial->get('composite_reference')->entity->label());
344     $this->assertEquals('Initial Nested Source Composite', $node_initial->get('composite_reference')->entity->get('composite_reference')->entity->label());
345     $this->assertEquals('Initial untranslatable field', $node_initial->get('composite_reference')->entity->get('field_untranslatable')->value);
346     $this->assertCount(1, $node_initial->get('composite_reference')->entity->get('composite_reference'));
347
348     // The current node_fr pending revision still has the initial value before
349     // "merging" it, but it will get the new value for the untranslatable field
350     // in the new revision.
351     $node_fr = $node_storage->loadRevision($node_fr->getRevisionId());
352     $this->assertEquals('Initial untranslatable field', $node_fr->get('composite_reference')->entity->get('field_untranslatable')->value);
353     $this->assertCount(1, $node_fr->get('composite_reference')->entity->get('composite_reference'));
354
355     // Now publish the FR pending revision and also add a translation for
356     // the second composite that it now has.
357     $new_revision = $node_storage->createRevision($node_fr->getTranslation('fr'));
358     $this->assertCount(2, $new_revision->get('composite_reference')->entity->get('composite_reference'));
359     $new_revision->get('composite_reference')->entity->get('composite_reference')[1]->entity->getTranslation('fr')->set('name', 'FR Nested Composite #2');
360
361     $violations = $new_revision->validate();
362     $this->assertEquals(0, count($violations));
363     $new_revision->save();
364
365     $this->assertRevisionCount(7, $node);
366     $this->assertRevisionCount(7, $composite);
367     $this->assertRevisionCount(7, $nested_composite);
368     $this->assertRevisionCount(3, $second_nested_composite);
369
370     // The new default revision should now have the updated english source,
371     // original german translation and the french pending revision.
372     $node = $node_storage->load($node->id());
373     $this->assertTrue($node->isDefaultRevision());
374     $this->assertTrue($node->hasTranslation('de'));
375     $this->assertTrue($node->hasTranslation('fr'));
376     $this->assertFalse((bool) $node->isRevisionTranslationAffected());
377     $this->assertTrue((bool) $node->getTranslation('fr')->isRevisionTranslationAffected());
378     $this->assertEquals('Updated Source Node', $node->label());
379     $this->assertTrue($node->get('composite_reference')->entity->isDefaultRevision());
380     $this->assertTrue($node->get('composite_reference')->entity->hasTranslation('de'));
381     $this->assertTrue($node->get('composite_reference')->entity->hasTranslation('fr'));
382     $this->assertEquals('Pending Revision Node #1 FR', $node->getTranslation('fr')->label());
383     $this->assertEquals('Pending Revision Composite #1 FR', $node->get('composite_reference')->entity->getTranslation('fr')->label());
384     $this->assertEquals('Pending Nested Composite #1 FR', $node->get('composite_reference')->entity->get('composite_reference')->entity->getTranslation('fr')->label());
385     $this->assertEquals('New Node #1 DE', $node->getTranslation('de')->label());
386     $this->assertEquals('New Composite #1 DE', $node->get('composite_reference')->entity->getTranslation('de')->label());
387     $this->assertEquals('New Nested Composite #1 DE', $node->get('composite_reference')->entity->get('composite_reference')->entity->getTranslation('de')->label());
388     $this->assertEquals('Updated Source Composite', $node->get('composite_reference')->entity->label());
389     $this->assertEquals('Updated untranslatable field', $node->get('composite_reference')->entity->get('field_untranslatable')->value);
390     $this->assertEquals('Draft Nested Source Composite #2', $node->get('composite_reference')->entity->get('composite_reference')[1]->entity->label());
391     $this->assertEquals('FR Nested Composite #2', $node->get('composite_reference')->entity->get('composite_reference')[1]->entity->getTranslation('fr')->label());
392
393     // Now publish the DE pending revision as well.
394     $new_revision = $node_storage->createRevision($node_de->getTranslation('de'));
395     $violations = $new_revision->validate();
396     $this->assertCount(2, $new_revision->get('composite_reference')->entity->get('composite_reference'));
397     $this->assertEquals(0, count($violations));
398     $new_revision->save();
399
400     $this->assertRevisionCount(8, $node);
401     $this->assertRevisionCount(8, $composite);
402     $this->assertRevisionCount(8, $nested_composite);
403     $this->assertRevisionCount(4, $second_nested_composite);
404
405     // The new default revision should now have the updated source and both
406     // translations.
407     $node = $node_storage->load($node->id());
408     $this->assertTrue($node->isDefaultRevision());
409     $this->assertTrue($node->hasTranslation('de'));
410     $this->assertTrue($node->hasTranslation('fr'));
411     $this->assertFalse((bool) $node->isRevisionTranslationAffected());
412     $this->assertFalse((bool) $node->getTranslation('fr')->isRevisionTranslationAffected());
413     $this->assertTrue((bool) $node->getTranslation('de')->isRevisionTranslationAffected());
414     $this->assertEquals('Updated Source Node', $node->label());
415     $this->assertTrue($node->get('composite_reference')->entity->isDefaultRevision());
416     $this->assertTrue($node->get('composite_reference')->entity->hasTranslation('de'));
417     $this->assertTrue($node->get('composite_reference')->entity->hasTranslation('fr'));
418     $this->assertEquals('Pending Revision Node #1 FR', $node->getTranslation('fr')->label());
419     $this->assertEquals('Pending Revision Composite #1 FR', $node->get('composite_reference')->entity->getTranslation('fr')->label());
420     $this->assertEquals('Pending Revision Node #1 DE', $node->getTranslation('de')->label());
421     $this->assertEquals('Pending Revision Composite #1 DE', $node->get('composite_reference')->entity->getTranslation('de')->label());
422     $this->assertEquals('Pending Nested Composite #1 DE', $node->get('composite_reference')->entity->get('composite_reference')->entity->getTranslation('de')->label());
423     $this->assertEquals('Updated Source Composite', $node->get('composite_reference')->entity->label());
424     $this->assertEquals('Updated untranslatable field', $node->get('composite_reference')->entity->get('field_untranslatable')->value);
425     $this->assertEquals('Draft Nested Source Composite #2', $node->get('composite_reference')->entity->get('composite_reference')[1]->entity->label());
426     $this->assertEquals('FR Nested Composite #2', $node->get('composite_reference')->entity->get('composite_reference')[1]->entity->getTranslation('fr')->label());
427
428     // The second nested composite of DE inherited the default values for its
429     // translation.
430     $this->assertEquals('Draft Nested Source Composite #2', $node->get('composite_reference')->entity->get('composite_reference')[1]->entity->getTranslation('de')->label());
431
432     // Simulate creating a new pending revision like
433     // \Drupal\content_moderation\EntityTypeInfo::entityPrepareForm().
434     $new_revision = $node_storage->createRevision($node);
435     $revision_key = $new_revision->getEntityType()->getKey('revision');
436     $new_revision->set($revision_key, $new_revision->getLoadedRevisionId());
437     $new_revision->save();
438     $this->assertEquals('Pending Nested Composite #1 DE', $new_revision->get('composite_reference')->entity->get('composite_reference')->entity->getTranslation('de')->label());
439
440   }
441
442   /**
443    * Asserts the revision count of an entity.
444    *
445    * @param int $expected
446    *   The expected amount of revisions.
447    * @param \Drupal\Core\Entity\EntityInterface $entity
448    *   The entity.
449    */
450   protected function assertRevisionCount($expected, EntityInterface $entity) {
451     $node_revisions_count = \Drupal::entityQuery($entity->getEntityTypeId())
452       ->condition($entity->getEntityType()->getKey('id'), $entity->id())
453       ->allRevisions()
454       ->count()
455       ->execute();
456     $this->assertEquals($expected, $node_revisions_count);
457   }
458
459 }