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