Skip to content

Instantly share code, notes, and snippets.

@leevigraham
Last active April 18, 2018 22:01
Show Gist options
  • Save leevigraham/11af9731e0cbeb5ce8c8e10c56766378 to your computer and use it in GitHub Desktop.
Save leevigraham/11af9731e0cbeb5ce8c8e10c56766378 to your computer and use it in GitHub Desktop.
AbstractRepository::filterByCriteria - Parses a nested array and adds expressions to a query builder
<?php
namespace App\Doctrine\ORM\Repository;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\QueryBuilder;
abstract class AbstractRepository extends ServiceEntityRepository
{
/**
* Parses a nested array and adds expressions to a query builder
*
* Example Array:
*
* Input:
*
* $criteria = [
* 'condition' => 'andX',
* 'expressions' => [
* [
* 'propertyPath' => 'taskList.project.id',
* 'operator' => 'eq',
* 'value' => '1',
* ],
* [
* 'propertyPath' => 'taskList.project.workspace.id',
* 'operator' => 'eq',
* 'value' => '1',
* ],
* ],
* ];
*
* DQL:
*
* SELECT Task
* FROM App\Entity\Task Task
* INNER JOIN Task.taskList taskList
* INNER JOIN Task.project taskList_project
* INNER JOIN Task.workspace taskList_project_workspace
* WHERE taskList_project.id = 1
* AND taskList_project_workspace.id = 1
*
* @param QueryBuilder $qb
* @param array $criteria
* @param null $alias
*/
public function filterByCriteria(QueryBuilder $qb, array $criteria, $alias = null)
{
$alias = $alias ?: $qb->getRootAliases()[0];
$expressionBuilder = $this->getEntityManager()->getExpressionBuilder();
$expressions = [];
foreach ($criteria['expressions'] as $expression) {
if (key_exists('condition', $expression)) {
$expressions[] = $this->filterByCriteria($qb, $expression, $alias);
} else {
// Get the operator and value
switch ($expression['operator']) {
case 'eq':
case 'neq':
case 'lt':
case 'lte':
case 'gt':
case 'gte':
case 'between':
case 'isNull':
case 'isNotNull':
break;
case 'startsWith':
$expression['operator'] = 'LIKE';
$expression['value'] = $expression['value'] . '%';
break;
case 'endsWith':
$expression['operator'] = 'LIKE';
$expression['value'] = '%' . $expression['value'];
break;
case 'contains':
$expression['operator'] = 'LIKE';
$expression['value'] = '%' . $expression['value'] . '%';
break;
}
// Explode the property path into it's parts
$propertyPathParts = explode('.', $expression['propertyPath']);
// Pop the property off the end
$property = array_pop($propertyPathParts);
/*
* Property is a dot delimited list.
* We need to make sure each alias is added
* These next few lines add the joins if required
*
* - task.taskList -> `$qb->join(task.taskList taskList)`
* - task.taskList.project -> `$qb->join(taskList.project taskList_project)`
* - task.taskList.project.workspace -> `$qb->oin(taskList_project.workspace taskList_project_workspace)`
*/
$joinAlias = $alias;
foreach($propertyPathParts as $count => $propertyPart) {
$join = sprintf('%s.%s', $alias, $propertyPart);
$joinAlias = implode("_", array_slice($propertyPathParts, 0, $count+1));
if(!in_array($joinAlias, $qb->getAllAliases())) {
$qb->join($join, $joinAlias);
}
}
// Now we have the final alias
// Join the alias and the property together
$expression['propertyPath'] = sprintf('%s.%s', $joinAlias, $property);
// Create the arguments
$expressionArguments = [$expression['propertyPath'], $expression['value']];
// Create the expression
$expressions[] = call_user_func_array(
[$expressionBuilder, $expression['operator']],
$expressionArguments
);
}
}
// Apply all the expressions
$expression = call_user_func_array([
$expressionBuilder,
$criteria['condition'],
], $expressions);
$qb->andWhere($expression);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment