3 namespace Drupal\Tests\field\Kernel\EntityReference;
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;
28 * Tests the new entity API for the entity reference field type.
30 * @group entity_reference
32 class EntityReferenceItemTest extends FieldKernelTestBase {
34 use EntityReferenceTestTrait;
41 public static $modules = ['node', 'comment', 'file', 'taxonomy', 'text', 'filter', 'views', 'field'];
44 * The taxonomy vocabulary to test with.
46 * @var \Drupal\taxonomy\VocabularyInterface
48 protected $vocabulary;
51 * The taxonomy term to test with.
53 * @var \Drupal\taxonomy\TermInterface
58 * The test entity with a string ID.
60 * @var \Drupal\entity_test\Entity\EntityTestStringId
62 protected $entityStringId;
67 protected function setUp() {
70 $this->installEntitySchema('entity_test_string_id');
71 $this->installEntitySchema('taxonomy_term');
72 $this->installEntitySchema('node');
73 $this->installEntitySchema('comment');
74 $this->installEntitySchema('file');
76 $this->installSchema('comment', ['comment_entity_statistics']);
77 $this->installSchema('node', ['node_access']);
79 $this->vocabulary = Vocabulary::create([
80 'name' => $this->randomMachineName(),
81 'vid' => Unicode::strtolower($this->randomMachineName()),
82 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
84 $this->vocabulary->save();
86 $this->term = Term::create([
87 'name' => $this->randomMachineName(),
88 'vid' => $this->vocabulary->id(),
89 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
93 $this->entityStringId = EntityTestStringId::create([
94 'id' => $this->randomMachineName(),
96 $this->entityStringId->save();
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');
109 * Tests the entity reference field type for referencing content entities.
111 public function testContentEntityReferenceItem() {
112 $tid = $this->term->id();
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();
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');
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);
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,
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());
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());
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());
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());
166 // Test assigning an invalid item throws an exception.
168 $entity->field_test_taxonomy_term = ['target_id' => 'invalid', 'entity' => $term2];
169 $this->fail('Assigning an invalid item throws an exception.');
171 catch (\InvalidArgumentException $e) {
172 $this->pass('Assigning an invalid item throws an exception.');
175 // Delete terms so we have nothing to reference and try again
178 $entity = EntityTest::create(['name' => $this->randomMachineName()]);
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);
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
191 // @see \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::setValue().
192 $user = User::create(['name' => $this->randomString()]);
194 $entity = EntityTest::create(['user_id' => ['target_id' => (int) $user->id(), 'entity' => $user]]);
198 * Tests the ::generateSampleValue() method.
200 public function testGenerateSampleValue() {
201 $entity = EntityTest::create();
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);
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);
216 * Tests referencing content entities with string IDs.
218 public function testContentEntityReferenceItemWithStringId() {
219 $entity = EntityTest::create();
220 $entity->field_test_entity_test_string_id->target_id = $this->entityStringId->id();
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');
232 * Tests the entity reference field type for referencing config entities.
234 public function testConfigEntityReferenceItem() {
235 $referenced_entity_id = $this->vocabulary->id();
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();
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());
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);
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,
265 $vocabulary2->save();
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());
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()]);
279 * Tests entity auto create.
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,
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.
298 $term = \Drupal::entityManager()->loadEntityByUuid($term->getEntityTypeId(), $term->uuid());
299 $this->assertEqual($entity->field_test_taxonomy_term->entity->id(), $term->id());
303 * Test saving order sequence doesn't matter.
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,
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);
321 $entity->field_test_taxonomy_term = $value;
322 // Now save the term.
324 // And then the entity.
326 $this->assertEqual($entity->field_test_taxonomy_term->entity->id(), $term->id());
330 * Tests that the 'handler' field setting stores the proper plugin ID.
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',
339 'target_type' => 'entity_test'
342 $field_storage->save();
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',
351 $field = FieldConfig::load($field->id());
352 $this->assertEqual($field->getSetting('handler'), 'default:entity_test');
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');
361 // Change the handler to another, non-derivative plugin.
362 $field->setSetting('handler', 'views');
364 $field = FieldConfig::load($field->id());
365 $this->assertEqual($field->getSetting('handler'), 'views');
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');
376 * Tests ValidReferenceConstraint with newly created and unsaved entities.
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,
385 $entity = EntityTest::create([
386 'field_test_taxonomy_term' => [
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.
396 $entity = EntityTest::create([
397 'field_test_taxonomy_term' => [
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.
408 $errors = $entity->validate();
409 $this->assertEqual(0, count($errors));
411 // Test with an unpublished and unsaved node.
412 $title = $this->randomString();
413 $node = Node::create([
416 'status' => NodeInterface::NOT_PUBLISHED,
419 $entity = EntityTest::create([
420 'field_test_node' => [
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');
430 // Publish the node and try again.
431 $node->setPublished(TRUE);
432 $errors = $entity->validate();
433 $this->assertEqual(0, count($errors));
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,
440 'status' => NodeInterface::NOT_PUBLISHED,
443 $saved_unpublished_node_title = $this->randomString();
444 $saved_unpublished_node = Node::create([
445 'title' => $saved_unpublished_node_title,
447 'status' => NodeInterface::NOT_PUBLISHED,
449 $saved_unpublished_node->save();
451 $saved_published_node_title = $this->randomString();
452 $saved_published_node = Node::create([
453 'title' => $saved_published_node_title,
455 'status' => NodeInterface::PUBLISHED,
457 $saved_published_node->save();
459 $entity = EntityTest::create([
460 'field_test_node' => [
462 'entity' => $unsaved_unpublished_node,
465 'target_id' => $saved_unpublished_node->id(),
468 'target_id' => $saved_published_node->id(),
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');
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');
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));
493 // Test with an unpublished and unsaved comment.
494 $title = $this->randomString();
495 $comment = Comment::create([
497 'comment_type' => 'comment',
501 $entity = EntityTest::create([
502 'field_test_comment' => [
503 'entity' => $comment,
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');
512 // Publish the comment and try again.
513 $comment->setPublished(TRUE);
514 $errors = $entity->validate();
515 $this->assertEqual(0, count($errors));
517 // Test with an inactive and unsaved user.
518 $name = $this->randomString();
519 $user = User::create([
524 $entity = EntityTest::create([
525 'field_test_user' => [
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');
535 // Activate the user and try again.
537 $errors = $entity->validate();
538 $this->assertEqual(0, count($errors));
540 // Test with a temporary and unsaved file.
541 $filename = $this->randomMachineName() . '.txt';
542 $file = File::create([
543 'filename' => $filename,
547 $entity = EntityTest::create([
548 'field_test_file' => [
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');
558 // Set the file as permanent and try again.
559 $file->setPermanent();
560 $errors = $entity->validate();
561 $this->assertEqual(0, count($errors));