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\EntityTest;
20 use Drupal\entity_test\Entity\EntityTestComputedField;
21 use Drupal\node\Entity\Node;
22 use Drupal\node\Entity\NodeType;
25 * Tests the Entity Field API.
29 class EntityFieldTest extends EntityKernelTestBase {
36 public static $modules = ['filter', 'text', 'node', 'user', 'field_test'];
41 protected $entityName;
44 * @var \Drupal\user\Entity\User
46 protected $entityUser;
51 protected $entityFieldText;
53 protected function setUp() {
56 foreach (entity_test_entity_types() as $entity_type_id) {
57 // The entity_test schema is installed by the parent.
58 if ($entity_type_id != 'entity_test') {
59 $this->installEntitySchema($entity_type_id);
63 // Create the test field.
64 module_load_install('entity_test');
65 entity_test_install();
67 // Install required default configuration for filter module.
68 $this->installConfig(['system', 'filter']);
72 * Creates a test entity.
74 * @return \Drupal\Core\Entity\EntityInterface
76 protected function createTestEntity($entity_type) {
77 $this->entityName = $this->randomMachineName();
78 $this->entityUser = $this->createUser();
79 $this->entityFieldText = $this->randomMachineName();
81 // Pass in the value of the name field when creating. With the user
82 // field we test setting a field after creation.
83 $entity = $this->container->get('entity_type.manager')
84 ->getStorage($entity_type)
86 $entity->user_id->target_id = $this->entityUser->id();
87 $entity->name->value = $this->entityName;
89 // Set a value for the test field.
90 $entity->field_test_text->value = $this->entityFieldText;
96 * Tests reading and writing properties and field items.
98 public function testReadWrite() {
99 // All entity variations have to have the same results.
100 foreach (entity_test_entity_types() as $entity_type) {
101 $this->doTestReadWrite($entity_type);
106 * Executes the read write test set for a defined entity type.
108 * @param string $entity_type
109 * The entity type to run the tests with.
111 protected function doTestReadWrite($entity_type) {
112 $entity = $this->createTestEntity($entity_type);
116 // Access the name field.
117 $this->assertTrue($entity->name instanceof FieldItemListInterface, format_string('%entity_type: Field implements interface', ['%entity_type' => $entity_type]));
118 $this->assertTrue($entity->name[0] instanceof FieldItemInterface, format_string('%entity_type: Field item implements interface', ['%entity_type' => $entity_type]));
120 $this->assertEqual($this->entityName, $entity->name->value, format_string('%entity_type: Name value can be read.', ['%entity_type' => $entity_type]));
121 $this->assertEqual($this->entityName, $entity->name[0]->value, format_string('%entity_type: Name value can be read through list access.', ['%entity_type' => $entity_type]));
122 $this->assertEqual($entity->name->getValue(), [0 => ['value' => $this->entityName]], format_string('%entity_type: Plain field value returned.', ['%entity_type' => $entity_type]));
125 $new_name = $this->randomMachineName();
126 $entity->name->value = $new_name;
127 $this->assertEqual($new_name, $entity->name->value, format_string('%entity_type: Name can be updated and read.', ['%entity_type' => $entity_type]));
128 $this->assertEqual($entity->name->getValue(), [0 => ['value' => $new_name]], format_string('%entity_type: Plain field value reflects the update.', ['%entity_type' => $entity_type]));
130 $new_name = $this->randomMachineName();
131 $entity->name[0]->value = $new_name;
132 $this->assertEqual($new_name, $entity->name->value, format_string('%entity_type: Name can be updated and read through list access.', ['%entity_type' => $entity_type]));
134 // Access the user field.
135 $this->assertTrue($entity->user_id instanceof FieldItemListInterface, format_string('%entity_type: Field implements interface', ['%entity_type' => $entity_type]));
136 $this->assertTrue($entity->user_id[0] instanceof FieldItemInterface, format_string('%entity_type: Field item implements interface', ['%entity_type' => $entity_type]));
138 $this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', ['%entity_type' => $entity_type]));
139 $this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', ['%entity_type' => $entity_type]));
141 // Change the assigned user by entity.
142 $new_user1 = $this->createUser();
143 $entity->user_id->entity = $new_user1;
144 $this->assertEqual($new_user1->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
145 $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]));
147 // Change the assigned user by id.
148 $new_user2 = $this->createUser();
149 $entity->user_id->target_id = $new_user2->id();
150 $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
151 $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]));
153 // Try unsetting a field property.
154 $entity->name->value = NULL;
155 $entity->user_id->target_id = NULL;
156 $this->assertNull($entity->name->value, format_string('%entity_type: Name field is not set.', ['%entity_type' => $entity_type]));
157 $this->assertNull($entity->user_id->target_id, format_string('%entity_type: User ID field is not set.', ['%entity_type' => $entity_type]));
158 $this->assertNull($entity->user_id->entity, format_string('%entity_type: User entity field is not set.', ['%entity_type' => $entity_type]));
160 // Test setting the values via the typed data API works as well.
161 // Change the assigned user by entity.
162 $entity->user_id->first()->get('entity')->setValue($new_user2);
163 $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
164 $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]));
166 // Change the assigned user by id.
167 $entity->user_id->first()->get('target_id')->setValue($new_user2->id());
168 $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
169 $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]));
171 // Try unsetting a field.
172 $entity->name->first()->get('value')->setValue(NULL);
173 $entity->user_id->first()->get('target_id')->setValue(NULL);
174 $this->assertNull($entity->name->value, format_string('%entity_type: Name field is not set.', ['%entity_type' => $entity_type]));
175 $this->assertNull($entity->user_id->target_id, format_string('%entity_type: User ID field is not set.', ['%entity_type' => $entity_type]));
176 $this->assertNull($entity->user_id->entity, format_string('%entity_type: User entity field is not set.', ['%entity_type' => $entity_type]));
178 // Create a fresh entity so target_id does not get its property object
179 // instantiated, then verify setting a new value via typed data API works.
180 $entity2 = $this->container->get('entity_type.manager')
181 ->getStorage($entity_type)
183 'user_id' => ['target_id' => $new_user1->id()],
185 // Access the property object, and set a value.
186 $entity2->user_id->first()->get('target_id')->setValue($new_user2->id());
187 $this->assertEqual($new_user2->id(), $entity2->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
188 $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]));
190 // Test using isset(), empty() and unset().
191 $entity->name->value = 'test unset';
192 unset($entity->name->value);
193 $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name is not set.', ['%entity_type' => $entity_type]));
194 $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: Name is not set.', ['%entity_type' => $entity_type]));
195 $this->assertTrue(empty($entity->name->value), format_string('%entity_type: Name is empty.', ['%entity_type' => $entity_type]));
196 $this->assertTrue(empty($entity->name[0]->value), format_string('%entity_type: Name is empty.', ['%entity_type' => $entity_type]));
198 $entity->name->value = 'a value';
199 $this->assertTrue(isset($entity->name->value), format_string('%entity_type: Name is set.', ['%entity_type' => $entity_type]));
200 $this->assertTrue(isset($entity->name[0]->value), format_string('%entity_type: Name is set.', ['%entity_type' => $entity_type]));
201 $this->assertFalse(empty($entity->name->value), format_string('%entity_type: Name is not empty.', ['%entity_type' => $entity_type]));
202 $this->assertFalse(empty($entity->name[0]->value), format_string('%entity_type: Name is not empty.', ['%entity_type' => $entity_type]));
203 $this->assertTrue(isset($entity->name[0]), format_string('%entity_type: Name string item is set.', ['%entity_type' => $entity_type]));
204 $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]));
205 $this->assertTrue(isset($entity->name), format_string('%entity_type: Name field is set.', ['%entity_type' => $entity_type]));
206 $this->assertFalse(isset($entity->nameInvalid), format_string('%entity_type: Not existing field is not set.', ['%entity_type' => $entity_type]));
208 unset($entity->name[0]);
209 $this->assertFalse(isset($entity->name[0]), format_string('%entity_type: Name field item is not set.', ['%entity_type' => $entity_type]));
210 $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: Name is not set.', ['%entity_type' => $entity_type]));
211 $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name is not set.', ['%entity_type' => $entity_type]));
213 // Test emptying a field by assigning an empty value. NULL and array()
215 foreach ([NULL, [], 'unset'] as $empty) {
216 // Make sure a value is present
217 $entity->name->value = 'a value';
218 $this->assertTrue(isset($entity->name->value), format_string('%entity_type: Name is set.', ['%entity_type' => $entity_type]));
219 // Now, empty the field.
220 if ($empty === 'unset') {
221 unset($entity->name);
224 $entity->name = $empty;
226 $this->assertTrue(isset($entity->name), format_string('%entity_type: Name field is set.', ['%entity_type' => $entity_type]));
227 $this->assertTrue($entity->name->isEmpty(), format_string('%entity_type: Name field is set.', ['%entity_type' => $entity_type]));
228 $this->assertIdentical(count($entity->name), 0, format_string('%entity_type: Name field contains no items.', ['%entity_type' => $entity_type]));
229 $this->assertIdentical($entity->name->getValue(), [], format_string('%entity_type: Name field value is an empty array.', ['%entity_type' => $entity_type]));
230 $this->assertFalse(isset($entity->name[0]), format_string('%entity_type: Name field item is not set.', ['%entity_type' => $entity_type]));
231 $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: First name item value is not set.', ['%entity_type' => $entity_type]));
232 $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name value is not set.', ['%entity_type' => $entity_type]));
235 // Access the language field.
236 $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
237 $this->assertEqual($langcode, $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', ['%entity_type' => $entity_type]));
238 $this->assertEqual(\Drupal::languageManager()->getLanguage($langcode), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', ['%entity_type' => $entity_type]));
240 // Change the language by code.
241 $entity->{$langcode_key}->value = \Drupal::languageManager()->getDefaultLanguage()->getId();
242 $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage()->getId(), $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', ['%entity_type' => $entity_type]));
243 $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage(), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', ['%entity_type' => $entity_type]));
245 // Revert language by code then try setting it by language object.
246 $entity->{$langcode_key}->value = $langcode;
247 $entity->{$langcode_key}->language = \Drupal::languageManager()->getDefaultLanguage();
248 $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage()->getId(), $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', ['%entity_type' => $entity_type]));
249 $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage(), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', ['%entity_type' => $entity_type]));
251 // Access the text field and test updating.
252 $this->assertEqual($entity->field_test_text->value, $this->entityFieldText, format_string('%entity_type: Text field can be read.', ['%entity_type' => $entity_type]));
253 $new_text = $this->randomMachineName();
254 $entity->field_test_text->value = $new_text;
255 $this->assertEqual($entity->field_test_text->value, $new_text, format_string('%entity_type: Updated text field can be read.', ['%entity_type' => $entity_type]));
257 // Test creating the entity by passing in plain values.
258 $this->entityName = $this->randomMachineName();
259 $name_item[0]['value'] = $this->entityName;
260 $this->entityUser = $this->createUser();
261 $user_item[0]['target_id'] = $this->entityUser->id();
262 $this->entityFieldText = $this->randomMachineName();
263 $text_item[0]['value'] = $this->entityFieldText;
265 $entity = $this->container->get('entity_type.manager')
266 ->getStorage($entity_type)
268 'name' => $name_item,
269 'user_id' => $user_item,
270 'field_test_text' => $text_item,
272 $this->assertEqual($this->entityName, $entity->name->value, format_string('%entity_type: Name value can be read.', ['%entity_type' => $entity_type]));
273 $this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', ['%entity_type' => $entity_type]));
274 $this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', ['%entity_type' => $entity_type]));
275 $this->assertEqual($this->entityFieldText, $entity->field_test_text->value, format_string('%entity_type: Text field can be read.', ['%entity_type' => $entity_type]));
277 // Tests copying field values by assigning the TypedData objects.
278 $entity2 = $this->createTestEntity($entity_type);
279 $entity2->name = $entity->name;
280 $entity2->user_id = $entity->user_id;
281 $entity2->field_test_text = $entity->field_test_text;
282 $this->assertFalse($entity->name === $entity2->name, format_string('%entity_type: Copying properties results in a different field object.', ['%entity_type' => $entity_type]));
283 $this->assertEqual($entity->name->value, $entity2->name->value, format_string('%entity_type: Name field copied.', ['%entity_type' => $entity_type]));
284 $this->assertEqual($entity->user_id->target_id, $entity2->user_id->target_id, format_string('%entity_type: User id field copied.', ['%entity_type' => $entity_type]));
285 $this->assertEqual($entity->field_test_text->value, $entity2->field_test_text->value, format_string('%entity_type: Text field copied.', ['%entity_type' => $entity_type]));
287 // Tests that assigning TypedData objects to non-field properties keeps the
288 // assigned value as is.
289 $entity2 = $this->createTestEntity($entity_type);
290 $entity2->_not_a_field = $entity->name;
291 $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]));
293 // Tests adding a value to a field item list.
294 $entity->name[] = 'Another name';
295 $this->assertEqual($entity->name[1]->value, 'Another name', format_string('%entity_type: List item added via [] and the first property.', ['%entity_type' => $entity_type]));
296 $entity->name[] = ['value' => 'Third name'];
297 $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]));
298 $entity->name[3] = ['value' => 'Fourth name'];
299 $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]));
300 unset($entity->name[3]);
302 // Test removing and empty-ing list items.
303 $this->assertEqual(count($entity->name), 3, format_string('%entity_type: List has 3 items.', ['%entity_type' => $entity_type]));
304 unset($entity->name[1]);
305 $this->assertEqual(count($entity->name), 2, format_string('%entity_type: Second list item has been removed.', ['%entity_type' => $entity_type]));
306 $this->assertEqual($entity->name[1]->value, 'Third name', format_string('%entity_type: The subsequent items have been shifted up.', ['%entity_type' => $entity_type]));
307 $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]));
308 $entity->name[1] = NULL;
309 $this->assertEqual(count($entity->name), 2, format_string('%entity_type: Assigning NULL does not reduce array count.', ['%entity_type' => $entity_type]));
310 $this->assertTrue($entity->name[1]->isEmpty(), format_string('%entity_type: Assigning NULL empties the item.', ['%entity_type' => $entity_type]));
312 // Test using isEmpty().
313 unset($entity->name[1]);
314 $this->assertFalse($entity->name[0]->isEmpty(), format_string('%entity_type: Name item is not empty.', ['%entity_type' => $entity_type]));
315 $entity->name->value = NULL;
316 $this->assertTrue($entity->name[0]->isEmpty(), format_string('%entity_type: Name item is empty.', ['%entity_type' => $entity_type]));
317 $this->assertTrue($entity->name->isEmpty(), format_string('%entity_type: Name field is empty.', ['%entity_type' => $entity_type]));
318 $this->assertEqual(count($entity->name), 1, format_string('%entity_type: Empty item is considered when counting.', ['%entity_type' => $entity_type]));
319 $this->assertEqual(count(iterator_to_array($entity->name->getIterator())), count($entity->name), format_string('%entity_type: Count matches iterator count.', ['%entity_type' => $entity_type]));
320 $this->assertTrue($entity->name->getValue() === [0 => ['value' => NULL]], format_string('%entity_type: Name field value contains a NULL value.', ['%entity_type' => $entity_type]));
322 // Test using filterEmptyItems().
323 $entity->name = [NULL, 'foo'];
324 $this->assertEqual(count($entity->name), 2, format_string('%entity_type: List has 2 items.', ['%entity_type' => $entity_type]));
325 $entity->name->filterEmptyItems();
326 $this->assertEqual(count($entity->name), 1, format_string('%entity_type: The empty item was removed.', ['%entity_type' => $entity_type]));
327 $this->assertEqual($entity->name[0]->value, 'foo', format_string('%entity_type: The items were renumbered.', ['%entity_type' => $entity_type]));
328 $this->assertEqual($entity->name[0]->getName(), 0, format_string('%entity_type: The deltas were updated in the items.', ['%entity_type' => $entity_type]));
330 // Test get and set field values.
331 $entity->name = 'foo';
332 $this->assertEqual($entity->name[0]->toArray(), ['value' => 'foo'], format_string('%entity_type: Field value has been retrieved via toArray()', ['%entity_type' => $entity_type]));
334 $values = $entity->toArray();
335 $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]));
337 // Make sure the user id can be set to zero.
338 $user_item[0]['target_id'] = 0;
339 $entity = $this->container->get('entity_type.manager')
340 ->getStorage($entity_type)
342 'name' => $name_item,
343 'user_id' => $user_item,
344 'field_test_text' => $text_item,
346 $this->assertNotNull($entity->user_id->target_id, format_string('%entity_type: User id is not NULL', ['%entity_type' => $entity_type]));
347 $this->assertIdentical($entity->user_id->target_id, 0, format_string('%entity_type: User id has been set to 0', ['%entity_type' => $entity_type]));
349 // Test setting the ID with the value only.
350 $entity = $this->container->get('entity_type.manager')
351 ->getStorage($entity_type)
353 'name' => $name_item,
355 'field_test_text' => $text_item,
357 $this->assertNotNull($entity->user_id->target_id, format_string('%entity_type: User id is not NULL', ['%entity_type' => $entity_type]));
358 $this->assertIdentical($entity->user_id->target_id, 0, format_string('%entity_type: User id has been set to 0', ['%entity_type' => $entity_type]));
362 * Tries to save and load an entity again.
364 public function testSave() {
365 // All entity variations have to have the same results.
366 foreach (entity_test_entity_types() as $entity_type) {
367 $this->doTestSave($entity_type);
372 * Executes the save tests for the given entity type.
374 * @param string $entity_type
375 * The entity type to run the tests with.
377 protected function doTestSave($entity_type) {
378 $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
379 $entity = $this->createTestEntity($entity_type);
381 $this->assertTrue((bool) $entity->id(), format_string('%entity_type: Entity has received an id.', ['%entity_type' => $entity_type]));
383 $entity = $this->container->get('entity_type.manager')
384 ->getStorage($entity_type)
385 ->load($entity->id());
386 $this->assertTrue((bool) $entity->id(), format_string('%entity_type: Entity loaded.', ['%entity_type' => $entity_type]));
388 // Access the name field.
389 $this->assertEqual(1, $entity->id->value, format_string('%entity_type: ID value can be read.', ['%entity_type' => $entity_type]));
390 $this->assertTrue(is_string($entity->uuid->value), format_string('%entity_type: UUID value can be read.', ['%entity_type' => $entity_type]));
391 $this->assertEqual('en', $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', ['%entity_type' => $entity_type]));
392 $this->assertEqual(\Drupal::languageManager()->getLanguage('en'), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', ['%entity_type' => $entity_type]));
393 $this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', ['%entity_type' => $entity_type]));
394 $this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', ['%entity_type' => $entity_type]));
395 $this->assertEqual($this->entityFieldText, $entity->field_test_text->value, format_string('%entity_type: Text field can be read.', ['%entity_type' => $entity_type]));
399 * Tests introspection and getting metadata upfront.
401 public function testIntrospection() {
402 // All entity variations have to have the same results.
403 foreach (entity_test_entity_types() as $entity_type) {
404 $this->doTestIntrospection($entity_type);
409 * Executes the introspection tests for the given entity type.
411 * @param string $entity_type
412 * The entity type to run the tests with.
414 protected function doTestIntrospection($entity_type) {
415 // Test getting metadata upfront. The entity types used for this test have
416 // a default bundle that is the same as the entity type.
417 $definitions = \Drupal::entityManager()->getFieldDefinitions($entity_type, $entity_type);
418 $this->assertEqual($definitions['name']->getType(), 'string', $entity_type . ': Name field found.');
419 $this->assertEqual($definitions['user_id']->getType(), 'entity_reference', $entity_type . ': User field found.');
420 $this->assertEqual($definitions['field_test_text']->getType(), 'text', $entity_type . ': Test-text-field field found.');
422 // Test deriving further metadata.
423 $this->assertTrue($definitions['name'] instanceof FieldDefinitionInterface);
424 $field_item_definition = $definitions['name']->getItemDefinition();
425 $this->assertTrue($field_item_definition instanceof ComplexDataDefinitionInterface);
426 $this->assertEqual($field_item_definition->getDataType(), 'field_item:string');
427 $value_definition = $field_item_definition->getPropertyDefinition('value');
428 $this->assertTrue($value_definition instanceof DataDefinitionInterface);
429 $this->assertEqual($value_definition->getDataType(), 'string');
431 // Test deriving metadata from references.
432 $entity_definition = EntityDataDefinition::create($entity_type);
433 $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
434 $reference_definition = $entity_definition->getPropertyDefinition($langcode_key)
435 ->getPropertyDefinition('language')
436 ->getTargetDefinition();
437 $this->assertEqual($reference_definition->getDataType(), 'language');
439 $reference_definition = $entity_definition->getPropertyDefinition('user_id')
440 ->getPropertyDefinition('entity')
441 ->getTargetDefinition();
443 $this->assertTrue($reference_definition instanceof EntityDataDefinitionInterface, 'Definition of the referenced user retrieved.');
444 $this->assertEqual($reference_definition->getEntityTypeId(), 'user', 'Referenced entity is of type "user".');
446 // Test propagating down.
447 $name_definition = $reference_definition->getPropertyDefinition('name');
448 $this->assertTrue($name_definition instanceof FieldDefinitionInterface);
449 $this->assertEqual($name_definition->getPropertyDefinition('value')->getDataType(), 'string');
451 // Test introspecting an entity object.
452 // @todo: Add bundles and test bundles as well.
453 $entity = $this->container->get('entity_type.manager')
454 ->getStorage($entity_type)
457 $definitions = $entity->getFieldDefinitions();
458 $this->assertEqual($definitions['name']->getType(), 'string', $entity_type . ': Name field found.');
459 $this->assertEqual($definitions['user_id']->getType(), 'entity_reference', $entity_type . ': User field found.');
460 $this->assertEqual($definitions['field_test_text']->getType(), 'text', $entity_type . ': Test-text-field field found.');
462 $name_properties = $entity->name->getFieldDefinition()->getPropertyDefinitions();
463 $this->assertEqual($name_properties['value']->getDataType(), 'string', $entity_type . ': String value property of the name found.');
465 $userref_properties = $entity->user_id->getFieldDefinition()->getPropertyDefinitions();
466 $this->assertEqual($userref_properties['target_id']->getDataType(), 'integer', $entity_type . ': Entity id property of the user found.');
467 $this->assertEqual($userref_properties['entity']->getDataType(), 'entity_reference', $entity_type . ': Entity reference property of the user found.');
469 $textfield_properties = $entity->field_test_text->getFieldDefinition()->getFieldStorageDefinition()->getPropertyDefinitions();
470 $this->assertEqual($textfield_properties['value']->getDataType(), 'string', $entity_type . ': String value property of the test-text field found.');
471 $this->assertEqual($textfield_properties['format']->getDataType(), 'filter_format', $entity_type . ': String format field of the test-text field found.');
472 $this->assertEqual($textfield_properties['processed']->getDataType(), 'string', $entity_type . ': String processed property of the test-text field found.');
474 // Make sure provided contextual information is right.
475 $entity_adapter = $entity->getTypedData();
476 $this->assertSame($entity_adapter->getRoot(), $entity_adapter, 'Entity is root object.');
477 $this->assertEqual($entity_adapter->getPropertyPath(), '');
478 $this->assertEqual($entity_adapter->getName(), '');
479 $this->assertEqual($entity_adapter->getParent(), NULL);
481 $field = $entity->user_id;
482 $this->assertSame($field->getRoot()->getValue(), $entity, 'Entity is root object.');
483 $this->assertSame($field->getEntity(), $entity, 'getEntity() returns the entity.');
484 $this->assertEqual($field->getPropertyPath(), 'user_id');
485 $this->assertEqual($field->getName(), 'user_id');
486 $this->assertSame($field->getParent()->getValue(), $entity, 'Parent object matches.');
488 $field_item = $field[0];
489 $this->assertSame($field_item->getRoot()->getValue(), $entity, 'Entity is root object.');
490 $this->assertSame($field_item->getEntity(), $entity, 'getEntity() returns the entity.');
491 $this->assertEqual($field_item->getPropertyPath(), 'user_id.0');
492 $this->assertEqual($field_item->getName(), '0');
493 $this->assertSame($field_item->getParent(), $field, 'Parent object matches.');
495 $item_value = $field_item->get('entity');
496 $this->assertSame($item_value->getRoot()->getValue(), $entity, 'Entity is root object.');
497 $this->assertEqual($item_value->getPropertyPath(), 'user_id.0.entity');
498 $this->assertEqual($item_value->getName(), 'entity');
499 $this->assertSame($item_value->getParent(), $field_item, 'Parent object matches.');
503 * Tests iterating over properties.
505 public function testIterator() {
506 // All entity variations have to have the same results.
507 foreach (entity_test_entity_types() as $entity_type) {
508 $this->doTestIterator($entity_type);
513 * Executes the iterator tests for the given entity type.
515 * @param string $entity_type
516 * The entity type to run the tests with.
518 protected function doTestIterator($entity_type) {
519 $entity = $this->createTestEntity($entity_type);
521 foreach ($entity as $name => $field) {
522 $this->assertTrue($field instanceof FieldItemListInterface, $entity_type . ": Field $name implements interface.");
524 foreach ($field as $delta => $item) {
525 $this->assertTrue($field[0] instanceof FieldItemInterface, $entity_type . ": Item $delta of field $name implements interface.");
527 foreach ($item as $value_name => $value_property) {
528 $this->assertTrue($value_property instanceof TypedDataInterface, $entity_type . ": Value $value_name of item $delta of field $name implements interface.");
530 $value = $value_property->getValue();
531 $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.");
536 $fields = $entity->getFields();
537 $this->assertEqual(array_keys($fields), array_keys($entity->getTypedData()->getDataDefinition()->getPropertyDefinitions()), format_string('%entity_type: All fields returned.', ['%entity_type' => $entity_type]));
538 $this->assertEqual($fields, iterator_to_array($entity->getIterator()), format_string('%entity_type: Entity iterator iterates over all fields.', ['%entity_type' => $entity_type]));
542 * Tests working with the entity based upon the TypedData API.
544 public function testDataStructureInterfaces() {
545 // All entity variations have to have the same results.
546 foreach (entity_test_entity_types() as $entity_type) {
547 $this->doTestDataStructureInterfaces($entity_type);
552 * Executes the data structure interfaces tests for the given entity type.
554 * @param string $entity_type
555 * The entity type to run the tests with.
557 protected function doTestDataStructureInterfaces($entity_type) {
558 $entity = $this->createTestEntity($entity_type);
560 // Test using the whole tree of typed data by navigating through the tree of
561 // contained properties and getting all contained strings, limited by a
564 $this->getContainedStrings($entity->getTypedData(), 0, $strings);
566 // @todo: Once the user entity has defined properties this should contain
567 // the user name and other user entity strings as well.
569 $entity->uuid->value,
574 $this->entityFieldText,
579 if ($entity instanceof RevisionLogInterface) {
580 // Adding empty string for revision message.
581 $target_strings[] = '';
585 asort($target_strings);
586 $this->assertEqual(array_values($strings), array_values($target_strings), format_string('%entity_type: All contained strings found.', ['%entity_type' => $entity_type]));
590 * Recursive helper for getting all contained strings,
591 * i.e. properties of type string.
593 public function getContainedStrings(TypedDataInterface $wrapper, $depth, array &$strings) {
595 if ($wrapper instanceof StringInterface) {
596 $strings[] = $wrapper->getValue();
599 // Recurse until a certain depth is reached if possible.
601 if ($wrapper instanceof ListInterface) {
602 foreach ($wrapper as $item) {
603 $this->getContainedStrings($item, $depth + 1, $strings);
606 elseif ($wrapper instanceof ComplexDataInterface) {
607 foreach ($wrapper as $property) {
608 $this->getContainedStrings($property, $depth + 1, $strings);
615 * Makes sure data types are correctly derived for all entity types.
617 public function testDataTypes() {
618 $types = \Drupal::typedDataManager()->getDefinitions();
619 foreach (entity_test_entity_types() as $entity_type) {
620 $this->assertTrue($types['entity:' . $entity_type]['class'], 'Entity data type registered.');
622 // Check bundle types are provided as well.
623 entity_test_create_bundle('bundle');
624 $types = \Drupal::typedDataManager()->getDefinitions();
625 $this->assertTrue($types['entity:entity_test:bundle']['class'], 'Entity bundle data type registered.');
629 * Tests a base field override on a non-existing base field.
631 * @see entity_test_entity_base_field_info_alter()
633 public function testBaseFieldNonExistingBaseField() {
634 $this->entityManager->getStorage('node_type')->create([
638 $this->entityManager->clearCachedFieldDefinitions();
639 $fields = $this->entityManager->getFieldDefinitions('node', 'page');
640 $override = $fields['status']->getConfig('page');
641 $override->setLabel($this->randomString())->save();
642 \Drupal::state()->set('entity_test.node_remove_status_field', TRUE);
643 $this->entityManager->clearCachedFieldDefinitions();
644 $fields = $this->entityManager->getFieldDefinitions('node', 'page');
645 // A base field override on a non-existing base field should not cause a
646 // field definition to come into existence.
647 $this->assertFalse(isset($fields['status']), 'Node\'s status base field does not exist.');
651 * Tests creating a field override config for a bundle field.
653 * @see entity_test_entity_base_field_info_alter()
655 public function testFieldOverrideBundleField() {
656 // First make sure the bundle field override in code, which is provided by
657 // the test entity works.
658 entity_test_create_bundle('some_test_bundle', 'Some test bundle', 'entity_test_field_override');
659 $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'entity_test_field_override');
660 $this->assertEqual($field_definitions['name']->getDescription(), 'The default description.');
661 $this->assertNull($field_definitions['name']->getTargetBundle());
663 $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'some_test_bundle');
664 $this->assertEqual($field_definitions['name']->getDescription(), 'Custom description.');
665 $this->assertEqual($field_definitions['name']->getTargetBundle(), 'some_test_bundle');
667 // Now create a config override of the bundle field.
668 $field_config = $field_definitions['name']->getConfig('some_test_bundle');
669 $field_config->setTranslatable(FALSE);
670 $field_config->save();
672 // Make sure both overrides are present.
673 $this->entityManager->clearCachedFieldDefinitions();
674 $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'some_test_bundle');
675 $this->assertEqual($field_definitions['name']->getDescription(), 'Custom description.');
676 $this->assertEqual($field_definitions['name']->getTargetBundle(), 'some_test_bundle');
677 $this->assertFalse($field_definitions['name']->isTranslatable());
681 * Tests validation constraints provided by the Entity API.
683 public function testEntityConstraintValidation() {
684 $entity = $this->createTestEntity('entity_test');
686 // Create a reference field item and let it reference the entity.
687 $definition = BaseFieldDefinition::create('entity_reference')
688 ->setLabel('Test entity')
689 ->setSetting('target_type', 'entity_test');
690 $reference_field = \Drupal::typedDataManager()->create($definition);
691 $reference = $reference_field->appendItem(['entity' => $entity])->get('entity');
693 // Test validation the typed data object.
694 $violations = $reference->validate();
695 $this->assertEqual($violations->count(), 0);
697 // Test validating an entity of the wrong type.
698 $user = $this->createUser();
700 $node = $node = Node::create([
702 'uid' => $user->id(),
703 'title' => $this->randomString(),
705 $reference->setValue($node);
706 $violations = $reference->validate();
707 $this->assertEqual($violations->count(), 1);
709 // Test bundle validation.
710 NodeType::create(['type' => 'article'])
712 $definition = BaseFieldDefinition::create('entity_reference')
713 ->setLabel('Test entity')
714 ->setSetting('target_type', 'node')
715 ->setSetting('handler_settings', ['target_bundles' => ['article' => 'article']]);
716 $reference_field = \Drupal::TypedDataManager()->create($definition);
717 $reference_field->appendItem(['entity' => $node]);
718 $violations = $reference_field->validate();
719 $this->assertEqual($violations->count(), 1);
721 $node = Node::create([
723 'uid' => $user->id(),
724 'title' => $this->randomString(),
727 $reference_field->entity = $node;
728 $violations = $reference_field->validate();
729 $this->assertEqual($violations->count(), 0);
733 * Tests getting processed property values via a computed property.
735 public function testComputedProperties() {
736 // All entity variations have to have the same results.
737 foreach (entity_test_entity_types() as $entity_type) {
738 $this->doTestComputedProperties($entity_type);
743 * Tests all the interaction points of a computed field.
745 public function testComputedFields() {
746 $this->installEntitySchema('entity_test_computed_field');
748 \Drupal::state()->set('entity_test_computed_field_item_list_value', ['foo computed']);
750 // Check that the values are not computed unnecessarily during the lifecycle
751 // of an entity when the field is not interacted with directly.
752 \Drupal::state()->set('computed_test_field_execution', 0);
753 $entity = EntityTestComputedField::create([]);
754 $this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0));
756 $entity->name->value = $this->randomString();
757 $this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0));
760 $this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0));
762 // Test \Drupal\Core\TypedData\ComputedItemListTrait::getValue().
763 \Drupal::state()->set('computed_test_field_execution', 0);
764 $entity = EntityTestComputedField::create([]);
765 $this->assertSame([['value' => 'foo computed']], $entity->computed_string_field->getValue());
767 // Check that the values are only computed once.
768 $this->assertSame(1, \Drupal::state()->get('computed_test_field_execution', 0));
770 // Test \Drupal\Core\TypedData\ComputedItemListTrait::setValue(). This also
771 // checks that a subsequent getter does not try to re-compute the value.
772 \Drupal::state()->set('computed_test_field_execution', 0);
773 $entity = EntityTestComputedField::create([]);
774 $entity->computed_string_field->setValue([
775 ['value' => 'foo computed 1'],
776 ['value' => 'foo computed 2'],
778 $this->assertSame([['value' => 'foo computed 1'], ['value' => 'foo computed 2']], $entity->computed_string_field->getValue());
780 // Check that the values have not been computed when they were explicitly
782 $this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0));
784 // Test \Drupal\Core\TypedData\ComputedItemListTrait::getString().
785 $entity = EntityTestComputedField::create([]);
786 $this->assertSame('foo computed', $entity->computed_string_field->getString());
788 // Test \Drupal\Core\TypedData\ComputedItemListTrait::get().
789 $entity = EntityTestComputedField::create([]);
790 $this->assertSame('foo computed', $entity->computed_string_field->get(0)->value);
791 $this->assertEmpty($entity->computed_string_field->get(1));
793 // Test \Drupal\Core\TypedData\ComputedItemListTrait::set().
794 $entity = EntityTestComputedField::create([]);
795 $entity->computed_string_field->set(1, 'foo computed 1');
796 $this->assertSame('foo computed', $entity->computed_string_field[0]->value);
797 $this->assertSame('foo computed 1', $entity->computed_string_field[1]->value);
798 $entity->computed_string_field->set(0, 'foo computed 0');
799 $this->assertSame('foo computed 0', $entity->computed_string_field[0]->value);
800 $this->assertSame('foo computed 1', $entity->computed_string_field[1]->value);
802 // Test \Drupal\Core\TypedData\ComputedItemListTrait::appendItem().
803 $entity = EntityTestComputedField::create([]);
804 $entity->computed_string_field->appendItem('foo computed 1');
805 $this->assertSame('foo computed', $entity->computed_string_field[0]->value);
806 $this->assertSame('foo computed 1', $entity->computed_string_field[1]->value);
808 // Test \Drupal\Core\TypedData\ComputedItemListTrait::removeItem().
809 $entity = EntityTestComputedField::create([]);
810 $entity->computed_string_field->removeItem(0);
811 $this->assertTrue($entity->computed_string_field->isEmpty());
813 // Test \Drupal\Core\TypedData\ComputedItemListTrait::isEmpty().
814 \Drupal::state()->set('entity_test_computed_field_item_list_value', []);
815 $entity = EntityTestComputedField::create([]);
816 $this->assertTrue($entity->computed_string_field->isEmpty());
818 \Drupal::state()->set('entity_test_computed_field_item_list_value', ['foo computed']);
819 $entity = EntityTestComputedField::create([]);
820 $this->assertFalse($entity->computed_string_field->isEmpty());
822 // Test \Drupal\Core\TypedData\ComputedItemListTrait::filter().
823 $filter_callback = function ($item) {
824 return !$item->isEmpty();
826 $entity = EntityTestComputedField::create([]);
827 $entity->computed_string_field->filter($filter_callback);
828 $this->assertCount(1, $entity->computed_string_field);
830 // Add an empty item to the list and check that it is filtered out.
831 $entity->computed_string_field->appendItem();
832 $entity->computed_string_field->filter($filter_callback);
833 $this->assertCount(1, $entity->computed_string_field);
835 // Add a non-empty item to the list and check that it is not filtered out.
836 $entity->computed_string_field->appendItem('foo computed 1');
837 $entity->computed_string_field->filter($filter_callback);
838 $this->assertCount(2, $entity->computed_string_field);
840 // Test \Drupal\Core\TypedData\ComputedItemListTrait::offsetExists().
841 $entity = EntityTestComputedField::create([]);
842 $this->assertTrue($entity->computed_string_field->offsetExists(0));
843 $this->assertFalse($entity->computed_string_field->offsetExists(1));
845 // Test \Drupal\Core\TypedData\ComputedItemListTrait::getIterator().
846 $entity = EntityTestComputedField::create([]);
847 foreach ($entity->computed_string_field as $delta => $item) {
848 $this->assertSame('foo computed', $item->value);
851 // Test \Drupal\Core\TypedData\ComputedItemListTrait::count().
852 $entity = EntityTestComputedField::create([]);
853 $this->assertCount(1, $entity->computed_string_field);
855 // Check that computed items are not auto-created when they have no values.
856 \Drupal::state()->set('entity_test_computed_field_item_list_value', []);
857 $entity = EntityTestComputedField::create([]);
858 $this->assertCount(0, $entity->computed_string_field);
860 // Test \Drupal\Core\Field\FieldItemList::equals() for a computed field.
861 \Drupal::state()->set('entity_test_computed_field_item_list_value', ['foo computed']);
862 $entity = EntityTestComputedField::create([]);
863 $computed_item_list1 = $entity->computed_string_field;
865 $entity = EntityTestComputedField::create([]);
866 $computed_item_list2 = $entity->computed_string_field;
868 $this->assertTrue($computed_item_list1->equals($computed_item_list2));
870 $computed_item_list2->value = 'foo computed 2';
871 $this->assertFalse($computed_item_list1->equals($computed_item_list2));
875 * Tests an entity reference computed field.
877 public function testEntityReferenceComputedField() {
878 $this->installEntitySchema('entity_test_computed_field');
880 // Create 2 entities to be referenced.
881 $ref1 = EntityTest::create(['name' => 'foo', 'type' => 'bar']);
883 $ref2 = EntityTest::create(['name' => 'baz', 'type' => 'bar']);
885 \Drupal::state()->set('entity_test_reference_computed_target_ids', [$ref1->id(), $ref2->id()]);
887 $entity = EntityTestComputedField::create([]);
890 /** @var \Drupal\entity_test\Plugin\Field\ComputedReferenceTestFieldItemList $field */
891 $field = $entity->get('computed_reference_field');
892 /** @var \Drupal\Core\Entity\EntityInterface[] $referenced_entities */
893 $referenced_entities = $field->referencedEntities();
895 // Check that ::referencedEntities() is working with computed fields.
896 $this->assertEquals($ref1->id(), $referenced_entities[0]->id());
897 $this->assertEquals($ref2->id(), $referenced_entities[1]->id());
901 * Executes the computed properties tests for the given entity type.
903 * @param string $entity_type
904 * The entity type to run the tests with.
906 protected function doTestComputedProperties($entity_type) {
907 $entity = $this->createTestEntity($entity_type);
908 $entity->field_test_text->value = "The <strong>text</strong> text to filter.";
909 $entity->field_test_text->format = filter_default_format();
911 $target = "<p>The <strong>text</strong> text to filter.</p>\n";
912 $this->assertEqual($entity->field_test_text->processed, $target, format_string('%entity_type: Text is processed with the default filter.', ['%entity_type' => $entity_type]));
914 // Save and load entity and make sure it still works.
916 $entity = $this->container->get('entity_type.manager')
917 ->getStorage($entity_type)
918 ->load($entity->id());
919 $this->assertEqual($entity->field_test_text->processed, $target, format_string('%entity_type: Text is processed with the default filter.', ['%entity_type' => $entity_type]));