e4a088459fed4e7be2498867504d7214affb0dc6
[yaffs-website] / web / core / modules / views / tests / src / Kernel / QueryGroupByTest.php
1 <?php
2
3 namespace Drupal\Tests\views\Kernel;
4
5 use Drupal\entity_test\Entity\EntityTestMul;
6 use Drupal\field\Entity\FieldConfig;
7 use Drupal\field\Entity\FieldStorageConfig;
8 use Drupal\language\Entity\ConfigurableLanguage;
9 use Drupal\views\Views;
10
11 /**
12  * Tests aggregate functionality of views, for example count.
13  *
14  * @group views
15  */
16 class QueryGroupByTest extends ViewsKernelTestBase {
17
18   /**
19    * Views used by this test.
20    *
21    * @var array
22    */
23   public static $testViews = ['test_group_by_in_filters', 'test_aggregate_count', 'test_group_by_count', 'test_group_by_count_multicardinality', 'test_group_by_field_not_within_bundle'];
24
25   /**
26    * Modules to enable.
27    *
28    * @var array
29    */
30   public static $modules = ['entity_test', 'system', 'field', 'user', 'language'];
31
32   /**
33    * The storage for the test entity type.
34    *
35    * @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage
36    */
37   public $storage;
38
39   /**
40    * {@inheritdoc}
41    */
42   protected function setUp($import_test_views = TRUE) {
43     parent::setUp();
44
45     $this->installEntitySchema('user');
46     $this->installEntitySchema('entity_test');
47     $this->installEntitySchema('entity_test_mul');
48
49     $this->storage = $this->container->get('entity.manager')->getStorage('entity_test');
50
51     ConfigurableLanguage::createFromLangcode('it')->save();
52   }
53
54
55   /**
56    * Tests aggregate count feature.
57    */
58   public function testAggregateCount() {
59     $this->setupTestEntities();
60
61     $view = Views::getView('test_aggregate_count');
62     $this->executeView($view);
63
64     $this->assertEqual(count($view->result), 2, 'Make sure the count of items is right.');
65
66     $types = [];
67     foreach ($view->result as $item) {
68       // num_records is a alias for id.
69       $types[$item->entity_test_name] = $item->num_records;
70     }
71
72     $this->assertEqual($types['name1'], 4, 'Groupby the name: name1 returned the expected amount of results.');
73     $this->assertEqual($types['name2'], 3, 'Groupby the name: name2 returned the expected amount of results.');
74   }
75
76   /**
77    * Provides a test helper which runs a view with some aggregation function.
78    *
79    * @param string|null $aggregation_function
80    *   Which aggregation function should be used, for example sum or count. If
81    *   NULL is passed the aggregation will be tested with no function.
82    * @param array $values
83    *   The expected views result.
84    */
85   public function groupByTestHelper($aggregation_function, $values) {
86     $this->setupTestEntities();
87
88     $view = Views::getView('test_group_by_count');
89     $view->setDisplay();
90     // There is no need for a function in order to have aggregation.
91     if (empty($aggregation_function)) {
92       // The test table has 2 fields ('id' and 'name'). We'll remove 'id'
93       // because it's unique and will test aggregation on 'name'.
94       unset($view->displayHandlers->get('default')->options['fields']['id']);
95     }
96     else {
97       $view->displayHandlers->get('default')->options['fields']['id']['group_type'] = $aggregation_function;
98     }
99
100     $this->executeView($view);
101
102     $this->assertEqual(count($view->result), 2, 'Make sure the count of items is right.');
103     // Group by name to identify the right count.
104     $results = [];
105     foreach ($view->result as $item) {
106       $results[$item->entity_test_name] = $item->id;
107     }
108     $this->assertEqual($results['name1'], $values[0], format_string('Aggregation with @aggregation_function and groupby name: name1 returned the expected amount of results', ['@aggregation_function' => $aggregation_function]));
109     $this->assertEqual($results['name2'], $values[1], format_string('Aggregation with @aggregation_function and groupby name: name2 returned the expected amount of results', ['@aggregation_function' => $aggregation_function]));
110   }
111
112   /**
113    * Helper method that creates some test entities.
114    */
115   protected function setupTestEntities() {
116     // Create 4 entities with name1 and 3 entities with name2.
117     $entity_1 = [
118       'name' => 'name1',
119     ];
120
121     $this->storage->create($entity_1)->save();
122     $this->storage->create($entity_1)->save();
123     $this->storage->create($entity_1)->save();
124     $this->storage->create($entity_1)->save();
125
126     $entity_2 = [
127       'name' => 'name2',
128     ];
129     $this->storage->create($entity_2)->save();
130     $this->storage->create($entity_2)->save();
131     $this->storage->create($entity_2)->save();
132   }
133
134   /**
135    * Tests the count aggregation function.
136    */
137   public function testGroupByCount() {
138     $this->groupByTestHelper('count', [4, 3]);
139   }
140
141   /**
142    * Tests the sum aggregation function.
143    */
144   public function testGroupBySum() {
145     $this->groupByTestHelper('sum', [10, 18]);
146   }
147
148   /**
149    * Tests the average aggregation function.
150    */
151   public function testGroupByAverage() {
152     $this->groupByTestHelper('avg', [2.5, 6]);
153   }
154
155   /**
156    * Tests the min aggregation function.
157    */
158   public function testGroupByMin() {
159     $this->groupByTestHelper('min', [1, 5]);
160   }
161
162   /**
163    * Tests the max aggregation function.
164    */
165   public function testGroupByMax() {
166     $this->groupByTestHelper('max', [4, 7]);
167   }
168
169   /**
170    * Tests aggregation with no specific function.
171    */
172   public function testGroupByNone() {
173     $this->groupByTestHelper(NULL, [1, 5]);
174   }
175
176   /**
177    * Tests groupby with filters.
178    */
179   public function testGroupByCountOnlyFilters() {
180     // Check if GROUP BY and HAVING are included when a view
181     // doesn't display SUM, COUNT, MAX, etc. functions in SELECT statement.
182
183     for ($x = 0; $x < 10; $x++) {
184       $this->storage->create(['name' => 'name1'])->save();
185     }
186
187     $view = Views::getView('test_group_by_in_filters');
188     $this->executeView($view);
189
190     $this->assertTrue(strpos($view->build_info['query'], 'GROUP BY'), 'Make sure that GROUP BY is in the query');
191     $this->assertTrue(strpos($view->build_info['query'], 'HAVING'), 'Make sure that HAVING is in the query');
192   }
193
194   /**
195    * Tests grouping on base field.
196    */
197   public function testGroupByBaseField() {
198     $this->setupTestEntities();
199
200     $view = Views::getView('test_group_by_count');
201     $view->setDisplay();
202     // This tests that the GROUP BY portion of the query is properly formatted
203     // to include the base table to avoid ambiguous field errors.
204     $view->displayHandlers->get('default')->options['fields']['name']['group_type'] = 'min';
205     unset($view->displayHandlers->get('default')->options['fields']['id']['group_type']);
206     $this->executeView($view);
207     $this->assertTrue(strpos($view->build_info['query'], 'GROUP BY entity_test.id'), 'GROUP BY field includes the base table name when grouping on the base field.');
208   }
209
210   /**
211    * Tests grouping a field with cardinality > 1.
212    */
213   public function testGroupByFieldWithCardinality() {
214     $field_storage = FieldStorageConfig::create([
215       'type' => 'integer',
216       'field_name' => 'field_test',
217       'cardinality' => 4,
218       'entity_type' => 'entity_test_mul',
219     ]);
220     $field_storage->save();
221     $field = FieldConfig::create([
222       'field_name' => 'field_test',
223       'entity_type' => 'entity_test_mul',
224       'bundle' => 'entity_test_mul',
225     ]);
226     $field->save();
227
228     $entities = [];
229     $entity = EntityTestMul::create([
230       'field_test' => [1, 1, 1],
231     ]);
232     $entity->save();
233     $entities[] = $entity;
234
235     $entity = EntityTestMul::create([
236       'field_test' => [2, 2, 2],
237     ]);
238     $entity->save();
239     $entities[] = $entity;
240
241     $entity = EntityTestMul::create([
242       'field_test' => [2, 2, 2],
243     ]);
244     $entity->save();
245     $entities[] = $entity;
246
247     $view = Views::getView('test_group_by_count_multicardinality');
248     $this->executeView($view);
249     $this->assertEqual(2, count($view->result));
250
251     $this->assertEqual('3', $view->getStyle()->getField(0, 'id'));
252     $this->assertEqual('1', $view->getStyle()->getField(0, 'field_test'));
253     $this->assertEqual('6', $view->getStyle()->getField(1, 'id'));
254     $this->assertEqual('2', $view->getStyle()->getField(1, 'field_test'));
255
256     $entities[2]->field_test[0]->value = 3;
257     $entities[2]->field_test[1]->value = 4;
258     $entities[2]->field_test[2]->value = 5;
259     $entities[2]->save();
260
261     $view = Views::getView('test_group_by_count_multicardinality');
262     $this->executeView($view);
263     $this->assertEqual(5, count($view->result));
264
265     $this->assertEqual('3', $view->getStyle()->getField(0, 'id'));
266     $this->assertEqual('1', $view->getStyle()->getField(0, 'field_test'));
267     $this->assertEqual('3', $view->getStyle()->getField(1, 'id'));
268     $this->assertEqual('2', $view->getStyle()->getField(1, 'field_test'));
269     $this->assertEqual('1', $view->getStyle()->getField(2, 'id'));
270     $this->assertEqual('3', $view->getStyle()->getField(2, 'field_test'));
271     $this->assertEqual('1', $view->getStyle()->getField(3, 'id'));
272     $this->assertEqual('4', $view->getStyle()->getField(3, 'field_test'));
273     $this->assertEqual('1', $view->getStyle()->getField(4, 'id'));
274     $this->assertEqual('5', $view->getStyle()->getField(4, 'field_test'));
275
276     // Check that translated values are correctly retrieved and are not grouped
277     // into the original entity.
278     $translation = $entity->addTranslation('it');
279     $translation->field_test = [6, 6, 6];
280     $translation->save();
281
282     $view = Views::getView('test_group_by_count_multicardinality');
283     $this->executeView($view);
284
285     $this->assertEqual(6, count($view->result));
286     $this->assertEqual('3', $view->getStyle()->getField(5, 'id'));
287     $this->assertEqual('6', $view->getStyle()->getField(5, 'field_test'));
288   }
289
290   /**
291    * Tests groupby with a field not existing on some bundle.
292    */
293   public function testGroupByWithFieldsNotExistingOnBundle() {
294     $field_storage = FieldStorageConfig::create([
295       'type' => 'integer',
296       'field_name' => 'field_test',
297       'cardinality' => 4,
298       'entity_type' => 'entity_test_mul',
299     ]);
300     $field_storage->save();
301     $field = FieldConfig::create([
302       'field_name' => 'field_test',
303       'entity_type' => 'entity_test_mul',
304       'bundle' => 'entity_test_mul',
305     ]);
306     $field->save();
307
308     $entities = [];
309     $entity = EntityTestMul::create([
310       'field_test' => [1],
311       'type' => 'entity_test_mul',
312     ]);
313     $entity->save();
314     $entities[] = $entity;
315
316     $entity = EntityTestMul::create([
317       'type' => 'entity_test_mul2',
318     ]);
319     $entity->save();
320     $entities[] = $entity;
321
322     $view = Views::getView('test_group_by_field_not_within_bundle');
323     $this->executeView($view);
324
325     $this->assertEqual(2, count($view->result));
326     // The first result is coming from entity_test_mul2, so no field could be
327     // rendered.
328     $this->assertEqual('', $view->getStyle()->getField(0, 'field_test'));
329     // The second result is coming from entity_test_mul, so its field value
330     // could be rendered.
331     $this->assertEqual('1', $view->getStyle()->getField(1, 'field_test'));
332   }
333
334 }