/** * Parses `CriteriaExpression` to a set of expressions. * * @param QueryBuilder $queryBuilder Query builder to be configured. * @param CriteriaExpression $criteria Criteria to be parsed. * * @return CompositeExpression */ private function parseCriteria(QueryBuilder $queryBuilder, CriteriaExpression $criteria) { $expressions = []; foreach ($criteria->getCriteria() as $criterium) { if ($criterium instanceof CriteriaExpression) { $expressions[] = $this->parseCriteria($queryBuilder, $criterium); continue; } $expressions[] = $this->createExpression($queryBuilder, $criterium); } // now join all the expressions by the predefined logic $logic = $criteria->getLogic() === Knit::LOGIC_OR ? 'orX' : 'andX'; return call_user_func_array([$queryBuilder->expr(), $logic], $expressions); }
/** * Checks if the given object matches the given criteria. * * @param array $object Object to check. * @param CriteriaExpression $criteria Criteria expression to check against. Optional. * * @return bool */ public function matches(array $object, CriteriaExpression $criteria = null) { if (!$criteria || count($criteria->getCriteria()) === 0) { return true; } $matches = []; foreach ($criteria->getCriteria() as $criterium) { if ($criterium instanceof CriteriaExpression) { $matches[] = $this->matches($object, $criterium); continue; } $matches[] = $this->matchesProperty($object, $criterium); } // map bool's to ints so they can be summed $matches = array_map('intval', $matches); $sum = array_sum($matches); return $criteria->getLogic() === Knit::LOGIC_AND ? $sum === count($matches) : $sum > 0; // at least 1 must match for OR }
/** * Does the actual work of parsing criteria. * * @param CriteriaExpression $criteria Knit's criteria expression to be parsed. * @param boolean $asCollection [optional] Should this criteria be parsed as a collection? * Required for OR logic. For internal use. Default: `false`. * * @return array */ private function parseCriteria(CriteriaExpression $criteria, $asCollection = false) { $result = []; foreach ($criteria->getCriteria() as $criterium) { if ($criterium instanceof CriteriaExpression) { $logic = $criterium->getLogic() === Knit::LOGIC_OR ? '$or' : '$and'; $parsedCriterium = $this->parseCriteria($criterium, true); // if parent is OR then add this as a collection if ($criteria->getLogic() === Knit::LOGIC_OR) { $result[][$logic] = $parsedCriterium; } else { $result[$logic] = $parsedCriterium; } continue; } $result = $this->parseCriterium($criterium, $result, $asCollection); } return $result; }
/** * Tests generating the criteria object. * * @param array $criteria Criteria array. * @param array $expected Expected result. * * @dataProvider provideCriteria */ public function testCriteria(array $criteria, array $expected) { $expression = new CriteriaExpression($criteria); $validator = function (CriteriaExpression $expression, $expected, $validator) { $this->assertEquals($expected['logic'], $expression->getLogic()); $criteria = $expression->getCriteria(); foreach ($criteria as $i => $criterium) { $expectedItem = $expected['properties'][$i]; if ($expectedItem[0] === 'criteria') { $validator($criterium, $expectedItem[1], $validator); } elseif ($expectedItem[0] === 'property') { $this->assertInstanceOf(PropertyValue::class, $criterium); $this->assertEquals($expectedItem[1], $criterium->getProperty()); $this->assertEquals($expectedItem[2], $criterium->getOperator()); $this->assertEquals($expectedItem[3], $criterium->getValue()); } } }; $validator($expression, $expected, $validator); }