04c308315b9a9a7a5e082db3fc5cddf7ff5d6fd8
[yaffs-website] / web / core / modules / field / tests / src / Kernel / FieldAttachStorageTest.php
1 <?php
2
3 namespace Drupal\Tests\field\Kernel;
4
5 use Drupal\Component\Utility\Unicode;
6 use Drupal\field\Entity\FieldConfig;
7 use Drupal\field\Entity\FieldStorageConfig;
8
9 /**
10  * Tests storage-related Field Attach API functions.
11  *
12  * @group field
13  * @todo move this to the Entity module
14  */
15 class FieldAttachStorageTest extends FieldKernelTestBase {
16
17   protected function setUp() {
18     parent::setUp();
19     $this->installEntitySchema('entity_test_rev');
20   }
21
22   /**
23    * Check field values insert, update and load.
24    *
25    * Works independently of the underlying field storage backend. Inserts or
26    * updates random field data and then loads and verifies the data.
27    */
28   public function testFieldAttachSaveLoad() {
29     $entity_type = 'entity_test_rev';
30     $this->createFieldWithStorage('', $entity_type);
31     $cardinality = $this->fieldTestData->field_storage->getCardinality();
32
33     // TODO : test empty values filtering and "compression" (store consecutive deltas).
34     // Preparation: create three revisions and store them in $revision array.
35     $values = [];
36     $entity = $this->container->get('entity_type.manager')
37       ->getStorage($entity_type)
38       ->create();
39     for ($revision_id = 0; $revision_id < 3; $revision_id++) {
40       // Note: we try to insert one extra value.
41       $current_values = $this->_generateTestFieldValues($cardinality + 1);
42       $entity->{$this->fieldTestData->field_name}->setValue($current_values);
43       $entity->setNewRevision();
44       $entity->save();
45       $entity_id = $entity->id();
46       $current_revision = $entity->getRevisionId();
47       $values[$current_revision] = $current_values;
48     }
49
50     $storage = $this->container->get('entity.manager')->getStorage($entity_type);
51     $storage->resetCache();
52     $entity = $storage->load($entity_id);
53     // Confirm current revision loads the correct data.
54     // Number of values per field loaded equals the field cardinality.
55     $this->assertEqual(count($entity->{$this->fieldTestData->field_name}), $cardinality, 'Current revision: expected number of values');
56     for ($delta = 0; $delta < $cardinality; $delta++) {
57       // The field value loaded matches the one inserted or updated.
58       $this->assertEqual($entity->{$this->fieldTestData->field_name}[$delta]->value, $values[$current_revision][$delta]['value'], format_string('Current revision: expected value %delta was found.', ['%delta' => $delta]));
59     }
60
61     // Confirm each revision loads the correct data.
62     foreach (array_keys($values) as $revision_id) {
63       $entity = $storage->loadRevision($revision_id);
64       // Number of values per field loaded equals the field cardinality.
65       $this->assertEqual(count($entity->{$this->fieldTestData->field_name}), $cardinality, format_string('Revision %revision_id: expected number of values.', ['%revision_id' => $revision_id]));
66       for ($delta = 0; $delta < $cardinality; $delta++) {
67         // The field value loaded matches the one inserted or updated.
68         $this->assertEqual($entity->{$this->fieldTestData->field_name}[$delta]->value, $values[$revision_id][$delta]['value'], format_string('Revision %revision_id: expected value %delta was found.', ['%revision_id' => $revision_id, '%delta' => $delta]));
69       }
70     }
71   }
72
73   /**
74    * Test the 'multiple' load feature.
75    */
76   public function testFieldAttachLoadMultiple() {
77     $entity_type = 'entity_test_rev';
78
79     // Define 2 bundles.
80     $bundles = [
81       1 => 'test_bundle_1',
82       2 => 'test_bundle_2',
83     ];
84     entity_test_create_bundle($bundles[1]);
85     entity_test_create_bundle($bundles[2]);
86     // Define 3 fields:
87     // - field_1 is in bundle_1 and bundle_2,
88     // - field_2 is in bundle_1,
89     // - field_3 is in bundle_2.
90     $field_bundles_map = [
91       1 => [1, 2],
92       2 => [1],
93       3 => [2],
94     ];
95     for ($i = 1; $i <= 3; $i++) {
96       $field_names[$i] = 'field_' . $i;
97       $field_storage = FieldStorageConfig::create([
98         'field_name' => $field_names[$i],
99         'entity_type' => $entity_type,
100         'type' => 'test_field',
101       ]);
102       $field_storage->save();
103       $field_ids[$i] = $field_storage->uuid();
104       foreach ($field_bundles_map[$i] as $bundle) {
105         FieldConfig::create([
106           'field_name' => $field_names[$i],
107           'entity_type' => $entity_type,
108           'bundle' => $bundles[$bundle],
109         ])->save();
110       }
111     }
112
113     // Create one test entity per bundle, with random values.
114     foreach ($bundles as $index => $bundle) {
115       $entities[$index] = $this->container->get('entity_type.manager')
116         ->getStorage($entity_type)
117         ->create(['id' => $index, 'revision_id' => $index, 'type' => $bundle]);
118       $entity = clone($entities[$index]);
119       foreach ($field_names as $field_name) {
120         if (!$entity->hasField($field_name)) {
121           continue;
122         }
123         $values[$index][$field_name] = mt_rand(1, 127);
124         $entity->$field_name->setValue(['value' => $values[$index][$field_name]]);
125       }
126       $entity->enforceIsnew();
127       $entity->save();
128     }
129
130     // Check that a single load correctly loads field values for both entities.
131     $controller = \Drupal::entityManager()->getStorage($entity->getEntityTypeId());
132     $controller->resetCache();
133     $entities = $controller->loadMultiple();
134     foreach ($entities as $index => $entity) {
135       foreach ($field_names as $field_name) {
136         if (!$entity->hasField($field_name)) {
137           continue;
138         }
139         // The field value loaded matches the one inserted.
140         $this->assertEqual($entity->{$field_name}->value, $values[$index][$field_name], format_string('Entity %index: expected value was found.', ['%index' => $index]));
141       }
142     }
143   }
144
145   /**
146    * Tests insert and update with empty or NULL fields.
147    */
148   public function testFieldAttachSaveEmptyData() {
149     $entity_type = 'entity_test';
150     $this->createFieldWithStorage('', $entity_type);
151
152     $entity_init = $this->container->get('entity_type.manager')
153       ->getStorage($entity_type)
154       ->create(['id' => 1]);
155
156     // Insert: Field is NULL.
157     $entity = clone $entity_init;
158     $entity->{$this->fieldTestData->field_name} = NULL;
159     $entity->enforceIsNew();
160     $entity = $this->entitySaveReload($entity);
161     $this->assertTrue($entity->{$this->fieldTestData->field_name}->isEmpty(), 'Insert: NULL field results in no value saved');
162
163     // All saves after this point should be updates, not inserts.
164     $entity_init->enforceIsNew(FALSE);
165
166     // Add some real data.
167     $entity = clone($entity_init);
168     $values = $this->_generateTestFieldValues(1);
169     $entity->{$this->fieldTestData->field_name} = $values;
170     $entity = $this->entitySaveReload($entity);
171     $this->assertEqual($entity->{$this->fieldTestData->field_name}->getValue(), $values, 'Field data saved');
172
173     // Update: Field is NULL. Data should be wiped.
174     $entity = clone($entity_init);
175     $entity->{$this->fieldTestData->field_name} = NULL;
176     $entity = $this->entitySaveReload($entity);
177     $this->assertTrue($entity->{$this->fieldTestData->field_name}->isEmpty(), 'Update: NULL field removes existing values');
178
179     // Re-add some data.
180     $entity = clone($entity_init);
181     $values = $this->_generateTestFieldValues(1);
182     $entity->{$this->fieldTestData->field_name} = $values;
183     $entity = $this->entitySaveReload($entity);
184     $this->assertEqual($entity->{$this->fieldTestData->field_name}->getValue(), $values, 'Field data saved');
185
186     // Update: Field is empty array. Data should be wiped.
187     $entity = clone($entity_init);
188     $entity->{$this->fieldTestData->field_name} = [];
189     $entity = $this->entitySaveReload($entity);
190     $this->assertTrue($entity->{$this->fieldTestData->field_name}->isEmpty(), 'Update: empty array removes existing values');
191   }
192
193   /**
194    * Test insert with empty or NULL fields, with default value.
195    */
196   public function testFieldAttachSaveEmptyDataDefaultValue() {
197     $entity_type = 'entity_test_rev';
198     $this->createFieldWithStorage('', $entity_type);
199
200     // Add a default value function.
201     $this->fieldTestData->field->set('default_value_callback', 'field_test_default_value');
202     $this->fieldTestData->field->save();
203
204     // Verify that fields are populated with default values.
205     $entity_init = $this->container->get('entity_type.manager')
206       ->getStorage($entity_type)
207       ->create(['id' => 1, 'revision_id' => 1]);
208     $default = field_test_default_value($entity_init, $this->fieldTestData->field);
209     $this->assertEqual($entity_init->{$this->fieldTestData->field_name}->getValue(), $default, 'Default field value correctly populated.');
210
211     // Insert: Field is NULL.
212     $entity = clone($entity_init);
213     $entity->{$this->fieldTestData->field_name} = NULL;
214     $entity->enforceIsNew();
215     $entity = $this->entitySaveReload($entity);
216     $this->assertTrue($entity->{$this->fieldTestData->field_name}->isEmpty(), 'Insert: NULL field results in no value saved');
217
218     // Verify that prepopulated field values are not overwritten by defaults.
219     $value = [['value' => $default[0]['value'] - mt_rand(1, 127)]];
220     $entity = $this->container->get('entity_type.manager')
221       ->getStorage($entity_type)
222       ->create(['type' => $entity_init->bundle(), $this->fieldTestData->field_name => $value]);
223     $this->assertEqual($entity->{$this->fieldTestData->field_name}->getValue(), $value, 'Prepopulated field value correctly maintained.');
224   }
225
226   /**
227    * Test entity deletion.
228    */
229   public function testFieldAttachDelete() {
230     $entity_type = 'entity_test_rev';
231     $this->createFieldWithStorage('', $entity_type);
232     $cardinality = $this->fieldTestData->field_storage->getCardinality();
233     $entity = $this->container->get('entity_type.manager')
234       ->getStorage($entity_type)
235       ->create(['type' => $this->fieldTestData->field->getTargetBundle()]);
236     $vids = [];
237
238     // Create revision 0
239     $values = $this->_generateTestFieldValues($cardinality);
240     $entity->{$this->fieldTestData->field_name} = $values;
241     $entity->save();
242     $vids[] = $entity->getRevisionId();
243
244     // Create revision 1
245     $entity->setNewRevision();
246     $entity->save();
247     $vids[] = $entity->getRevisionId();
248
249     // Create revision 2
250     $entity->setNewRevision();
251     $entity->save();
252     $vids[] = $entity->getRevisionId();
253     $controller = $this->container->get('entity.manager')->getStorage($entity->getEntityTypeId());
254     $controller->resetCache();
255
256     // Confirm each revision loads
257     foreach ($vids as $vid) {
258       $revision = $controller->loadRevision($vid);
259       $this->assertEqual(count($revision->{$this->fieldTestData->field_name}), $cardinality, "The test entity revision $vid has $cardinality values.");
260     }
261
262     // Delete revision 1, confirm the other two still load.
263     $controller->deleteRevision($vids[1]);
264     $controller->resetCache();
265     foreach ([0, 2] as $key) {
266       $vid = $vids[$key];
267       $revision = $controller->loadRevision($vid);
268       $this->assertEqual(count($revision->{$this->fieldTestData->field_name}), $cardinality, "The test entity revision $vid has $cardinality values.");
269     }
270
271     // Confirm the current revision still loads
272     $controller->resetCache();
273     $current = $controller->load($entity->id());
274     $this->assertEqual(count($current->{$this->fieldTestData->field_name}), $cardinality, "The test entity current revision has $cardinality values.");
275
276     // Delete all field data, confirm nothing loads
277     $entity->delete();
278     $controller->resetCache();
279     foreach ([0, 1, 2] as $vid) {
280       $revision = $controller->loadRevision($vid);
281       $this->assertFalse($revision);
282     }
283     $this->assertFalse($controller->load($entity->id()));
284   }
285
286   /**
287    * Test entity_bundle_create().
288    */
289   public function testEntityCreateBundle() {
290     $entity_type = 'entity_test_rev';
291     $this->createFieldWithStorage('', $entity_type);
292     $cardinality = $this->fieldTestData->field_storage->getCardinality();
293
294     // Create a new bundle.
295     $new_bundle = 'test_bundle_' . Unicode::strtolower($this->randomMachineName());
296     entity_test_create_bundle($new_bundle, NULL, $entity_type);
297
298     // Add a field to that bundle.
299     $this->fieldTestData->field_definition['bundle'] = $new_bundle;
300     FieldConfig::create($this->fieldTestData->field_definition)->save();
301
302     // Save an entity with data in the field.
303     $entity = $this->container->get('entity_type.manager')
304       ->getStorage($entity_type)
305       ->create(['type' => $this->fieldTestData->field->getTargetBundle()]);
306     $values = $this->_generateTestFieldValues($cardinality);
307     $entity->{$this->fieldTestData->field_name} = $values;
308
309     // Verify the field data is present on load.
310     $entity = $this->entitySaveReload($entity);
311     $this->assertEqual(count($entity->{$this->fieldTestData->field_name}), $cardinality, "Data is retrieved for the new bundle");
312   }
313
314   /**
315    * Test entity_bundle_delete().
316    */
317   public function testEntityDeleteBundle() {
318     $entity_type = 'entity_test_rev';
319     $this->createFieldWithStorage('', $entity_type);
320
321     // Create a new bundle.
322     $new_bundle = 'test_bundle_' . Unicode::strtolower($this->randomMachineName());
323     entity_test_create_bundle($new_bundle, NULL, $entity_type);
324
325     // Add a field to that bundle.
326     $this->fieldTestData->field_definition['bundle'] = $new_bundle;
327     FieldConfig::create($this->fieldTestData->field_definition)->save();
328
329     // Create a second field for the test bundle
330     $field_name = Unicode::strtolower($this->randomMachineName() . '_field_name');
331     $field_storage = [
332       'field_name' => $field_name,
333       'entity_type' => $entity_type,
334       'type' => 'test_field',
335       'cardinality' => 1,
336     ];
337     FieldStorageConfig::create($field_storage)->save();
338     $field = [
339       'field_name' => $field_name,
340       'entity_type' => $entity_type,
341       'bundle' => $this->fieldTestData->field->getTargetBundle(),
342       'label' => $this->randomMachineName() . '_label',
343       'description' => $this->randomMachineName() . '_description',
344       'weight' => mt_rand(0, 127),
345     ];
346     FieldConfig::create($field)->save();
347
348     // Save an entity with data for both fields
349     $entity = $this->container->get('entity_type.manager')
350       ->getStorage($entity_type)
351       ->create(['type' => $this->fieldTestData->field->getTargetBundle()]);
352     $values = $this->_generateTestFieldValues($this->fieldTestData->field_storage->getCardinality());
353     $entity->{$this->fieldTestData->field_name} = $values;
354     $entity->{$field_name} = $this->_generateTestFieldValues(1);
355     $entity = $this->entitySaveReload($entity);
356
357     // Verify the fields are present on load
358     $this->assertEqual(count($entity->{$this->fieldTestData->field_name}), 4, 'First field got loaded');
359     $this->assertEqual(count($entity->{$field_name}), 1, 'Second field got loaded');
360
361     // Delete the bundle.
362     entity_test_delete_bundle($this->fieldTestData->field->getTargetBundle(), $entity_type);
363
364     // Verify no data gets loaded
365     $controller = $this->container->get('entity.manager')->getStorage($entity->getEntityTypeId());
366     $controller->resetCache();
367     $entity = $controller->load($entity->id());
368
369     $this->assertTrue(empty($entity->{$this->fieldTestData->field_name}), 'No data for first field');
370     $this->assertTrue(empty($entity->{$field_name}), 'No data for second field');
371
372     // Verify that the fields are gone.
373     $this->assertFalse(FieldConfig::load('entity_test.' . $this->fieldTestData->field->getTargetBundle() . '.' . $this->fieldTestData->field_name), "First field is deleted");
374     $this->assertFalse(FieldConfig::load('entity_test.' . $field['bundle'] . '.' . $field_name), "Second field is deleted");
375   }
376
377 }