b4308726096c224e532ceb20cfadf1c9259a8127
[yaffs-website] / web / core / tests / Drupal / KernelTests / Core / Entity / EntityReferenceFieldTest.php
1 <?php
2
3 namespace Drupal\KernelTests\Core\Entity;
4
5 use Drupal\Tests\SchemaCheckTestTrait;
6 use Drupal\Core\Entity\EntityInterface;
7 use Drupal\Core\Entity\EntityStorageException;
8 use Drupal\Core\Field\BaseFieldDefinition;
9 use Drupal\Core\Field\FieldStorageDefinitionInterface;
10 use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
11 use Drupal\field\Entity\FieldConfig;
12 use Drupal\field\Entity\FieldStorageConfig;
13 use Drupal\user\Entity\Role;
14 use Drupal\user\Entity\User;
15 use Drupal\user\RoleInterface;
16 use Drupal\user\UserInterface;
17 use Drupal\entity_test\Entity\EntityTestStringId;
18
19 /**
20  * Tests for the entity reference field.
21  *
22  * @group Entity
23  */
24 class EntityReferenceFieldTest extends EntityKernelTestBase {
25
26   use SchemaCheckTestTrait;
27   use EntityReferenceTestTrait;
28
29   /**
30    * The entity type used in this test.
31    *
32    * @var string
33    */
34   protected $entityType = 'entity_test';
35
36   /**
37    * The entity type that is being referenced.
38    *
39    * @var string
40    */
41   protected $referencedEntityType = 'entity_test_rev';
42
43   /**
44    * The bundle used in this test.
45    *
46    * @var string
47    */
48   protected $bundle = 'entity_test';
49
50   /**
51    * The name of the field used in this test.
52    *
53    * @var string
54    */
55   protected $fieldName = 'field_test';
56
57   /**
58    * Modules to install.
59    *
60    * @var array
61    */
62   public static $modules = ['entity_reference_test', 'entity_test_update'];
63
64   /**
65    * {@inheritdoc}
66    */
67   protected function setUp() {
68     parent::setUp();
69
70     $this->installEntitySchema('entity_test_rev');
71
72     // Create a field.
73     $this->createEntityReferenceField(
74       $this->entityType,
75       $this->bundle,
76       $this->fieldName,
77       'Field test',
78       $this->referencedEntityType,
79       'default',
80       ['target_bundles' => [$this->bundle]],
81       FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
82     );
83
84   }
85
86   /**
87    * Tests reference field validation.
88    */
89   public function testEntityReferenceFieldValidation() {
90     // Test a valid reference.
91     $referenced_entity = $this->container->get('entity_type.manager')
92       ->getStorage($this->referencedEntityType)
93       ->create(['type' => $this->bundle]);
94     $referenced_entity->save();
95
96     $entity = $this->container->get('entity_type.manager')
97       ->getStorage($this->entityType)
98       ->create(['type' => $this->bundle]);
99     $entity->{$this->fieldName}->target_id = $referenced_entity->id();
100     $violations = $entity->{$this->fieldName}->validate();
101     $this->assertEqual($violations->count(), 0, 'Validation passes.');
102
103     // Test an invalid reference.
104     $entity->{$this->fieldName}->target_id = 9999;
105     $violations = $entity->{$this->fieldName}->validate();
106     $this->assertEqual($violations->count(), 1, 'Validation throws a violation.');
107     $this->assertEqual($violations[0]->getMessage(), t('The referenced entity (%type: %id) does not exist.', ['%type' => $this->referencedEntityType, '%id' => 9999]));
108
109     // Test a non-referenceable bundle.
110     entity_test_create_bundle('non_referenceable', NULL, $this->referencedEntityType);
111     $referenced_entity = entity_create($this->referencedEntityType, ['type' => 'non_referenceable']);
112     $referenced_entity->save();
113     $entity->{$this->fieldName}->target_id = $referenced_entity->id();
114     $violations = $entity->{$this->fieldName}->validate();
115     $this->assertEqual($violations->count(), 1, 'Validation throws a violation.');
116     $this->assertEqual($violations[0]->getMessage(), t('This entity (%type: %id) cannot be referenced.', ['%type' => $this->referencedEntityType, '%id' => $referenced_entity->id()]));
117   }
118
119   /**
120    * Tests the multiple target entities loader.
121    */
122   public function testReferencedEntitiesMultipleLoad() {
123     // Create the parent entity.
124     $entity = $this->container->get('entity_type.manager')
125       ->getStorage($this->entityType)
126       ->create(['type' => $this->bundle]);
127
128     // Create three target entities and attach them to parent field.
129     $target_entities = [];
130     $reference_field = [];
131     for ($i = 0; $i < 3; $i++) {
132       $target_entity = $this->container->get('entity_type.manager')
133         ->getStorage($this->referencedEntityType)
134         ->create(['type' => $this->bundle]);
135       $target_entity->save();
136       $target_entities[] = $target_entity;
137       $reference_field[]['target_id'] = $target_entity->id();
138     }
139
140     // Also attach a non-existent entity and a NULL target id.
141     $reference_field[3]['target_id'] = 99999;
142     $target_entities[3] = NULL;
143     $reference_field[4]['target_id'] = NULL;
144     $target_entities[4] = NULL;
145
146     // Attach the first created target entity as the sixth item ($delta == 5) of
147     // the parent entity field. We want to test the case when the same target
148     // entity is referenced twice (or more times) in the same entity reference
149     // field.
150     $reference_field[5] = $reference_field[0];
151     $target_entities[5] = $target_entities[0];
152
153     // Create a new target entity that is not saved, thus testing the
154     // "autocreate" feature.
155     $target_entity_unsaved = $this->container->get('entity_type.manager')
156       ->getStorage($this->referencedEntityType)
157       ->create(['type' => $this->bundle, 'name' => $this->randomString()]);
158     $reference_field[6]['entity'] = $target_entity_unsaved;
159     $target_entities[6] = $target_entity_unsaved;
160
161     // Set the field value.
162     $entity->{$this->fieldName}->setValue($reference_field);
163
164     // Load the target entities using EntityReferenceField::referencedEntities().
165     $entities = $entity->{$this->fieldName}->referencedEntities();
166
167     // Test returned entities:
168     // - Deltas must be preserved.
169     // - Non-existent entities must not be retrieved in target entities result.
170     foreach ($target_entities as $delta => $target_entity) {
171       if (!empty($target_entity)) {
172         if (!$target_entity->isNew()) {
173           // There must be an entity in the loaded set having the same id for
174           // the same delta.
175           $this->assertEqual($target_entity->id(), $entities[$delta]->id());
176         }
177         else {
178           // For entities that were not yet saved, there must an entity in the
179           // loaded set having the same label for the same delta.
180           $this->assertEqual($target_entity->label(), $entities[$delta]->label());
181         }
182       }
183       else {
184         // A non-existent or NULL entity target id must not return any item in
185         // the target entities set.
186         $this->assertFalse(isset($entities[$delta]));
187       }
188     }
189   }
190
191   /**
192    * Tests referencing entities with string IDs.
193    */
194   public function testReferencedEntitiesStringId() {
195     $field_name = 'entity_reference_string_id';
196     $this->installEntitySchema('entity_test_string_id');
197     $this->createEntityReferenceField(
198       $this->entityType,
199       $this->bundle,
200       $field_name,
201       'Field test',
202       'entity_test_string_id',
203       'default',
204       ['target_bundles' => [$this->bundle]],
205       FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
206     );
207     // Create the parent entity.
208     $entity = $this->container->get('entity_type.manager')
209       ->getStorage($this->entityType)
210       ->create(['type' => $this->bundle]);
211
212     // Create the default target entity.
213     $target_entity = EntityTestStringId::create([
214       'id' => $this->randomString(),
215       'type' => $this->bundle
216     ]);
217     $target_entity->save();
218
219     // Set the field value.
220     $entity->{$field_name}->setValue([['target_id' => $target_entity->id()]]);
221
222     // Load the target entities using EntityReferenceField::referencedEntities().
223     $entities = $entity->{$field_name}->referencedEntities();
224     $this->assertEqual($entities[0]->id(), $target_entity->id());
225
226     // Test that a string ID works as a default value and the field's config
227     // schema is correct.
228     $field = FieldConfig::loadByName($this->entityType, $this->bundle, $field_name);
229     $field->setDefaultValue($target_entity->id());
230     $field->save();
231     $this->assertConfigSchema(\Drupal::service('config.typed'), 'field.field.' . $field->id(), $field->toArray());
232
233     // Test that the default value works.
234     $entity = $this->container->get('entity_type.manager')
235       ->getStorage($this->entityType)
236       ->create(['type' => $this->bundle]);
237     $entities = $entity->{$field_name}->referencedEntities();
238     $this->assertEqual($entities[0]->id(), $target_entity->id());
239   }
240
241   /**
242    * Tests all the possible ways to autocreate an entity via the API.
243    */
244   public function testAutocreateApi() {
245     $entity = $this->entityManager
246       ->getStorage($this->entityType)
247       ->create(['name' => $this->randomString()]);
248
249     // Test content entity autocreation.
250     $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
251       $entity->set('user_id', $user);
252     });
253     $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
254       $entity->set('user_id', $user, FALSE);
255     });
256     $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
257       $entity->user_id->setValue($user);
258     });
259     $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
260       $entity->user_id[0]->get('entity')->setValue($user);
261     });
262     $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
263       $entity->user_id->setValue(['entity' => $user, 'target_id' => NULL]);
264     });
265     try {
266       $message = 'Setting both the entity and an invalid target_id property fails.';
267       $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
268         $user->save();
269         $entity->user_id->setValue(['entity' => $user, 'target_id' => $this->generateRandomEntityId()]);
270       });
271       $this->fail($message);
272     }
273     catch (\InvalidArgumentException $e) {
274       $this->pass($message);
275     }
276     $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
277       $entity->user_id = $user;
278     });
279     $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
280       $entity->user_id->entity = $user;
281     });
282
283     // Test config entity autocreation.
284     $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
285       $entity->set('user_role', $role);
286     });
287     $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
288       $entity->set('user_role', $role, FALSE);
289     });
290     $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
291       $entity->user_role->setValue($role);
292     });
293     $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
294       $entity->user_role[0]->get('entity')->setValue($role);
295     });
296     $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
297       $entity->user_role->setValue(['entity' => $role, 'target_id' => NULL]);
298     });
299     try {
300       $message = 'Setting both the entity and an invalid target_id property fails.';
301       $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
302         $role->save();
303         $entity->user_role->setValue(['entity' => $role, 'target_id' => $this->generateRandomEntityId(TRUE)]);
304       });
305       $this->fail($message);
306     }
307     catch (\InvalidArgumentException $e) {
308       $this->pass($message);
309     }
310     $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
311       $entity->user_role = $role;
312     });
313     $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
314       $entity->user_role->entity = $role;
315     });
316
317     // Test target entity saving after setting it as new.
318     $storage = $this->entityManager->getStorage('user');
319     $user_id = $this->generateRandomEntityId();
320     $user = $storage->create(['uid' => $user_id, 'name' => $this->randomString()]);
321     $entity->user_id = $user;
322     $user->save();
323     $entity->save();
324     $this->assertEqual($entity->user_id->target_id, $user->id());
325   }
326
327   /**
328    * Asserts that the setter callback performs autocreation for users.
329    *
330    * @param \Drupal\Core\Entity\EntityInterface $entity
331    *   The referencing entity.
332    * @param $setter_callback
333    *   A callback setting the target entity on the referencing entity.
334    *
335    * @return bool
336    *   TRUE if the user was autocreated, FALSE otherwise.
337    */
338   protected function assertUserAutocreate(EntityInterface $entity, $setter_callback) {
339     $storage = $this->entityManager->getStorage('user');
340     $user_id = $this->generateRandomEntityId();
341     $user = $storage->create(['uid' => $user_id, 'name' => $this->randomString()]);
342     $setter_callback($entity, $user);
343     $entity->save();
344     $storage->resetCache();
345     $user = User::load($user_id);
346     return $this->assertEqual($entity->user_id->target_id, $user->id());
347   }
348
349   /**
350    * Asserts that the setter callback performs autocreation for user roles.
351    *
352    * @param \Drupal\Core\Entity\EntityInterface $entity
353    *   The referencing entity.
354    * @param $setter_callback
355    *   A callback setting the target entity on the referencing entity.
356    *
357    * @return bool
358    *   TRUE if the user was autocreated, FALSE otherwise.
359    */
360   protected function assertUserRoleAutocreate(EntityInterface $entity, $setter_callback) {
361     $storage = $this->entityManager->getStorage('user_role');
362     $role_id = $this->generateRandomEntityId(TRUE);
363     $role = $storage->create(['id' => $role_id, 'label' => $this->randomString()]);
364     $setter_callback($entity, $role);
365     $entity->save();
366     $storage->resetCache();
367     $role = Role::load($role_id);
368     return $this->assertEqual($entity->user_role->target_id, $role->id());
369   }
370
371   /**
372    * Tests that the target entity is not unnecessarily loaded.
373    */
374   public function testTargetEntityNoLoad() {
375     // Setup a test entity type with an entity reference field to itself. We use
376     // a special storage class throwing exceptions when a load operation is
377     // triggered to be able to detect them.
378     $entity_type = clone $this->entityManager->getDefinition('entity_test_update');
379     $entity_type->setHandlerClass('storage', '\Drupal\entity_test\EntityTestNoLoadStorage');
380     $this->state->set('entity_test_update.entity_type', $entity_type);
381     $definitions = [
382       'target_reference' => BaseFieldDefinition::create('entity_reference')
383         ->setSetting('target_type', $entity_type->id())
384         ->setSetting('handler', 'default')
385     ];
386     $this->state->set('entity_test_update.additional_base_field_definitions', $definitions);
387     $this->entityManager->clearCachedDefinitions();
388     $this->installEntitySchema($entity_type->id());
389
390     // Create the target entity.
391     $storage = $this->entityManager->getStorage($entity_type->id());
392     $target_id = $this->generateRandomEntityId();
393     $target = $storage->create(['id' => $target_id, 'name' => $this->randomString()]);
394     $target->save();
395     $this->assertEqual($target_id, $target->id(), 'The target entity has a random identifier.');
396
397     // Check that populating the reference with an existing target id does not
398     // trigger a load operation.
399     $message = 'The target entity was not loaded.';
400     try {
401       $entity = $this->entityManager
402         ->getStorage($entity_type->id())
403         ->create(['name' => $this->randomString()]);
404       $entity->target_reference = $target_id;
405       $this->pass($message);
406     }
407     catch (EntityStorageException $e) {
408       $this->fail($message);
409     }
410
411     // Check that the storage actually triggers the expected exception when
412     // trying to load the target entity.
413     $message = 'An exception is thrown when trying to load the target entity';
414     try {
415       $storage->load($target_id);
416       $this->fail($message);
417     }
418     catch (EntityStorageException $e) {
419       $this->pass($message);
420     }
421   }
422
423   /**
424    * Tests the dependencies entity reference fields are created with.
425    */
426   public function testEntityReferenceFieldDependencies() {
427     $field_name = 'user_reference_field';
428     $entity_type = 'entity_test';
429
430     $field_storage = FieldStorageConfig::create([
431       'field_name' => $field_name,
432       'type' => 'entity_reference',
433       'entity_type' => $entity_type,
434       'settings' => [
435         'target_type' => 'user',
436       ],
437     ]);
438     $field_storage->save();
439     $this->assertEqual(['module' => ['entity_test', 'user']], $field_storage->getDependencies());
440
441     $field = FieldConfig::create([
442       'field_name' => $field_name,
443       'entity_type' => $entity_type,
444       'bundle' => 'entity_test',
445       'label' => $field_name,
446       'settings' => [
447         'handler' => 'default',
448       ],
449     ]);
450     $field->save();
451     $this->assertEqual(['config' => ['field.storage.entity_test.user_reference_field'], 'module' => ['entity_test']], $field->getDependencies());
452   }
453
454 }