3 namespace Drupal\KernelTests\Core\Entity;
5 use Drupal\Core\Entity\EntityInterface;
6 use Drupal\Core\Entity\EntityStorageException;
7 use Drupal\Core\Entity\RevisionLogInterface;
8 use Drupal\Core\Entity\TypedData\EntityDataDefinition;
9 use Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface;
10 use Drupal\Core\Field\BaseFieldDefinition;
11 use Drupal\Core\Field\FieldDefinitionInterface;
12 use Drupal\Core\Field\FieldItemInterface;
13 use Drupal\Core\Field\FieldItemListInterface;
14 use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
15 use Drupal\Core\TypedData\ComplexDataInterface;
16 use Drupal\Core\TypedData\DataDefinitionInterface;
17 use Drupal\Core\TypedData\ListInterface;
18 use Drupal\Core\TypedData\Type\StringInterface;
19 use Drupal\Core\TypedData\TypedDataInterface;
20 use Drupal\entity_test\Entity\EntityTest;
21 use Drupal\entity_test\Entity\EntityTestComputedField;
22 use Drupal\node\Entity\Node;
23 use Drupal\node\Entity\NodeType;
26 * Tests the Entity Field API.
30 class EntityFieldTest extends EntityKernelTestBase {
37 public static $modules = ['filter', 'text', 'node', 'user', 'field_test'];
42 protected $entityName;
45 * @var \Drupal\user\Entity\User
47 protected $entityUser;
52 protected $entityFieldText;
54 protected function setUp() {
57 foreach (entity_test_entity_types() as $entity_type_id) {
58 // The entity_test schema is installed by the parent.
59 if ($entity_type_id != 'entity_test') {
60 $this->installEntitySchema($entity_type_id);
64 // Create the test field.
65 module_load_install('entity_test');
66 entity_test_install();
68 // Install required default configuration for filter module.
69 $this->installConfig(['system', 'filter']);
73 * Creates a test entity.
75 * @return \Drupal\Core\Entity\EntityInterface
77 protected function createTestEntity($entity_type) {
78 $this->entityName = $this->randomMachineName();
79 $this->entityUser = $this->createUser();
80 $this->entityFieldText = $this->randomMachineName();
82 // Pass in the value of the name field when creating. With the user
83 // field we test setting a field after creation.
84 $entity = $this->container->get('entity_type.manager')
85 ->getStorage($entity_type)
87 $entity->user_id->target_id = $this->entityUser->id();
88 $entity->name->value = $this->entityName;
90 // Set a value for the test field.
91 $entity->field_test_text->value = $this->entityFieldText;
97 * Tests reading and writing properties and field items.
99 public function testReadWrite() {
100 // All entity variations have to have the same results.
101 foreach (entity_test_entity_types() as $entity_type) {
102 $this->doTestReadWrite($entity_type);
107 * Executes the read write test set for a defined entity type.
109 * @param string $entity_type
110 * The entity type to run the tests with.
112 protected function doTestReadWrite($entity_type) {
113 $entity = $this->createTestEntity($entity_type);
117 // Access the name field.
118 $this->assertTrue($entity->name instanceof FieldItemListInterface, format_string('%entity_type: Field implements interface', ['%entity_type' => $entity_type]));
119 $this->assertTrue($entity->name[0] instanceof FieldItemInterface, format_string('%entity_type: Field item implements interface', ['%entity_type' => $entity_type]));
121 $this->assertEqual($this->entityName, $entity->name->value, format_string('%entity_type: Name value can be read.', ['%entity_type' => $entity_type]));
122 $this->assertEqual($this->entityName, $entity->name[0]->value, format_string('%entity_type: Name value can be read through list access.', ['%entity_type' => $entity_type]));
123 $this->assertEqual($entity->name->getValue(), [0 => ['value' => $this->entityName]], format_string('%entity_type: Plain field value returned.', ['%entity_type' => $entity_type]));
126 $new_name = $this->randomMachineName();
127 $entity->name->value = $new_name;
128 $this->assertEqual($new_name, $entity->name->value, format_string('%entity_type: Name can be updated and read.', ['%entity_type' => $entity_type]));
129 $this->assertEqual($entity->name->getValue(), [0 => ['value' => $new_name]], format_string('%entity_type: Plain field value reflects the update.', ['%entity_type' => $entity_type]));
131 $new_name = $this->randomMachineName();
132 $entity->name[0]->value = $new_name;
133 $this->assertEqual($new_name, $entity->name->value, format_string('%entity_type: Name can be updated and read through list access.', ['%entity_type' => $entity_type]));
135 // Access the user field.
136 $this->assertTrue($entity->user_id instanceof FieldItemListInterface, format_string('%entity_type: Field implements interface', ['%entity_type' => $entity_type]));
137 $this->assertTrue($entity->user_id[0] instanceof FieldItemInterface, format_string('%entity_type: Field item implements interface', ['%entity_type' => $entity_type]));
139 $this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', ['%entity_type' => $entity_type]));
140 $this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', ['%entity_type' => $entity_type]));
142 // Change the assigned user by entity.
143 $new_user1 = $this->createUser();
144 $entity->user_id->entity = $new_user1;
145 $this->assertEqual($new_user1->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
146 $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]));
148 // Change the assigned user by id.
149 $new_user2 = $this->createUser();
150 $entity->user_id->target_id = $new_user2->id();
151 $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
152 $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]));
154 // Try unsetting a field property.
155 $entity->name->value = NULL;
156 $entity->user_id->target_id = NULL;
157 $this->assertNull($entity->name->value, format_string('%entity_type: Name field is not set.', ['%entity_type' => $entity_type]));
158 $this->assertNull($entity->user_id->target_id, format_string('%entity_type: User ID field is not set.', ['%entity_type' => $entity_type]));
159 $this->assertNull($entity->user_id->entity, format_string('%entity_type: User entity field is not set.', ['%entity_type' => $entity_type]));
161 // Test setting the values via the typed data API works as well.
162 // Change the assigned user by entity.
163 $entity->user_id->first()->get('entity')->setValue($new_user2);
164 $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
165 $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]));
167 // Change the assigned user by id.
168 $entity->user_id->first()->get('target_id')->setValue($new_user2->id());
169 $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
170 $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]));
172 // Try unsetting a field.
173 $entity->name->first()->get('value')->setValue(NULL);
174 $entity->user_id->first()->get('target_id')->setValue(NULL);
175 $this->assertNull($entity->name->value, format_string('%entity_type: Name field is not set.', ['%entity_type' => $entity_type]));
176 $this->assertNull($entity->user_id->target_id, format_string('%entity_type: User ID field is not set.', ['%entity_type' => $entity_type]));
177 $this->assertNull($entity->user_id->entity, format_string('%entity_type: User entity field is not set.', ['%entity_type' => $entity_type]));
179 // Create a fresh entity so target_id does not get its property object
180 // instantiated, then verify setting a new value via typed data API works.
181 $entity2 = $this->container->get('entity_type.manager')
182 ->getStorage($entity_type)
184 'user_id' => ['target_id' => $new_user1->id()],
186 // Access the property object, and set a value.
187 $entity2->user_id->first()->get('target_id')->setValue($new_user2->id());
188 $this->assertEqual($new_user2->id(), $entity2->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
189 $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]));
191 // Test using isset(), empty() and unset().
192 $entity->name->value = 'test unset';
193 unset($entity->name->value);
194 $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name is not set.', ['%entity_type' => $entity_type]));
195 $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: Name is not set.', ['%entity_type' => $entity_type]));
196 $this->assertTrue(empty($entity->name->value), format_string('%entity_type: Name is empty.', ['%entity_type' => $entity_type]));
197 $this->assertTrue(empty($entity->name[0]->value), format_string('%entity_type: Name is empty.', ['%entity_type' => $entity_type]));
199 $entity->name->value = 'a value';
200 $this->assertTrue(isset($entity->name->value), format_string('%entity_type: Name is set.', ['%entity_type' => $entity_type]));
201 $this->assertTrue(isset($entity->name[0]->value), format_string('%entity_type: Name is set.', ['%entity_type' => $entity_type]));
202 $this->assertFalse(empty($entity->name->value), format_string('%entity_type: Name is not empty.', ['%entity_type' => $entity_type]));
203 $this->assertFalse(empty($entity->name[0]->value), format_string('%entity_type: Name is not empty.', ['%entity_type' => $entity_type]));
204 $this->assertTrue(isset($entity->name[0]), format_string('%entity_type: Name string item is set.', ['%entity_type' => $entity_type]));
205 $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]));
206 $this->assertTrue(isset($entity->name), format_string('%entity_type: Name field is set.', ['%entity_type' => $entity_type]));
207 $this->assertFalse(isset($entity->nameInvalid), format_string('%entity_type: Not existing field is not set.', ['%entity_type' => $entity_type]));
209 unset($entity->name[0]);
210 $this->assertFalse(isset($entity->name[0]), format_string('%entity_type: Name field item is not set.', ['%entity_type' => $entity_type]));
211 $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: Name is not set.', ['%entity_type' => $entity_type]));
212 $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name is not set.', ['%entity_type' => $entity_type]));
214 // Test emptying a field by assigning an empty value. NULL and array()
216 foreach ([NULL, [], 'unset'] as $empty) {
217 // Make sure a value is present
218 $entity->name->value = 'a value';
219 $this->assertTrue(isset($entity->name->value), format_string('%entity_type: Name is set.', ['%entity_type' => $entity_type]));
220 // Now, empty the field.
221 if ($empty === 'unset') {
222 unset($entity->name);
225 $entity->name = $empty;
227 $this->assertTrue(isset($entity->name), format_string('%entity_type: Name field is set.', ['%entity_type' => $entity_type]));
228 $this->assertTrue($entity->name->isEmpty(), format_string('%entity_type: Name field is set.', ['%entity_type' => $entity_type]));
229 $this->assertIdentical(count($entity->name), 0, format_string('%entity_type: Name field contains no items.', ['%entity_type' => $entity_type]));
230 $this->assertIdentical($entity->name->getValue(), [], format_string('%entity_type: Name field value is an empty array.', ['%entity_type' => $entity_type]));
231 $this->assertFalse(isset($entity->name[0]), format_string('%entity_type: Name field item is not set.', ['%entity_type' => $entity_type]));
232 $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: First name item value is not set.', ['%entity_type' => $entity_type]));
233 $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name value is not set.', ['%entity_type' => $entity_type]));
236 // Access the language field.
237 $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
238 $this->assertEqual($langcode, $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', ['%entity_type' => $entity_type]));
239 $this->assertEqual(\Drupal::languageManager()->getLanguage($langcode), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', ['%entity_type' => $entity_type]));
241 // Change the language by code.
242 $entity->{$langcode_key}->value = \Drupal::languageManager()->getDefaultLanguage()->getId();
243 $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage()->getId(), $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', ['%entity_type' => $entity_type]));
244 $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage(), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', ['%entity_type' => $entity_type]));
246 // Revert language by code then try setting it by language object.
247 $entity->{$langcode_key}->value = $langcode;
248 $entity->{$langcode_key}->language = \Drupal::languageManager()->getDefaultLanguage();
249 $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage()->getId(), $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', ['%entity_type' => $entity_type]));
250 $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage(), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', ['%entity_type' => $entity_type]));
252 // Access the text field and test updating.
253 $this->assertEqual($entity->field_test_text->value, $this->entityFieldText, format_string('%entity_type: Text field can be read.', ['%entity_type' => $entity_type]));
254 $new_text = $this->randomMachineName();
255 $entity->field_test_text->value = $new_text;
256 $this->assertEqual($entity->field_test_text->value, $new_text, format_string('%entity_type: Updated text field can be read.', ['%entity_type' => $entity_type]));
258 // Test creating the entity by passing in plain values.
259 $this->entityName = $this->randomMachineName();
260 $name_item[0]['value'] = $this->entityName;
261 $this->entityUser = $this->createUser();
262 $user_item[0]['target_id'] = $this->entityUser->id();
263 $this->entityFieldText = $this->randomMachineName();
264 $text_item[0]['value'] = $this->entityFieldText;
266 $entity = $this->container->get('entity_type.manager')
267 ->getStorage($entity_type)
269 'name' => $name_item,
270 'user_id' => $user_item,
271 'field_test_text' => $text_item,
273 $this->assertEqual($this->entityName, $entity->name->value, format_string('%entity_type: Name value can be read.', ['%entity_type' => $entity_type]));
274 $this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', ['%entity_type' => $entity_type]));
275 $this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', ['%entity_type' => $entity_type]));
276 $this->assertEqual($this->entityFieldText, $entity->field_test_text->value, format_string('%entity_type: Text field can be read.', ['%entity_type' => $entity_type]));
278 // Tests copying field values by assigning the TypedData objects.
279 $entity2 = $this->createTestEntity($entity_type);
280 $entity2->name = $entity->name;
281 $entity2->user_id = $entity->user_id;
282 $entity2->field_test_text = $entity->field_test_text;
283 $this->assertFalse($entity->name === $entity2->name, format_string('%entity_type: Copying properties results in a different field object.', ['%entity_type' => $entity_type]));
284 $this->assertEqual($entity->name->value, $entity2->name->value, format_string('%entity_type: Name field copied.', ['%entity_type' => $entity_type]));
285 $this->assertEqual($entity->user_id->target_id, $entity2->user_id->target_id, format_string('%entity_type: User id field copied.', ['%entity_type' => $entity_type]));
286 $this->assertEqual($entity->field_test_text->value, $entity2->field_test_text->value, format_string('%entity_type: Text field copied.', ['%entity_type' => $entity_type]));
288 // Tests that assigning TypedData objects to non-field properties keeps the
289 // assigned value as is.
290 $entity2 = $this->createTestEntity($entity_type);
291 $entity2->_not_a_field = $entity->name;
292 $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]));
294 // Tests adding a value to a field item list.
295 $entity->name[] = 'Another name';
296 $this->assertEqual($entity->name[1]->value, 'Another name', format_string('%entity_type: List item added via [] and the first property.', ['%entity_type' => $entity_type]));
297 $entity->name[] = ['value' => 'Third name'];
298 $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]));
299 $entity->name[3] = ['value' => 'Fourth name'];
300 $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]));
301 unset($entity->name[3]);
303 // Test removing and empty-ing list items.
304 $this->assertEqual(count($entity->name), 3, format_string('%entity_type: List has 3 items.', ['%entity_type' => $entity_type]));
305 unset($entity->name[1]);
306 $this->assertEqual(count($entity->name), 2, format_string('%entity_type: Second list item has been removed.', ['%entity_type' => $entity_type]));
307 $this->assertEqual($entity->name[1]->value, 'Third name', format_string('%entity_type: The subsequent items have been shifted up.', ['%entity_type' => $entity_type]));
308 $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]));
309 $entity->name[1] = NULL;
310 $this->assertEqual(count($entity->name), 2, format_string('%entity_type: Assigning NULL does not reduce array count.', ['%entity_type' => $entity_type]));
311 $this->assertTrue($entity->name[1]->isEmpty(), format_string('%entity_type: Assigning NULL empties the item.', ['%entity_type' => $entity_type]));
313 // Test using isEmpty().
314 unset($entity->name[1]);
315 $this->assertFalse($entity->name[0]->isEmpty(), format_string('%entity_type: Name item is not empty.', ['%entity_type' => $entity_type]));
316 $entity->name->value = NULL;
317 $this->assertTrue($entity->name[0]->isEmpty(), format_string('%entity_type: Name item is empty.', ['%entity_type' => $entity_type]));
318 $this->assertTrue($entity->name->isEmpty(), format_string('%entity_type: Name field is empty.', ['%entity_type' => $entity_type]));
319 $this->assertEqual(count($entity->name), 1, format_string('%entity_type: Empty item is considered when counting.', ['%entity_type' => $entity_type]));
320 $this->assertEqual(count(iterator_to_array($entity->name->getIterator())), count($entity->name), format_string('%entity_type: Count matches iterator count.', ['%entity_type' => $entity_type]));
321 $this->assertTrue($entity->name->getValue() === [0 => ['value' => NULL]], format_string('%entity_type: Name field value contains a NULL value.', ['%entity_type' => $entity_type]));
323 // Test using filterEmptyItems().
324 $entity->name = [NULL, 'foo'];
325 $this->assertEqual(count($entity->name), 2, format_string('%entity_type: List has 2 items.', ['%entity_type' => $entity_type]));
326 $entity->name->filterEmptyItems();
327 $this->assertEqual(count($entity->name), 1, format_string('%entity_type: The empty item was removed.', ['%entity_type' => $entity_type]));
328 $this->assertEqual($entity->name[0]->value, 'foo', format_string('%entity_type: The items were renumbered.', ['%entity_type' => $entity_type]));
329 $this->assertEqual($entity->name[0]->getName(), 0, format_string('%entity_type: The deltas were updated in the items.', ['%entity_type' => $entity_type]));
331 // Test get and set field values.
332 $entity->name = 'foo';
333 $this->assertEqual($entity->name[0]->toArray(), ['value' => 'foo'], format_string('%entity_type: Field value has been retrieved via toArray()', ['%entity_type' => $entity_type]));
335 $values = $entity->toArray();
336 $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]));
338 // Make sure the user id can be set to zero.
339 $user_item[0]['target_id'] = 0;
340 $entity = $this->container->get('entity_type.manager')
341 ->getStorage($entity_type)
343 'name' => $name_item,
344 'user_id' => $user_item,
345 'field_test_text' => $text_item,
347 $this->assertNotNull($entity->user_id->target_id, format_string('%entity_type: User id is not NULL', ['%entity_type' => $entity_type]));
348 $this->assertIdentical($entity->user_id->target_id, 0, format_string('%entity_type: User id has been set to 0', ['%entity_type' => $entity_type]));
350 // Test setting the ID with the value only.
351 $entity = $this->container->get('entity_type.manager')
352 ->getStorage($entity_type)
354 'name' => $name_item,
356 'field_test_text' => $text_item,
358 $this->assertNotNull($entity->user_id->target_id, format_string('%entity_type: User id is not NULL', ['%entity_type' => $entity_type]));
359 $this->assertIdentical($entity->user_id->target_id, 0, format_string('%entity_type: User id has been set to 0', ['%entity_type' => $entity_type]));
363 * Tries to save and load an entity again.
365 public function testSave() {
366 // All entity variations have to have the same results.
367 foreach (entity_test_entity_types() as $entity_type) {
368 $this->doTestSave($entity_type);
373 * Executes the save tests for the given entity type.
375 * @param string $entity_type
376 * The entity type to run the tests with.
378 protected function doTestSave($entity_type) {
379 $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
380 $entity = $this->createTestEntity($entity_type);
382 $this->assertTrue((bool) $entity->id(), format_string('%entity_type: Entity has received an id.', ['%entity_type' => $entity_type]));
384 $entity = $this->container->get('entity_type.manager')
385 ->getStorage($entity_type)
386 ->load($entity->id());
387 $this->assertTrue((bool) $entity->id(), format_string('%entity_type: Entity loaded.', ['%entity_type' => $entity_type]));
389 // Access the name field.
390 $this->assertEqual(1, $entity->id->value, format_string('%entity_type: ID value can be read.', ['%entity_type' => $entity_type]));
391 $this->assertTrue(is_string($entity->uuid->value), format_string('%entity_type: UUID value can be read.', ['%entity_type' => $entity_type]));
392 $this->assertEqual('en', $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', ['%entity_type' => $entity_type]));
393 $this->assertEqual(\Drupal::languageManager()->getLanguage('en'), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', ['%entity_type' => $entity_type]));
394 $this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', ['%entity_type' => $entity_type]));
395 $this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', ['%entity_type' => $entity_type]));
396 $this->assertEqual($this->entityFieldText, $entity->field_test_text->value, format_string('%entity_type: Text field can be read.', ['%entity_type' => $entity_type]));
400 * Tests introspection and getting metadata upfront.
402 public function testIntrospection() {
403 // All entity variations have to have the same results.
404 foreach (entity_test_entity_types() as $entity_type) {
405 $this->doTestIntrospection($entity_type);
410 * Executes the introspection tests for the given entity type.
412 * @param string $entity_type
413 * The entity type to run the tests with.
415 protected function doTestIntrospection($entity_type) {
416 // Test getting metadata upfront. The entity types used for this test have
417 // a default bundle that is the same as the entity type.
418 $definitions = \Drupal::entityManager()->getFieldDefinitions($entity_type, $entity_type);
419 $this->assertEqual($definitions['name']->getType(), 'string', $entity_type . ': Name field found.');
420 $this->assertEqual($definitions['user_id']->getType(), 'entity_reference', $entity_type . ': User field found.');
421 $this->assertEqual($definitions['field_test_text']->getType(), 'text', $entity_type . ': Test-text-field field found.');
423 // Test deriving further metadata.
424 $this->assertTrue($definitions['name'] instanceof FieldDefinitionInterface);
425 $field_item_definition = $definitions['name']->getItemDefinition();
426 $this->assertTrue($field_item_definition instanceof ComplexDataDefinitionInterface);
427 $this->assertEqual($field_item_definition->getDataType(), 'field_item:string');
428 $value_definition = $field_item_definition->getPropertyDefinition('value');
429 $this->assertTrue($value_definition instanceof DataDefinitionInterface);
430 $this->assertEqual($value_definition->getDataType(), 'string');
432 // Test deriving metadata from references.
433 $entity_definition = EntityDataDefinition::create($entity_type);
434 $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
435 $reference_definition = $entity_definition->getPropertyDefinition($langcode_key)
436 ->getPropertyDefinition('language')
437 ->getTargetDefinition();
438 $this->assertEqual($reference_definition->getDataType(), 'language');
440 $reference_definition = $entity_definition->getPropertyDefinition('user_id')
441 ->getPropertyDefinition('entity')
442 ->getTargetDefinition();
444 $this->assertTrue($reference_definition instanceof EntityDataDefinitionInterface, 'Definition of the referenced user retrieved.');
445 $this->assertEqual($reference_definition->getEntityTypeId(), 'user', 'Referenced entity is of type "user".');
447 // Test propagating down.
448 $name_definition = $reference_definition->getPropertyDefinition('name');
449 $this->assertTrue($name_definition instanceof FieldDefinitionInterface);
450 $this->assertEqual($name_definition->getPropertyDefinition('value')->getDataType(), 'string');
452 // Test introspecting an entity object.
453 // @todo: Add bundles and test bundles as well.
454 $entity = $this->container->get('entity_type.manager')
455 ->getStorage($entity_type)
458 $definitions = $entity->getFieldDefinitions();
459 $this->assertEqual($definitions['name']->getType(), 'string', $entity_type . ': Name field found.');
460 $this->assertEqual($definitions['user_id']->getType(), 'entity_reference', $entity_type . ': User field found.');
461 $this->assertEqual($definitions['field_test_text']->getType(), 'text', $entity_type . ': Test-text-field field found.');
463 $name_properties = $entity->name->getFieldDefinition()->getPropertyDefinitions();
464 $this->assertEqual($name_properties['value']->getDataType(), 'string', $entity_type . ': String value property of the name found.');
466 $userref_properties = $entity->user_id->getFieldDefinition()->getPropertyDefinitions();
467 $this->assertEqual($userref_properties['target_id']->getDataType(), 'integer', $entity_type . ': Entity id property of the user found.');
468 $this->assertEqual($userref_properties['entity']->getDataType(), 'entity_reference', $entity_type . ': Entity reference property of the user found.');
470 $textfield_properties = $entity->field_test_text->getFieldDefinition()->getFieldStorageDefinition()->getPropertyDefinitions();
471 $this->assertEqual($textfield_properties['value']->getDataType(), 'string', $entity_type . ': String value property of the test-text field found.');
472 $this->assertEqual($textfield_properties['format']->getDataType(), 'filter_format', $entity_type . ': String format field of the test-text field found.');
473 $this->assertEqual($textfield_properties['processed']->getDataType(), 'string', $entity_type . ': String processed property of the test-text field found.');
475 // Make sure provided contextual information is right.
476 $entity_adapter = $entity->getTypedData();
477 $this->assertSame($entity_adapter->getRoot(), $entity_adapter, 'Entity is root object.');
478 $this->assertEqual($entity_adapter->getPropertyPath(), '');
479 $this->assertEqual($entity_adapter->getName(), '');
480 $this->assertEqual($entity_adapter->getParent(), NULL);
482 $field = $entity->user_id;
483 $this->assertSame($field->getRoot()->getValue(), $entity, 'Entity is root object.');
484 $this->assertSame($field->getEntity(), $entity, 'getEntity() returns the entity.');
485 $this->assertEqual($field->getPropertyPath(), 'user_id');
486 $this->assertEqual($field->getName(), 'user_id');
487 $this->assertSame($field->getParent()->getValue(), $entity, 'Parent object matches.');
489 $field_item = $field[0];
490 $this->assertSame($field_item->getRoot()->getValue(), $entity, 'Entity is root object.');
491 $this->assertSame($field_item->getEntity(), $entity, 'getEntity() returns the entity.');
492 $this->assertEqual($field_item->getPropertyPath(), 'user_id.0');
493 $this->assertEqual($field_item->getName(), '0');
494 $this->assertSame($field_item->getParent(), $field, 'Parent object matches.');
496 $item_value = $field_item->get('entity');
497 $this->assertSame($item_value->getRoot()->getValue(), $entity, 'Entity is root object.');
498 $this->assertEqual($item_value->getPropertyPath(), 'user_id.0.entity');
499 $this->assertEqual($item_value->getName(), 'entity');
500 $this->assertSame($item_value->getParent(), $field_item, 'Parent object matches.');
504 * Tests iterating over properties.
506 public function testIterator() {
507 // All entity variations have to have the same results.
508 foreach (entity_test_entity_types() as $entity_type) {
509 $this->doTestIterator($entity_type);
514 * Executes the iterator tests for the given entity type.
516 * @param string $entity_type
517 * The entity type to run the tests with.
519 protected function doTestIterator($entity_type) {
520 $entity = $this->createTestEntity($entity_type);
522 foreach ($entity as $name => $field) {
523 $this->assertTrue($field instanceof FieldItemListInterface, $entity_type . ": Field $name implements interface.");
525 foreach ($field as $delta => $item) {
526 $this->assertTrue($field[0] instanceof FieldItemInterface, $entity_type . ": Item $delta of field $name implements interface.");
528 foreach ($item as $value_name => $value_property) {
529 $this->assertTrue($value_property instanceof TypedDataInterface, $entity_type . ": Value $value_name of item $delta of field $name implements interface.");
531 $value = $value_property->getValue();
532 $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.");
537 $fields = $entity->getFields();
538 $this->assertEqual(array_keys($fields), array_keys($entity->getTypedData()->getDataDefinition()->getPropertyDefinitions()), format_string('%entity_type: All fields returned.', ['%entity_type' => $entity_type]));
539 $this->assertEqual($fields, iterator_to_array($entity->getIterator()), format_string('%entity_type: Entity iterator iterates over all fields.', ['%entity_type' => $entity_type]));
543 * Tests working with the entity based upon the TypedData API.
545 public function testDataStructureInterfaces() {
546 // All entity variations have to have the same results.
547 foreach (entity_test_entity_types() as $entity_type) {
548 $this->doTestDataStructureInterfaces($entity_type);
553 * Executes the data structure interfaces tests for the given entity type.
555 * @param string $entity_type
556 * The entity type to run the tests with.
558 protected function doTestDataStructureInterfaces($entity_type) {
559 $entity = $this->createTestEntity($entity_type);
561 // Test using the whole tree of typed data by navigating through the tree of
562 // contained properties and getting all contained strings, limited by a
565 $this->getContainedStrings($entity->getTypedData(), 0, $strings);
567 // @todo: Once the user entity has defined properties this should contain
568 // the user name and other user entity strings as well.
570 $entity->uuid->value,
575 $this->entityFieldText,
580 if ($entity instanceof RevisionLogInterface) {
581 // Adding empty string for revision message.
582 $target_strings[] = '';
586 asort($target_strings);
587 $this->assertEqual(array_values($strings), array_values($target_strings), format_string('%entity_type: All contained strings found.', ['%entity_type' => $entity_type]));
591 * Recursive helper for getting all contained strings,
592 * i.e. properties of type string.
594 public function getContainedStrings(TypedDataInterface $wrapper, $depth, array &$strings) {
596 if ($wrapper instanceof StringInterface) {
597 $strings[] = $wrapper->getValue();
600 // Recurse until a certain depth is reached if possible.
602 if ($wrapper instanceof ListInterface) {
603 foreach ($wrapper as $item) {
604 $this->getContainedStrings($item, $depth + 1, $strings);
607 elseif ($wrapper instanceof ComplexDataInterface) {
608 foreach ($wrapper as $property) {
609 $this->getContainedStrings($property, $depth + 1, $strings);
616 * Makes sure data types are correctly derived for all entity types.
618 public function testDataTypes() {
619 $types = \Drupal::typedDataManager()->getDefinitions();
620 foreach (entity_test_entity_types() as $entity_type) {
621 $this->assertTrue($types['entity:' . $entity_type]['class'], 'Entity data type registered.');
623 // Check bundle types are provided as well.
624 entity_test_create_bundle('bundle');
625 $types = \Drupal::typedDataManager()->getDefinitions();
626 $this->assertTrue($types['entity:entity_test:bundle']['class'], 'Entity bundle data type registered.');
630 * Tests a base field override on a non-existing base field.
632 * @see entity_test_entity_base_field_info_alter()
634 public function testBaseFieldNonExistingBaseField() {
635 $this->entityManager->getStorage('node_type')->create([
639 $this->entityManager->clearCachedFieldDefinitions();
640 $fields = $this->entityManager->getFieldDefinitions('node', 'page');
641 $override = $fields['status']->getConfig('page');
642 $override->setLabel($this->randomString())->save();
643 \Drupal::state()->set('entity_test.node_remove_status_field', TRUE);
644 $this->entityManager->clearCachedFieldDefinitions();
645 $fields = $this->entityManager->getFieldDefinitions('node', 'page');
646 // A base field override on a non-existing base field should not cause a
647 // field definition to come into existence.
648 $this->assertFalse(isset($fields['status']), 'Node\'s status base field does not exist.');
652 * Tests creating a field override config for a bundle field.
654 * @see entity_test_entity_base_field_info_alter()
656 public function testFieldOverrideBundleField() {
657 // First make sure the bundle field override in code, which is provided by
658 // the test entity works.
659 entity_test_create_bundle('some_test_bundle', 'Some test bundle', 'entity_test_field_override');
660 $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'entity_test_field_override');
661 $this->assertEqual($field_definitions['name']->getDescription(), 'The default description.');
662 $this->assertNull($field_definitions['name']->getTargetBundle());
664 $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'some_test_bundle');
665 $this->assertEqual($field_definitions['name']->getDescription(), 'Custom description.');
666 $this->assertEqual($field_definitions['name']->getTargetBundle(), 'some_test_bundle');
668 // Now create a config override of the bundle field.
669 $field_config = $field_definitions['name']->getConfig('some_test_bundle');
670 $field_config->setTranslatable(FALSE);
671 $field_config->save();
673 // Make sure both overrides are present.
674 $this->entityManager->clearCachedFieldDefinitions();
675 $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'some_test_bundle');
676 $this->assertEqual($field_definitions['name']->getDescription(), 'Custom description.');
677 $this->assertEqual($field_definitions['name']->getTargetBundle(), 'some_test_bundle');
678 $this->assertFalse($field_definitions['name']->isTranslatable());
682 * Tests validation constraints provided by the Entity API.
684 public function testEntityConstraintValidation() {
685 $entity = $this->createTestEntity('entity_test');
687 // Create a reference field item and let it reference the entity.
688 $definition = BaseFieldDefinition::create('entity_reference')
689 ->setLabel('Test entity')
690 ->setSetting('target_type', 'entity_test');
691 $reference_field = \Drupal::typedDataManager()->create($definition);
692 $reference = $reference_field->appendItem(['entity' => $entity])->get('entity');
694 // Test validation the typed data object.
695 $violations = $reference->validate();
696 $this->assertEqual($violations->count(), 0);
698 // Test validating an entity of the wrong type.
699 $user = $this->createUser();
701 $node = $node = Node::create([
703 'uid' => $user->id(),
704 'title' => $this->randomString(),
706 $reference->setValue($node);
707 $violations = $reference->validate();
708 $this->assertEqual($violations->count(), 1);
710 // Test bundle validation.
711 NodeType::create(['type' => 'article'])
713 $definition = BaseFieldDefinition::create('entity_reference')
714 ->setLabel('Test entity')
715 ->setSetting('target_type', 'node')
716 ->setSetting('handler_settings', ['target_bundles' => ['article' => 'article']]);
717 $reference_field = \Drupal::TypedDataManager()->create($definition);
718 $reference_field->appendItem(['entity' => $node]);
719 $violations = $reference_field->validate();
720 $this->assertEqual($violations->count(), 1);
722 $node = Node::create([
724 'uid' => $user->id(),
725 'title' => $this->randomString(),
728 $reference_field->entity = $node;
729 $violations = $reference_field->validate();
730 $this->assertEqual($violations->count(), 0);
734 * Tests getting processed property values via a computed property.
736 public function testComputedProperties() {
737 // All entity variations have to have the same results.
738 foreach (entity_test_entity_types() as $entity_type) {
739 $this->doTestComputedProperties($entity_type);
744 * Tests all the interaction points of a computed field.
746 public function testComputedFields() {
747 $this->installEntitySchema('entity_test_computed_field');
749 \Drupal::state()->set('entity_test_computed_field_item_list_value', ['foo computed']);
751 // Check that the values are not computed unnecessarily during the lifecycle
752 // of an entity when the field is not interacted with directly.
753 \Drupal::state()->set('computed_test_field_execution', 0);
754 $entity = EntityTestComputedField::create([]);
755 $this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0));
757 $entity->name->value = $this->randomString();
758 $this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0));
761 $this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0));
763 // Test \Drupal\Core\TypedData\ComputedItemListTrait::getValue().
764 \Drupal::state()->set('computed_test_field_execution', 0);
765 $entity = EntityTestComputedField::create([]);
766 $this->assertSame([['value' => 'foo computed']], $entity->computed_string_field->getValue());
768 // Check that the values are only computed once.
769 $this->assertSame(1, \Drupal::state()->get('computed_test_field_execution', 0));
771 // Test \Drupal\Core\TypedData\ComputedItemListTrait::setValue(). This also
772 // checks that a subsequent getter does not try to re-compute the value.
773 \Drupal::state()->set('computed_test_field_execution', 0);
774 $entity = EntityTestComputedField::create([]);
775 $entity->computed_string_field->setValue([
776 ['value' => 'foo computed 1'],
777 ['value' => 'foo computed 2'],
779 $this->assertSame([['value' => 'foo computed 1'], ['value' => 'foo computed 2']], $entity->computed_string_field->getValue());
781 // Check that the values have not been computed when they were explicitly
783 $this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0));
785 // Test \Drupal\Core\TypedData\ComputedItemListTrait::getString().
786 $entity = EntityTestComputedField::create([]);
787 $this->assertSame('foo computed', $entity->computed_string_field->getString());
789 // Test \Drupal\Core\TypedData\ComputedItemListTrait::get().
790 $entity = EntityTestComputedField::create([]);
791 $this->assertSame('foo computed', $entity->computed_string_field->get(0)->value);
792 $this->assertEmpty($entity->computed_string_field->get(1));
794 // Test \Drupal\Core\TypedData\ComputedItemListTrait::set().
795 $entity = EntityTestComputedField::create([]);
796 $entity->computed_string_field->set(1, 'foo computed 1');
797 $this->assertSame('foo computed', $entity->computed_string_field[0]->value);
798 $this->assertSame('foo computed 1', $entity->computed_string_field[1]->value);
799 $entity->computed_string_field->set(0, 'foo computed 0');
800 $this->assertSame('foo computed 0', $entity->computed_string_field[0]->value);
801 $this->assertSame('foo computed 1', $entity->computed_string_field[1]->value);
803 // Test \Drupal\Core\TypedData\ComputedItemListTrait::appendItem().
804 $entity = EntityTestComputedField::create([]);
805 $entity->computed_string_field->appendItem('foo computed 1');
806 $this->assertSame('foo computed', $entity->computed_string_field[0]->value);
807 $this->assertSame('foo computed 1', $entity->computed_string_field[1]->value);
809 // Test \Drupal\Core\TypedData\ComputedItemListTrait::removeItem().
810 $entity = EntityTestComputedField::create([]);
811 $entity->computed_string_field->removeItem(0);
812 $this->assertTrue($entity->computed_string_field->isEmpty());
814 // Test \Drupal\Core\TypedData\ComputedItemListTrait::isEmpty().
815 \Drupal::state()->set('entity_test_computed_field_item_list_value', []);
816 $entity = EntityTestComputedField::create([]);
817 $this->assertTrue($entity->computed_string_field->isEmpty());
819 \Drupal::state()->set('entity_test_computed_field_item_list_value', ['foo computed']);
820 $entity = EntityTestComputedField::create([]);
821 $this->assertFalse($entity->computed_string_field->isEmpty());
823 // Test \Drupal\Core\TypedData\ComputedItemListTrait::filter().
824 $filter_callback = function ($item) {
825 return !$item->isEmpty();
827 $entity = EntityTestComputedField::create([]);
828 $entity->computed_string_field->filter($filter_callback);
829 $this->assertCount(1, $entity->computed_string_field);
831 // Add an empty item to the list and check that it is filtered out.
832 $entity->computed_string_field->appendItem();
833 $entity->computed_string_field->filter($filter_callback);
834 $this->assertCount(1, $entity->computed_string_field);
836 // Add a non-empty item to the list and check that it is not filtered out.
837 $entity->computed_string_field->appendItem('foo computed 1');
838 $entity->computed_string_field->filter($filter_callback);
839 $this->assertCount(2, $entity->computed_string_field);
841 // Test \Drupal\Core\TypedData\ComputedItemListTrait::offsetExists().
842 $entity = EntityTestComputedField::create([]);
843 $this->assertTrue($entity->computed_string_field->offsetExists(0));
844 $this->assertFalse($entity->computed_string_field->offsetExists(1));
846 // Test \Drupal\Core\TypedData\ComputedItemListTrait::getIterator().
847 $entity = EntityTestComputedField::create([]);
848 foreach ($entity->computed_string_field as $delta => $item) {
849 $this->assertSame('foo computed', $item->value);
852 // Test \Drupal\Core\TypedData\ComputedItemListTrait::count().
853 $entity = EntityTestComputedField::create([]);
854 $this->assertCount(1, $entity->computed_string_field);
856 // Check that computed items are not auto-created when they have no values.
857 \Drupal::state()->set('entity_test_computed_field_item_list_value', []);
858 $entity = EntityTestComputedField::create([]);
859 $this->assertCount(0, $entity->computed_string_field);
861 // Test \Drupal\Core\Field\FieldItemList::equals() for a computed field.
862 \Drupal::state()->set('entity_test_computed_field_item_list_value', ['foo computed']);
863 $entity = EntityTestComputedField::create([]);
864 $computed_item_list1 = $entity->computed_string_field;
866 $entity = EntityTestComputedField::create([]);
867 $computed_item_list2 = $entity->computed_string_field;
869 $this->assertTrue($computed_item_list1->equals($computed_item_list2));
871 $computed_item_list2->value = 'foo computed 2';
872 $this->assertFalse($computed_item_list1->equals($computed_item_list2));
876 * Tests an entity reference computed field.
878 public function testEntityReferenceComputedField() {
879 $this->installEntitySchema('entity_test_computed_field');
881 // Create 2 entities to be referenced.
882 $ref1 = EntityTest::create(['name' => 'foo', 'type' => 'bar']);
884 $ref2 = EntityTest::create(['name' => 'baz', 'type' => 'bar']);
886 \Drupal::state()->set('entity_test_reference_computed_target_ids', [$ref1->id(), $ref2->id()]);
888 $entity = EntityTestComputedField::create([]);
891 /** @var \Drupal\entity_test\Plugin\Field\ComputedReferenceTestFieldItemList $field */
892 $field = $entity->get('computed_reference_field');
893 /** @var \Drupal\Core\Entity\EntityInterface[] $referenced_entities */
894 $referenced_entities = $field->referencedEntities();
896 // Check that ::referencedEntities() is working with computed fields.
897 $this->assertEquals($ref1->id(), $referenced_entities[0]->id());
898 $this->assertEquals($ref2->id(), $referenced_entities[1]->id());
902 * Executes the computed properties tests for the given entity type.
904 * @param string $entity_type
905 * The entity type to run the tests with.
907 protected function doTestComputedProperties($entity_type) {
908 $entity = $this->createTestEntity($entity_type);
909 $entity->field_test_text->value = "The <strong>text</strong> text to filter.";
910 $entity->field_test_text->format = filter_default_format();
912 $target = "<p>The <strong>text</strong> text to filter.</p>\n";
913 $this->assertEqual($entity->field_test_text->processed, $target, format_string('%entity_type: Text is processed with the default filter.', ['%entity_type' => $entity_type]));
915 // Save and load entity and make sure it still works.
917 $entity = $this->container->get('entity_type.manager')
918 ->getStorage($entity_type)
919 ->load($entity->id());
920 $this->assertEqual($entity->field_test_text->processed, $target, format_string('%entity_type: Text is processed with the default filter.', ['%entity_type' => $entity_type]));
924 * Tests explicit entity ID assignment.
926 public function testEntityIdAssignment() {
927 $entity_type = 'entity_test';
928 /** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
929 $storage = $this->container->get('entity_type.manager')->getStorage($entity_type);
931 // Check that an ID can be explicitly assigned on creation.
932 /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
933 $entity = $this->createTestEntity($entity_type);
935 $entity->set('id', $entity_id);
936 $this->assertSame($entity_id, $entity->id());
937 $storage->save($entity);
938 $entity = $storage->loadUnchanged($entity->id());
939 $this->assertTrue($entity);
941 // Check that an explicitly-assigned ID is preserved on update.
942 $storage->save($entity);
943 $entity = $storage->loadUnchanged($entity->id());
944 $this->assertTrue($entity);
946 // Check that an ID cannot be explicitly assigned on update.
947 $this->setExpectedException(EntityStorageException::class);
948 $entity->set('id', $entity_id + 1);
949 $storage->save($entity);