diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/filter/Compare.php b/core/modules/views/lib/Drupal/views/Plugin/views/filter/Compare.php new file mode 100644 index 0000000..1ab32be --- /dev/null +++ b/core/modules/views/lib/Drupal/views/Plugin/views/filter/Compare.php @@ -0,0 +1,140 @@ + ''); + $options['right_field'] = array('default' => ''); + + return $options; + } + + /** + * Provides a list of all operators. + * + * @return array + * An array of operators. + */ + protected function fieldsOperatorOptions() { + return array( + '<' => t('Is less than'), + '<=' => t('Is less than or equal to'), + '=' => t('Is equal to'), + '<>' => t('Is not equal to'), + '>=' => t('Is greater than or equal to'), + '>' => t('Is greater than') + ); + } + + /** + * {@inheritdoc} + */ + public function buildOptionsForm(&$form, &$form_state) { + parent::buildOptionsForm($form, $form_state); + + $field_options = $this->displayHandler->getFieldLabels(); + // Filter out the views table as it will not be filterable. + foreach (array_keys($field_options) as $id) { + if ($this->view->field[$id]->table == 'views') { + unset($field_options[$id]); + } + } + + $form['left_field'] = array( + '#type' => 'select', + '#title' => t('Left field'), + '#default_value' => $this->options['left_field'], + '#options' => $field_options, + '#weight' => -3, + ); + + $form['operator'] = array( + '#type' => 'select', + '#title' => t('Operator'), + '#default_value' => $this->options['operator'], + '#options' => $this->fieldsOperatorOptions(), + '#weight' => -2, + ); + + $form['right_field'] = array( + '#type' => 'select', + '#title' => t('Right field'), + '#default_value' => $this->options['right_field'], + '#options' => $field_options, + '#weight' => -1, + ); + } + + /** + * {@inheritdoc} + */ + public function adminSummary() { + return String::format( + $this->options['left_field'] . ' ' . + $this->options['operator'] . ' ' . + $this->options['right_field'] + ); + } + + /** + * {@inheritdoc} + * + * Builds extra condition from existing fields (from existing joins). + */ + public function query() { + $left = $this->options['left_field']; + $right = $this->options['right_field']; + + // Get all existing field handlers. + $field_handlers = $this->displayHandler->getHandlers('field'); + + // Make sure the selected fields still exist. + if (!isset($field_handlers[$left], $field_handlers[$right])) { + return; + } + + // Get the left table and field. + $left_handler = $field_handlers[$left]; + $left_handler->setRelationship(); + $left_table_alias = $this->query->ensureTable($left_handler->table, $left_handler->relationship); + + // Get the right table and field. + $right_handler = $field_handlers[$right]; + $right_handler->setRelationship(); + $right_table_alias = $this->query->ensureTable($right_handler->table, $right_handler->relationship); + + // Build piece of SQL. + $snippet = $left_table_alias . '.' . $left_handler->realField . ' ' . $this->options['operator'] . ' ' . $right_table_alias . '.' . $right_handler->realField; + + $this->query->addWhereExpression($this->options['group'], $snippet); + } + +} diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/FilterCompareTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/FilterCompareTest.php new file mode 100644 index 0000000..b4298a2 --- /dev/null +++ b/core/modules/views/lib/Drupal/views/Tests/Handler/FilterCompareTest.php @@ -0,0 +1,121 @@ + 'name', + ); + + /** + * Views used by this test. + * + * @var array + */ + public static $testViews = array('test_filter_compare'); + + public static function getInfo() { + return array( + 'name' => 'Filter: Compare', + 'description' => 'Tests the compare filter handler.', + 'group' => 'Views Handlers', + ); + } + + /** + * {@inheritdoc} + */ + protected function dataSet() { + // Setup age/created so comparing fields can be tested. + $data = parent::dataSet(); + + $data[0]['age'] = $data[0]['created']; + $data[1]['age'] = $data[1]['created'] + 1000; + $data[2]['age'] = $data[2]['created'] + 1; + $data[3]['age'] = $data[3]['created'] - 1; + $data[4]['age'] = $data[4]['created'] - 1000; + + return $data; + } + + /** + * Tests the compare handler. + */ + public function testCompare() { + $view = views_get_view('test_filter_compare'); + + // Test the equality operator. + $this->executeView($view); + $this->assertIdenticalResultset($view, array(array('name' => 'John')), $this->columnMap); + $view->destroy(); + + $tests = array(); + $tests[] = array('=', array( + array('name' => 'John'), + )); + + $tests[] = array('!=', array( + array('name' => 'George'), + array('name' => 'Ringo'), + array('name' => 'Paul'), + array('name' => 'Meredith') + )); + + $tests[] = array('>', array( + array('name' => 'George'), + array('name' => 'Ringo'), + )); + + $tests[] = array('>=', array( + array('name' => 'John'), + array('name' => 'George'), + array('name' => 'Ringo'), + )); + + $tests[] = array('<', array( + array('name' => 'Paul'), + array('name' => 'Meredith'), + )); + + $tests[] = array('<=', array( + array('name' => 'John'), + array('name' => 'Paul'), + array('name' => 'Meredith'), + )); + + foreach ($tests as $parts) { + list($operator, $expected) = $parts; + + $view->initDisplay(); + $item = $view->getItem('default', 'filter', 'fields_compare'); + $item['operator'] = $operator; + $view->setItem('default', 'filter', 'fields_compare', $item); + + $this->executeView($view); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + $view->destroy(); + } + + + } + +} diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_filter_compare.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_filter_compare.yml new file mode 100644 index 0000000..8a4f4b7 --- /dev/null +++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_filter_compare.yml @@ -0,0 +1,61 @@ +base_table: views_test_data +core: '8' +description: '' +status: '1' +display: + default: + display_options: + defaults: + fields: '0' + filters: '0' + pager: '0' + pager_options: '0' + sorts: '0' + fields: + created: + field: created + id: created + relationship: none + table: views_test_data + plugin_id: numeric + age: + field: age + id: age + relationship: none + table: views_test_data + plugin_id: numeric + name: + field: name + id: name + relationship: none + table: views_test_data + plugin_id: string + pager: + options: + offset: '0' + type: none + filters: + fields_compare: + table: views + field: fields_compare + id: fields_compare + plugin_id: compare + left_field: age + right_field: created + operator: '=' + pager_options: { } + sorts: + id: + field: id + id: id + order: ASC + relationship: none + table: views_test_data + plugin_id: numeric + display_plugin: default + display_title: Master + id: default + position: '0' +label: 'Test view' +id: test_filter_compare +tag: '' diff --git a/core/modules/views/views.views.inc b/core/modules/views/views.views.inc index 99d23c1..beeff4c 100644 --- a/core/modules/views/views.views.inc +++ b/core/modules/views/views.views.inc @@ -122,5 +122,13 @@ function views_views_data() { } } + $data['views']['fields_compare'] = array( + 'title' => t('Fields comparison'), + 'help' => t('Compare two fields to filter the result of a view'), + 'filter' => array( + 'id' => 'compare', + ) + ); + return $data; }