diff --git a/include/class.orm.php b/include/class.orm.php index 7c5aaabce0886475500257e9e40bfb08ef620f1b..3ae84a2b2cf43582f6de7a983736fa54414d7ddd 100644 --- a/include/class.orm.php +++ b/include/class.orm.php @@ -289,12 +289,20 @@ class QuerySet implements IteratorAggregate, ArrayAccess { function filter() { // Multiple arrays passes means OR - $this->constraints[] = func_get_args(); + $filter = array(); + foreach (func_get_args() as $Q) { + $filter[] = $Q instanceof Q ? $Q : new Q($Q); + } + $this->constraints[] = new Q($filter, Q::ANY); return $this; } function exclude() { - $this->exclusions[] = func_get_args(); + $filter = array(); + foreach (func_get_args() as $Q) { + $filter[] = $Q instanceof Q ? $Q->negate() : Q::not($Q); + } + $this->constraints[] = new Q($filter, Q::ANY); return $this; } @@ -729,29 +737,39 @@ class SqlCompiler { return $alias; } - function compileConstraints($where, $model) { - $constraints = array(); - foreach ($where as $constraint) { - $filter = array(); - foreach ($constraint as $field=>$value) { + function compileQ(Q $Q, $model) { + $filter = array(); + foreach ($Q->constraints as $field=>$value) { + if ($value instanceof Q) { + $filter[] = $this->compileQ($value, $model); + } + else { list($field, $op) = $this->getField($field, $model); - // Allow operators to be callable rather than sprintf - // strings if ($value === null) $filter[] = sprintf('%s IS NULL', $field); + // Allow operators to be callable rather than sprintf + // strings elseif (is_callable($op)) $filter[] = call_user_func($op, $field, $value); else $filter[] = sprintf($op, $field, $this->input($value)); } - // Multiple constraints here are ANDed together - $constraints[] = implode(' AND ', $filter); } - // Multiple constrains here are ORed together - $filter = implode(' OR ', $constraints); - if (count($constraints) > 1) - $filter = '(' . $filter . ')'; - return $filter; + $glue = $Q->isOred() ? ' OR ' : ' AND '; + $clause = implode($glue, $filter); + if (count($filter) > 1) + $clause = '(' . $clause . ')'; + if ($Q->isNegated()) + $clause = ' NOT '.$clause; + return $clause; + } + + function compileConstraints($where, $model) { + $constraints = array(); + foreach ($where as $Q) { + $constraints[] = $this->compileQ($Q, $model); + } + return implode(' AND ', $constraints); } function getParams() { @@ -890,21 +908,10 @@ class MySqlCompiler extends SqlCompiler { */ protected function getWhereClause($queryset) { $model = $queryset->model; - $where_pos = array(); - $where_neg = array(); - foreach ($queryset->constraints as $where) { - $where_pos[] = $this->compileConstraints($where, $model); - } - foreach ($queryset->exclusions as $where) { - $where_neg[] = $this->compileConstraints($where, $model); - } - - $where = ''; - if ($where_pos || $where_neg) { - $where = ' WHERE '.implode(' AND ', $where_pos) - .implode(' AND NOT ', $where_neg); - } - return $where; + $where = $this->compileConstraints($queryset->constraints, $model); + if ($where) + $where = ' WHERE '.$where; + return $where ?: ''; } function compileCount($queryset) { @@ -995,7 +1002,7 @@ class MySqlCompiler extends SqlCompiler { $filter = array(); foreach ($pk as $p) $filter[$p] = $model->get($p); - $sql .= ' WHERE '.$this->compileConstraints(array($filter), $model); + $sql .= ' WHERE '.$this->compileQ(new Q($filter), $model); $sql .= ' LIMIT 1'; return new MySqlExecutor($sql, $this->params); @@ -1203,4 +1210,41 @@ class MysqlExecutor { return $this->sql; } } + +class Q { + const NEGATED = 0x0001; + const ANY = 0x0002; + + var $constraints; + var $flags; + var $negated = false; + var $ored = false; + + function __construct($filter, $flags=0) { + $this->constraints = $filter; + $this->negated = $flags & self::NEGATED; + $this->ored = $flags & self::ANY; + } + + function isNegated() { + return $this->negated; + } + + function isOred() { + return $this->ored; + } + + function negate() { + $this->negated = !$this->negated; + return $this; + } + + static function not(array $constraints) { + return new static($constraints, self::NEGATED); + } + + static function any(array $constraints) { + return new static($constraints, self::ORED); + } +} ?>