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