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