1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Copyright 2009-2013, Cake Development Corporation (http://cakedc.com) |
4
|
|
|
* |
5
|
|
|
* Licensed under The MIT License |
6
|
|
|
* Redistributions of files must retain the above copyright notice. |
7
|
|
|
* |
8
|
|
|
* @copyright Copyright 2009 - 2013, Cake Development Corporation (http://cakedc.com) |
9
|
|
|
* @license MIT License (http://www.opensource.org/licenses/mit-license.php) |
10
|
|
|
*/ |
11
|
|
|
App::uses('ModelBehavior', 'Model'); |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* Searchable behavior |
15
|
|
|
* |
16
|
|
|
*/ |
17
|
|
|
class SearchableBehavior extends ModelBehavior { |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* Default settings |
21
|
|
|
* - wildcardAny: the character used instead of % (% is a normal character then) |
22
|
|
|
* - wildcardOne: the character used instead of _ (_ is a normal character then) |
23
|
|
|
* - like: auto add % wildcard to beginning, end or both (both false => user can enter wildcards himself) |
24
|
|
|
* - connectorAnd: the character between search terms to specify an "and" relationship (binds stronger than or, similar to * and + in math) |
25
|
|
|
* - connectorOr: the character between search terms to specify an "or" relationship |
26
|
|
|
* |
27
|
|
|
* @var array |
28
|
|
|
*/ |
29
|
|
|
protected $_defaults = array( |
30
|
|
|
'wildcardAny' => '*', //on windows/unix/mac/google/... thats the default one |
31
|
|
|
'wildcardOne' => '?', //on windows/unix/mac thats the default one |
32
|
|
|
'like' => array('before' => true, 'after' => true), |
33
|
|
|
'connectorAnd' => null, |
34
|
|
|
'connectorOr' => null, |
35
|
|
|
); |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* Configuration of model |
39
|
|
|
* |
40
|
|
|
* @param Model $Model |
41
|
|
|
* @param array $config |
42
|
|
|
* @return void |
43
|
|
|
*/ |
44
|
|
|
public function setup(Model $Model, $config = array()) { |
45
|
|
|
$this->_defaults = array_merge($this->_defaults, (array)Configure::read('Search.Searchable')); |
46
|
|
|
$this->settings[$Model->alias] = array_merge($this->_defaults, $config); |
47
|
|
|
if (empty($Model->filterArgs)) { |
48
|
|
|
return; |
49
|
|
|
} |
50
|
|
|
foreach ($Model->filterArgs as $key => $val) { |
51
|
|
|
if (!isset($val['name'])) { |
52
|
|
|
$Model->filterArgs[$key]['name'] = $key; |
53
|
|
|
} |
54
|
|
|
if (!isset($val['field'])) { |
55
|
|
|
$Model->filterArgs[$key]['field'] = $Model->filterArgs[$key]['name']; |
56
|
|
|
} |
57
|
|
|
if (!isset($val['type'])) { |
58
|
|
|
$Model->filterArgs[$key]['type'] = 'value'; |
59
|
|
|
} |
60
|
|
|
} |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* parseCriteria |
65
|
|
|
* parses the GET data and returns the conditions for the find('all')/paginate |
66
|
|
|
* we are just going to test if the params are legit |
67
|
|
|
* |
68
|
|
|
* @param Model $Model |
69
|
|
|
* @param array $data Criteria of key->value pairs from post/named parameters |
70
|
|
|
* @return array Array of conditions that express the conditions needed for the search |
71
|
|
|
*/ |
72
|
|
|
public function parseCriteria(Model $Model, $data) { |
73
|
|
|
$conditions = array(); |
74
|
|
|
foreach ($Model->filterArgs as $field) { |
75
|
|
|
// If this field was not passed and a default value exists, use that instead. |
76
|
|
View Code Duplication |
if (!array_key_exists($field['name'], $data) && array_key_exists('defaultValue', $field)) { |
|
|
|
|
77
|
|
|
$data[$field['name']] = $field['defaultValue']; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
if (in_array($field['type'], array('like'))) { |
81
|
|
|
$this->_addCondLike($Model, $conditions, $data, $field); |
82
|
|
|
} elseif (in_array($field['type'], array('value'))) { |
83
|
|
|
$this->_addCondValue($Model, $conditions, $data, $field); |
84
|
|
|
} elseif ($field['type'] === 'expression') { |
85
|
|
|
$this->_addCondExpression($Model, $conditions, $data, $field); |
86
|
|
|
} elseif ($field['type'] === 'query') { |
87
|
|
|
$this->_addCondQuery($Model, $conditions, $data, $field); |
88
|
|
|
} elseif ($field['type'] === 'subquery') { |
89
|
|
|
$this->_addCondSubquery($Model, $conditions, $data, $field); |
90
|
|
|
} |
91
|
|
|
} |
92
|
|
|
return $conditions; |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* Validate search |
97
|
|
|
* |
98
|
|
|
* @param Model $Model |
99
|
|
|
* @param null $data |
100
|
|
|
* @return boolean always true |
101
|
|
|
*/ |
102
|
|
|
public function validateSearch(Model $Model, $data = null) { |
103
|
|
|
if (!empty($data)) { |
104
|
|
|
$Model->set($data); |
105
|
|
|
} |
106
|
|
|
$keys = array_keys($Model->data[$Model->alias]); |
107
|
|
|
foreach ($keys as $key) { |
108
|
|
|
if (empty($Model->data[$Model->alias][$key])) { |
109
|
|
|
unset($Model->data[$Model->alias][$key]); |
110
|
|
|
} |
111
|
|
|
} |
112
|
|
|
return true; |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* filter retrieving variables only that present in Model::filterArgs |
117
|
|
|
* |
118
|
|
|
* @param Model $Model |
119
|
|
|
* @param array $vars |
120
|
|
|
* @return array, filtered args |
|
|
|
|
121
|
|
|
*/ |
122
|
|
|
public function passedArgs(Model $Model, $vars) { |
123
|
|
|
$result = array(); |
124
|
|
|
foreach ($vars as $var => $val) { |
125
|
|
|
if (in_array($var, Set::extract($Model->filterArgs, '{n}.name'))) { |
|
|
|
|
126
|
|
|
$result[$var] = $val; |
127
|
|
|
} |
128
|
|
|
} |
129
|
|
|
return $result; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Generates a query string using the same API Model::find() uses, calling the beforeFind process for the model |
134
|
|
|
* |
135
|
|
|
* @param Model $Model |
136
|
|
|
* @param string $type Type of find operation (all / first / count / neighbors / list / threaded) |
137
|
|
|
* @param array $query Option fields (conditions / fields / joins / limit / offset / order / page / group / callbacks) |
138
|
|
|
* @return array Array of records |
139
|
|
|
* @link http://book.cakephp.org/view/1018/find |
140
|
|
|
*/ |
141
|
|
|
public function getQuery(Model $Model, $type = 'first', $query = array()) { |
142
|
|
|
$Model->findQueryType = $type; |
143
|
|
|
$Model->id = $Model->getID(); |
144
|
|
|
$query = $Model->buildQuery($type, $query); |
145
|
|
|
$this->findQueryType = null; |
|
|
|
|
146
|
|
|
return $this->_queryGet($Model, $query); |
|
|
|
|
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* Clear all associations |
151
|
|
|
* |
152
|
|
|
* @param Model $Model |
153
|
|
|
* @param boolean $reset |
154
|
|
|
* @return void |
155
|
|
|
*/ |
156
|
|
|
public function unbindAllModels(Model $Model, $reset = false) { |
157
|
|
|
$assocs = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'); |
158
|
|
|
$unbind = array(); |
159
|
|
|
foreach ($assocs as $assoc) { |
160
|
|
|
$unbind[$assoc] = array_keys($Model->{$assoc}); |
161
|
|
|
} |
162
|
|
|
$Model->unbindModel($unbind, $reset); |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* For custom queries inside the model |
167
|
|
|
* example "makePhoneCondition": $cond = array('OR' => array_merge($this->condLike('cell_number', $filter), $this->condLike('landline_number', $filter, array('before' => false)))); |
168
|
|
|
* |
169
|
|
|
* @param Model $Model |
170
|
|
|
* @param $name |
171
|
|
|
* @param $data |
172
|
|
|
* @param array $field |
173
|
|
|
* @return array of conditions |
174
|
|
|
*/ |
175
|
|
|
public function condLike(Model $Model, $name, $data, $field = array()) { |
176
|
|
|
$conditions = array(); |
177
|
|
|
$field['name'] = $name; |
178
|
|
|
if (!is_array($data)) { |
179
|
|
|
$data = array($name => $data); |
180
|
|
|
} |
181
|
|
|
if (!isset($field['field'])) { |
182
|
|
|
$field['field'] = $field['name']; |
183
|
|
|
} |
184
|
|
|
return $this->_addCondLike($Model, $conditions, $data, $field); |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
/** |
188
|
|
|
* Replace substitutions with original wildcards |
189
|
|
|
* but first, escape the original wildcards in the text to use them as normal search text |
190
|
|
|
* |
191
|
|
|
* @param Model $Model |
192
|
|
|
* @param $data |
193
|
|
|
* @param array $options |
194
|
|
|
* @return string queryLikeString |
195
|
|
|
*/ |
196
|
|
|
public function formatLike(Model $Model, $data, $options = array()) { |
197
|
|
|
$options = array_merge($this->settings[$Model->alias], $options); |
198
|
|
|
$from = $to = $substFrom = $substTo = array(); |
199
|
|
View Code Duplication |
if ($options['wildcardAny'] !== '%') { |
|
|
|
|
200
|
|
|
$from[] = '%'; |
201
|
|
|
$to[] = '\%'; |
202
|
|
|
$substFrom[] = $options['wildcardAny']; |
203
|
|
|
$substTo[] = '%'; |
204
|
|
|
} |
205
|
|
View Code Duplication |
if ($options['wildcardOne'] !== '_') { |
|
|
|
|
206
|
|
|
$from[] = '_'; |
207
|
|
|
$to[] = '\_'; |
208
|
|
|
$substFrom[] = $options['wildcardOne']; |
209
|
|
|
$substTo[] = '_'; |
210
|
|
|
} |
211
|
|
|
if (!empty($from)) { |
212
|
|
|
// escape first |
213
|
|
|
$data = str_replace($from, $to, $data); |
214
|
|
|
// replace wildcards |
215
|
|
|
$data = str_replace($substFrom, $substTo, $data); |
216
|
|
|
} |
217
|
|
|
return $data; |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* Return the current chars for querying LIKE statements on this model |
222
|
|
|
* |
223
|
|
|
* @param Model $Model Reference to the model |
224
|
|
|
* @param array $options |
225
|
|
|
* @return array, [one=>..., any=>...] |
|
|
|
|
226
|
|
|
*/ |
227
|
|
|
public function getWildcards(Model $Model, $options = array()) { |
228
|
|
|
$options = array_merge($this->settings[$Model->alias], $options); |
229
|
|
|
return array('any' => $options['wildcardAny'], 'one' => $options['wildcardOne']); |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* Add Conditions based on fuzzy comparison |
234
|
|
|
* |
235
|
|
|
* @param Model $Model Reference to the model |
236
|
|
|
* @param array $conditions existing Conditions collected for the model |
237
|
|
|
* @param array $data Array of data used in search query |
238
|
|
|
* @param array $field Field definition information |
239
|
|
|
* @return array Conditions |
240
|
|
|
*/ |
241
|
|
|
protected function _addCondLike(Model $Model, &$conditions, $data, $field) { |
242
|
|
|
if (!is_array($this->settings[$Model->alias]['like'])) { |
243
|
|
|
$this->settings[$Model->alias]['like'] = array('before' => $this->settings[$Model->alias]['like'], 'after' => $this->settings[$Model->alias]['like']); |
244
|
|
|
} |
245
|
|
|
$field = array_merge($this->settings[$Model->alias]['like'], $field); |
246
|
|
|
if (empty($data[$field['name']])) { |
247
|
|
|
return $conditions; |
248
|
|
|
} |
249
|
|
|
$fieldNames = (array)$field['field']; |
250
|
|
|
|
251
|
|
|
$cond = array(); |
252
|
|
|
foreach ($fieldNames as $fieldName) { |
253
|
|
View Code Duplication |
if (strpos($fieldName, '.') === false) { |
|
|
|
|
254
|
|
|
$fieldName = $Model->alias . '.' . $fieldName; |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
if ($field['before'] === true) { |
258
|
|
|
$field['before'] = '%'; |
259
|
|
|
} |
260
|
|
|
if ($field['after'] === true) { |
261
|
|
|
$field['after'] = '%'; |
262
|
|
|
} |
263
|
|
|
//if both before and after are false, LIKE allows custom placeholders, % and _ are always treated as normal chars |
264
|
|
|
$options = $this->settings[$Model->alias]; |
265
|
|
|
$from = $to = $substFrom = $substTo = array(); |
266
|
|
View Code Duplication |
if ($options['wildcardAny'] !== '%' || ($field['before'] !== false || $field['after'] !== false)) { |
|
|
|
|
267
|
|
|
$from[] = '%'; |
268
|
|
|
$to[] = '\%'; |
269
|
|
|
} |
270
|
|
View Code Duplication |
if ($options['wildcardOne'] !== '_' || ($field['before'] !== false || $field['after'] !== false)) { |
|
|
|
|
271
|
|
|
$from[] = '_'; |
272
|
|
|
$to[] = '\_'; |
273
|
|
|
} |
274
|
|
|
$value = $data[$field['name']]; |
275
|
|
|
if (!empty($from)) { |
276
|
|
|
$value = str_replace($from, $to, $value); |
277
|
|
|
} |
278
|
|
|
if ($field['before'] === false && $field['after'] === false) { |
279
|
|
|
if ($options['wildcardAny'] !== '%') { |
280
|
|
|
$substFrom[] = $options['wildcardAny']; |
281
|
|
|
$substTo[] = '%'; |
282
|
|
|
} |
283
|
|
|
if ($options['wildcardOne'] !== '_') { |
284
|
|
|
$substFrom[] = $options['wildcardOne']; |
285
|
|
|
$substTo[] = '_'; |
286
|
|
|
} |
287
|
|
|
$value = str_replace($substFrom, $substTo, $value); |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
if (!empty($field['connectorAnd']) || !empty($field['connectorOr'])) { |
291
|
|
|
$cond[] = $this->_connectedLike($value, $field, $fieldName); |
292
|
|
|
} else { |
293
|
|
|
$cond[$fieldName . " LIKE"] = $field['before'] . $value . $field['after']; |
294
|
|
|
} |
295
|
|
|
} |
296
|
|
View Code Duplication |
if (count($cond) > 1) { |
|
|
|
|
297
|
|
|
if (isset($conditions['OR'])) { |
298
|
|
|
$conditions[]['OR'] = $cond; |
299
|
|
|
} else { |
300
|
|
|
$conditions['OR'] = $cond; |
301
|
|
|
} |
302
|
|
|
} else { |
303
|
|
|
$conditions = array_merge($conditions, $cond); |
304
|
|
|
} |
305
|
|
|
return $conditions; |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
/** |
309
|
|
|
* Form AND/OR query array using String::tokenize to separate |
310
|
|
|
* search terms by or/and connectors. |
311
|
|
|
* |
312
|
|
|
* @param mixed $value |
313
|
|
|
* @param array $field |
314
|
|
|
* @param string $fieldName |
315
|
|
|
* @return array Conditions |
316
|
|
|
*/ |
317
|
|
|
protected function _connectedLike($value, $field, $fieldName) { |
318
|
|
|
$or = array(); |
319
|
|
|
$orValues = String::tokenize($value, $field['connectorOr']); |
320
|
|
|
foreach ($orValues as $orValue) { |
321
|
|
|
$andValues = String::tokenize($orValue, $field['connectorAnd']); |
322
|
|
|
$and = array(); |
323
|
|
|
foreach ($andValues as $andValue) { |
324
|
|
|
$and[] = array($fieldName . " LIKE" => $field['before'] . $andValue . $field['after']); |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
$or[] = array('AND' => $and); |
328
|
|
|
} |
329
|
|
|
|
330
|
|
|
return array('OR' => $or); |
331
|
|
|
} |
332
|
|
|
|
333
|
|
|
/** |
334
|
|
|
* Add Conditions based on exact comparison |
335
|
|
|
* |
336
|
|
|
* @param Model $Model Reference to the model |
337
|
|
|
* @param array $conditions existing Conditions collected for the model |
338
|
|
|
* @param array $data Array of data used in search query |
339
|
|
|
* @param array $field Field definition information |
340
|
|
|
* @return array of conditions |
341
|
|
|
*/ |
342
|
|
|
protected function _addCondValue(Model $Model, &$conditions, $data, $field) { |
343
|
|
|
$fieldNames = (array)$field['field']; |
344
|
|
|
$fieldValue = isset($data[$field['name']]) ? $data[$field['name']] : null; |
345
|
|
|
|
346
|
|
|
$cond = array(); |
347
|
|
|
foreach ($fieldNames as $fieldName) { |
348
|
|
View Code Duplication |
if (strpos($fieldName, '.') === false) { |
|
|
|
|
349
|
|
|
$fieldName = $Model->alias . '.' . $fieldName; |
350
|
|
|
} |
351
|
|
|
if (is_array($fieldValue) && empty($fieldValue)) { |
352
|
|
|
continue; |
353
|
|
|
} |
354
|
|
|
if (!is_array($fieldValue) && ($fieldValue === null || $fieldValue === '' && empty($field['allowEmpty']))) { |
355
|
|
|
continue; |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
if (is_array($fieldValue) || !is_array($fieldValue) && (string)$fieldValue !== '') { |
359
|
|
|
$cond[$fieldName] = $fieldValue; |
360
|
|
|
} elseif (isset($data[$field['name']]) && !empty($field['allowEmpty'])) { |
361
|
|
|
$schema = $Model->schema($field['name']); |
362
|
|
|
if (isset($schema) && ($schema['default'] !== null || !empty($schema['null']))) { |
363
|
|
|
$cond[$fieldName] = $schema['default']; |
364
|
|
|
} elseif (!empty($fieldValue)) { |
365
|
|
|
$cond[$fieldName] = $fieldValue; |
366
|
|
|
} else { |
367
|
|
|
$cond[$fieldName] = $fieldValue; |
368
|
|
|
} |
369
|
|
|
} |
370
|
|
|
} |
371
|
|
View Code Duplication |
if (count($cond) > 1) { |
|
|
|
|
372
|
|
|
if (isset($conditions['OR'])) { |
373
|
|
|
$conditions[]['OR'] = $cond; |
374
|
|
|
} else { |
375
|
|
|
$conditions['OR'] = $cond; |
376
|
|
|
} |
377
|
|
|
} else { |
378
|
|
|
$conditions = array_merge($conditions, $cond); |
379
|
|
|
} |
380
|
|
|
return $conditions; |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
/** |
384
|
|
|
* Add Conditions based expressions to search conditions. |
385
|
|
|
* |
386
|
|
|
* @param Model $Model Instance of AppModel |
387
|
|
|
* @param array $conditions Existing conditions. |
388
|
|
|
* @param array $data Data for a field. |
389
|
|
|
* @param array $field Info for field. |
390
|
|
|
* @return array of conditions modified by this method |
391
|
|
|
*/ |
392
|
|
|
protected function _addCondExpression(Model $Model, &$conditions, $data, $field) { |
393
|
|
|
$fieldName = $field['field']; |
394
|
|
|
|
395
|
|
|
if ((method_exists($Model, $field['method']) || $this->_checkBehaviorMethods($Model, $field['method'])) && (!empty($field['allowEmpty']) || !empty($data[$field['name']]) || (isset($data[$field['name']]) && (string)$data[$field['name']] !== ''))) { |
396
|
|
|
$fieldValues = $Model->{$field['method']}($data, $field); |
397
|
|
|
if (!empty($conditions[$fieldName]) && is_array($conditions[$fieldName])) { |
398
|
|
|
$conditions[$fieldName] = array_unique(array_merge(array($conditions[$fieldName]), array($fieldValues))); |
399
|
|
|
} else { |
400
|
|
|
$conditions[$fieldName] = $fieldValues; |
401
|
|
|
} |
402
|
|
|
} |
403
|
|
|
return $conditions; |
404
|
|
|
} |
405
|
|
|
|
406
|
|
|
/** |
407
|
|
|
* Add Conditions based query to search conditions. |
408
|
|
|
* |
409
|
|
|
* @param Model $Model Instance of AppModel |
410
|
|
|
* @param array $conditions Existing conditions. |
411
|
|
|
* @param array $data Data for a field. |
412
|
|
|
* @param array $field Info for field. |
413
|
|
|
* @return array of conditions modified by this method |
414
|
|
|
*/ |
415
|
|
|
protected function _addCondQuery(Model $Model, &$conditions, $data, $field) { |
416
|
|
|
if ((method_exists($Model, $field['method']) || $this->_checkBehaviorMethods($Model, $field['method'])) && (!empty($field['allowEmpty']) || !empty($data[$field['name']]) || (isset($data[$field['name']]) && (string)$data[$field['name']] !== ''))) { |
417
|
|
|
$conditionsAdd = $Model->{$field['method']}($data, $field); |
418
|
|
|
// if our conditions function returns something empty, nothing to merge in |
419
|
|
|
if (!empty($conditionsAdd)) { |
420
|
|
|
$conditions = Set::merge($conditions, (array)$conditionsAdd); |
421
|
|
|
} |
422
|
|
|
} |
423
|
|
|
return $conditions; |
424
|
|
|
} |
425
|
|
|
|
426
|
|
|
/** |
427
|
|
|
* Add Conditions based subquery to search conditions. |
428
|
|
|
* |
429
|
|
|
* @param Model $Model Instance of AppModel |
430
|
|
|
* @param array $conditions Existing conditions. |
431
|
|
|
* @param array $data Data for a field. |
432
|
|
|
* @param array $field Info for field. |
433
|
|
|
* @return array of conditions modified by this method |
434
|
|
|
*/ |
435
|
|
|
protected function _addCondSubquery(Model $Model, &$conditions, $data, $field) { |
436
|
|
|
$fieldName = $field['field']; |
437
|
|
|
if ((method_exists($Model, $field['method']) || $this->_checkBehaviorMethods($Model, $field['method'])) && (!empty($field['allowEmpty']) || !empty($data[$field['name']]) || (isset($data[$field['name']]) && (string)$data[$field['name']] !== ''))) { |
438
|
|
|
$subquery = $Model->{$field['method']}($data, $field); |
439
|
|
|
// if our subquery function returns something empty, nothing to merge in |
440
|
|
|
if (!empty($subquery)) { |
441
|
|
|
$conditions[] = $Model->getDataSource()->expression("$fieldName in ($subquery)"); |
|
|
|
|
442
|
|
|
} |
443
|
|
|
} |
444
|
|
|
return $conditions; |
445
|
|
|
} |
446
|
|
|
|
447
|
|
|
/** |
448
|
|
|
* Helper method for getQuery. |
449
|
|
|
* extension of dbo source method. Create association query. |
450
|
|
|
* |
451
|
|
|
* @param Model $Model |
452
|
|
|
* @param array $queryData |
453
|
|
|
* @return string |
454
|
|
|
*/ |
455
|
|
|
protected function _queryGet(Model $Model, $queryData = array()) { |
456
|
|
|
/** @var DboSource $db */ |
457
|
|
|
$db = $Model->getDataSource(); |
458
|
|
|
$queryData = $this->_scrubQueryData($queryData); |
459
|
|
|
$recursive = null; |
460
|
|
|
$byPass = false; |
461
|
|
|
$null = null; |
462
|
|
|
$linkedModels = array(); |
463
|
|
|
|
464
|
|
|
if (isset($queryData['recursive'])) { |
465
|
|
|
$recursive = $queryData['recursive']; |
466
|
|
|
} |
467
|
|
|
|
468
|
|
|
if ($recursive !== null) { |
469
|
|
|
$_recursive = $Model->recursive; |
|
|
|
|
470
|
|
|
$Model->recursive = $recursive; |
471
|
|
|
} |
472
|
|
|
|
473
|
|
|
if (!empty($queryData['fields'])) { |
474
|
|
|
$byPass = true; |
475
|
|
|
$queryData['fields'] = $db->fields($Model, null, $queryData['fields']); |
476
|
|
|
} else { |
477
|
|
|
$queryData['fields'] = $db->fields($Model); |
478
|
|
|
} |
479
|
|
|
|
480
|
|
|
$_associations = $Model->associations(); |
481
|
|
|
|
482
|
|
|
if ($Model->recursive == -1) { |
483
|
|
|
$_associations = array(); |
484
|
|
|
} elseif ($Model->recursive == 0) { |
485
|
|
|
unset($_associations[2], $_associations[3]); |
486
|
|
|
} |
487
|
|
|
|
488
|
|
|
foreach ($_associations as $type) { |
489
|
|
|
foreach ($Model->{$type} as $assoc => $assocData) { |
490
|
|
|
$linkModel = $Model->{$assoc}; |
491
|
|
|
$external = isset($assocData['external']); |
492
|
|
|
|
493
|
|
|
$linkModel->getDataSource(); |
494
|
|
|
if ($Model->useDbConfig === $linkModel->useDbConfig) { |
495
|
|
|
if ($byPass) { |
496
|
|
|
$assocData['fields'] = false; |
497
|
|
|
} |
498
|
|
|
if ($db->generateAssociationQuery($Model, $linkModel, $type, $assoc, $assocData, $queryData, $external, $null) === true) { |
|
|
|
|
499
|
|
|
$linkedModels[$type . '/' . $assoc] = true; |
500
|
|
|
} |
501
|
|
|
} |
502
|
|
|
} |
503
|
|
|
} |
504
|
|
|
|
505
|
|
|
return trim($db->generateAssociationQuery($Model, null, null, null, null, $queryData, false, $null)); |
|
|
|
|
506
|
|
|
} |
507
|
|
|
|
508
|
|
|
/** |
509
|
|
|
* Private helper method to remove query metadata in given data array. |
510
|
|
|
* |
511
|
|
|
* @param array $data |
512
|
|
|
* @return array |
513
|
|
|
*/ |
514
|
|
|
protected function _scrubQueryData($data) { |
515
|
|
|
static $base = null; |
516
|
|
|
if ($base === null) { |
517
|
|
|
$base = array_fill_keys(array('conditions', 'fields', 'joins', 'order', 'limit', 'offset', 'group'), array()); |
518
|
|
|
} |
519
|
|
|
return (array)$data + $base; |
520
|
|
|
} |
521
|
|
|
|
522
|
|
|
/** |
523
|
|
|
* Check if model have some method in attached behaviors |
524
|
|
|
* |
525
|
|
|
* @param Model $Model |
526
|
|
|
* @param string $method |
527
|
|
|
* @return boolean, true if method exists in attached and enabled behaviors |
|
|
|
|
528
|
|
|
*/ |
529
|
|
|
protected function _checkBehaviorMethods(Model $Model, $method) { |
530
|
|
|
$behaviors = $Model->Behaviors->enabled(); |
531
|
|
|
$count = count($behaviors); |
532
|
|
|
$found = false; |
533
|
|
|
for ($i = 0; $i < $count; $i++) { |
534
|
|
|
$name = $behaviors[$i]; |
535
|
|
|
$methods = get_class_methods($Model->Behaviors->{$name}); |
536
|
|
|
$check = array_flip($methods); |
537
|
|
|
$found = isset($check[$method]); |
538
|
|
|
if ($found) { |
539
|
|
|
return true; |
540
|
|
|
} |
541
|
|
|
} |
542
|
|
|
return $found; |
543
|
|
|
} |
544
|
|
|
|
545
|
|
|
} |
546
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.