3 namespace Drupal\Tests\views\Kernel;
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;
12 * Tests aggregate functionality of views, for example count.
16 class QueryGroupByTest extends ViewsKernelTestBase {
19 * Views used by this test.
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'];
30 public static $modules = ['entity_test', 'system', 'field', 'user', 'language'];
33 * The storage for the test entity type.
35 * @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage
42 protected function setUp($import_test_views = TRUE) {
45 $this->installEntitySchema('user');
46 $this->installEntitySchema('entity_test');
47 $this->installEntitySchema('entity_test_mul');
49 $this->storage = $this->container->get('entity.manager')->getStorage('entity_test');
51 ConfigurableLanguage::createFromLangcode('it')->save();
55 * Tests aggregate count feature.
57 public function testAggregateCount() {
58 $this->setupTestEntities();
60 $view = Views::getView('test_aggregate_count');
61 $this->executeView($view);
63 $this->assertEqual(count($view->result), 2, 'Make sure the count of items is right.');
66 foreach ($view->result as $item) {
67 // num_records is a alias for id.
68 $types[$item->entity_test_name] = $item->num_records;
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.');
76 * Provides a test helper which runs a view with some aggregation function.
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.
84 public function groupByTestHelper($aggregation_function, $values) {
85 $this->setupTestEntities();
87 $view = Views::getView('test_group_by_count');
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']);
96 $view->displayHandlers->get('default')->options['fields']['id']['group_type'] = $aggregation_function;
99 $this->executeView($view);
101 $this->assertEqual(count($view->result), 2, 'Make sure the count of items is right.');
102 // Group by name to identify the right count.
104 foreach ($view->result as $item) {
105 $results[$item->entity_test_name] = $item->id;
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]));
112 * Helper method that creates some test entities.
114 protected function setupTestEntities() {
115 // Create 4 entities with name1 and 3 entities with name2.
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();
128 $this->storage->create($entity_2)->save();
129 $this->storage->create($entity_2)->save();
130 $this->storage->create($entity_2)->save();
134 * Tests the count aggregation function.
136 public function testGroupByCount() {
137 $this->groupByTestHelper('count', [4, 3]);
141 * Tests the sum aggregation function.
143 public function testGroupBySum() {
144 $this->groupByTestHelper('sum', [10, 18]);
148 * Tests the average aggregation function.
150 public function testGroupByAverage() {
151 $this->groupByTestHelper('avg', [2.5, 6]);
155 * Tests the min aggregation function.
157 public function testGroupByMin() {
158 $this->groupByTestHelper('min', [1, 5]);
162 * Tests the max aggregation function.
164 public function testGroupByMax() {
165 $this->groupByTestHelper('max', [4, 7]);
169 * Tests aggregation with no specific function.
171 public function testGroupByNone() {
172 $this->groupByTestHelper(NULL, [1, 5]);
176 * Tests groupby with filters.
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.
182 for ($x = 0; $x < 10; $x++) {
183 $this->storage->create(['name' => 'name1'])->save();
186 $view = Views::getView('test_group_by_in_filters');
187 $this->executeView($view);
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');
194 * Tests grouping on base field.
196 public function testGroupByBaseField() {
197 $this->setupTestEntities();
199 $view = Views::getView('test_group_by_count');
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.');
210 * Tests grouping a field with cardinality > 1.
212 public function testGroupByFieldWithCardinality() {
213 $field_storage = FieldStorageConfig::create([
215 'field_name' => 'field_test',
217 'entity_type' => 'entity_test_mul',
219 $field_storage->save();
220 $field = FieldConfig::create([
221 'field_name' => 'field_test',
222 'entity_type' => 'entity_test_mul',
223 'bundle' => 'entity_test_mul',
228 $entity = EntityTestMul::create([
229 'field_test' => [1, 1, 1],
232 $entities[] = $entity;
234 $entity = EntityTestMul::create([
235 'field_test' => [2, 2, 2],
238 $entities[] = $entity;
240 $entity = EntityTestMul::create([
241 'field_test' => [2, 2, 2],
244 $entities[] = $entity;
246 $view = Views::getView('test_group_by_count_multicardinality');
247 $this->executeView($view);
248 $this->assertEqual(2, count($view->result));
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'));
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();
260 $view = Views::getView('test_group_by_count_multicardinality');
261 $this->executeView($view);
262 $this->assertEqual(5, count($view->result));
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'));
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();
281 $view = Views::getView('test_group_by_count_multicardinality');
282 $this->executeView($view);
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'));
290 * Tests groupby with a field not existing on some bundle.
292 public function testGroupByWithFieldsNotExistingOnBundle() {
293 $field_storage = FieldStorageConfig::create([
295 'field_name' => 'field_test',
297 'entity_type' => 'entity_test_mul',
299 $field_storage->save();
300 $field = FieldConfig::create([
301 'field_name' => 'field_test',
302 'entity_type' => 'entity_test_mul',
303 'bundle' => 'entity_test_mul',
308 $entity = EntityTestMul::create([
310 'type' => 'entity_test_mul',
313 $entities[] = $entity;
315 $entity = EntityTestMul::create([
316 'type' => 'entity_test_mul2',
319 $entities[] = $entity;
321 $view = Views::getView('test_group_by_field_not_within_bundle');
322 $this->executeView($view);
324 $this->assertEqual(2, count($view->result));
325 // The first result is coming from entity_test_mul2, so no field could be
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'));