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\node\Entity\Node;
20 use Drupal\node\Entity\NodeType;
23 * Tests the Entity Field API.
27 class EntityFieldTest extends EntityKernelTestBase {
34 public static $modules = ['filter', 'text', 'node', 'user', 'field_test'];
39 protected $entityName;
42 * @var \Drupal\user\Entity\User
44 protected $entityUser;
49 protected $entityFieldText;
51 protected function setUp() {
54 foreach (entity_test_entity_types() as $entity_type_id) {
55 // The entity_test schema is installed by the parent.
56 if ($entity_type_id != 'entity_test') {
57 $this->installEntitySchema($entity_type_id);
61 // Create the test field.
62 module_load_install('entity_test');
63 entity_test_install();
65 // Install required default configuration for filter module.
66 $this->installConfig(['system', 'filter']);
70 * Creates a test entity.
72 * @return \Drupal\Core\Entity\EntityInterface
74 protected function createTestEntity($entity_type) {
75 $this->entityName = $this->randomMachineName();
76 $this->entityUser = $this->createUser();
77 $this->entityFieldText = $this->randomMachineName();
79 // Pass in the value of the name field when creating. With the user
80 // field we test setting a field after creation.
81 $entity = $this->container->get('entity_type.manager')
82 ->getStorage($entity_type)
84 $entity->user_id->target_id = $this->entityUser->id();
85 $entity->name->value = $this->entityName;
87 // Set a value for the test field.
88 $entity->field_test_text->value = $this->entityFieldText;
94 * Tests reading and writing properties and field items.
96 public function testReadWrite() {
97 // All entity variations have to have the same results.
98 foreach (entity_test_entity_types() as $entity_type) {
99 $this->doTestReadWrite($entity_type);
104 * Executes the read write test set for a defined entity type.
106 * @param string $entity_type
107 * The entity type to run the tests with.
109 protected function doTestReadWrite($entity_type) {
110 $entity = $this->createTestEntity($entity_type);
114 // Access the name field.
115 $this->assertTrue($entity->name instanceof FieldItemListInterface, format_string('%entity_type: Field implements interface', ['%entity_type' => $entity_type]));
116 $this->assertTrue($entity->name[0] instanceof FieldItemInterface, format_string('%entity_type: Field item implements interface', ['%entity_type' => $entity_type]));
118 $this->assertEqual($this->entityName, $entity->name->value, format_string('%entity_type: Name value can be read.', ['%entity_type' => $entity_type]));
119 $this->assertEqual($this->entityName, $entity->name[0]->value, format_string('%entity_type: Name value can be read through list access.', ['%entity_type' => $entity_type]));
120 $this->assertEqual($entity->name->getValue(), [0 => ['value' => $this->entityName]], format_string('%entity_type: Plain field value returned.', ['%entity_type' => $entity_type]));
123 $new_name = $this->randomMachineName();
124 $entity->name->value = $new_name;
125 $this->assertEqual($new_name, $entity->name->value, format_string('%entity_type: Name can be updated and read.', ['%entity_type' => $entity_type]));
126 $this->assertEqual($entity->name->getValue(), [0 => ['value' => $new_name]], format_string('%entity_type: Plain field value reflects the update.', ['%entity_type' => $entity_type]));
128 $new_name = $this->randomMachineName();
129 $entity->name[0]->value = $new_name;
130 $this->assertEqual($new_name, $entity->name->value, format_string('%entity_type: Name can be updated and read through list access.', ['%entity_type' => $entity_type]));
132 // Access the user field.
133 $this->assertTrue($entity->user_id instanceof FieldItemListInterface, format_string('%entity_type: Field implements interface', ['%entity_type' => $entity_type]));
134 $this->assertTrue($entity->user_id[0] instanceof FieldItemInterface, format_string('%entity_type: Field item implements interface', ['%entity_type' => $entity_type]));
136 $this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', ['%entity_type' => $entity_type]));
137 $this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', ['%entity_type' => $entity_type]));
139 // Change the assigned user by entity.
140 $new_user1 = $this->createUser();
141 $entity->user_id->entity = $new_user1;
142 $this->assertEqual($new_user1->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
143 $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]));
145 // Change the assigned user by id.
146 $new_user2 = $this->createUser();
147 $entity->user_id->target_id = $new_user2->id();
148 $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
149 $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]));
151 // Try unsetting a field property.
152 $entity->name->value = NULL;
153 $entity->user_id->target_id = NULL;
154 $this->assertNull($entity->name->value, format_string('%entity_type: Name field is not set.', ['%entity_type' => $entity_type]));
155 $this->assertNull($entity->user_id->target_id, format_string('%entity_type: User ID field is not set.', ['%entity_type' => $entity_type]));
156 $this->assertNull($entity->user_id->entity, format_string('%entity_type: User entity field is not set.', ['%entity_type' => $entity_type]));
158 // Test setting the values via the typed data API works as well.
159 // Change the assigned user by entity.
160 $entity->user_id->first()->get('entity')->setValue($new_user2);
161 $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
162 $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]));
164 // Change the assigned user by id.
165 $entity->user_id->first()->get('target_id')->setValue($new_user2->id());
166 $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
167 $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]));
169 // Try unsetting a field.
170 $entity->name->first()->get('value')->setValue(NULL);
171 $entity->user_id->first()->get('target_id')->setValue(NULL);
172 $this->assertNull($entity->name->value, format_string('%entity_type: Name field is not set.', ['%entity_type' => $entity_type]));
173 $this->assertNull($entity->user_id->target_id, format_string('%entity_type: User ID field is not set.', ['%entity_type' => $entity_type]));
174 $this->assertNull($entity->user_id->entity, format_string('%entity_type: User entity field is not set.', ['%entity_type' => $entity_type]));
176 // Create a fresh entity so target_id does not get its property object
177 // instantiated, then verify setting a new value via typed data API works.
178 $entity2 = $this->container->get('entity_type.manager')
179 ->getStorage($entity_type)
181 'user_id' => ['target_id' => $new_user1->id()],
183 // Access the property object, and set a value.
184 $entity2->user_id->first()->get('target_id')->setValue($new_user2->id());
185 $this->assertEqual($new_user2->id(), $entity2->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
186 $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]));
188 // Test using isset(), empty() and unset().
189 $entity->name->value = 'test unset';
190 unset($entity->name->value);
191 $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name is not set.', ['%entity_type' => $entity_type]));
192 $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: Name is not set.', ['%entity_type' => $entity_type]));
193 $this->assertTrue(empty($entity->name->value), format_string('%entity_type: Name is empty.', ['%entity_type' => $entity_type]));
194 $this->assertTrue(empty($entity->name[0]->value), format_string('%entity_type: Name is empty.', ['%entity_type' => $entity_type]));
196 $entity->name->value = 'a value';
197 $this->assertTrue(isset($entity->name->value), format_string('%entity_type: Name is set.', ['%entity_type' => $entity_type]));
198 $this->assertTrue(isset($entity->name[0]->value), format_string('%entity_type: Name is set.', ['%entity_type' => $entity_type]));
199 $this->assertFalse(empty($entity->name->value), format_string('%entity_type: Name is not empty.', ['%entity_type' => $entity_type]));
200 $this->assertFalse(empty($entity->name[0]->value), format_string('%entity_type: Name is not empty.', ['%entity_type' => $entity_type]));
201 $this->assertTrue(isset($entity->name[0]), format_string('%entity_type: Name string item is set.', ['%entity_type' => $entity_type]));
202 $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]));
203 $this->assertTrue(isset($entity->name), format_string('%entity_type: Name field is set.', ['%entity_type' => $entity_type]));
204 $this->assertFalse(isset($entity->nameInvalid), format_string('%entity_type: Not existing field is not set.', ['%entity_type' => $entity_type]));
206 unset($entity->name[0]);
207 $this->assertFalse(isset($entity->name[0]), format_string('%entity_type: Name field item is not set.', ['%entity_type' => $entity_type]));
208 $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: Name is not set.', ['%entity_type' => $entity_type]));
209 $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name is not set.', ['%entity_type' => $entity_type]));
211 // Test emptying a field by assigning an empty value. NULL and array()
213 foreach ([NULL, [], 'unset'] as $empty) {
214 // Make sure a value is present
215 $entity->name->value = 'a value';
216 $this->assertTrue(isset($entity->name->value), format_string('%entity_type: Name is set.', ['%entity_type' => $entity_type]));
217 // Now, empty the field.
218 if ($empty === 'unset') {
219 unset($entity->name);
222 $entity->name = $empty;
224 $this->assertTrue(isset($entity->name), format_string('%entity_type: Name field is set.', ['%entity_type' => $entity_type]));
225 $this->assertTrue($entity->name->isEmpty(), format_string('%entity_type: Name field is set.', ['%entity_type' => $entity_type]));
226 $this->assertIdentical(count($entity->name), 0, format_string('%entity_type: Name field contains no items.', ['%entity_type' => $entity_type]));
227 $this->assertIdentical($entity->name->getValue(), [], format_string('%entity_type: Name field value is an empty array.', ['%entity_type' => $entity_type]));
228 $this->assertFalse(isset($entity->name[0]), format_string('%entity_type: Name field item is not set.', ['%entity_type' => $entity_type]));
229 $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: First name item value is not set.', ['%entity_type' => $entity_type]));
230 $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name value is not set.', ['%entity_type' => $entity_type]));
233 // Access the language field.
234 $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
235 $this->assertEqual($langcode, $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', ['%entity_type' => $entity_type]));
236 $this->assertEqual(\Drupal::languageManager()->getLanguage($langcode), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', ['%entity_type' => $entity_type]));
238 // Change the language by code.
239 $entity->{$langcode_key}->value = \Drupal::languageManager()->getDefaultLanguage()->getId();
240 $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage()->getId(), $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', ['%entity_type' => $entity_type]));
241 $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage(), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', ['%entity_type' => $entity_type]));
243 // Revert language by code then try setting it by language object.
244 $entity->{$langcode_key}->value = $langcode;
245 $entity->{$langcode_key}->language = \Drupal::languageManager()->getDefaultLanguage();
246 $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage()->getId(), $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', ['%entity_type' => $entity_type]));
247 $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage(), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', ['%entity_type' => $entity_type]));
249 // Access the text field and test updating.
250 $this->assertEqual($entity->field_test_text->value, $this->entityFieldText, format_string('%entity_type: Text field can be read.', ['%entity_type' => $entity_type]));
251 $new_text = $this->randomMachineName();
252 $entity->field_test_text->value = $new_text;
253 $this->assertEqual($entity->field_test_text->value, $new_text, format_string('%entity_type: Updated text field can be read.', ['%entity_type' => $entity_type]));
255 // Test creating the entity by passing in plain values.
256 $this->entityName = $this->randomMachineName();
257 $name_item[0]['value'] = $this->entityName;
258 $this->entityUser = $this->createUser();
259 $user_item[0]['target_id'] = $this->entityUser->id();
260 $this->entityFieldText = $this->randomMachineName();
261 $text_item[0]['value'] = $this->entityFieldText;
263 $entity = $this->container->get('entity_type.manager')
264 ->getStorage($entity_type)
266 'name' => $name_item,
267 'user_id' => $user_item,
268 'field_test_text' => $text_item,
270 $this->assertEqual($this->entityName, $entity->name->value, format_string('%entity_type: Name value can be read.', ['%entity_type' => $entity_type]));
271 $this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', ['%entity_type' => $entity_type]));
272 $this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', ['%entity_type' => $entity_type]));
273 $this->assertEqual($this->entityFieldText, $entity->field_test_text->value, format_string('%entity_type: Text field can be read.', ['%entity_type' => $entity_type]));
275 // Tests copying field values by assigning the TypedData objects.
276 $entity2 = $this->createTestEntity($entity_type);
277 $entity2->name = $entity->name;
278 $entity2->user_id = $entity->user_id;
279 $entity2->field_test_text = $entity->field_test_text;
280 $this->assertFalse($entity->name === $entity2->name, format_string('%entity_type: Copying properties results in a different field object.', ['%entity_type' => $entity_type]));
281 $this->assertEqual($entity->name->value, $entity2->name->value, format_string('%entity_type: Name field copied.', ['%entity_type' => $entity_type]));
282 $this->assertEqual($entity->user_id->target_id, $entity2->user_id->target_id, format_string('%entity_type: User id field copied.', ['%entity_type' => $entity_type]));
283 $this->assertEqual($entity->field_test_text->value, $entity2->field_test_text->value, format_string('%entity_type: Text field copied.', ['%entity_type' => $entity_type]));
285 // Tests that assigning TypedData objects to non-field properties keeps the
286 // assigned value as is.
287 $entity2 = $this->createTestEntity($entity_type);
288 $entity2->_not_a_field = $entity->name;
289 $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]));
291 // Tests adding a value to a field item list.
292 $entity->name[] = 'Another name';
293 $this->assertEqual($entity->name[1]->value, 'Another name', format_string('%entity_type: List item added via [] and the first property.', ['%entity_type' => $entity_type]));
294 $entity->name[] = ['value' => 'Third name'];
295 $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]));
296 $entity->name[3] = ['value' => 'Fourth name'];
297 $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]));
298 unset($entity->name[3]);
300 // Test removing and empty-ing list items.
301 $this->assertEqual(count($entity->name), 3, format_string('%entity_type: List has 3 items.', ['%entity_type' => $entity_type]));
302 unset($entity->name[1]);
303 $this->assertEqual(count($entity->name), 2, format_string('%entity_type: Second list item has been removed.', ['%entity_type' => $entity_type]));
304 $this->assertEqual($entity->name[1]->value, 'Third name', format_string('%entity_type: The subsequent items have been shifted up.', ['%entity_type' => $entity_type]));
305 $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]));
306 $entity->name[1] = NULL;
307 $this->assertEqual(count($entity->name), 2, format_string('%entity_type: Assigning NULL does not reduce array count.', ['%entity_type' => $entity_type]));
308 $this->assertTrue($entity->name[1]->isEmpty(), format_string('%entity_type: Assigning NULL empties the item.', ['%entity_type' => $entity_type]));
310 // Test using isEmpty().
311 unset($entity->name[1]);
312 $this->assertFalse($entity->name[0]->isEmpty(), format_string('%entity_type: Name item is not empty.', ['%entity_type' => $entity_type]));
313 $entity->name->value = NULL;
314 $this->assertTrue($entity->name[0]->isEmpty(), format_string('%entity_type: Name item is empty.', ['%entity_type' => $entity_type]));
315 $this->assertTrue($entity->name->isEmpty(), format_string('%entity_type: Name field is empty.', ['%entity_type' => $entity_type]));
316 $this->assertEqual(count($entity->name), 1, format_string('%entity_type: Empty item is considered when counting.', ['%entity_type' => $entity_type]));
317 $this->assertEqual(count(iterator_to_array($entity->name->getIterator())), count($entity->name), format_string('%entity_type: Count matches iterator count.', ['%entity_type' => $entity_type]));
318 $this->assertTrue($entity->name->getValue() === [0 => ['value' => NULL]], format_string('%entity_type: Name field value contains a NULL value.', ['%entity_type' => $entity_type]));
320 // Test using filterEmptyItems().
321 $entity->name = [NULL, 'foo'];
322 $this->assertEqual(count($entity->name), 2, format_string('%entity_type: List has 2 items.', ['%entity_type' => $entity_type]));
323 $entity->name->filterEmptyItems();
324 $this->assertEqual(count($entity->name), 1, format_string('%entity_type: The empty item was removed.', ['%entity_type' => $entity_type]));
325 $this->assertEqual($entity->name[0]->value, 'foo', format_string('%entity_type: The items were renumbered.', ['%entity_type' => $entity_type]));
326 $this->assertEqual($entity->name[0]->getName(), 0, format_string('%entity_type: The deltas were updated in the items.', ['%entity_type' => $entity_type]));
328 // Test get and set field values.
329 $entity->name = 'foo';
330 $this->assertEqual($entity->name[0]->toArray(), ['value' => 'foo'], format_string('%entity_type: Field value has been retrieved via toArray()', ['%entity_type' => $entity_type]));
332 $values = $entity->toArray();
333 $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]));
335 // Make sure the user id can be set to zero.
336 $user_item[0]['target_id'] = 0;
337 $entity = $this->container->get('entity_type.manager')
338 ->getStorage($entity_type)
340 'name' => $name_item,
341 'user_id' => $user_item,
342 'field_test_text' => $text_item,
344 $this->assertNotNull($entity->user_id->target_id, format_string('%entity_type: User id is not NULL', ['%entity_type' => $entity_type]));
345 $this->assertIdentical($entity->user_id->target_id, 0, format_string('%entity_type: User id has been set to 0', ['%entity_type' => $entity_type]));
347 // Test setting the ID with the value only.
348 $entity = $this->container->get('entity_type.manager')
349 ->getStorage($entity_type)
351 'name' => $name_item,
353 'field_test_text' => $text_item,
355 $this->assertNotNull($entity->user_id->target_id, format_string('%entity_type: User id is not NULL', ['%entity_type' => $entity_type]));
356 $this->assertIdentical($entity->user_id->target_id, 0, format_string('%entity_type: User id has been set to 0', ['%entity_type' => $entity_type]));
360 * Tries to save and load an entity again.
362 public function testSave() {
363 // All entity variations have to have the same results.
364 foreach (entity_test_entity_types() as $entity_type) {
365 $this->doTestSave($entity_type);
370 * Executes the save tests for the given entity type.
372 * @param string $entity_type
373 * The entity type to run the tests with.
375 protected function doTestSave($entity_type) {
376 $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
377 $entity = $this->createTestEntity($entity_type);
379 $this->assertTrue((bool) $entity->id(), format_string('%entity_type: Entity has received an id.', ['%entity_type' => $entity_type]));
381 $entity = $this->container->get('entity_type.manager')
382 ->getStorage($entity_type)
383 ->load($entity->id());
384 $this->assertTrue((bool) $entity->id(), format_string('%entity_type: Entity loaded.', ['%entity_type' => $entity_type]));
386 // Access the name field.
387 $this->assertEqual(1, $entity->id->value, format_string('%entity_type: ID value can be read.', ['%entity_type' => $entity_type]));
388 $this->assertTrue(is_string($entity->uuid->value), format_string('%entity_type: UUID value can be read.', ['%entity_type' => $entity_type]));
389 $this->assertEqual('en', $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', ['%entity_type' => $entity_type]));
390 $this->assertEqual(\Drupal::languageManager()->getLanguage('en'), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', ['%entity_type' => $entity_type]));
391 $this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', ['%entity_type' => $entity_type]));
392 $this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', ['%entity_type' => $entity_type]));
393 $this->assertEqual($this->entityFieldText, $entity->field_test_text->value, format_string('%entity_type: Text field can be read.', ['%entity_type' => $entity_type]));
397 * Tests introspection and getting metadata upfront.
399 public function testIntrospection() {
400 // All entity variations have to have the same results.
401 foreach (entity_test_entity_types() as $entity_type) {
402 $this->doTestIntrospection($entity_type);
407 * Executes the introspection tests for the given entity type.
409 * @param string $entity_type
410 * The entity type to run the tests with.
412 protected function doTestIntrospection($entity_type) {
413 // Test getting metadata upfront. The entity types used for this test have
414 // a default bundle that is the same as the entity type.
415 $definitions = \Drupal::entityManager()->getFieldDefinitions($entity_type, $entity_type);
416 $this->assertEqual($definitions['name']->getType(), 'string', $entity_type . ': Name field found.');
417 $this->assertEqual($definitions['user_id']->getType(), 'entity_reference', $entity_type . ': User field found.');
418 $this->assertEqual($definitions['field_test_text']->getType(), 'text', $entity_type . ': Test-text-field field found.');
420 // Test deriving further metadata.
421 $this->assertTrue($definitions['name'] instanceof FieldDefinitionInterface);
422 $field_item_definition = $definitions['name']->getItemDefinition();
423 $this->assertTrue($field_item_definition instanceof ComplexDataDefinitionInterface);
424 $this->assertEqual($field_item_definition->getDataType(), 'field_item:string');
425 $value_definition = $field_item_definition->getPropertyDefinition('value');
426 $this->assertTrue($value_definition instanceof DataDefinitionInterface);
427 $this->assertEqual($value_definition->getDataType(), 'string');
429 // Test deriving metadata from references.
430 $entity_definition = EntityDataDefinition::create($entity_type);
431 $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
432 $reference_definition = $entity_definition->getPropertyDefinition($langcode_key)
433 ->getPropertyDefinition('language')
434 ->getTargetDefinition();
435 $this->assertEqual($reference_definition->getDataType(), 'language');
437 $reference_definition = $entity_definition->getPropertyDefinition('user_id')
438 ->getPropertyDefinition('entity')
439 ->getTargetDefinition();
441 $this->assertTrue($reference_definition instanceof EntityDataDefinitionInterface, 'Definition of the referenced user retrieved.');
442 $this->assertEqual($reference_definition->getEntityTypeId(), 'user', 'Referenced entity is of type "user".');
444 // Test propagating down.
445 $name_definition = $reference_definition->getPropertyDefinition('name');
446 $this->assertTrue($name_definition instanceof FieldDefinitionInterface);
447 $this->assertEqual($name_definition->getPropertyDefinition('value')->getDataType(), 'string');
449 // Test introspecting an entity object.
450 // @todo: Add bundles and test bundles as well.
451 $entity = $this->container->get('entity_type.manager')
452 ->getStorage($entity_type)
455 $definitions = $entity->getFieldDefinitions();
456 $this->assertEqual($definitions['name']->getType(), 'string', $entity_type . ': Name field found.');
457 $this->assertEqual($definitions['user_id']->getType(), 'entity_reference', $entity_type . ': User field found.');
458 $this->assertEqual($definitions['field_test_text']->getType(), 'text', $entity_type . ': Test-text-field field found.');
460 $name_properties = $entity->name->getFieldDefinition()->getPropertyDefinitions();
461 $this->assertEqual($name_properties['value']->getDataType(), 'string', $entity_type . ': String value property of the name found.');
463 $userref_properties = $entity->user_id->getFieldDefinition()->getPropertyDefinitions();
464 $this->assertEqual($userref_properties['target_id']->getDataType(), 'integer', $entity_type . ': Entity id property of the user found.');
465 $this->assertEqual($userref_properties['entity']->getDataType(), 'entity_reference', $entity_type . ': Entity reference property of the user found.');
467 $textfield_properties = $entity->field_test_text->getFieldDefinition()->getFieldStorageDefinition()->getPropertyDefinitions();
468 $this->assertEqual($textfield_properties['value']->getDataType(), 'string', $entity_type . ': String value property of the test-text field found.');
469 $this->assertEqual($textfield_properties['format']->getDataType(), 'filter_format', $entity_type . ': String format field of the test-text field found.');
470 $this->assertEqual($textfield_properties['processed']->getDataType(), 'string', $entity_type . ': String processed property of the test-text field found.');
472 // Make sure provided contextual information is right.
473 $entity_adapter = $entity->getTypedData();
474 $this->assertIdentical($entity_adapter->getRoot(), $entity_adapter, 'Entity is root object.');
475 $this->assertEqual($entity_adapter->getPropertyPath(), '');
476 $this->assertEqual($entity_adapter->getName(), '');
477 $this->assertEqual($entity_adapter->getParent(), NULL);
479 $field = $entity->user_id;
480 $this->assertIdentical($field->getRoot()->getValue(), $entity, 'Entity is root object.');
481 $this->assertIdentical($field->getEntity(), $entity, 'getEntity() returns the entity.');
482 $this->assertEqual($field->getPropertyPath(), 'user_id');
483 $this->assertEqual($field->getName(), 'user_id');
484 $this->assertIdentical($field->getParent()->getValue(), $entity, 'Parent object matches.');
486 $field_item = $field[0];
487 $this->assertIdentical($field_item->getRoot()->getValue(), $entity, 'Entity is root object.');
488 $this->assertIdentical($field_item->getEntity(), $entity, 'getEntity() returns the entity.');
489 $this->assertEqual($field_item->getPropertyPath(), 'user_id.0');
490 $this->assertEqual($field_item->getName(), '0');
491 $this->assertIdentical($field_item->getParent(), $field, 'Parent object matches.');
493 $item_value = $field_item->get('entity');
494 $this->assertIdentical($item_value->getRoot()->getValue(), $entity, 'Entity is root object.');
495 $this->assertEqual($item_value->getPropertyPath(), 'user_id.0.entity');
496 $this->assertEqual($item_value->getName(), 'entity');
497 $this->assertIdentical($item_value->getParent(), $field_item, 'Parent object matches.');
501 * Tests iterating over properties.
503 public function testIterator() {
504 // All entity variations have to have the same results.
505 foreach (entity_test_entity_types() as $entity_type) {
506 $this->doTestIterator($entity_type);
511 * Executes the iterator tests for the given entity type.
513 * @param string $entity_type
514 * The entity type to run the tests with.
516 protected function doTestIterator($entity_type) {
517 $entity = $this->createTestEntity($entity_type);
519 foreach ($entity as $name => $field) {
520 $this->assertTrue($field instanceof FieldItemListInterface, $entity_type . ": Field $name implements interface.");
522 foreach ($field as $delta => $item) {
523 $this->assertTrue($field[0] instanceof FieldItemInterface, $entity_type . ": Item $delta of field $name implements interface.");
525 foreach ($item as $value_name => $value_property) {
526 $this->assertTrue($value_property instanceof TypedDataInterface, $entity_type . ": Value $value_name of item $delta of field $name implements interface.");
528 $value = $value_property->getValue();
529 $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.");
534 $fields = $entity->getFields();
535 $this->assertEqual(array_keys($fields), array_keys($entity->getTypedData()->getDataDefinition()->getPropertyDefinitions()), format_string('%entity_type: All fields returned.', ['%entity_type' => $entity_type]));
536 $this->assertEqual($fields, iterator_to_array($entity->getIterator()), format_string('%entity_type: Entity iterator iterates over all fields.', ['%entity_type' => $entity_type]));
540 * Tests working with the entity based upon the TypedData API.
542 public function testDataStructureInterfaces() {
543 // All entity variations have to have the same results.
544 foreach (entity_test_entity_types() as $entity_type) {
545 $this->doTestDataStructureInterfaces($entity_type);
550 * Executes the data structure interfaces tests for the given entity type.
552 * @param string $entity_type
553 * The entity type to run the tests with.
555 protected function doTestDataStructureInterfaces($entity_type) {
556 $entity = $this->createTestEntity($entity_type);
558 // Test using the whole tree of typed data by navigating through the tree of
559 // contained properties and getting all contained strings, limited by a
562 $this->getContainedStrings($entity->getTypedData(), 0, $strings);
564 // @todo: Once the user entity has defined properties this should contain
565 // the user name and other user entity strings as well.
567 $entity->uuid->value,
572 $this->entityFieldText,
577 if ($entity instanceof RevisionLogInterface) {
578 // Adding empty string for revision message.
579 $target_strings[] = '';
583 asort($target_strings);
584 $this->assertEqual(array_values($strings), array_values($target_strings), format_string('%entity_type: All contained strings found.', ['%entity_type' => $entity_type]));
588 * Recursive helper for getting all contained strings,
589 * i.e. properties of type string.
591 public function getContainedStrings(TypedDataInterface $wrapper, $depth, array &$strings) {
593 if ($wrapper instanceof StringInterface) {
594 $strings[] = $wrapper->getValue();
597 // Recurse until a certain depth is reached if possible.
599 if ($wrapper instanceof ListInterface) {
600 foreach ($wrapper as $item) {
601 $this->getContainedStrings($item, $depth + 1, $strings);
604 elseif ($wrapper instanceof ComplexDataInterface) {
605 foreach ($wrapper as $property) {
606 $this->getContainedStrings($property, $depth + 1, $strings);
613 * Makes sure data types are correctly derived for all entity types.
615 public function testDataTypes() {
616 $types = \Drupal::typedDataManager()->getDefinitions();
617 foreach (entity_test_entity_types() as $entity_type) {
618 $this->assertTrue($types['entity:' . $entity_type]['class'], 'Entity data type registered.');
620 // Check bundle types are provided as well.
621 entity_test_create_bundle('bundle');
622 $types = \Drupal::typedDataManager()->getDefinitions();
623 $this->assertTrue($types['entity:entity_test:bundle']['class'], 'Entity bundle data type registered.');
627 * Tests a base field override on a non-existing base field.
629 * @see entity_test_entity_base_field_info_alter()
631 public function testBaseFieldNonExistingBaseField() {
632 $this->entityManager->getStorage('node_type')->create([
636 $this->entityManager->clearCachedFieldDefinitions();
637 $fields = $this->entityManager->getFieldDefinitions('node', 'page');
638 $override = $fields['status']->getConfig('page');
639 $override->setLabel($this->randomString())->save();
640 \Drupal::state()->set('entity_test.node_remove_status_field', TRUE);
641 $this->entityManager->clearCachedFieldDefinitions();
642 $fields = $this->entityManager->getFieldDefinitions('node', 'page');
643 // A base field override on a non-existing base field should not cause a
644 // field definition to come into existence.
645 $this->assertFalse(isset($fields['status']), 'Node\'s status base field does not exist.');
649 * Tests creating a field override config for a bundle field.
651 * @see entity_test_entity_base_field_info_alter()
653 public function testFieldOverrideBundleField() {
654 // First make sure the bundle field override in code, which is provided by
655 // the test entity works.
656 entity_test_create_bundle('some_test_bundle', 'Some test bundle', 'entity_test_field_override');
657 $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'entity_test_field_override');
658 $this->assertEqual($field_definitions['name']->getDescription(), 'The default description.');
659 $this->assertNull($field_definitions['name']->getTargetBundle());
661 $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'some_test_bundle');
662 $this->assertEqual($field_definitions['name']->getDescription(), 'Custom description.');
663 $this->assertEqual($field_definitions['name']->getTargetBundle(), 'some_test_bundle');
665 // Now create a config override of the bundle field.
666 $field_config = $field_definitions['name']->getConfig('some_test_bundle');
667 $field_config->setTranslatable(FALSE);
668 $field_config->save();
670 // Make sure both overrides are present.
671 $this->entityManager->clearCachedFieldDefinitions();
672 $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'some_test_bundle');
673 $this->assertEqual($field_definitions['name']->getDescription(), 'Custom description.');
674 $this->assertEqual($field_definitions['name']->getTargetBundle(), 'some_test_bundle');
675 $this->assertFalse($field_definitions['name']->isTranslatable());
679 * Tests validation constraints provided by the Entity API.
681 public function testEntityConstraintValidation() {
682 $entity = $this->createTestEntity('entity_test');
684 // Create a reference field item and let it reference the entity.
685 $definition = BaseFieldDefinition::create('entity_reference')
686 ->setLabel('Test entity')
687 ->setSetting('target_type', 'entity_test');
688 $reference_field = \Drupal::typedDataManager()->create($definition);
689 $reference = $reference_field->appendItem(['entity' => $entity])->get('entity');
691 // Test validation the typed data object.
692 $violations = $reference->validate();
693 $this->assertEqual($violations->count(), 0);
695 // Test validating an entity of the wrong type.
696 $user = $this->createUser();
698 $node = $node = Node::create([
700 'uid' => $user->id(),
701 'title' => $this->randomString(),
703 $reference->setValue($node);
704 $violations = $reference->validate();
705 $this->assertEqual($violations->count(), 1);
707 // Test bundle validation.
708 NodeType::create(['type' => 'article'])
710 $definition = BaseFieldDefinition::create('entity_reference')
711 ->setLabel('Test entity')
712 ->setSetting('target_type', 'node')
713 ->setSetting('handler_settings', ['target_bundles' => ['article' => 'article']]);
714 $reference_field = \Drupal::TypedDataManager()->create($definition);
715 $reference_field->appendItem(['entity' => $node]);
716 $violations = $reference_field->validate();
717 $this->assertEqual($violations->count(), 1);
719 $node = Node::create([
721 'uid' => $user->id(),
722 'title' => $this->randomString(),
725 $reference_field->entity = $node;
726 $violations = $reference_field->validate();
727 $this->assertEqual($violations->count(), 0);
731 * Tests getting processed property values via a computed property.
733 public function testComputedProperties() {
734 // All entity variations have to have the same results.
735 foreach (entity_test_entity_types() as $entity_type) {
736 $this->doTestComputedProperties($entity_type);
741 * Executes the computed properties tests for the given entity type.
743 * @param string $entity_type
744 * The entity type to run the tests with.
746 protected function doTestComputedProperties($entity_type) {
747 $entity = $this->createTestEntity($entity_type);
748 $entity->field_test_text->value = "The <strong>text</strong> text to filter.";
749 $entity->field_test_text->format = filter_default_format();
751 $target = "<p>The <strong>text</strong> text to filter.</p>\n";
752 $this->assertEqual($entity->field_test_text->processed, $target, format_string('%entity_type: Text is processed with the default filter.', ['%entity_type' => $entity_type]));
754 // Save and load entity and make sure it still works.
756 $entity = $this->container->get('entity_type.manager')
757 ->getStorage($entity_type)
758 ->load($entity->id());
759 $this->assertEqual($entity->field_test_text->processed, $target, format_string('%entity_type: Text is processed with the default filter.', ['%entity_type' => $entity_type]));