c0e58e04e9e5917a20001014862f9b2d032e7990
[yaffs-website] / web / core / modules / field / tests / src / Kernel / EntityReference / EntityReferenceItemTest.php
1 <?php
2
3 namespace Drupal\Tests\field\Kernel\EntityReference;
4
5 use Drupal\comment\Entity\Comment;
6 use Drupal\Component\Render\FormattableMarkup;
7 use Drupal\Component\Utility\Unicode;
8 use Drupal\Core\Field\FieldItemListInterface;
9 use Drupal\Core\Field\FieldItemInterface;
10 use Drupal\Core\Field\FieldStorageDefinitionInterface;
11 use Drupal\Core\StringTranslation\TranslatableMarkup;
12 use Drupal\Core\Language\LanguageInterface;
13 use Drupal\entity_test\Entity\EntityTest;
14 use Drupal\entity_test\Entity\EntityTestStringId;
15 use Drupal\field\Entity\FieldConfig;
16 use Drupal\field\Entity\FieldStorageConfig;
17 use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
18 use Drupal\node\NodeInterface;
19 use Drupal\taxonomy\TermInterface;
20 use Drupal\Tests\field\Kernel\FieldKernelTestBase;
21 use Drupal\file\Entity\File;
22 use Drupal\node\Entity\Node;
23 use Drupal\taxonomy\Entity\Term;
24 use Drupal\taxonomy\Entity\Vocabulary;
25 use Drupal\user\Entity\User;
26
27 /**
28  * Tests the new entity API for the entity reference field type.
29  *
30  * @group entity_reference
31  */
32 class EntityReferenceItemTest extends FieldKernelTestBase {
33
34   use EntityReferenceTestTrait;
35
36   /**
37    * Modules to install.
38    *
39    * @var array
40    */
41   public static $modules = ['node', 'comment', 'file', 'taxonomy', 'text', 'filter', 'views', 'field'];
42
43   /**
44    * The taxonomy vocabulary to test with.
45    *
46    * @var \Drupal\taxonomy\VocabularyInterface
47    */
48   protected $vocabulary;
49
50   /**
51    * The taxonomy term to test with.
52    *
53    * @var \Drupal\taxonomy\TermInterface
54    */
55   protected $term;
56
57   /**
58    * The test entity with a string ID.
59    *
60    * @var \Drupal\entity_test\Entity\EntityTestStringId
61    */
62   protected $entityStringId;
63
64   /**
65    * Sets up the test.
66    */
67   protected function setUp() {
68     parent::setUp();
69
70     $this->installEntitySchema('entity_test_string_id');
71     $this->installEntitySchema('taxonomy_term');
72     $this->installEntitySchema('node');
73     $this->installEntitySchema('comment');
74     $this->installEntitySchema('file');
75
76     $this->installSchema('comment', ['comment_entity_statistics']);
77     $this->installSchema('node', ['node_access']);
78
79     $this->vocabulary = Vocabulary::create([
80       'name' => $this->randomMachineName(),
81       'vid' => Unicode::strtolower($this->randomMachineName()),
82       'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
83     ]);
84     $this->vocabulary->save();
85
86     $this->term = Term::create([
87       'name' => $this->randomMachineName(),
88       'vid' => $this->vocabulary->id(),
89       'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
90     ]);
91     $this->term->save();
92
93     $this->entityStringId = EntityTestStringId::create([
94       'id' => $this->randomMachineName(),
95     ]);
96     $this->entityStringId->save();
97
98     // Use the util to create an instance.
99     $this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_taxonomy_term', 'Test content entity reference', 'taxonomy_term');
100     $this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_entity_test_string_id', 'Test content entity reference with string ID', 'entity_test_string_id');
101     $this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_taxonomy_vocabulary', 'Test config entity reference', 'taxonomy_vocabulary');
102     $this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_node', 'Test node entity reference', 'node', 'default', [], FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
103     $this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_user', 'Test user entity reference', 'user');
104     $this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_comment', 'Test comment entity reference', 'comment');
105     $this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_file', 'Test file entity reference', 'file');
106   }
107
108   /**
109    * Tests the entity reference field type for referencing content entities.
110    */
111   public function testContentEntityReferenceItem() {
112     $tid = $this->term->id();
113
114     // Just being able to create the entity like this verifies a lot of code.
115     $entity = EntityTest::create();
116     $entity->field_test_taxonomy_term->target_id = $tid;
117     $entity->name->value = $this->randomMachineName();
118     $entity->save();
119
120     $entity = EntityTest::load($entity->id());
121     $this->assertTrue($entity->field_test_taxonomy_term instanceof FieldItemListInterface, 'Field implements interface.');
122     $this->assertTrue($entity->field_test_taxonomy_term[0] instanceof FieldItemInterface, 'Field item implements interface.');
123     $this->assertEqual($entity->field_test_taxonomy_term->target_id, $tid);
124     $this->assertEqual($entity->field_test_taxonomy_term->entity->getName(), $this->term->getName());
125     $this->assertEqual($entity->field_test_taxonomy_term->entity->id(), $tid);
126     $this->assertEqual($entity->field_test_taxonomy_term->entity->uuid(), $this->term->uuid());
127     // Verify that the label for the target ID property definition is correct.
128     $label = $entity->field_test_taxonomy_term->getFieldDefinition()->getFieldStorageDefinition()->getPropertyDefinition('target_id')->getLabel();
129     $this->assertTrue($label instanceof TranslatableMarkup);
130     $this->assertEqual($label->render(), 'Taxonomy term ID');
131
132     // Change the name of the term via the reference.
133     $new_name = $this->randomMachineName();
134     $entity->field_test_taxonomy_term->entity->setName($new_name);
135     $entity->field_test_taxonomy_term->entity->save();
136     // Verify it is the correct name.
137     $term = Term::load($tid);
138     $this->assertEqual($term->getName(), $new_name);
139
140     // Make sure the computed term reflects updates to the term id.
141     $term2 = Term::create([
142       'name' => $this->randomMachineName(),
143       'vid' => $this->term->bundle(),
144       'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
145     ]);
146     $term2->save();
147
148     // Test all the possible ways of assigning a value.
149     $entity->field_test_taxonomy_term->target_id = $term->id();
150     $this->assertEqual($entity->field_test_taxonomy_term->entity->id(), $term->id());
151     $this->assertEqual($entity->field_test_taxonomy_term->entity->getName(), $term->getName());
152
153     $entity->field_test_taxonomy_term = [['target_id' => $term2->id()]];
154     $this->assertEqual($entity->field_test_taxonomy_term->entity->id(), $term2->id());
155     $this->assertEqual($entity->field_test_taxonomy_term->entity->getName(), $term2->getName());
156
157     // Test value assignment via the computed 'entity' property.
158     $entity->field_test_taxonomy_term->entity = $term;
159     $this->assertEqual($entity->field_test_taxonomy_term->target_id, $term->id());
160     $this->assertEqual($entity->field_test_taxonomy_term->entity->getName(), $term->getName());
161
162     $entity->field_test_taxonomy_term = [['entity' => $term2]];
163     $this->assertEqual($entity->field_test_taxonomy_term->target_id, $term2->id());
164     $this->assertEqual($entity->field_test_taxonomy_term->entity->getName(), $term2->getName());
165
166     // Test assigning an invalid item throws an exception.
167     try {
168       $entity->field_test_taxonomy_term = ['target_id' => 'invalid', 'entity' => $term2];
169       $this->fail('Assigning an invalid item throws an exception.');
170     }
171     catch (\InvalidArgumentException $e) {
172       $this->pass('Assigning an invalid item throws an exception.');
173     }
174
175     // Delete terms so we have nothing to reference and try again
176     $term->delete();
177     $term2->delete();
178     $entity = EntityTest::create(['name' => $this->randomMachineName()]);
179     $entity->save();
180
181     // Test the generateSampleValue() method.
182     $entity = EntityTest::create();
183     $entity->field_test_taxonomy_term->generateSampleItems();
184     $entity->field_test_taxonomy_vocabulary->generateSampleItems();
185     $this->entityValidateAndSave($entity);
186
187     // Tests that setting an integer target ID together with an entity object
188     // succeeds and does not cause any exceptions. There is no assertion here,
189     // as the assignment should not throw any exceptions and if it does the
190     // test will fail.
191     // @see \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::setValue().
192     $user = User::create(['name' => $this->randomString()]);
193     $user->save();
194     $entity = EntityTest::create(['user_id' => ['target_id' => (int) $user->id(), 'entity' => $user]]);
195   }
196
197   /**
198    * Tests the ::generateSampleValue() method.
199    */
200   public function testGenerateSampleValue() {
201     $entity = EntityTest::create();
202
203     // Test while a term exists.
204     $entity->field_test_taxonomy_term->generateSampleItems();
205     $this->assertInstanceOf(TermInterface::class, $entity->field_test_taxonomy_term->entity);
206     $this->entityValidateAndSave($entity);
207
208     // Delete the term and test again.
209     $this->term->delete();
210     $entity->field_test_taxonomy_term->generateSampleItems();
211     $this->assertInstanceOf(TermInterface::class, $entity->field_test_taxonomy_term->entity);
212     $this->entityValidateAndSave($entity);
213   }
214
215   /**
216    * Tests referencing content entities with string IDs.
217    */
218   public function testContentEntityReferenceItemWithStringId() {
219     $entity = EntityTest::create();
220     $entity->field_test_entity_test_string_id->target_id = $this->entityStringId->id();
221     $entity->save();
222     $storage = \Drupal::entityManager()->getStorage('entity_test');
223     $storage->resetCache();
224     $this->assertEqual($this->entityStringId->id(), $storage->load($entity->id())->field_test_entity_test_string_id->target_id);
225     // Verify that the label for the target ID property definition is correct.
226     $label = $entity->field_test_taxonomy_term->getFieldDefinition()->getFieldStorageDefinition()->getPropertyDefinition('target_id')->getLabel();
227     $this->assertTrue($label instanceof TranslatableMarkup);
228     $this->assertEqual($label->render(), 'Taxonomy term ID');
229   }
230
231   /**
232    * Tests the entity reference field type for referencing config entities.
233    */
234   public function testConfigEntityReferenceItem() {
235     $referenced_entity_id = $this->vocabulary->id();
236
237     // Just being able to create the entity like this verifies a lot of code.
238     $entity = EntityTest::create();
239     $entity->field_test_taxonomy_vocabulary->target_id = $referenced_entity_id;
240     $entity->name->value = $this->randomMachineName();
241     $entity->save();
242
243     $entity = EntityTest::load($entity->id());
244     $this->assertTrue($entity->field_test_taxonomy_vocabulary instanceof FieldItemListInterface, 'Field implements interface.');
245     $this->assertTrue($entity->field_test_taxonomy_vocabulary[0] instanceof FieldItemInterface, 'Field item implements interface.');
246     $this->assertEqual($entity->field_test_taxonomy_vocabulary->target_id, $referenced_entity_id);
247     $this->assertEqual($entity->field_test_taxonomy_vocabulary->entity->label(), $this->vocabulary->label());
248     $this->assertEqual($entity->field_test_taxonomy_vocabulary->entity->id(), $referenced_entity_id);
249     $this->assertEqual($entity->field_test_taxonomy_vocabulary->entity->uuid(), $this->vocabulary->uuid());
250
251     // Change the name of the term via the reference.
252     $new_name = $this->randomMachineName();
253     $entity->field_test_taxonomy_vocabulary->entity->set('name', $new_name);
254     $entity->field_test_taxonomy_vocabulary->entity->save();
255     // Verify it is the correct name.
256     $vocabulary = Vocabulary::load($referenced_entity_id);
257     $this->assertEqual($vocabulary->label(), $new_name);
258
259     // Make sure the computed term reflects updates to the term id.
260     $vocabulary2 = $vocabulary = Vocabulary::create([
261       'name' => $this->randomMachineName(),
262       'vid' => Unicode::strtolower($this->randomMachineName()),
263       'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
264     ]);
265     $vocabulary2->save();
266
267     $entity->field_test_taxonomy_vocabulary->target_id = $vocabulary2->id();
268     $this->assertEqual($entity->field_test_taxonomy_vocabulary->entity->id(), $vocabulary2->id());
269     $this->assertEqual($entity->field_test_taxonomy_vocabulary->entity->label(), $vocabulary2->label());
270
271     // Delete terms so we have nothing to reference and try again
272     $this->vocabulary->delete();
273     $vocabulary2->delete();
274     $entity = EntityTest::create(['name' => $this->randomMachineName()]);
275     $entity->save();
276   }
277
278   /**
279    * Tests entity auto create.
280    */
281   public function testEntityAutoCreate() {
282     // The term entity is unsaved here.
283     $term = Term::create([
284       'name' => $this->randomMachineName(),
285       'vid' => $this->term->bundle(),
286       'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
287     ]);
288     $entity = EntityTest::create();
289     // Now assign the unsaved term to the field.
290     $entity->field_test_taxonomy_term->entity = $term;
291     $entity->name->value = $this->randomMachineName();
292     // This is equal to storing an entity to tempstore or cache and retrieving
293     // it back. An example for this is node preview.
294     $entity = serialize($entity);
295     $entity = unserialize($entity);
296     // And then the entity.
297     $entity->save();
298     $term = \Drupal::entityManager()->loadEntityByUuid($term->getEntityTypeId(), $term->uuid());
299     $this->assertEqual($entity->field_test_taxonomy_term->entity->id(), $term->id());
300   }
301
302   /**
303    * Test saving order sequence doesn't matter.
304    */
305   public function testEntitySaveOrder() {
306     // The term entity is unsaved here.
307     $term = Term::create([
308       'name' => $this->randomMachineName(),
309       'vid' => $this->term->bundle(),
310       'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
311     ]);
312     $entity = EntityTest::create();
313     // Now assign the unsaved term to the field.
314     $entity->field_test_taxonomy_term->entity = $term;
315     $entity->name->value = $this->randomMachineName();
316     // Now get the field value.
317     $value = $entity->get('field_test_taxonomy_term');
318     $this->assertTrue(empty($value['target_id']));
319     $this->assertNull($entity->field_test_taxonomy_term->target_id);
320     // And then set it.
321     $entity->field_test_taxonomy_term = $value;
322     // Now save the term.
323     $term->save();
324     // And then the entity.
325     $entity->save();
326     $this->assertEqual($entity->field_test_taxonomy_term->entity->id(), $term->id());
327   }
328
329   /**
330    * Tests that the 'handler' field setting stores the proper plugin ID.
331    */
332   public function testSelectionHandlerSettings() {
333     $field_name = Unicode::strtolower($this->randomMachineName());
334     $field_storage = FieldStorageConfig::create([
335       'field_name' => $field_name,
336       'entity_type' => 'entity_test',
337       'type' => 'entity_reference',
338       'settings' => [
339         'target_type' => 'entity_test'
340       ],
341     ]);
342     $field_storage->save();
343
344     // Do not specify any value for the 'handler' setting in order to verify
345     // that the default handler with the correct derivative is used.
346     $field = FieldConfig::create([
347       'field_storage' => $field_storage,
348       'bundle' => 'entity_test',
349     ]);
350     $field->save();
351     $field = FieldConfig::load($field->id());
352     $this->assertEqual($field->getSetting('handler'), 'default:entity_test');
353
354     // Change the target_type in the field storage, and check that the handler
355     // was correctly reassigned in the field.
356     $field_storage->setSetting('target_type', 'entity_test_rev');
357     $field_storage->save();
358     $field = FieldConfig::load($field->id());
359     $this->assertEqual($field->getSetting('handler'), 'default:entity_test_rev');
360
361     // Change the handler to another, non-derivative plugin.
362     $field->setSetting('handler', 'views');
363     $field->save();
364     $field = FieldConfig::load($field->id());
365     $this->assertEqual($field->getSetting('handler'), 'views');
366
367     // Change the target_type in the field storage again, and check that the
368     // non-derivative handler was unchanged.
369     $field_storage->setSetting('target_type', 'entity_test_rev');
370     $field_storage->save();
371     $field = FieldConfig::load($field->id());
372     $this->assertEqual($field->getSetting('handler'), 'views');
373   }
374
375   /**
376    * Tests ValidReferenceConstraint with newly created and unsaved entities.
377    */
378   public function testAutocreateValidation() {
379     // The term entity is unsaved here.
380     $term = Term::create([
381       'name' => $this->randomMachineName(),
382       'vid' => $this->term->bundle(),
383       'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
384     ]);
385     $entity = EntityTest::create([
386       'field_test_taxonomy_term' => [
387         'entity' => $term,
388         'target_id' => NULL,
389       ],
390     ]);
391     $errors = $entity->validate();
392     // Using target_id of NULL is valid with an unsaved entity.
393     $this->assertEqual(0, count($errors));
394     // Using target_id of NULL is not valid with a saved entity.
395     $term->save();
396     $entity = EntityTest::create([
397       'field_test_taxonomy_term' => [
398         'entity' => $term,
399         'target_id' => NULL,
400       ],
401     ]);
402     $errors = $entity->validate();
403     $this->assertEqual(1, count($errors));
404     $this->assertEqual($errors[0]->getMessage(), 'This value should not be null.');
405     $this->assertEqual($errors[0]->getPropertyPath(), 'field_test_taxonomy_term.0');
406     // This should rectify the issue, favoring the entity over the target_id.
407     $entity->save();
408     $errors = $entity->validate();
409     $this->assertEqual(0, count($errors));
410
411     // Test with an unpublished and unsaved node.
412     $title = $this->randomString();
413     $node = Node::create([
414       'title' => $title,
415       'type' => 'node',
416       'status' => NodeInterface::NOT_PUBLISHED,
417     ]);
418
419     $entity = EntityTest::create([
420       'field_test_node' => [
421         'entity' => $node,
422       ],
423     ]);
424
425     $errors = $entity->validate();
426     $this->assertEqual(1, count($errors));
427     $this->assertEqual($errors[0]->getMessage(), new FormattableMarkup('This entity (%type: %label) cannot be referenced.', ['%type' => 'node', '%label' => $title]));
428     $this->assertEqual($errors[0]->getPropertyPath(), 'field_test_node.0.entity');
429
430     // Publish the node and try again.
431     $node->setPublished(TRUE);
432     $errors = $entity->validate();
433     $this->assertEqual(0, count($errors));
434
435     // Test with a mix of valid and invalid nodes.
436     $unsaved_unpublished_node_title = $this->randomString();
437     $unsaved_unpublished_node = Node::create([
438       'title' => $unsaved_unpublished_node_title,
439       'type' => 'node',
440       'status' => NodeInterface::NOT_PUBLISHED,
441     ]);
442
443     $saved_unpublished_node_title = $this->randomString();
444     $saved_unpublished_node = Node::create([
445       'title' => $saved_unpublished_node_title,
446       'type' => 'node',
447       'status' => NodeInterface::NOT_PUBLISHED,
448     ]);
449     $saved_unpublished_node->save();
450
451     $saved_published_node_title = $this->randomString();
452     $saved_published_node = Node::create([
453       'title' => $saved_published_node_title,
454       'type' => 'node',
455       'status' => NodeInterface::PUBLISHED,
456     ]);
457     $saved_published_node->save();
458
459     $entity = EntityTest::create([
460       'field_test_node' => [
461         [
462           'entity' => $unsaved_unpublished_node,
463         ],
464         [
465           'target_id' => $saved_unpublished_node->id(),
466         ],
467         [
468           'target_id' => $saved_published_node->id(),
469         ],
470       ],
471     ]);
472
473     $errors = $entity->validate();
474     $this->assertEqual(2, count($errors));
475     $this->assertEqual($errors[0]->getMessage(), new FormattableMarkup('This entity (%type: %label) cannot be referenced.', ['%type' => 'node', '%label' => $unsaved_unpublished_node_title]));
476     $this->assertEqual($errors[0]->getPropertyPath(), 'field_test_node.0.entity');
477     $this->assertEqual($errors[1]->getMessage(), new FormattableMarkup('This entity (%type: %label) cannot be referenced.', ['%type' => 'node', '%label' => $saved_unpublished_node->id()]));
478     $this->assertEqual($errors[1]->getPropertyPath(), 'field_test_node.1.target_id');
479
480     // Publish one of the nodes and try again.
481     $saved_unpublished_node->setPublished(TRUE);
482     $saved_unpublished_node->save();
483     $errors = $entity->validate();
484     $this->assertEqual(1, count($errors));
485     $this->assertEqual($errors[0]->getMessage(), new FormattableMarkup('This entity (%type: %label) cannot be referenced.', ['%type' => 'node', '%label' => $unsaved_unpublished_node_title]));
486     $this->assertEqual($errors[0]->getPropertyPath(), 'field_test_node.0.entity');
487
488     // Publish the last invalid node and try again.
489     $unsaved_unpublished_node->setPublished(TRUE);
490     $errors = $entity->validate();
491     $this->assertEqual(0, count($errors));
492
493     // Test with an unpublished and unsaved comment.
494     $title = $this->randomString();
495     $comment = Comment::create([
496       'subject' => $title,
497       'comment_type' => 'comment',
498       'status' => 0,
499     ]);
500
501     $entity = EntityTest::create([
502       'field_test_comment' => [
503         'entity' => $comment,
504       ],
505     ]);
506
507     $errors = $entity->validate();
508     $this->assertEqual(1, count($errors));
509     $this->assertEqual($errors[0]->getMessage(), new FormattableMarkup('This entity (%type: %label) cannot be referenced.', ['%type' => 'comment', '%label' => $title]));
510     $this->assertEqual($errors[0]->getPropertyPath(), 'field_test_comment.0.entity');
511
512     // Publish the comment and try again.
513     $comment->setPublished(TRUE);
514     $errors = $entity->validate();
515     $this->assertEqual(0, count($errors));
516
517     // Test with an inactive and unsaved user.
518     $name = $this->randomString();
519     $user = User::create([
520       'name' => $name,
521       'status' => 0,
522     ]);
523
524     $entity = EntityTest::create([
525       'field_test_user' => [
526         'entity' => $user,
527       ],
528     ]);
529
530     $errors = $entity->validate();
531     $this->assertEqual(1, count($errors));
532     $this->assertEqual($errors[0]->getMessage(), new FormattableMarkup('This entity (%type: %label) cannot be referenced.', ['%type' => 'user', '%label' => $name]));
533     $this->assertEqual($errors[0]->getPropertyPath(), 'field_test_user.0.entity');
534
535     // Activate the user and try again.
536     $user->activate();
537     $errors = $entity->validate();
538     $this->assertEqual(0, count($errors));
539
540     // Test with a temporary and unsaved file.
541     $filename = $this->randomMachineName() . '.txt';
542     $file = File::create([
543       'filename' => $filename,
544       'status' => 0,
545     ]);
546
547     $entity = EntityTest::create([
548       'field_test_file' => [
549         'entity' => $file,
550       ],
551     ]);
552
553     $errors = $entity->validate();
554     $this->assertEqual(1, count($errors));
555     $this->assertEqual($errors[0]->getMessage(), new FormattableMarkup('This entity (%type: %label) cannot be referenced.', ['%type' => 'file', '%label' => $filename]));
556     $this->assertEqual($errors[0]->getPropertyPath(), 'field_test_file.0.entity');
557
558     // Set the file as permanent and try again.
559     $file->setPermanent();
560     $errors = $entity->validate();
561     $this->assertEqual(0, count($errors));
562   }
563
564 }