diff --git a/core/modules/views/src/Plugin/views/field/Field.php b/core/modules/views/src/Plugin/views/field/Field.php index ff27181..d9e905a 100644 --- a/core/modules/views/src/Plugin/views/field/Field.php +++ b/core/modules/views/src/Plugin/views/field/Field.php @@ -669,6 +669,7 @@ public function submitGroupByForm(&$form, FormStateInterface $form_state) { */ public function renderItems($items) { if (!empty($items)) { + $items = $this->prepareItemsByDelta($items); if ($this->options['multi_type'] == 'separator' || !$this->options['group_rows']) { $separator = $this->options['multi_type'] == 'separator' ? SafeMarkup::checkAdminXss($this->options['separator']) : ''; $build = [ @@ -690,6 +691,74 @@ public function renderItems($items) { } /** + * Adapts the $items according to the delta configuration. + * + * Therefore this method filters out limits the items, reorders the items + * as well as takes into account an offset. + * + * @param array $all_values + * The items for individual rendering. + * + * @return array + * The manipulated items. + */ + protected function prepareItemsByDelta(array $all_values) { + if ($this->options['delta_reversed']) { + $all_values = array_reverse($all_values); + } + + // We are supposed to show only certain deltas. + if ($this->limit_values) { + $row = $this->view->result[$this->view->row_index]; + + // Offset is calculated differently when row grouping for a field is + // not enabled. Since there are multiple rows, the delta needs to be + // taken into account, so that different values are shown per row. + if (!$this->options['group_rows'] && isset($this->aliases['delta']) && isset($row->{$this->aliases['delta']})) { + $delta_limit = 1; + $offset = $row->{$this->aliases['delta']}; + } + // Single fields don't have a delta available so choose 0. + elseif (!$this->options['group_rows'] && !$this->multiple) { + $delta_limit = 1; + $offset = 0; + } + else { + $delta_limit = $this->options['delta_limit']; + $offset = intval($this->options['delta_offset']); + + // We should only get here in this case if there's an offset, and + // in that case we're limiting to all values after the offset. + if ($delta_limit === 0) { + $delta_limit = count($all_values) - $offset; + } + } + + // Determine if only the first and last values should be shown + $delta_first_last = $this->options['delta_first_last']; + + $new_values = array(); + for ($i = 0; $i < $delta_limit; $i++) { + $new_delta = $offset + $i; + + if (isset($all_values[$new_delta])) { + // If first-last option was selected, only use the first and last values + if (!$delta_first_last + // Use the first value. + || $new_delta == $offset + // Use the last value. + || $new_delta == ($delta_limit + $offset - 1)) { + $new_values[] = $all_values[$new_delta]; + } + } + } + $all_values = $new_values; + } + + return $all_values; + } + + /** * {@inheritdoc} */ public function preRender(&$values) { @@ -817,57 +886,6 @@ public function processEntity(ResultRow $values, EntityInterface $entity, $clone return FALSE; } - // We are supposed to show only certain deltas. - if ($this->limit_values && !empty($processed_entity->{$this->definition['field_name']})) { - $all_values = !empty($processed_entity->{$this->definition['field_name']}) ? $processed_entity->{$this->definition['field_name']}->getValue() : array(); - if ($this->options['delta_reversed']) { - $all_values = array_reverse($all_values); - } - - // Offset is calculated differently when row grouping for a field is - // not enabled. Since there are multiple rows, the delta needs to be - // taken into account, so that different values are shown per row. - if (!$this->options['group_rows'] && isset($this->aliases['delta']) && isset($values->{$this->aliases['delta']})) { - $delta_limit = 1; - $offset = $values->{$this->aliases['delta']}; - } - // Single fields don't have a delta available so choose 0. - elseif (!$this->options['group_rows'] && !$this->multiple) { - $delta_limit = 1; - $offset = 0; - } - else { - $delta_limit = $this->options['delta_limit']; - $offset = intval($this->options['delta_offset']); - - // We should only get here in this case if there's an offset, and - // in that case we're limiting to all values after the offset. - if ($delta_limit === 0) { - $delta_limit = count($all_values) - $offset; - } - } - - // Determine if only the first and last values should be shown - $delta_first_last = $this->options['delta_first_last']; - - $new_values = array(); - for ($i = 0; $i < $delta_limit; $i++) { - $new_delta = $offset + $i; - - if (isset($all_values[$new_delta])) { - // If first-last option was selected, only use the first and last values - if (!$delta_first_last - // Use the first value. - || $new_delta == $offset - // Use the last value. - || $new_delta == ($delta_limit + $offset - 1)) { - $new_values[] = $all_values[$new_delta]; - } - } - } - $processed_entity->{$this->definition['field_name']} = $new_values; - } - return $processed_entity; } diff --git a/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php b/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php index c516bcc..0341859 100644 --- a/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php +++ b/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php @@ -7,9 +7,11 @@ namespace Drupal\Tests\views\Unit\Plugin\field; +use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Tests\UnitTestCase; use Drupal\Tests\views\Unit\Plugin\HandlerTestTrait; use Drupal\views\Plugin\views\field\Field; +use Drupal\views\ResultRow; use Symfony\Component\DependencyInjection\ContainerBuilder; /** @@ -553,6 +555,82 @@ public function testQueryWithGroupByForConfigField() { } /** + * @covers ::prepareItemsByDelta + * + * @dataProvider providerTestPrepareItemsByDelta + */ + public function testPrepareItemsByDelta(array $options, array $expected_values) { + $definition = [ + 'entity_type' => 'test_entity', + 'field_name' => 'integer', + ]; + $handler = new TestField([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer); + $handler->view = $this->executable; + $handler->view->field = [$handler]; + + $this->setupLanguageRenderer($handler, $definition); + + $field_storage = $this->getConfigFieldStorage(); + $field_storage->expects($this->any()) + ->method('getCardinality') + ->willReturn(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED); + + $this->entityManager->expects($this->any()) + ->method('getFieldStorageDefinitions') + ->with('test_entity') + ->willReturn([ + 'integer' => $field_storage, + ]); + + $table_mapping = $this->getMock('Drupal\Core\Entity\Sql\TableMappingInterface'); + $table_mapping + ->expects($this->any()) + ->method('getFieldColumnName') + ->with($field_storage, 'value') + ->willReturn('integer_value'); + $entity_storage = $this->getMock('Drupal\Core\Entity\Sql\SqlEntityStorageInterface'); + $entity_storage->expects($this->any()) + ->method('getTableMapping') + ->willReturn($table_mapping); + $this->entityManager->expects($this->any()) + ->method('getStorage') + ->with('test_entity') + ->willReturn($entity_storage); + + $options = [ + 'group_column' => 'value', + 'group_columns' => [], + 'table' => 'test_entity__integer', + ] + $options; + $handler->init($this->executable, $this->display, $options); + + $this->executable->row_index = 0; + $this->executable->result = [0 => new ResultRow([])]; + + $items = [3, 1, 4, 1, 5, 9]; + $this->assertEquals($expected_values, $handler->executePrepareItemsByDelta($items)); + } + + public function providerTestPrepareItemsByDelta() { + $data = []; + + // Let's display all values. + $data[] = [[], [3, 1, 4, 1, 5, 9]]; + // Test just reversed deltas. + $data[] = [['delta_reversed' => TRUE], [9, 5, 1, 4, 1, 3]]; + + // Test combinations of delta limit, offset and first_last. + $data[] = [['group_rows' => TRUE, 'delta_limit' => 3], [3, 1, 4]]; + $data[] = [['group_rows' => TRUE, 'delta_limit' => 3, 'delta_offset' => 2], [4, 1, 5]]; + $data[] = [['group_rows' => TRUE, 'delta_reversed' => TRUE, 'delta_limit' => 3, 'delta_offset' => 2], [1, 4, 1]]; + $data[] = [['group_rows' => TRUE, 'delta_first_last' => TRUE], [3, 9]]; + $data[] = [['group_rows' => TRUE, 'delta_limit' => 1, 'delta_first_last' => TRUE], [3]]; + $data[] = [['group_rows' => TRUE, 'delta_offset' => 1, 'delta_first_last' => TRUE], [1, 9]]; + + return $data; + } + + /** * Returns a mocked base field storage object. * * @return \Drupal\Core\Field\FieldStorageDefinitionInterface|\PHPUnit_Framework_MockObject_MockObject @@ -642,3 +720,11 @@ protected function setupLanguageRenderer(Field $handler, $definition) { } } + +class TestField extends Field { + + public function executePrepareItemsByDelta(array $all_values) { + return $this->prepareItemsByDelta($all_values); + } + +}