This is the documentation for concrete5 version 5.6 and earlier. View Current Documentation

Lets say we have a multi select attribute my_att it's often very useful to get items that have the attribute set to one option OR another. But by default when applying attribute filters to lists (collections, users, files or custom ) the logical relationship between them is AND, so something like this :

$myList->filterByAttribute('my_att',$val1);
$myList->filterByAttribute('my_att',$val2);

will get all the items in the in myList that have my_att set to $val1 AND my_att set to $val2 . But how do we get the items that have my_att set to $val1 OR my_att set to $val2

One way is to use the filter($column, $value, $comparison = '=') functions that part of the DatabaseItemList class with setting $column to false and $value to a pure mySQL statement that represents the logic we need, but for me doing anything non trivial in mySQL is like going to the dentist, so I noticed that concrete5 does OR filtering in the backend for example in user search (advanced) and after some digging I found the code and created this more general function for OR filtering that can be used in your block or page or package controllers. Arguments are $list: anything that extends DatabaseItemList (collections, users, files or custom ), $options: array of values to filter by , $ak the attribute key to filter by.

private function orFilter($list,$options,$ak) {

        $optionText = array();
        $db = Loader::db();
        $tbl = $ak->getIndexedSearchTable();
        if (!is_array($options) || empty($options) ) {
            $list->filterByAttribute($ak->getAttributeKeyHandle(),'0');
            return $list;
        }

        foreach($options as $id) {
            if ($id > 0) {
                $opt = SelectAttributeTypeOption::getByID($id);
                if (is_object($opt)) {
                    $optionText[] = $opt->getSelectAttributeOptionValue(true);
                    $optionQuery[] = $opt->getSelectAttributeOptionValue(false);
                }
            }
        }
        if (count($optionText) == 0) {
            return false;
        }

        $i = 0;
        foreach($optionQuery as $val) {
            $val = $db->quote('%||' . $val . '||%');
            $multiString .= 'REPLACE(' . $tbl . '.ak_' . $ak->getAttributeKeyHandle() . ', "\n", "||") like ' . $val . ' ';
            if (($i + 1) < count($optionQuery)) {
                $multiString .= 'OR ';
            }
            $i++;
        }
        $this->set('d',$optionText);
        $list->filter(false, '(' . $multiString . ')');
        return $list;
    }

Hope this helps and that it will make it into DatabaseItemList class one day

UPDATE: If you want to filter a PageList and are on Concrete5.6.2 or higher, there is actually a built-in helper method called filterBySelectAttribute that will efficiently handle combining multiple criteria with an "OR" when you pass in multiple values via an array. For example:

$pl = new PageList;
$values = array('a first value', 'some second value', 'a third possible value');
$pl->filterBySelectAttribute('my_attribute_handle', $values);
$pages = $pl->get();
Loading Conversation