Skip to content

Commit 8a44c05

Browse files
committed
Implemented support for in and not in
1 parent 2d0182f commit 8a44c05

File tree

2 files changed

+88
-3
lines changed

2 files changed

+88
-3
lines changed

lib/Cake/Model/Datasource/Database/Expression/QueryExpression.php

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ class QueryExpression implements Countable {
1313

1414
protected $_identifier;
1515

16+
protected $_bindingsCount = 0;
17+
18+
protected $_replaceArrayParams = false;
19+
20+
1621
public function __construct($conditions = [], $types = [], $conjunction = 'AND') {
1722
$this->_conjunction = strtoupper($conjunction);
1823
$this->_identifier = substr(spl_object_hash($this), 7, 9);
@@ -89,6 +94,10 @@ public function in($field, $values, $type = null) {
8994
return $this->add([$field . ' IN' => $values], $type ? [$field => $type] : []);
9095
}
9196

97+
public function notIn($field, $values, $type = null) {
98+
return $this->add([$field . ' NOT IN' => $values], $type ? [$field => $type] : []);
99+
}
100+
92101
/**
93102
* Associates a query placeholder to a value and a type for next execution
94103
*
@@ -101,15 +110,20 @@ public function in($field, $values, $type = null) {
101110
*/
102111
public function bind($token, $value, $type) {
103112
$param = $token;
104-
$number = count($this->_bindings);
113+
$number = $this->_bindingsCount++;
105114

106115
if (is_numeric($token)) {
107116
$param = '?';
108117
} else if ($param[0] !== ':') {
109118
$param = sprintf(':c%s%s', $this->_identifier, $number);
110119
}
111120

112-
$this->_bindings[$number] = compact('value', 'type') + [
121+
if (strpos($type, '[]') !== false) {
122+
$param = sprintf(':array%d', $number);
123+
$type = str_replace('[]', '', $type);
124+
}
125+
126+
$this->_bindings[$number] = compact('value', 'type', 'token') + [
113127
'placeholder' => substr($param, 1)
114128
];
115129
return $param;
@@ -134,6 +148,9 @@ public function __toString() {
134148
}
135149

136150
public function sql() {
151+
if ($this->_replaceArrayParams) {
152+
$this->_replaceArrays();
153+
}
137154
$conjunction = $this->_conjunction;
138155
return '(' . implode(" $conjunction ", $this->_conditions) . ')';
139156
}
@@ -188,7 +205,34 @@ protected function _parseCondition($field, $value, $types) {
188205
}
189206

190207
$type = isset($types[$expression]) ? $types[$expression] : null;
191-
return sprintf('%s %s %s', $expression, $operator, $this->bind($field, $value, $type));
208+
$template = '%s %s %s';
209+
210+
if (in_array(strtolower(trim($operator)), ['in', 'not in'])) {
211+
$type = $type ?: 'string';
212+
$type .= strpos($type, '[]') === false ? '[]' : null;
213+
$template = '%s %s (%s)';
214+
$this->_replaceArrayParams = true;
215+
}
216+
217+
return sprintf($template, $expression, $operator, $this->bind($field, $value, $type));
218+
}
219+
220+
protected function _replaceArrays() {
221+
foreach ($this->_conditions as $k => $condition) {
222+
if (!is_string($condition)) {
223+
continue;
224+
}
225+
$condition = preg_replace_callback('/(:array(\d+))/', function($match) {
226+
$params = [];
227+
$binding = $this->_bindings[$match[2]];
228+
foreach ($this->_bindings[$match[2]]['value'] as $value) {
229+
$params[] = $this->bind($binding['token'], $value, $binding['type']);
230+
}
231+
unset($this->_bindings[$match[2]]);
232+
return implode(', ', $params);
233+
}, $condition);
234+
$this->_conditions[$k] = $condition;
235+
}
192236
}
193237

194238
}

lib/Cake/Test/TestCase/Model/Datasource/Database/QueryTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,47 @@ public function testSelectWhereOperatorMethods() {
692692
->where(function($exp) { return $exp->isNotNull('visible'); })
693693
->execute();
694694
$this->assertCount(2, $result);
695+
696+
$query = new Query($this->connection);
697+
$result = $query
698+
->select(['id'])
699+
->from('dates')
700+
->where(function($exp) {
701+
return $exp->in('visible', ['Y', 'N']);
702+
})
703+
->execute();
704+
$this->assertCount(2, $result);
705+
706+
$query = new Query($this->connection);
707+
$result = $query
708+
->select(['id'])
709+
->from('dates')
710+
->where(function($exp) {
711+
return $exp->in(
712+
'posted',
713+
[new \DateTime('2012-12-21 12:00'), new \DateTime('2012-12-22 12:00')],
714+
'datetime'
715+
);
716+
})
717+
->execute();
718+
$this->assertCount(2, $result);
719+
$this->assertEquals(['id' => 1], $result->fetch('assoc'));
720+
$this->assertEquals(['id' => 2], $result->fetch('assoc'));
721+
722+
$query = new Query($this->connection);
723+
$result = $query
724+
->select(['id'])
725+
->from('dates')
726+
->where(function($exp) {
727+
return $exp->notIn(
728+
'posted',
729+
[new \DateTime('2012-12-21 12:00'), new \DateTime('2012-12-22 12:00')],
730+
'datetime'
731+
);
732+
})
733+
->execute();
734+
$this->assertCount(1, $result);
735+
$this->assertEquals(['id' => 3], $result->fetch('assoc'));
695736
}
696737

697738
}

0 commit comments

Comments
 (0)