If we have a table t1 with an optional relationship to table t2, views will generate SQL that looks something like:

SELECT [fields]
FROM t1
LEFT JOIN t2 on t1.nid = t2.nid

This has the intended effect of returning results from table t1 even if it doesn't have a match to table t2. Suppose, however, that table t2 has a field called "title" that we want to create an exposed filter for. When we submit the search with out anything in the form field (with the intention of not filtering that field at all) The SQL that views generates now looks like this:

SELECT [fields]
FROM t1
LEFT JOIN t2 on t1.nid = t2.nid
WHERE upper(t2.title) LIKE upper('%%')

The way that LEFT JOINs work in SQL is that they return NULL for all the fields in the left table if the join fails. Since NULL isn't LIKE any string, even one as accepting as '%%', the WHERE condition is not true for any rows in t1 which don't have a match in t2, and our optional relationship has effectively become required.

Views is too large for me to wrap my head around well enough to make sure that this solution is the right way to go about fixing the problem, but what I've done to fix this for myself is add an if() clause to all of the op_whatever() functions in handlers/views_handler_filter_string.inc that checks to see if $this->value is empty before adding the where clause. Thus:

  function op_contains($field, $upper) {
    $this->query->add_where($this->options['group'], "$upper($field) LIKE $upper('%%%s%%')", $this->value);
  }

becomes:

  function op_contains($field, $upper) {
    if(!empty($this->value)) {
      $this->query->add_where($this->options['group'], "$upper($field) LIKE $upper('%%%s%%')", $this->value);
    }
  }

Hopefully I've provided a thorough enough description of the problem and a potential solution for this to be an easy fix; I couldn't figure out a way to get this to work without modifying Views code directly, and Views gets updated often enough that maintaining it across updates will get old really fast.

Comments

alieffring’s picture

Component: Miscellaneous » exposed filters
bachbach’s picture

works for me with views 7.x-3.3

function op_contains($field) {
  $this->query->add_where($this->options['group'], $field, '%' . db_like($this->value) . '%', 'LIKE');
}

become

function op_contains($field) {
  if(!empty($this->value))
    $this->query->add_where($this->options['group'], $field, '%' . db_like($this->value) . '%', 'LIKE');
}

thanks !

jason.fisher’s picture

Version: 6.x-2.16 » 7.x-3.x-dev
Category: bug » feature

Running into this same issue and this seems to be a reasonable approach?

johnv’s picture

Category: feature » bug
StatusFileSize
new638 bytes

Here is a proper patch.

esmerel’s picture

Status: Active » Needs review
Mołot’s picture

Status: Needs review » Needs work

There is philosophical question - if user wants only rows with certain filter applied to certain column, should views return any results at all if that column does not exist for a given row?
If yes, should it treat empty string filter as an - Any - filter value? Maybe.
Should it treat 0 as - Any - ? No way! And with this patch it does. Look at http://php.net/manual/en/function.empty.php#refsect1-function.empty-retu...

empty() as is is not the way to go. If anything, maybe test for a length of a string.

zerolab’s picture

Status: Needs work » Needs review
StatusFileSize
new640 bytes
new662 bytes

Here are 2 patches (D7 and D6) that check the length rather than using empty()

Best,
Dan

Status: Needs review » Needs work

The last submitted patch, 7: view-1350890-5-optional_exposed_relationship-D6.patch, failed testing.

zerolab’s picture

Status: Needs work » Needs review
StatusFileSize
new640 bytes
new662 bytes

Uploading the correctly named patches.

chris matthews’s picture

Status: Needs review » Needs work
Issue tags: +Needs reroll

The 5 year old patch in #9 to views_handler_filter_string.inc does not apply to the latest views 7.x-3.x-dev.

Checking patch handlers/views_handler_filter_string.inc...
error: while searching for:
  }

  function op_contains($field) {
    $this->query->add_where($this->options['group'], $field, '%' . db_like($this->value) . '%', 'LIKE');
  }

  function op_word($field) {

error: patch failed: handlers/views_handler_filter_string.inc:254
error: handlers/views_handler_filter_string.inc: patch does not apply
andrew answer’s picture

Status: Needs work » Closed (outdated)
Issue tags: -Needs reroll

views_handler_filter_string.inc:281 contain code:

public function op_contains($field) {
     if (!empty($this->value)) {
       $this->query->add_where($this->options['group'], $field, '%' . db_like($this->value) . '%', 'LIKE');
     }

so this patch not needed.