3 namespace Drupal\KernelTests\Core\Entity;
5 use Drupal\Core\Entity\EntityInterface;
6 use Drupal\Core\Entity\RevisionLogInterface;
7 use Drupal\Core\Entity\TypedData\EntityDataDefinition;
8 use Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface;
9 use Drupal\Core\Field\BaseFieldDefinition;
10 use Drupal\Core\Field\FieldDefinitionInterface;
11 use Drupal\Core\Field\FieldItemInterface;
12 use Drupal\Core\Field\FieldItemListInterface;
13 use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
14 use Drupal\Core\TypedData\ComplexDataInterface;
15 use Drupal\Core\TypedData\DataDefinitionInterface;
16 use Drupal\Core\TypedData\ListInterface;
17 use Drupal\Core\TypedData\Type\StringInterface;
18 use Drupal\Core\TypedData\TypedDataInterface;
19 use Drupal\entity_test\Entity\EntityTestComputedField;
20 use Drupal\node\Entity\Node;
21 use Drupal\node\Entity\NodeType;
24 * Tests the Entity Field API.
28 class EntityFieldTest extends EntityKernelTestBase {
35 public static $modules = ['filter', 'text', 'node', 'user', 'field_test'];
40 protected $entityName;
43 * @var \Drupal\user\Entity\User
45 protected $entityUser;
50 protected $entityFieldText;
52 protected function setUp() {
55 foreach (entity_test_entity_types() as $entity_type_id) {
56 // The entity_test schema is installed by the parent.
57 if ($entity_type_id != 'entity_test') {
58 $this->installEntitySchema($entity_type_id);
62 // Create the test field.
63 module_load_install('entity_test');
64 entity_test_install();
66 // Install required default configuration for filter module.
67 $this->installConfig(['system', 'filter']);
71 * Creates a test entity.
73 * @return \Drupal\Core\Entity\EntityInterface
75 protected function createTestEntity($entity_type) {
76 $this->entityName = $this->randomMachineName();
77 $this->entityUser = $this->createUser();
78 $this->entityFieldText = $this->randomMachineName();
80 // Pass in the value of the name field when creating. With the user
81 // field we test setting a field after creation.
82 $entity = $this->container->get('entity_type.manager')
83 ->getStorage($entity_type)
85 $entity->user_id->target_id = $this->entityUser->id();
86 $entity->name->value = $this->entityName;
88 // Set a value for the test field.
89 $entity->field_test_text->value = $this->entityFieldText;
95 * Tests reading and writing properties and field items.
97 public function testReadWrite() {
98 // All entity variations have to have the same results.
99 foreach (entity_test_entity_types() as $entity_type) {
100 $this->doTestReadWrite($entity_type);
105 * Executes the read write test set for a defined entity type.
107 * @param string $entity_type
108 * The entity type to run the tests with.
110 protected function doTestReadWrite($entity_type) {
111 $entity = $this->createTestEntity($entity_type);
115 // Access the name field.
116 $this->assertTrue($entity->name instanceof FieldItemListInterface, format_string('%entity_type: Field implements interface', ['%entity_type' => $entity_type]));
117 $this->assertTrue($entity->name[0] instanceof FieldItemInterface, format_string('%entity_type: Field item implements interface', ['%entity_type' => $entity_type]));
119 $this->assertEqual($this->entityName, $entity->name->value, format_string('%entity_type: Name value can be read.', ['%entity_type' => $entity_type]));
120 $this->assertEqual($this->entityName, $entity->name[0]->value, format_string('%entity_type: Name value can be read through list access.', ['%entity_type' => $entity_type]));
121 $this->assertEqual($entity->name->getValue(), [0 => ['value' => $this->entityName]], format_string('%entity_type: Plain field value returned.', ['%entity_type' => $entity_type]));
124 $new_name = $this->randomMachineName();
125 $entity->name->value = $new_name;
126 $this->assertEqual($new_name, $entity->name->value, format_string('%entity_type: Name can be updated and read.', ['%entity_type' => $entity_type]));
127 $this->assertEqual($entity->name->getValue(), [0 => ['value' => $new_name]], format_string('%entity_type: Plain field value reflects the update.', ['%entity_type' => $entity_type]));
129 $new_name = $this->randomMachineName();
130 $entity->name[0]->value = $new_name;
131 $this->assertEqual($new_name, $entity->name->value, format_string('%entity_type: Name can be updated and read through list access.', ['%entity_type' => $entity_type]));
133 // Access the user field.
134 $this->assertTrue($entity->user_id instanceof FieldItemListInterface, format_string('%entity_type: Field implements interface', ['%entity_type' => $entity_type]));
135 $this->assertTrue($entity->user_id[0] instanceof FieldItemInterface, format_string('%entity_type: Field item implements interface', ['%entity_type' => $entity_type]));
137 $this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', ['%entity_type' => $entity_type]));
138 $this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', ['%entity_type' => $entity_type]));
140 // Change the assigned user by entity.
141 $new_user1 = $this->createUser();
142 $entity->user_id->entity = $new_user1;
143 $this->assertEqual($new_user1->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
144 $this->assertEqual($new_user1->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated username value can be read.', ['%entity_type' => $entity_type]));
146 // Change the assigned user by id.
147 $new_user2 = $this->createUser();
148 $entity->user_id->target_id = $new_user2->id();
149 $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
150 $this->assertEqual($new_user2->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated username value can be read.', ['%entity_type' => $entity_type]));
152 // Try unsetting a field property.
153 $entity->name->value = NULL;
154 $entity->user_id->target_id = NULL;
155 $this->assertNull($entity->name->value, format_string('%entity_type: Name field is not set.', ['%entity_type' => $entity_type]));
156 $this->assertNull($entity->user_id->target_id, format_string('%entity_type: User ID field is not set.', ['%entity_type' => $entity_type]));
157 $this->assertNull($entity->user_id->entity, format_string('%entity_type: User entity field is not set.', ['%entity_type' => $entity_type]));
159 // Test setting the values via the typed data API works as well.
160 // Change the assigned user by entity.
161 $entity->user_id->first()->get('entity')->setValue($new_user2);
162 $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
163 $this->assertEqual($new_user2->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated user name value can be read.', ['%entity_type' => $entity_type]));
165 // Change the assigned user by id.
166 $entity->user_id->first()->get('target_id')->setValue($new_user2->id());
167 $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
168 $this->assertEqual($new_user2->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated user name value can be read.', ['%entity_type' => $entity_type]));
170 // Try unsetting a field.
171 $entity->name->first()->get('value')->setValue(NULL);
172 $entity->user_id->first()->get('target_id')->setValue(NULL);
173 $this->assertNull($entity->name->value, format_string('%entity_type: Name field is not set.', ['%entity_type' => $entity_type]));
174 $this->assertNull($entity->user_id->target_id, format_string('%entity_type: User ID field is not set.', ['%entity_type' => $entity_type]));
175 $this->assertNull($entity->user_id->entity, format_string('%entity_type: User entity field is not set.', ['%entity_type' => $entity_type]));
177 // Create a fresh entity so target_id does not get its property object
178 // instantiated, then verify setting a new value via typed data API works.
179 $entity2 = $this->container->get('entity_type.manager')
180 ->getStorage($entity_type)
182 'user_id' => ['target_id' => $new_user1->id()],
184 // Access the property object, and set a value.
185 $entity2->user_id->first()->get('target_id')->setValue($new_user2->id());
186 $this->assertEqual($new_user2->id(), $entity2->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
187 $this->assertEqual($new_user2->name->value, $entity2->user_id->entity->name->value, format_string('%entity_type: Updated user name value can be read.', ['%entity_type' => $entity_type]));
189 // Test using isset(), empty() and unset().
190 $entity->name->value = 'test unset';
191 unset($entity->name->value);
192 $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name is not set.', ['%entity_type' => $entity_type]));
193 $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: Name is not set.', ['%entity_type' => $entity_type]));
194 $this->assertTrue(empty($entity->name->value), format_string('%entity_type: Name is empty.', ['%entity_type' => $entity_type]));
195 $this->assertTrue(empty($entity->name[0]->value), format_string('%entity_type: Name is empty.', ['%entity_type' => $entity_type]));
197 $entity->name->value = 'a value';
198 $this->assertTrue(isset($entity->name->value), format_string('%entity_type: Name is set.', ['%entity_type' => $entity_type]));
199 $this->assertTrue(isset($entity->name[0]->value), format_string('%entity_type: Name is set.', ['%entity_type' => $entity_type]));
200 $this->assertFalse(empty($entity->name->value), format_string('%entity_type: Name is not empty.', ['%entity_type' => $entity_type]));
201 $this->assertFalse(empty($entity->name[0]->value), format_string('%entity_type: Name is not empty.', ['%entity_type' => $entity_type]));
202 $this->assertTrue(isset($entity->name[0]), format_string('%entity_type: Name string item is set.', ['%entity_type' => $entity_type]));
203 $this->assertFalse(isset($entity->name[1]), format_string('%entity_type: Second name string item is not set as it does not exist', ['%entity_type' => $entity_type]));
204 $this->assertTrue(isset($entity->name), format_string('%entity_type: Name field is set.', ['%entity_type' => $entity_type]));
205 $this->assertFalse(isset($entity->nameInvalid), format_string('%entity_type: Not existing field is not set.', ['%entity_type' => $entity_type]));
207 unset($entity->name[0]);
208 $this->assertFalse(isset($entity->name[0]), format_string('%entity_type: Name field item is not set.', ['%entity_type' => $entity_type]));
209 $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: Name is not set.', ['%entity_type' => $entity_type]));
210 $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name is not set.', ['%entity_type' => $entity_type]));
212 // Test emptying a field by assigning an empty value. NULL and array()
214 foreach ([NULL, [], 'unset'] as $empty) {
215 // Make sure a value is present
216 $entity->name->value = 'a value';
217 $this->assertTrue(isset($entity->name->value), format_string('%entity_type: Name is set.', ['%entity_type' => $entity_type]));
218 // Now, empty the field.
219 if ($empty === 'unset') {
220 unset($entity->name);
223 $entity->name = $empty;
225 $this->assertTrue(isset($entity->name), format_string('%entity_type: Name field is set.', ['%entity_type' => $entity_type]));
226 $this->assertTrue($entity->name->isEmpty(), format_string('%entity_type: Name field is set.', ['%entity_type' => $entity_type]));
227 $this->assertIdentical(count($entity->name), 0, format_string('%entity_type: Name field contains no items.', ['%entity_type' => $entity_type]));
228 $this->assertIdentical($entity->name->getValue(), [], format_string('%entity_type: Name field value is an empty array.', ['%entity_type' => $entity_type]));
229 $this->assertFalse(isset($entity->name[0]), format_string('%entity_type: Name field item is not set.', ['%entity_type' => $entity_type]));
230 $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: First name item value is not set.', ['%entity_type' => $entity_type]));
231 $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name value is not set.', ['%entity_type' => $entity_type]));
234 // Access the language field.
235 $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
236 $this->assertEqual($langcode, $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', ['%entity_type' => $entity_type]));
237 $this->assertEqual(\Drupal::languageManager()->getLanguage($langcode), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', ['%entity_type' => $entity_type]));
239 // Change the language by code.
240 $entity->{$langcode_key}->value = \Drupal::languageManager()->getDefaultLanguage()->getId();
241 $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage()->getId(), $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', ['%entity_type' => $entity_type]));
242 $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage(), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', ['%entity_type' => $entity_type]));
244 // Revert language by code then try setting it by language object.
245 $entity->{$langcode_key}->value = $langcode;
246 $entity->{$langcode_key}->language = \Drupal::languageManager()->getDefaultLanguage();
247 $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage()->getId(), $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', ['%entity_type' => $entity_type]));
248 $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage(), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', ['%entity_type' => $entity_type]));
250 // Access the text field and test updating.
251 $this->assertEqual($entity->field_test_text->value, $this->entityFieldText, format_string('%entity_type: Text field can be read.', ['%entity_type' => $entity_type]));
252 $new_text = $this->randomMachineName();
253 $entity->field_test_text->value = $new_text;
254 $this->assertEqual($entity->field_test_text->value, $new_text, format_string('%entity_type: Updated text field can be read.', ['%entity_type' => $entity_type]));
256 // Test creating the entity by passing in plain values.
257 $this->entityName = $this->randomMachineName();
258 $name_item[0]['value'] = $this->entityName;
259 $this->entityUser = $this->createUser();
260 $user_item[0]['target_id'] = $this->entityUser->id();
261 $this->entityFieldText = $this->randomMachineName();
262 $text_item[0]['value'] = $this->entityFieldText;
264 $entity = $this->container->get('entity_type.manager')
265 ->getStorage($entity_type)
267 'name' => $name_item,
268 'user_id' => $user_item,
269 'field_test_text' => $text_item,
271 $this->assertEqual($this->entityName, $entity->name->value, format_string('%entity_type: Name value can be read.', ['%entity_type' => $entity_type]));
272 $this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', ['%entity_type' => $entity_type]));
273 $this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', ['%entity_type' => $entity_type]));
274 $this->assertEqual($this->entityFieldText, $entity->field_test_text->value, format_string('%entity_type: Text field can be read.', ['%entity_type' => $entity_type]));
276 // Tests copying field values by assigning the TypedData objects.
277 $entity2 = $this->createTestEntity($entity_type);
278 $entity2->name = $entity->name;
279 $entity2->user_id = $entity->user_id;
280 $entity2->field_test_text = $entity->field_test_text;
281 $this->assertFalse($entity->name === $entity2->name, format_string('%entity_type: Copying properties results in a different field object.', ['%entity_type' => $entity_type]));
282 $this->assertEqual($entity->name->value, $entity2->name->value, format_string('%entity_type: Name field copied.', ['%entity_type' => $entity_type]));
283 $this->assertEqual($entity->user_id->target_id, $entity2->user_id->target_id, format_string('%entity_type: User id field copied.', ['%entity_type' => $entity_type]));
284 $this->assertEqual($entity->field_test_text->value, $entity2->field_test_text->value, format_string('%entity_type: Text field copied.', ['%entity_type' => $entity_type]));
286 // Tests that assigning TypedData objects to non-field properties keeps the
287 // assigned value as is.
288 $entity2 = $this->createTestEntity($entity_type);
289 $entity2->_not_a_field = $entity->name;
290 $this->assertTrue($entity2->_not_a_field === $entity->name, format_string('%entity_type: Typed data objects can be copied to non-field properties as is.', ['%entity_type' => $entity_type]));
292 // Tests adding a value to a field item list.
293 $entity->name[] = 'Another name';
294 $this->assertEqual($entity->name[1]->value, 'Another name', format_string('%entity_type: List item added via [] and the first property.', ['%entity_type' => $entity_type]));
295 $entity->name[] = ['value' => 'Third name'];
296 $this->assertEqual($entity->name[2]->value, 'Third name', format_string('%entity_type: List item added via [] and an array of properties.', ['%entity_type' => $entity_type]));
297 $entity->name[3] = ['value' => 'Fourth name'];
298 $this->assertEqual($entity->name[3]->value, 'Fourth name', format_string('%entity_type: List item added via offset and an array of properties.', ['%entity_type' => $entity_type]));
299 unset($entity->name[3]);
301 // Test removing and empty-ing list items.
302 $this->assertEqual(count($entity->name), 3, format_string('%entity_type: List has 3 items.', ['%entity_type' => $entity_type]));
303 unset($entity->name[1]);
304 $this->assertEqual(count($entity->name), 2, format_string('%entity_type: Second list item has been removed.', ['%entity_type' => $entity_type]));
305 $this->assertEqual($entity->name[1]->value, 'Third name', format_string('%entity_type: The subsequent items have been shifted up.', ['%entity_type' => $entity_type]));
306 $this->assertEqual($entity->name[1]->getName(), 1, format_string('%entity_type: The items names have been updated to their new delta.', ['%entity_type' => $entity_type]));
307 $entity->name[1] = NULL;
308 $this->assertEqual(count($entity->name), 2, format_string('%entity_type: Assigning NULL does not reduce array count.', ['%entity_type' => $entity_type]));
309 $this->assertTrue($entity->name[1]->isEmpty(), format_string('%entity_type: Assigning NULL empties the item.', ['%entity_type' => $entity_type]));
311 // Test using isEmpty().
312 unset($entity->name[1]);
313 $this->assertFalse($entity->name[0]->isEmpty(), format_string('%entity_type: Name item is not empty.', ['%entity_type' => $entity_type]));
314 $entity->name->value = NULL;
315 $this->assertTrue($entity->name[0]->isEmpty(), format_string('%entity_type: Name item is empty.', ['%entity_type' => $entity_type]));
316 $this->assertTrue($entity->name->isEmpty(), format_string('%entity_type: Name field is empty.', ['%entity_type' => $entity_type]));
317 $this->assertEqual(count($entity->name), 1, format_string('%entity_type: Empty item is considered when counting.', ['%entity_type' => $entity_type]));
318 $this->assertEqual(count(iterator_to_array($entity->name->getIterator())), count($entity->name), format_string('%entity_type: Count matches iterator count.', ['%entity_type' => $entity_type]));
319 $this->assertTrue($entity->name->getValue() === [0 => ['value' => NULL]], format_string('%entity_type: Name field value contains a NULL value.', ['%entity_type' => $entity_type]));
321 // Test using filterEmptyItems().
322 $entity->name = [NULL, 'foo'];
323 $this->assertEqual(count($entity->name), 2, format_string('%entity_type: List has 2 items.', ['%entity_type' => $entity_type]));
324 $entity->name->filterEmptyItems();
325 $this->assertEqual(count($entity->name), 1, format_string('%entity_type: The empty item was removed.', ['%entity_type' => $entity_type]));
326 $this->assertEqual($entity->name[0]->value, 'foo', format_string('%entity_type: The items were renumbered.', ['%entity_type' => $entity_type]));
327 $this->assertEqual($entity->name[0]->getName(), 0, format_string('%entity_type: The deltas were updated in the items.', ['%entity_type' => $entity_type]));
329 // Test get and set field values.
330 $entity->name = 'foo';
331 $this->assertEqual($entity->name[0]->toArray(), ['value' => 'foo'], format_string('%entity_type: Field value has been retrieved via toArray()', ['%entity_type' => $entity_type]));
333 $values = $entity->toArray();
334 $this->assertEqual($values['name'], [0 => ['value' => 'foo']], format_string('%entity_type: Field value has been retrieved via toArray() from an entity.', ['%entity_type' => $entity_type]));
336 // Make sure the user id can be set to zero.
337 $user_item[0]['target_id'] = 0;
338 $entity = $this->container->get('entity_type.manager')
339 ->getStorage($entity_type)
341 'name' => $name_item,
342 'user_id' => $user_item,
343 'field_test_text' => $text_item,
345 $this->assertNotNull($entity->user_id->target_id, format_string('%entity_type: User id is not NULL', ['%entity_type' => $entity_type]));
346 $this->assertIdentical($entity->user_id->target_id, 0, format_string('%entity_type: User id has been set to 0', ['%entity_type' => $entity_type]));
348 // Test setting the ID with the value only.
349 $entity = $this->container->get('entity_type.manager')
350 ->getStorage($entity_type)
352 'name' => $name_item,
354 'field_test_text' => $text_item,
356 $this->assertNotNull($entity->user_id->target_id, format_string('%entity_type: User id is not NULL', ['%entity_type' => $entity_type]));
357 $this->assertIdentical($entity->user_id->target_id, 0, format_string('%entity_type: User id has been set to 0', ['%entity_type' => $entity_type]));
361 * Tries to save and load an entity again.
363 public function testSave() {
364 // All entity variations have to have the same results.
365 foreach (entity_test_entity_types() as $entity_type) {
366 $this->doTestSave($entity_type);
371 * Executes the save tests for the given entity type.
373 * @param string $entity_type
374 * The entity type to run the tests with.
376 protected function doTestSave($entity_type) {
377 $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
378 $entity = $this->createTestEntity($entity_type);
380 $this->assertTrue((bool) $entity->id(), format_string('%entity_type: Entity has received an id.', ['%entity_type' => $entity_type]));
382 $entity = $this->container->get('entity_type.manager')
383 ->getStorage($entity_type)
384 ->load($entity->id());
385 $this->assertTrue((bool) $entity->id(), format_string('%entity_type: Entity loaded.', ['%entity_type' => $entity_type]));
387 // Access the name field.
388 $this->assertEqual(1, $entity->id->value, format_string('%entity_type: ID value can be read.', ['%entity_type' => $entity_type]));
389 $this->assertTrue(is_string($entity->uuid->value), format_string('%entity_type: UUID value can be read.', ['%entity_type' => $entity_type]));
390 $this->assertEqual('en', $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', ['%entity_type' => $entity_type]));
391 $this->assertEqual(\Drupal::languageManager()->getLanguage('en'), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', ['%entity_type' => $entity_type]));
392 $this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', ['%entity_type' => $entity_type]));
393 $this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', ['%entity_type' => $entity_type]));
394 $this->assertEqual($this->entityFieldText, $entity->field_test_text->value, format_string('%entity_type: Text field can be read.', ['%entity_type' => $entity_type]));
398 * Tests introspection and getting metadata upfront.
400 public function testIntrospection() {
401 // All entity variations have to have the same results.
402 foreach (entity_test_entity_types() as $entity_type) {
403 $this->doTestIntrospection($entity_type);
408 * Executes the introspection tests for the given entity type.
410 * @param string $entity_type
411 * The entity type to run the tests with.
413 protected function doTestIntrospection($entity_type) {
414 // Test getting metadata upfront. The entity types used for this test have
415 // a default bundle that is the same as the entity type.
416 $definitions = \Drupal::entityManager()->getFieldDefinitions($entity_type, $entity_type);
417 $this->assertEqual($definitions['name']->getType(), 'string', $entity_type . ': Name field found.');
418 $this->assertEqual($definitions['user_id']->getType(), 'entity_reference', $entity_type . ': User field found.');
419 $this->assertEqual($definitions['field_test_text']->getType(), 'text', $entity_type . ': Test-text-field field found.');
421 // Test deriving further metadata.
422 $this->assertTrue($definitions['name'] instanceof FieldDefinitionInterface);
423 $field_item_definition = $definitions['name']->getItemDefinition();
424 $this->assertTrue($field_item_definition instanceof ComplexDataDefinitionInterface);
425 $this->assertEqual($field_item_definition->getDataType(), 'field_item:string');
426 $value_definition = $field_item_definition->getPropertyDefinition('value');
427 $this->assertTrue($value_definition instanceof DataDefinitionInterface);
428 $this->assertEqual($value_definition->getDataType(), 'string');
430 // Test deriving metadata from references.
431 $entity_definition = EntityDataDefinition::create($entity_type);
432 $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
433 $reference_definition = $entity_definition->getPropertyDefinition($langcode_key)
434 ->getPropertyDefinition('language')
435 ->getTargetDefinition();
436 $this->assertEqual($reference_definition->getDataType(), 'language');
438 $reference_definition = $entity_definition->getPropertyDefinition('user_id')
439 ->getPropertyDefinition('entity')
440 ->getTargetDefinition();
442 $this->assertTrue($reference_definition instanceof EntityDataDefinitionInterface, 'Definition of the referenced user retrieved.');
443 $this->assertEqual($reference_definition->getEntityTypeId(), 'user', 'Referenced entity is of type "user".');
445 // Test propagating down.
446 $name_definition = $reference_definition->getPropertyDefinition('name');
447 $this->assertTrue($name_definition instanceof FieldDefinitionInterface);
448 $this->assertEqual($name_definition->getPropertyDefinition('value')->getDataType(), 'string');
450 // Test introspecting an entity object.
451 // @todo: Add bundles and test bundles as well.
452 $entity = $this->container->get('entity_type.manager')
453 ->getStorage($entity_type)
456 $definitions = $entity->getFieldDefinitions();
457 $this->assertEqual($definitions['name']->getType(), 'string', $entity_type . ': Name field found.');
458 $this->assertEqual($definitions['user_id']->getType(), 'entity_reference', $entity_type . ': User field found.');
459 $this->assertEqual($definitions['field_test_text']->getType(), 'text', $entity_type . ': Test-text-field field found.');
461 $name_properties = $entity->name->getFieldDefinition()->getPropertyDefinitions();
462 $this->assertEqual($name_properties['value']->getDataType(), 'string', $entity_type . ': String value property of the name found.');
464 $userref_properties = $entity->user_id->getFieldDefinition()->getPropertyDefinitions();
465 $this->assertEqual($userref_properties['target_id']->getDataType(), 'integer', $entity_type . ': Entity id property of the user found.');
466 $this->assertEqual($userref_properties['entity']->getDataType(), 'entity_reference', $entity_type . ': Entity reference property of the user found.');
468 $textfield_properties = $entity->field_test_text->getFieldDefinition()->getFieldStorageDefinition()->getPropertyDefinitions();
469 $this->assertEqual($textfield_properties['value']->getDataType(), 'string', $entity_type . ': String value property of the test-text field found.');
470 $this->assertEqual($textfield_properties['format']->getDataType(), 'filter_format', $entity_type . ': String format field of the test-text field found.');
471 $this->assertEqual($textfield_properties['processed']->getDataType(), 'string', $entity_type . ': String processed property of the test-text field found.');
473 // Make sure provided contextual information is right.
474 $entity_adapter = $entity->getTypedData();
475 $this->assertSame($entity_adapter->getRoot(), $entity_adapter, 'Entity is root object.');
476 $this->assertEqual($entity_adapter->getPropertyPath(), '');
477 $this->assertEqual($entity_adapter->getName(), '');
478 $this->assertEqual($entity_adapter->getParent(), NULL);
480 $field = $entity->user_id;
481 $this->assertSame($field->getRoot()->getValue(), $entity, 'Entity is root object.');
482 $this->assertSame($field->getEntity(), $entity, 'getEntity() returns the entity.');
483 $this->assertEqual($field->getPropertyPath(), 'user_id');
484 $this->assertEqual($field->getName(), 'user_id');
485 $this->assertSame($field->getParent()->getValue(), $entity, 'Parent object matches.');
487 $field_item = $field[0];
488 $this->assertSame($field_item->getRoot()->getValue(), $entity, 'Entity is root object.');
489 $this->assertSame($field_item->getEntity(), $entity, 'getEntity() returns the entity.');
490 $this->assertEqual($field_item->getPropertyPath(), 'user_id.0');
491 $this->assertEqual($field_item->getName(), '0');
492 $this->assertSame($field_item->getParent(), $field, 'Parent object matches.');
494 $item_value = $field_item->get('entity');
495 $this->assertSame($item_value->getRoot()->getValue(), $entity, 'Entity is root object.');
496 $this->assertEqual($item_value->getPropertyPath(), 'user_id.0.entity');
497 $this->assertEqual($item_value->getName(), 'entity');
498 $this->assertSame($item_value->getParent(), $field_item, 'Parent object matches.');
502 * Tests iterating over properties.
504 public function testIterator() {
505 // All entity variations have to have the same results.
506 foreach (entity_test_entity_types() as $entity_type) {
507 $this->doTestIterator($entity_type);
512 * Executes the iterator tests for the given entity type.
514 * @param string $entity_type
515 * The entity type to run the tests with.
517 protected function doTestIterator($entity_type) {
518 $entity = $this->createTestEntity($entity_type);
520 foreach ($entity as $name => $field) {
521 $this->assertTrue($field instanceof FieldItemListInterface, $entity_type . ": Field $name implements interface.");
523 foreach ($field as $delta => $item) {
524 $this->assertTrue($field[0] instanceof FieldItemInterface, $entity_type . ": Item $delta of field $name implements interface.");
526 foreach ($item as $value_name => $value_property) {
527 $this->assertTrue($value_property instanceof TypedDataInterface, $entity_type . ": Value $value_name of item $delta of field $name implements interface.");
529 $value = $value_property->getValue();
530 $this->assertTrue(!isset($value) || is_scalar($value) || $value instanceof EntityInterface, $entity_type . ": Value $value_name of item $delta of field $name is a primitive or an entity.");
535 $fields = $entity->getFields();
536 $this->assertEqual(array_keys($fields), array_keys($entity->getTypedData()->getDataDefinition()->getPropertyDefinitions()), format_string('%entity_type: All fields returned.', ['%entity_type' => $entity_type]));
537 $this->assertEqual($fields, iterator_to_array($entity->getIterator()), format_string('%entity_type: Entity iterator iterates over all fields.', ['%entity_type' => $entity_type]));
541 * Tests working with the entity based upon the TypedData API.
543 public function testDataStructureInterfaces() {
544 // All entity variations have to have the same results.
545 foreach (entity_test_entity_types() as $entity_type) {
546 $this->doTestDataStructureInterfaces($entity_type);
551 * Executes the data structure interfaces tests for the given entity type.
553 * @param string $entity_type
554 * The entity type to run the tests with.
556 protected function doTestDataStructureInterfaces($entity_type) {
557 $entity = $this->createTestEntity($entity_type);
559 // Test using the whole tree of typed data by navigating through the tree of
560 // contained properties and getting all contained strings, limited by a
563 $this->getContainedStrings($entity->getTypedData(), 0, $strings);
565 // @todo: Once the user entity has defined properties this should contain
566 // the user name and other user entity strings as well.
568 $entity->uuid->value,
573 $this->entityFieldText,
578 if ($entity instanceof RevisionLogInterface) {
579 // Adding empty string for revision message.
580 $target_strings[] = '';
584 asort($target_strings);
585 $this->assertEqual(array_values($strings), array_values($target_strings), format_string('%entity_type: All contained strings found.', ['%entity_type' => $entity_type]));
589 * Recursive helper for getting all contained strings,
590 * i.e. properties of type string.
592 public function getContainedStrings(TypedDataInterface $wrapper, $depth, array &$strings) {
594 if ($wrapper instanceof StringInterface) {
595 $strings[] = $wrapper->getValue();
598 // Recurse until a certain depth is reached if possible.
600 if ($wrapper instanceof ListInterface) {
601 foreach ($wrapper as $item) {
602 $this->getContainedStrings($item, $depth + 1, $strings);
605 elseif ($wrapper instanceof ComplexDataInterface) {
606 foreach ($wrapper as $property) {
607 $this->getContainedStrings($property, $depth + 1, $strings);
614 * Makes sure data types are correctly derived for all entity types.
616 public function testDataTypes() {
617 $types = \Drupal::typedDataManager()->getDefinitions();
618 foreach (entity_test_entity_types() as $entity_type) {
619 $this->assertTrue($types['entity:' . $entity_type]['class'], 'Entity data type registered.');
621 // Check bundle types are provided as well.
622 entity_test_create_bundle('bundle');
623 $types = \Drupal::typedDataManager()->getDefinitions();
624 $this->assertTrue($types['entity:entity_test:bundle']['class'], 'Entity bundle data type registered.');
628 * Tests a base field override on a non-existing base field.
630 * @see entity_test_entity_base_field_info_alter()
632 public function testBaseFieldNonExistingBaseField() {
633 $this->entityManager->getStorage('node_type')->create([
637 $this->entityManager->clearCachedFieldDefinitions();
638 $fields = $this->entityManager->getFieldDefinitions('node', 'page');
639 $override = $fields['status']->getConfig('page');
640 $override->setLabel($this->randomString())->save();
641 \Drupal::state()->set('entity_test.node_remove_status_field', TRUE);
642 $this->entityManager->clearCachedFieldDefinitions();
643 $fields = $this->entityManager->getFieldDefinitions('node', 'page');
644 // A base field override on a non-existing base field should not cause a
645 // field definition to come into existence.
646 $this->assertFalse(isset($fields['status']), 'Node\'s status base field does not exist.');
650 * Tests creating a field override config for a bundle field.
652 * @see entity_test_entity_base_field_info_alter()
654 public function testFieldOverrideBundleField() {
655 // First make sure the bundle field override in code, which is provided by
656 // the test entity works.
657 entity_test_create_bundle('some_test_bundle', 'Some test bundle', 'entity_test_field_override');
658 $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'entity_test_field_override');
659 $this->assertEqual($field_definitions['name']->getDescription(), 'The default description.');
660 $this->assertNull($field_definitions['name']->getTargetBundle());
662 $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'some_test_bundle');
663 $this->assertEqual($field_definitions['name']->getDescription(), 'Custom description.');
664 $this->assertEqual($field_definitions['name']->getTargetBundle(), 'some_test_bundle');
666 // Now create a config override of the bundle field.
667 $field_config = $field_definitions['name']->getConfig('some_test_bundle');
668 $field_config->setTranslatable(FALSE);
669 $field_config->save();
671 // Make sure both overrides are present.
672 $this->entityManager->clearCachedFieldDefinitions();
673 $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'some_test_bundle');
674 $this->assertEqual($field_definitions['name']->getDescription(), 'Custom description.');
675 $this->assertEqual($field_definitions['name']->getTargetBundle(), 'some_test_bundle');
676 $this->assertFalse($field_definitions['name']->isTranslatable());
680 * Tests validation constraints provided by the Entity API.
682 public function testEntityConstraintValidation() {
683 $entity = $this->createTestEntity('entity_test');
685 // Create a reference field item and let it reference the entity.
686 $definition = BaseFieldDefinition::create('entity_reference')
687 ->setLabel('Test entity')
688 ->setSetting('target_type', 'entity_test');
689 $reference_field = \Drupal::typedDataManager()->create($definition);
690 $reference = $reference_field->appendItem(['entity' => $entity])->get('entity');
692 // Test validation the typed data object.
693 $violations = $reference->validate();
694 $this->assertEqual($violations->count(), 0);
696 // Test validating an entity of the wrong type.
697 $user = $this->createUser();
699 $node = $node = Node::create([
701 'uid' => $user->id(),
702 'title' => $this->randomString(),
704 $reference->setValue($node);
705 $violations = $reference->validate();
706 $this->assertEqual($violations->count(), 1);
708 // Test bundle validation.
709 NodeType::create(['type' => 'article'])
711 $definition = BaseFieldDefinition::create('entity_reference')
712 ->setLabel('Test entity')
713 ->setSetting('target_type', 'node')
714 ->setSetting('handler_settings', ['target_bundles' => ['article' => 'article']]);
715 $reference_field = \Drupal::TypedDataManager()->create($definition);
716 $reference_field->appendItem(['entity' => $node]);
717 $violations = $reference_field->validate();
718 $this->assertEqual($violations->count(), 1);
720 $node = Node::create([
722 'uid' => $user->id(),
723 'title' => $this->randomString(),
726 $reference_field->entity = $node;
727 $violations = $reference_field->validate();
728 $this->assertEqual($violations->count(), 0);
732 * Tests getting processed property values via a computed property.
734 public function testComputedProperties() {
735 // All entity variations have to have the same results.
736 foreach (entity_test_entity_types() as $entity_type) {
737 $this->doTestComputedProperties($entity_type);
742 * Tests all the interaction points of a computed field.
744 public function testComputedFields() {
745 $this->installEntitySchema('entity_test_computed_field');
747 \Drupal::state()->set('entity_test_computed_field_item_list_value', ['foo computed']);
749 // Check that the values are not computed unnecessarily during the lifecycle
750 // of an entity when the field is not interacted with directly.
751 \Drupal::state()->set('computed_test_field_execution', 0);
752 $entity = EntityTestComputedField::create([]);
753 $this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0));
755 $entity->name->value = $this->randomString();
756 $this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0));
759 $this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0));
761 // Test \Drupal\Core\TypedData\ComputedItemListTrait::getValue().
762 \Drupal::state()->set('computed_test_field_execution', 0);
763 $entity = EntityTestComputedField::create([]);
764 $this->assertSame([['value' => 'foo computed']], $entity->computed_string_field->getValue());
766 // Check that the values are only computed once.
767 $this->assertSame(1, \Drupal::state()->get('computed_test_field_execution', 0));
769 // Test \Drupal\Core\TypedData\ComputedItemListTrait::setValue(). This also
770 // checks that a subsequent getter does not try to re-compute the value.
771 \Drupal::state()->set('computed_test_field_execution', 0);
772 $entity = EntityTestComputedField::create([]);
773 $entity->computed_string_field->setValue([
774 ['value' => 'foo computed 1'],
775 ['value' => 'foo computed 2'],
777 $this->assertSame([['value' => 'foo computed 1'], ['value' => 'foo computed 2']], $entity->computed_string_field->getValue());
779 // Check that the values have not been computed when they were explicitly
781 $this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0));
783 // Test \Drupal\Core\TypedData\ComputedItemListTrait::getString().
784 $entity = EntityTestComputedField::create([]);
785 $this->assertSame('foo computed', $entity->computed_string_field->getString());
787 // Test \Drupal\Core\TypedData\ComputedItemListTrait::get().
788 $entity = EntityTestComputedField::create([]);
789 $this->assertSame('foo computed', $entity->computed_string_field->get(0)->value);
790 $this->assertEmpty($entity->computed_string_field->get(1));
792 // Test \Drupal\Core\TypedData\ComputedItemListTrait::set().
793 $entity = EntityTestComputedField::create([]);
794 $entity->computed_string_field->set(1, 'foo computed 1');
795 $this->assertSame('foo computed', $entity->computed_string_field[0]->value);
796 $this->assertSame('foo computed 1', $entity->computed_string_field[1]->value);
797 $entity->computed_string_field->set(0, 'foo computed 0');
798 $this->assertSame('foo computed 0', $entity->computed_string_field[0]->value);
799 $this->assertSame('foo computed 1', $entity->computed_string_field[1]->value);
801 // Test \Drupal\Core\TypedData\ComputedItemListTrait::appendItem().
802 $entity = EntityTestComputedField::create([]);
803 $entity->computed_string_field->appendItem('foo computed 1');
804 $this->assertSame('foo computed', $entity->computed_string_field[0]->value);
805 $this->assertSame('foo computed 1', $entity->computed_string_field[1]->value);
807 // Test \Drupal\Core\TypedData\ComputedItemListTrait::removeItem().
808 $entity = EntityTestComputedField::create([]);
809 $entity->computed_string_field->removeItem(0);
810 $this->assertTrue($entity->computed_string_field->isEmpty());
812 // Test \Drupal\Core\TypedData\ComputedItemListTrait::isEmpty().
813 \Drupal::state()->set('entity_test_computed_field_item_list_value', []);
814 $entity = EntityTestComputedField::create([]);
815 $this->assertTrue($entity->computed_string_field->isEmpty());
817 \Drupal::state()->set('entity_test_computed_field_item_list_value', ['foo computed']);
818 $entity = EntityTestComputedField::create([]);
819 $this->assertFalse($entity->computed_string_field->isEmpty());
821 // Test \Drupal\Core\TypedData\ComputedItemListTrait::filter().
822 $filter_callback = function ($item) {
823 return !$item->isEmpty();
825 $entity = EntityTestComputedField::create([]);
826 $entity->computed_string_field->filter($filter_callback);
827 $this->assertCount(1, $entity->computed_string_field);
829 // Add an empty item to the list and check that it is filtered out.
830 $entity->computed_string_field->appendItem();
831 $entity->computed_string_field->filter($filter_callback);
832 $this->assertCount(1, $entity->computed_string_field);
834 // Add a non-empty item to the list and check that it is not filtered out.
835 $entity->computed_string_field->appendItem('foo computed 1');
836 $entity->computed_string_field->filter($filter_callback);
837 $this->assertCount(2, $entity->computed_string_field);
839 // Test \Drupal\Core\TypedData\ComputedItemListTrait::offsetExists().
840 $entity = EntityTestComputedField::create([]);
841 $this->assertTrue($entity->computed_string_field->offsetExists(0));
842 $this->assertFalse($entity->computed_string_field->offsetExists(1));
844 // Test \Drupal\Core\TypedData\ComputedItemListTrait::getIterator().
845 $entity = EntityTestComputedField::create([]);
846 foreach ($entity->computed_string_field as $delta => $item) {
847 $this->assertSame('foo computed', $item->value);
850 // Test \Drupal\Core\TypedData\ComputedItemListTrait::count().
851 $entity = EntityTestComputedField::create([]);
852 $this->assertCount(1, $entity->computed_string_field);
854 // Check that computed items are not auto-created when they have no values.
855 \Drupal::state()->set('entity_test_computed_field_item_list_value', []);
856 $entity = EntityTestComputedField::create([]);
857 $this->assertCount(0, $entity->computed_string_field);
859 // Test \Drupal\Core\Field\FieldItemList::equals() for a computed field.
860 \Drupal::state()->set('entity_test_computed_field_item_list_value', ['foo computed']);
861 $entity = EntityTestComputedField::create([]);
862 $computed_item_list1 = $entity->computed_string_field;
864 $entity = EntityTestComputedField::create([]);
865 $computed_item_list2 = $entity->computed_string_field;
867 $this->assertTrue($computed_item_list1->equals($computed_item_list2));
869 $computed_item_list2->value = 'foo computed 2';
870 $this->assertFalse($computed_item_list1->equals($computed_item_list2));
874 * Executes the computed properties tests for the given entity type.
876 * @param string $entity_type
877 * The entity type to run the tests with.
879 protected function doTestComputedProperties($entity_type) {
880 $entity = $this->createTestEntity($entity_type);
881 $entity->field_test_text->value = "The <strong>text</strong> text to filter.";
882 $entity->field_test_text->format = filter_default_format();
884 $target = "<p>The <strong>text</strong> text to filter.</p>\n";
885 $this->assertEqual($entity->field_test_text->processed, $target, format_string('%entity_type: Text is processed with the default filter.', ['%entity_type' => $entity_type]));
887 // Save and load entity and make sure it still works.
889 $entity = $this->container->get('entity_type.manager')
890 ->getStorage($entity_type)
891 ->load($entity->id());
892 $this->assertEqual($entity->field_test_text->processed, $target, format_string('%entity_type: Text is processed with the default filter.', ['%entity_type' => $entity_type]));