diff --git a/src/ZucchiDoctrine/Query/QueryBuilderTrait.php b/src/ZucchiDoctrine/Query/QueryBuilderTrait.php index cb9d31f..1803de9 100755 --- a/src/ZucchiDoctrine/Query/QueryBuilderTrait.php +++ b/src/ZucchiDoctrine/Query/QueryBuilderTrait.php @@ -37,131 +37,163 @@ protected function addWhere($qb, $where) { // process the $where if (is_string($where)) { - // straight DQL string + // @todo: test and provide security for straight DQL string $qb->andWhere($where); + } elseif (is_array($where) && count($where)) { - // create where expression - $whereExp = $qb->expr()->andx(); - $params = array(); + + $params = array(); // initialise container to store params + + $whereExp = $this->getParts($qb, $where, $params); - // index for the parameters - $i = 0; + // only add where expression if actually has parts + if (count($whereExp->getParts())) { + $qb->where($whereExp); + } - // loop through all the clauses supplied - foreach ($where as $col => $val) { - - if ((is_array($val) && (!isset($val['value']) || (is_string($val['value']) && strlen($val['value']) == 0))) || - (is_string($val) && (!$val || strlen($val) == 0)) - ){ - // skip if invalid value; - continue; - } - - // check if we've been provided with an operator as well as a value - if (!is_array($val)) { - $operator = Expr\Comparison::EQ; - $val = $val; - } elseif (count($val) == 1) { - $operator = Expr\Comparison::EQ; - $val = end($val); - } else { - $operator = isset($val['operator']) ? $val['operator'] : Expr\Comparison::EQ; - $val = array_key_exists('value', $val) ? $val['value'] : array(); - - } - - // set the alias to the default - $alias = $this->alias; + // set the params from the where clause above + $qb->setParameters($params); + } + + return $this; + } - // if col relates to a relation i.e. Role.id - // then perform a join and set up the alias and column names - if (strpos($col, '.') !== false) { - $parts = explode('.', $col); - $col = array_pop($parts); - $par = $this->alias; - - foreach ($parts AS $rel) { - $alias = strtolower($rel); - $jt = new Expr\Join(Expr\Join::LEFT_JOIN, $par . '.' . $rel, $alias); - if (!strpos($qb->getDql(), $jt->__toString()) !== false) { - $qb->leftJoin($par . '.' . $rel, $alias); - } - $par = $alias; + + protected function getParts($qb, $where, &$params = array()) + { + if (array_key_exists('mode', $where) && $where['mode'] == 'or') { + $whereExp = $qb->expr()->orX(); + } else { + $whereExp = $qb->expr()->andX(); + } + unset($where['mode']); // unset to allow flat iteration for simpler structures + + // index for the parameters + $i = count($params); + + if (array_key_exists('expressions', $where)) { + foreach($where['expressions'] as $expression) { + $part = $this->getParts($qb, $expression, $params); + $whereExp->add($part); + } + unset($where['expressions']); // unset to allow flat iteration for simpler structures + } + + // set fields to iterate over if no fields property assume using remaining elements in $where + $fields = (array_key_exists('fields', $where)) ? $where['fields'] : $where; + + // loop through all the clauses supplied + foreach ($fields as $col => $val) { + + if ((is_array($val) && (!isset($val['value']) || (is_string($val['value']) && strlen($val['value']) == 0))) || + (is_string($val) && (!$val || strlen($val) == 0)) + ){ + // skip if invalid value; + continue; + } + + // check if we've been provided with an operator as well as a value + if (!is_array($val)) { + $operator = Expr\Comparison::EQ; + $val = $val; + } elseif (count($val) == 1) { + $operator = Expr\Comparison::EQ; + $val = end($val); + } else { + $operator = isset($val['operator']) ? $val['operator'] : Expr\Comparison::EQ; + $val = array_key_exists('value', $val) ? $val['value'] : array(); + + } + + // set the alias to the default + $alias = $this->alias; + + // if col relates to a relation i.e. Role.id + // then perform a join and set up the alias and column names + if (strpos($col, '.') !== false) { + $parts = explode('.', $col); + $col = array_pop($parts); + $par = $this->alias; + + foreach ($parts AS $rel) { + $alias = strtolower($rel); + $jt = new Expr\Join(Expr\Join::LEFT_JOIN, $par . '.' . $rel, $alias); + if (!strpos($qb->getDql(), $jt->__toString()) !== false) { + $qb->leftJoin($par . '.' . $rel, $alias); } + $par = $alias; } + } - if ($val instanceof Expr\Base) { - $whereExp->add($val); - } else { - // process sets a little differently - if (!is_array($val)) { - $val = array($val); - } - switch ($operator) { - case 'regexp': - $whereExp->add("REGEXP(" . $alias . '.' . $col . ",'" . $val[0] . "') = 1"); - break; - case 'between': - if (count($val) == 2) { - // $value should now be an array with 2 values - $expr = new Expr(); - $from = (is_int($val[0])) ? $val[0] : "'" . $val[0] . "'"; - $to = (is_int($val[1])) ? $val[1] : "'" . $val[1] . "'"; - - $stmt = $expr->between($alias . '.' . $col, $from, $to); - $whereExp->add($stmt); - } - break; - case 'is': + if ($val instanceof Expr\Base) { + $whereExp->add($val); + } else { + // process sets a little differently + if (!is_array($val)) { + $val = array($val); + } + switch (strtolower($operator)) { + case 'regexp': + $whereExp->add("REGEXP(" . $alias . '.' . $col . ",'" . $val[0] . "') = 1"); + break; + case 'between': + if (count($val) == 2) { + // $value should now be an array with 2 values $expr = new Expr(); - $method = 'is' . ucfirst($val[0]); - if (method_exists($expr, $method)) { - $stmt = $expr->{$method}($alias . '.' . $col); - $whereExp->add($stmt); - } - break; - default: - // this holds the subquery for this field, each component being an OR - $subWhereExp = $qb->expr()->orX(); - - foreach ($val as $value) { - if ($value == null) { - $cmpValue = 'NULL'; - } else { - $cmpValue = '?' . $i; - - // wrap LIKE values - if ($operator == 'like') { - $value = '%' . trim($value, '%') . '%'; - } - - // add the parameter value into the parameters stack - $params[$i] = $value; - $i++; + $from = (is_int($val[0])) ? $val[0] : "'" . $val[0] . "'"; + $to = (is_int($val[1])) ? $val[1] : "'" . $val[1] . "'"; + + $stmt = $expr->between($alias . '.' . $col, $from, $to); + $whereExp->add($stmt); + } + break; + case 'is': + $expr = new Expr(); + $method = 'is' . ucfirst($val[0]); + if (method_exists($expr, $method)) { + $stmt = $expr->{$method}($alias . '.' . $col); + $whereExp->add($stmt); + } + break; + default: + // this holds the subquery for this field, each component being an OR + $subWhereExp = $qb->expr()->orX(); + + foreach ($val as $value) { + if ($value == null) { + $cmpValue = 'NULL'; + } else { + $cmpValue = '?' . $i; + + // wrap IN/NOT IN values with parenthesis + if ($operator == 'in' || $operator == 'not in') { + $cmpValue = '(' . trim($cmpValue, ')') . ')'; } - $comparison = new Expr\Comparison($alias . '.' . $col, $operator, $cmpValue); - $subWhereExp->add($comparison); + // wrap LIKE values + if ($operator == 'like') { + $value = '%' . trim($value, '%') . '%'; + } + + // add the parameter value into the parameters stack + $params[$i] = $value; + $i++; } - // add in the subquery as an AND - $whereExp->add($subWhereExp); - break; + $comparison = new Expr\Comparison($alias . '.' . $col, $operator, $cmpValue); + $subWhereExp->add($comparison); + } + + // add in the subquery as an AND + $whereExp->add($subWhereExp); + break; - } } } - - // only add where expression if actually has parts - if (count($whereExp->getParts())) { - $qb->where($whereExp); - } - - // set the params from the where clause above - $qb->setParameters($params); } - - return $this; + + return $whereExp; + } /**