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();
56 * Tests aggregate count feature.
58 public function testAggregateCount() {
59 $this->setupTestEntities();
61 $view = Views::getView('test_aggregate_count');
62 $this->executeView($view);
64 $this->assertEqual(count($view->result), 2, 'Make sure the count of items is right.');
67 foreach ($view->result as $item) {
68 // num_records is a alias for id.
69 $types[$item->entity_test_name] = $item->num_records;
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.');
77 * Provides a test helper which runs a view with some aggregation function.
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.
85 public function groupByTestHelper($aggregation_function, $values) {
86 $this->setupTestEntities();
88 $view = Views::getView('test_group_by_count');
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']);
97 $view->displayHandlers->get('default')->options['fields']['id']['group_type'] = $aggregation_function;
100 $this->executeView($view);
102 $this->assertEqual(count($view->result), 2, 'Make sure the count of items is right.');
103 // Group by name to identify the right count.
105 foreach ($view->result as $item) {
106 $results[$item->entity_test_name] = $item->id;
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]));
113 * Helper method that creates some test entities.
115 protected function setupTestEntities() {
116 // Create 4 entities with name1 and 3 entities with name2.
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();
129 $this->storage->create($entity_2)->save();
130 $this->storage->create($entity_2)->save();
131 $this->storage->create($entity_2)->save();
135 * Tests the count aggregation function.
137 public function testGroupByCount() {
138 $this->groupByTestHelper('count', [4, 3]);
142 * Tests the sum aggregation function.
144 public function testGroupBySum() {
145 $this->groupByTestHelper('sum', [10, 18]);
149 * Tests the average aggregation function.
151 public function testGroupByAverage() {
152 $this->groupByTestHelper('avg', [2.5, 6]);
156 * Tests the min aggregation function.
158 public function testGroupByMin() {
159 $this->groupByTestHelper('min', [1, 5]);
163 * Tests the max aggregation function.
165 public function testGroupByMax() {
166 $this->groupByTestHelper('max', [4, 7]);
170 * Tests aggregation with no specific function.
172 public function testGroupByNone() {
173 $this->groupByTestHelper(NULL, [1, 5]);
177 * Tests groupby with filters.
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.
183 for ($x = 0; $x < 10; $x++) {
184 $this->storage->create(['name' => 'name1'])->save();
187 $view = Views::getView('test_group_by_in_filters');
188 $this->executeView($view);
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');
195 * Tests grouping on base field.
197 public function testGroupByBaseField() {
198 $this->setupTestEntities();
200 $view = Views::getView('test_group_by_count');
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.');
211 * Tests grouping a field with cardinality > 1.
213 public function testGroupByFieldWithCardinality() {
214 $field_storage = FieldStorageConfig::create([
216 'field_name' => 'field_test',
218 'entity_type' => 'entity_test_mul',
220 $field_storage->save();
221 $field = FieldConfig::create([
222 'field_name' => 'field_test',
223 'entity_type' => 'entity_test_mul',
224 'bundle' => 'entity_test_mul',
229 $entity = EntityTestMul::create([
230 'field_test' => [1, 1, 1],
233 $entities[] = $entity;
235 $entity = EntityTestMul::create([
236 'field_test' => [2, 2, 2],
239 $entities[] = $entity;
241 $entity = EntityTestMul::create([
242 'field_test' => [2, 2, 2],
245 $entities[] = $entity;
247 $view = Views::getView('test_group_by_count_multicardinality');
248 $this->executeView($view);
249 $this->assertEqual(2, count($view->result));
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'));
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();
261 $view = Views::getView('test_group_by_count_multicardinality');
262 $this->executeView($view);
263 $this->assertEqual(5, count($view->result));
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'));
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();
282 $view = Views::getView('test_group_by_count_multicardinality');
283 $this->executeView($view);
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'));
291 * Tests groupby with a field not existing on some bundle.
293 public function testGroupByWithFieldsNotExistingOnBundle() {
294 $field_storage = FieldStorageConfig::create([
296 'field_name' => 'field_test',
298 'entity_type' => 'entity_test_mul',
300 $field_storage->save();
301 $field = FieldConfig::create([
302 'field_name' => 'field_test',
303 'entity_type' => 'entity_test_mul',
304 'bundle' => 'entity_test_mul',
309 $entity = EntityTestMul::create([
311 'type' => 'entity_test_mul',
314 $entities[] = $entity;
316 $entity = EntityTestMul::create([
317 'type' => 'entity_test_mul2',
320 $entities[] = $entity;
322 $view = Views::getView('test_group_by_field_not_within_bundle');
323 $this->executeView($view);
325 $this->assertEqual(2, count($view->result));
326 // The first result is coming from entity_test_mul2, so no field could be
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'));