1
|
|
|
<?php defined('SYSPATH') OR die('No direct script access.'); |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* A class to create queries for selecting data for jam models from the database |
5
|
|
|
* |
6
|
|
|
* @package Jam |
7
|
|
|
* @category Associations |
8
|
|
|
* @author Ivan Kerin |
9
|
|
|
* @copyright (c) 2011-2012 Despark Ltd. |
10
|
|
|
* @license http://www.opensource.org/licenses/isc-license.txt |
11
|
|
|
*/ |
12
|
|
|
abstract class Kohana_Jam_Query_Builder_Select extends Database_Query_Builder_Select { |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Create object of class Jam_Query_Builder_Select |
16
|
|
|
* @param string $model |
17
|
|
|
* @return Jam_Query_Builder_Select |
18
|
|
|
*/ |
19
|
1 |
|
public static function factory($model) |
20
|
|
|
{ |
21
|
1 |
|
return new Jam_Query_Builder_Select($model); |
22
|
|
|
} |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @var Jam_Meta The meta object (if found) that is attached to this builder |
26
|
|
|
*/ |
27
|
|
|
protected $_meta = NULL; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* A store for user defined values for the builder |
31
|
|
|
* @var array |
32
|
|
|
*/ |
33
|
|
|
protected $_params = array(); |
34
|
|
|
|
35
|
|
|
protected static $_modifiable = array('select', 'from', 'join', 'where', 'group_by', 'having', 'order_by', 'union', 'distinct', 'limit', 'offset', 'parameters'); |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* Constructs a new Jam_Builder instance. |
39
|
|
|
* |
40
|
|
|
* $model is not actually allowed to be NULL. It has |
41
|
|
|
* a default because PHP throws strict errors otherwise. |
42
|
|
|
* |
43
|
|
|
* @throws Kohana_Exception |
44
|
|
|
* @param string|null $model |
45
|
|
|
* @param mixed|null $key |
|
|
|
|
46
|
|
|
*/ |
47
|
140 |
|
public function __construct($model) |
48
|
|
|
{ |
49
|
140 |
|
parent::__construct(); |
50
|
|
|
|
51
|
140 |
|
$this->_meta = Jam::meta($model); |
52
|
|
|
|
53
|
140 |
|
if ( ! $this->_meta) |
54
|
|
|
throw new Kohana_Exception('There is no model :model for select', array(':model' => $model)); |
55
|
|
|
|
56
|
140 |
|
$this->meta()->events()->trigger('builder.after_construct', $this); |
57
|
140 |
|
} |
58
|
|
|
|
59
|
22 |
|
public function where_key($unique_key) |
60
|
|
|
{ |
61
|
22 |
|
Jam_Query_Builder::find_by_primary_key($this, $unique_key); |
62
|
|
|
|
63
|
22 |
|
return $this; |
64
|
|
|
} |
65
|
|
|
|
66
|
91 |
|
public function compile($db = NULL) |
67
|
|
|
{ |
68
|
91 |
|
if ($db === NULL AND $this->meta()) |
69
|
|
|
{ |
70
|
43 |
|
$db = Database::instance($this->meta()->db()); |
|
|
|
|
71
|
|
|
} |
72
|
|
|
|
73
|
91 |
|
$original_select = $this->_select; |
74
|
91 |
|
$original_from = $this->_from; |
75
|
|
|
|
76
|
91 |
|
if (empty($this->_from)) |
77
|
|
|
{ |
78
|
89 |
|
$this->_from[] = $this->meta()->model(); |
79
|
|
|
} |
80
|
|
|
|
81
|
91 |
|
if (empty($this->_select)) |
82
|
|
|
{ |
83
|
78 |
|
$this->_select[] = $this->meta()->table().'.*'; |
84
|
|
|
} |
85
|
|
|
|
86
|
91 |
|
foreach ($this->_from as & $from) |
87
|
|
|
{ |
88
|
91 |
|
$from = Jam_Query_Builder::resolve_table_alias($from); |
89
|
|
|
} |
90
|
|
|
|
91
|
91 |
|
foreach ($this->_select as & $attribute) |
92
|
|
|
{ |
93
|
91 |
|
$attribute = Jam_Query_Builder::resolve_attribute_name($attribute, $this->meta()->model()); |
94
|
|
|
} |
95
|
|
|
|
96
|
91 |
|
$this->meta()->events()->trigger('builder.before_select', $this); |
97
|
|
|
|
98
|
91 |
|
$result = parent::compile($db); |
99
|
|
|
|
100
|
91 |
|
$this->meta()->events()->trigger('builder.after_select', $this); |
101
|
|
|
|
102
|
91 |
|
$this->_select = $original_select; |
103
|
91 |
|
$this->_from = $original_from; |
104
|
|
|
|
105
|
91 |
|
return $result; |
106
|
|
|
} |
107
|
|
|
|
108
|
52 |
|
public function execute($db = NULL, $as_object = NULL, $object_params = NULL) |
109
|
|
|
{ |
110
|
52 |
|
if ($db === NULL AND $this->meta()) |
111
|
|
|
{ |
112
|
52 |
|
$db = Database::instance($this->meta()->db()); |
|
|
|
|
113
|
|
|
} |
114
|
|
|
|
115
|
52 |
|
return parent::execute($db, $as_object, $object_params); |
116
|
|
|
} |
117
|
|
|
|
118
|
19 |
|
protected function _join($association, $type, $resolve_table_model = TRUE) |
119
|
|
|
{ |
120
|
19 |
|
$join_key = is_array($association) ? join(':', $association) : $association; |
121
|
|
|
|
122
|
19 |
|
if ( ! isset($this->_join[$join_key])) |
123
|
|
|
{ |
124
|
19 |
|
$join = Jam_Query_Builder::resolve_join($association, $type, $this->meta()->model(), $resolve_table_model); |
125
|
|
|
|
126
|
19 |
|
$this->_join[$join_key] = $join; |
127
|
|
|
|
128
|
19 |
|
return $join; |
129
|
|
|
} |
130
|
|
|
else |
131
|
|
|
{ |
132
|
1 |
|
return $this->_join[$join_key]; |
133
|
|
|
} |
134
|
|
|
} |
135
|
|
|
|
136
|
8 |
|
public function join($association, $type = NULL) |
137
|
|
|
{ |
138
|
8 |
|
$this->_last_join = $this->_join($association, $type); |
139
|
|
|
|
140
|
8 |
|
return $this; |
141
|
|
|
} |
142
|
|
|
|
143
|
1 |
|
public function on($c1, $op, $c2) |
144
|
|
|
{ |
145
|
1 |
|
if ( ! $this->_last_join) |
146
|
|
|
throw new Kohana_Exception('You must specifiy a JOIN first!'); |
147
|
|
|
|
148
|
1 |
|
return parent::on($c1, $op, $c2); |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
|
152
|
1 |
|
public function join_nested($association, $type = NULL) |
153
|
|
|
{ |
154
|
1 |
|
return $this->_join($association, $type)->end($this); |
155
|
|
|
} |
156
|
|
|
|
157
|
10 |
|
public function join_table($table, $type = NULL) |
158
|
|
|
{ |
159
|
10 |
|
return $this->_join($table, $type, FALSE)->end($this); |
160
|
|
|
} |
161
|
|
|
|
162
|
32 |
|
protected function _compile_order_by(Database $db, array $order_by) |
163
|
|
|
{ |
164
|
32 |
|
foreach ($order_by as & $order) |
165
|
|
|
{ |
166
|
32 |
|
$order[0] = Jam_Query_Builder::resolve_attribute_name($order[0], $this->meta()->model()); |
167
|
|
|
} |
168
|
|
|
|
169
|
32 |
|
return parent::_compile_order_by($db, $order_by); |
170
|
|
|
} |
171
|
|
|
|
172
|
1 |
|
protected function _compile_group_by(Database $db, array $group_by) |
173
|
|
|
{ |
174
|
1 |
|
foreach ($group_by as & $group) |
175
|
|
|
{ |
176
|
1 |
|
$group = Jam_Query_Builder::resolve_attribute_name($group, $this->meta()->model()); |
177
|
|
|
} |
178
|
|
|
|
179
|
1 |
|
return parent::_compile_group_by($db, $group_by); |
180
|
|
|
} |
181
|
|
|
|
182
|
74 |
|
protected function _compile_conditions(Database $db, array $conditions) |
183
|
|
|
{ |
184
|
74 |
|
foreach ($conditions as & $group) |
185
|
|
|
{ |
186
|
74 |
|
foreach ($group as & $condition) |
187
|
|
|
{ |
188
|
74 |
|
if (is_array($condition)) |
189
|
|
|
{ |
190
|
74 |
|
$condition[0] = Jam_Query_Builder::resolve_attribute_name($condition[0], $this->meta()->model(), $condition[2]); |
191
|
|
|
} |
192
|
|
|
} |
193
|
|
|
} |
194
|
|
|
|
195
|
74 |
|
return parent::_compile_conditions($db, $conditions); |
196
|
|
|
} |
197
|
|
|
|
198
|
16 |
|
public function aggregate_query($function, $column = NULL) |
199
|
|
|
{ |
200
|
16 |
|
if ($column === NULL OR $column === '*') |
201
|
|
|
{ |
202
|
12 |
|
$column = '*'; |
203
|
|
|
} |
204
|
|
|
else |
205
|
|
|
{ |
206
|
4 |
|
$db = Database::instance($this->meta()->db()); |
|
|
|
|
207
|
4 |
|
$column = Jam_Query_Builder::resolve_attribute_name($column, $this->meta()->model()); |
208
|
4 |
|
$column = $db->quote_column($column); |
209
|
|
|
} |
210
|
|
|
|
211
|
16 |
|
$count = clone $this; |
212
|
16 |
|
return $count->select(array(DB::expr("{$function}({$column})"), 'result')); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
public function aggregate($function, $column = NULL) |
216
|
|
|
{ |
217
|
|
|
return $this->aggregate_query($function, $column)->execute()->get('result'); |
218
|
|
|
} |
219
|
|
|
|
220
|
10 |
|
public function count_all($without_grouping = FALSE) |
221
|
|
|
{ |
222
|
10 |
|
$query = $this->aggregate_query('COUNT'); |
223
|
|
|
|
224
|
10 |
|
$query->except('order_by'); |
225
|
|
|
|
226
|
10 |
|
if ($without_grouping) |
227
|
|
|
{ |
228
|
|
|
$query->except('group_by', 'limit', 'offset'); |
229
|
|
|
} |
230
|
|
|
|
231
|
10 |
|
return (int) $query->execute()->get('result'); |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
public function count_with_subquery() |
235
|
|
|
{ |
236
|
|
|
$query = clone $this; |
237
|
|
|
$query->except('order_by'); |
238
|
|
|
return (int) DB::select(array(DB::expr('COUNT(*)'), 'result'))->from(array($query, 'result_table'))->execute($query->meta()->db())->get('result'); |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
/** |
242
|
|
|
* Passes unknown methods along to the behaviors. |
243
|
|
|
* |
244
|
|
|
* @param string $method |
245
|
|
|
* @param array $args |
246
|
|
|
* @return mixed |
247
|
|
|
**/ |
248
|
40 |
|
public function __call($method, $args) |
249
|
|
|
{ |
250
|
40 |
|
$return = $this->_meta->events()->trigger_callback('builder', $this, $method, $args); |
251
|
32 |
|
return ($return !== NULL) ? $return : $this; |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
/** |
255
|
|
|
* Getter/setter for the params array used to store arbitrary values by the behaviors |
256
|
|
|
* |
257
|
|
|
* @param array|string $params |
258
|
|
|
* @param mixed $param |
259
|
|
|
* @return Jam_Builder $this |
260
|
|
|
*/ |
261
|
29 |
|
public function params($params = NULL, $param = NULL) |
262
|
|
|
{ |
263
|
|
|
// Accept params('name', 'param'); |
264
|
29 |
|
if ($param !== NULL) |
265
|
|
|
{ |
266
|
2 |
|
$params = array($params => $param); |
267
|
|
|
} |
268
|
|
|
|
269
|
29 |
|
if (is_array($params)) |
270
|
|
|
{ |
271
|
2 |
|
$this->_params = Arr::merge($params, $this->_params); |
272
|
2 |
|
return $this; |
|
|
|
|
273
|
|
|
} |
274
|
|
|
|
275
|
29 |
|
if (is_string($params)) |
276
|
|
|
{ |
277
|
29 |
|
return Arr::get($this->_params, $params); |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
return $this->_params; |
|
|
|
|
281
|
|
|
} |
282
|
|
|
|
283
|
140 |
|
public function meta() |
284
|
|
|
{ |
285
|
140 |
|
return $this->_meta; |
286
|
|
|
} |
287
|
|
|
|
288
|
|
|
/** |
289
|
|
|
* You can get some of the parameters of the jam query builder. |
290
|
|
|
* |
291
|
|
|
* @param string $name one of select, from, join, where, group_by, having, order_by, union, distinct, limit, offset, parameters |
292
|
|
|
* @return mixed |
293
|
|
|
*/ |
294
|
30 |
|
public function __get($name) |
295
|
|
|
{ |
296
|
30 |
|
if ( ! in_array($name, Jam_Query_Builder_Select::$_modifiable)) |
|
|
|
|
297
|
|
|
throw new Kohana_Exception('You cannot get :name, only :modifiable', array(':name' => $name, ':modifiable' => join(', ', Jam_Query_Builder_Select::$_modifiable))); |
|
|
|
|
298
|
|
|
|
299
|
30 |
|
return $this->{'_'.$name}; |
300
|
|
|
} |
301
|
|
|
|
302
|
43 |
|
public function __toString() |
303
|
|
|
{ |
304
|
|
|
try |
305
|
|
|
{ |
306
|
|
|
// Return the SQL string |
307
|
43 |
|
return $this->compile(); |
308
|
|
|
} |
309
|
|
|
catch (Exception $e) |
310
|
|
|
{ |
311
|
|
|
return Kohana_Exception::text($e); |
312
|
|
|
} |
313
|
|
|
} |
314
|
|
|
|
315
|
14 |
|
public function except($name) |
|
|
|
|
316
|
|
|
{ |
317
|
14 |
|
$except = func_get_args(); |
318
|
|
|
|
319
|
14 |
|
if ($not_modifiable = array_diff($except, Jam_Query_Builder_Select::$_modifiable)) |
|
|
|
|
320
|
1 |
|
throw new Kohana_Exception('You cannot modify :not_modifiable, only :modifiable', array(':not_modifiable' => join(', ', $not_modifiable), ':modifiable' => join(', ', Jam_Query_Builder_Select::$_modifiable))); |
|
|
|
|
321
|
|
|
|
322
|
13 |
|
$new = new Database_Query_Builder_Select; |
323
|
|
|
|
324
|
13 |
|
foreach ($except as $name) |
325
|
|
|
{ |
326
|
13 |
|
$this->{'_'.$name} = $new->{'_'.$name}; |
327
|
|
|
} |
328
|
|
|
|
329
|
13 |
|
return $this; |
330
|
|
|
} |
331
|
|
|
} |
332
|
|
|
|
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.
Consider the following example. The parameter
$italy
is not defined by the methodfinale(...)
.The most likely cause is that the parameter was removed, but the annotation was not.