/[drupal]/contributions/sandbox/crell/pdo/database_query_builder.inc
ViewVC logotype

Contents of /contributions/sandbox/crell/pdo/database_query_builder.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.14 - (show annotations) (download) (as text)
Fri Feb 15 06:15:53 2008 UTC (21 months, 1 week ago) by chx
Branch: MAIN
CVS Tags: HEAD
Changes since 1.13: +15 -40 lines
File MIME type: text/x-php
removing remove
1 <?php
2
3 class Query implements QueryConditionInterface {
4
5 protected $queryId;
6 protected $connection;
7 protected $queryOptions;
8
9 protected $condition;
10
11 public function __construct($query_id, DatabaseConnection $connection, $options) {
12 $this->queryId = $query_id;
13 $this->connection = $connection;
14 $this->queryOptions = $options;
15
16 $this->condition = new DB_Condition('AND');
17 }
18
19 function condition($field, $operator = NULL, $value = NULL, $num_args = NULL) {
20 if (!isset($num_args)) {
21 $num_args = func_num_args();
22 }
23 $this->condition->condition($field, $operator, $value, $num_args);
24 return $this;
25 }
26
27 function conditions() {
28 return $this->condition->conditions();
29 }
30
31 function values() {
32 return $this->condition->values();
33 }
34
35 public function where($snippet, $args) {
36 $this->condition->where($snippet, $args);
37 return $this;
38 }
39
40 protected function parseWhere($array) {
41 foreach ($array as $key => $value) {
42 if (is_array($value)) {
43 $array[$key] = $this->parseWhere($value);
44 }
45 }
46 $conjunction = ' '. $array['#conjunction'] .' ';
47 unset($array['#conjunction']);
48 return '('. implode($conjunction, $array) .')';
49 }
50 }
51
52 class InsertQuery extends Query {
53
54 protected $table;
55 protected $delay;
56 protected $insertFields = array();
57 protected $insertValues = array();
58
59 protected $connection;
60
61 public function __construct($query_id, $connection, $table, Array $options = array()) {
62 $options['return_affected'] = TRUE;
63 $options += array('delay' => FALSE);
64 parent::__construct($query_id, $connection, $options);
65 $this->table = $table;
66 }
67
68 public function fields(Array $fields) {
69 if (empty($this->insertFields)) {
70 $this->insertFields = array_keys($fields);
71 }
72 $this->insertValues[] = array_values($fields);
73
74 return $this;
75 }
76
77 public function execute() {
78 //drupal_alter('query', $this->queryId, $this);
79
80 // Each insert happens in its own query in the degenerate case. However,
81 // we wrap it in a transaction so that it is atomic where possible. On many
82 // databases, such as SQLite, this is also a notable performance boost.
83 $transaction = $this->connection->startTransaction();
84 foreach ($this->insertValues as $insert_values) {
85 $num_affected = $this->connection->runQuery((string)$this, $insert_values, $this->queryOptions);
86 }
87 $transaction->commit();
88
89 return $this->connection->lastInsertId();
90 }
91
92 public function __toString() {
93 $placeholders = array_fill(0, count($this->insertFields), '?');
94
95 return 'INSERT INTO {' . $this->connection->escapeTable($this->table) . '} (' . implode(', ', $this->insertFields) . ') VALUES (' . implode(', ', $placeholders) . ')';
96 }
97
98 }
99
100 class DeleteQuery extends Query {
101
102 protected $table;
103 protected $connection;
104
105 public function __construct($query_id, DatabaseConnection $connection, $table, Array $options = array()) {
106 $options['return_affected'] = TRUE;
107 $options += array('delay' => FALSE);
108 parent::__construct($query_id, $connection, $options);
109 $this->table = $table;
110 $this->connection = $connection;
111 }
112
113 public function execute() {
114 //drupal_alter('query', $this->queryId, $this);
115 $values = array();
116 if ($this->condition->conditions()) {
117 $values = $this->condition->values();
118 }
119
120 return $this->connection->runQuery((string)$this, $values, $this->queryOptions);
121 }
122
123 public function __toString() {
124 $query = 'DELETE FROM {'. $this->connection->escapeTable($this->table) .'} ';
125
126 $conditions = $this->condition->conditions();
127 if ($conditions) {
128 $query .= "\nWHERE ". $this->parseWhere($conditions);
129 }
130
131 return $query;
132 }
133 }
134
135 class UpdateQuery extends Query {
136
137 protected $table;
138 protected $delay;
139 protected $fields;
140 protected $values;
141
142 public function __construct($query_id, DatabaseConnection $connection, $table, Array $options = array()) {
143 $options['return_affected'] = TRUE;
144 $options += array('delay' => FALSE);
145 parent::__construct($query_id, $connection, $options);
146 $this->table = $table;
147 }
148
149 public function fields(Array $fields) {
150 // Parse out the fields into placeholders and values. We need to do this
151 // on assignment to avoid having to double-iterate, once to get
152 // the query string and once to get the values array.
153 $max_placeholder = 0;
154 $this->fields = array();
155 foreach ($fields as $field => $value) {
156 $placeholder = ':db_update_placeholder_'. ($max_placeholder++);
157 $this->fields[] = $field . '='. $placeholder;
158 $this->values[$placeholder] = $value;
159 }
160
161 return $this;
162 }
163
164 public function execute() {
165 //drupal_alter('query', $this->queryId, $this);
166 $update_values = $this->values;
167
168 $conditions = $this->condition->conditions();
169 if ($conditions) {
170 $update_values = array_merge($update_values, $this->condition->values());
171 }
172
173 return $this->connection->runQuery((string)$this, $update_values, $this->queryOptions);
174 }
175
176 public function __toString() {
177 $query = 'UPDATE {'. $this->connection->escapeTable($this->table) .'} SET '. implode(', ', $this->fields);
178
179 $conditions = $this->condition->conditions();
180 if ($conditions) {
181 $query .= "\nWHERE ". $this->parseWhere($conditions);
182 }
183
184 return $query;
185 }
186
187 }
188
189 class SelectQuery extends Query {
190
191 protected $fields = array();
192
193 protected $tables = array();
194
195 protected $values = array();
196
197 protected $order = array();
198
199 protected $group = array();
200
201 protected $having;
202
203 protected $distinct = FALSE;
204
205 protected $limits;
206
207 public function __construct($query_id, DatabaseConnection $connection, $options = array()) {
208 parent::__construct($query_id, $connection, $options);
209 $this->having = new DB_Condition('AND');
210 }
211
212 public function execute() {
213 //drupal_alter('query', $this->queryId, $this);
214
215 if ($this->limit) {
216 return $this->connection->queryRange((string)$this, $this->values, $this->limit['start'], $this->limit['length'], $this->queryOptions);
217 }
218
219 return $this->connection->runQuery($query, $this->values, $this->queryOptions);
220 }
221
222 public function countQuery() {
223 $count_query = clone($this);
224
225 $count_query->setFields('COUNT(*)');
226
227 return $count_query;
228 }
229
230 public function distinct($distinct = TRUE) {
231 $this->distinct = $distinct;
232 return $this;
233 }
234
235 public function fields() {
236 $args = func_get_args();
237 $this->fields = array_merge($this->fields, $args);
238 return $this;
239 }
240
241 public function setFields() {
242 $this->fields = func_get_args();
243 return $this;
244 }
245
246 public function join($table, $alias, $condition = NULL, $values = array()) {
247 return $this->addJoin('INNER', $table, $alias, $condition, $values);
248 }
249
250 public function innerJoin($table, $alias, $condition = NULL, $values = array()) {
251 return $this->addJoin('INNER', $table, $alias, $condition, $values);
252 }
253
254 public function leftJoin($table, $alias, $condition = NULL, $values = array()) {
255 return $this->addJoin('LEFT OUTER', $table, $alias, $condition, $values);
256 }
257
258 public function rightJoin($table, $alias, $condition = NULL, $values = array()) {
259 return $this->addJoin('RIGHT OUTER', $table, $alias, $condition, $values);
260 }
261
262 protected function addJoin($type, $table, $alias, $condition = NULL, $values = array()) {
263 if (!isset($conditions)) {
264 // Check the Schema API and build a direct equals on the appropriate foreign keys.
265 }
266
267 $this->tables[] = array(
268 'type' => "$type JOIN ",
269 'table' => $table,
270 'alias' => $alias,
271 'condition' => $condition,
272 );
273
274 return $this;
275 }
276
277 public function orderBy($field, $direction = 'ASC') {
278 $this->order[$field] = $direction;
279 return $this;
280 }
281
282 public function limit($start, $length) {
283 $this->limit = array('start' => $start, 'length' => $length);
284 return $this;
285 }
286
287 public function groupBy($field) {
288 $this->group[] = $field;
289 }
290
291 function havingCondition($field, $operator = NULL, $value = NULL, $num_args = NULL) {
292 if (!isset($num_args)) {
293 $num_args = func_num_args();
294 }
295 $this->having->condition($field, $operator, $value, $num_args);
296 return $this;
297 }
298
299 public function having($snippet, $args) {
300 $this->having->where($snippet, $args);
301 return $this;
302 }
303 public function __toString() {
304
305 // SELECT
306 $query = 'SELECT ';
307 if ($this->distinct) {
308 $query .= 'DISTINCT ';
309 }
310
311 // FIELDS
312 $query .= implode(', ', $this->fields);
313
314 // FROM
315 $tables = $this->tables;
316 $query .= "\nFROM ". $this->connection->escapeTable($tables[0]['table']) .' AS '. $tables[0]['alias'];
317 unset($tables[0]);
318 foreach ($tables as $params) {
319 $query .= "\n". $params['type'] .' JOIN '. $this->connection->escapeTable($params['table']) .' AS '. $params['alias'] .' ON '. $params['condition'];
320 }
321
322 // WHERE
323 $conditions = $this->condition->conditions();
324 if ($conditions) {
325 $query .= "\nWHERE ". $this->parseWhere($conditions);
326 }
327
328 // GROUP BY
329 if ($this->group) {
330 $query .= "\nGROUP BY " . implode(', ', $this->group);
331 }
332
333 // HAVING
334 $conditions = $this->having->conditions();
335 if ($conditions) {
336 $query .= "\nHAVING ". $this->parseWhere($conditions);
337 }
338
339 // ORDER BY
340 if ($this->order) {
341 $query .= "\nORDER BY ";
342 foreach ($this->order as $field => $direction) {
343 $query .= $field .' '. $direction .' ';
344 }
345 }
346
347 // LIMIT is database specific, so we can't do it here.
348
349 return $query;
350 }
351
352 }
353
354
355 interface QueryConditionInterface {
356 function condition($field, $operator = NULL, $value = NULL, $num_args = NULL);
357 function where($snippet, $args);
358 function conditions();
359 function values();
360 }
361
362 class Db_Condition implements QueryConditionInterface {
363
364 protected $conditions = array();
365 protected $values = array();
366
367 function __construct($conjunction) {
368 $this->conditions['#conjunction'] = $conjunction;
369 }
370
371 function condition($field, $operator = NULL, $value = NULL, $num_args = NULL) {
372 if (!isset($num_args)) {
373 $num_args = func_num_args();
374 }
375 list($condition, $value) = $this->_db_condition($field, $operator, $value, $num_args);
376 $this->conditions[] = $condition;
377 $this->values += $value;
378 return $this;
379 }
380
381 public function where($snippet, $args) {
382 $this->conditions[] = $snippet;
383 $this->values += $args;
384 return $this;
385 }
386
387 function conditions() {
388 // If there's only one entry, it's just the default #type, which means
389 // we don't really have any conditions to speak of.
390 return (count($this->conditions) > 1) ? $this->conditions : array();
391 }
392
393 function values() {
394 return $this->values;
395 }
396
397 protected function placeholderMultiple($count, $delimiter, $values) {
398 static $max_placeholder = 0;
399 $new_placeholder = $max_placeholder + $count;
400 for ($i = $max_placeholder; $i < $new_placeholder; ++$i) {
401 $placeholder = ':db_placeholder_multiple_'. $i;
402 list(, $arguments[$placeholder]) = each($values);
403 $placeholders[] = $placeholder;
404 }
405 $max_placeholder = $new_placeholder;
406 return array(implode($delimiter, $placeholders), $arguments);
407 }
408
409 protected function _db_condition($field, $operator, $value, $process_type) {
410 static $max_placeholder = 0;
411 static $specials = array(
412 'BETWEEN' => array('delimiter' => ' AND '),
413 'IN' => array('delimiter' => ', ', 'prefix' => ' (', 'postfix' => ')'),
414 'NOT IN' => array('delimiter' => ', ', 'prefix' => ' (', 'postfix' => ')'),
415 );
416 switch ($process_type) {
417 case 1:
418 return array($field->conditions(), $field->values());
419 case 2:
420 $value = $operator;
421 $operator = '=';
422 case 3:
423 $return = "$field $operator";
424 if (isset($specials[$operator]['prefix'])) {
425 $return .= $specials[$operator]['prefix'];
426 }
427 if ($value instanceOf SelectQuery) {
428 $values = $value->values();
429 // There is an implicit string cast on $value, since SelectQuery implements __toString().
430 $placeholder = '('. $value .')';
431 }
432 else {
433 $count = count($value);
434 if ($count == 1) {
435 $placeholder = ':db_placeholder_'. ($max_placeholder++);
436 $values = array($placeholder => $value);
437 }
438 elseif (isset($specials[$operator])) {
439 list($placeholder, $values) = $this->placeholderMultiple($count, $specials[$operator]['delimiter'], $value);
440 }
441 else {
442 // What are you doing here?
443 }
444 }
445 $return .= " $placeholder";
446 if (isset($specials[$operator]['postfix'])) {
447 $return .= $specials[$operator]['postfix'];
448 }
449 return array($return, $values);
450 }
451 }
452 }
453
454 function db_or() {
455 return new DB_Condition('OR');
456 }
457
458 function db_and() {
459 return new DB_Condition('AND');
460 }
461
462 function db_xor() {
463 return new DB_Condition('XOR');
464 }
465

  ViewVC Help
Powered by ViewVC 1.1.2