Chain custom finder with OR condition

How to chain custom finders with OR condition? I have a beforeFind() method where I check permissions.

if (isset($access->corporate)) {
    $query->find('forCorporate', ['corporate_id' => $access->corporate]);
}
if (isset($access->company)) {
    $query->find('forCompany', ['company_id' => $access->company]);
}

I would need to chain the 2 finders above with an OR condition to the original query.

Well a custom finder method passes you the query object directly into your method so you can basically do whatever you want with it.

multiple ->where() function would only appear with AND conditions, not with OR.

There I would pass your “deciding” variables to one custom finder method and do all your different variations in there instead of chaining multiple finder methods

Wl, that can work but I have 3 permission levels, so I have 9 variations for the finder. I am planning to add 2 more permissions levels than I will be ending up with 125 variations.

Mhmm sure, I can understand that. I will create an issue in the cakephp core repo and we can discuss is there if something like a ->orWhere() is needed since there is already an extra function called ->andWhere()

Well seems like there once was a ->orWhere() function but it was deprecated and removed.

Yep.
I think I will just add new query expressions here, the same ones that I used in the custom finders.

I found a solution and post here hoping it can help others.

First of all, I needed clean copies of the query to create subqueries without ending up in an infinite loop. There I removed all the previous WHERE conditions.

Then I called where() to the original query using a callback method. This gave me the access to manipulate the QueryExpression .

As I wanted to chain the custom finder queries with OR condition I needed a starting condition, what always false, that is why I have 1 = 0 condition. Then I was able to use the QueryExpression 's add() method to chain the queries.

$cleanQueryCorporate = $query->cleanCopy()
    ->select(['Accounts.id'])->where(null, [], true);
$cleanQueryCompany = $query->cleanCopy()
    ->select(['Accounts.id'])->where(null, [], true);

$query->where(function (QueryExpression $exp) use ($access, $cleanQueryCorporate, $cleanQueryCompany, $options) {
    $exp = $exp->or('1 = 0');       // this needed to add the next ones with OR
    if (isset($access->corporate)) {
        $exp = $exp->add(['Accounts.id IN'=> $cleanQueryCorporate
            ->find('forCorporate', ['corporate_id' => $access->corporate])]);
    }
    if (isset($access->company)) {
        $exp = $exp->add(['Accounts.id IN'=> $cleanQueryCompany
            ->find('forCompany', ['company_id' => $access->company])]);
    }
    return $exp;
});
1 Like