1

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.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
rrd
  • 1,441
  • 2
  • 15
  • 36
  • You actually need to check the permissions at: https://book.cakephp.org/authorization/2/en/policies.html#policy-scopes – Salines Aug 11 '21 at 09:29
  • @Salines I may use it there, but the problem is the same. Chaining with OR – rrd Aug 11 '21 at 12:40
  • How looks your finder queries? – Salines Aug 11 '21 at 12:51
  • @Salines like this. public function findForCorporate(Query $query, array $options) { return $query->where(['corporate_id' => $options['corporate_id']); } – rrd Aug 11 '21 at 17:24
  • try with Combinators https://book.cakephp.org/4/en/orm/query-builder.html#advanced-conditions – Salines Aug 11 '21 at 17:52
  • Yep it seems I can not do it with custom finders. I think using query expressions is the way to go. – rrd Aug 12 '21 at 03:43

1 Answers1

1

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 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;
});
rrd
  • 1,441
  • 2
  • 15
  • 36