Complex classes like DbQueryConstructor often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use DbQueryConstructor, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
17 | class DbQueryConstructor extends DbSqlAware { |
||
18 | |||
19 | const SELECT = 'SELECT'; |
||
20 | const INSERT = 'INSERT'; |
||
21 | const UPDATE = 'UPDATE'; |
||
22 | const DELETE = 'DELETE'; |
||
23 | const REPLACE = 'REPLACE'; |
||
24 | |||
25 | protected static $allowedOperations = array( |
||
26 | self::SELECT, |
||
27 | ); |
||
28 | |||
29 | /** |
||
30 | * List of array properties names that should be merged on each call of setter |
||
31 | * |
||
32 | * @var string[] $propListArrayMerge |
||
33 | */ |
||
34 | protected static $propListArrayMerge = array('fields', 'join', 'where', 'groupBy', 'orderBy', 'having'); |
||
35 | /** |
||
36 | * List of setters that will simple set property with default value TRUE |
||
37 | * |
||
38 | * @var string[] $propListSetDefaultTrue |
||
39 | */ |
||
40 | protected static $propListSetDefaultTrue = array('setFetchOne', 'setForUpdate', 'setSkipLock'); |
||
41 | |||
42 | public $operation = ''; |
||
43 | |||
44 | public $table = ''; |
||
45 | public $alias = ''; |
||
46 | |||
47 | public $idField = ''; |
||
48 | |||
49 | /** |
||
50 | * @var array |
||
51 | */ |
||
52 | public $fields = array(); |
||
53 | |||
54 | public $join = array(); |
||
55 | |||
56 | public $where = array(); |
||
57 | public $groupBy = array(); |
||
58 | public $orderBy = array(); |
||
59 | public $having = array(); |
||
60 | |||
61 | public $limit = 0; |
||
62 | public $offset = 0; |
||
63 | |||
64 | public $fetchOne = false; |
||
65 | public $forUpdate = false; |
||
66 | public $skipLock = false; |
||
67 | |||
68 | public $variables = array(); |
||
69 | |||
70 | protected $_compiledQuery = array(); |
||
71 | |||
72 | /** |
||
73 | * @param string $fieldName |
||
74 | * |
||
75 | * @return $this |
||
76 | */ |
||
77 | 2 | public function setIdField($fieldName) { |
|
82 | |||
83 | /** |
||
84 | * Sets internal variables |
||
85 | * |
||
86 | * @param array $values |
||
87 | * |
||
88 | * @return $this |
||
89 | */ |
||
90 | public function variables($values) { |
||
95 | |||
96 | |||
97 | /** |
||
98 | * @param string $alias |
||
99 | * |
||
100 | * @return $this |
||
101 | */ |
||
102 | 1 | public function setAlias($alias) { |
|
107 | |||
108 | /** |
||
109 | * @return $this |
||
110 | */ |
||
111 | 3 | public function select() { |
|
119 | |||
120 | |||
121 | /** |
||
122 | * @param string $tableName |
||
123 | * @param string $alias |
||
124 | * |
||
125 | * @return $this |
||
126 | */ |
||
127 | 2 | public function from($tableName, $alias = '') { |
|
133 | |||
134 | /** |
||
135 | * @param mixed ... |
||
136 | * |
||
137 | * @return $this |
||
138 | */ |
||
139 | 1 | public function field() { |
|
153 | |||
154 | public function fieldLiteral($field = '0', $alias = DbSqlLiteral::SQL_LITERAL_ALIAS_NONE) { |
||
157 | |||
158 | public function fieldSingleFunction($functionName, $field = '*', $alias = DbSqlLiteral::SQL_LITERAL_ALIAS_NONE) { |
||
161 | |||
162 | public function fieldCount($field = '*', $alias = DbSqlLiteral::SQL_LITERAL_ALIAS_NONE) { |
||
165 | |||
166 | public function fieldIsNull($field = '*', $alias = DbSqlLiteral::SQL_LITERAL_ALIAS_NONE) { |
||
169 | |||
170 | public function fieldMax($field = '*', $alias = DbSqlLiteral::SQL_LITERAL_ALIAS_NONE) { |
||
171 | return $this->field(DbSqlLiteral::build($this->db)->max($field, $alias)); |
||
172 | } |
||
173 | |||
174 | /** |
||
175 | * @param string $name |
||
176 | * @param mixed[] $arguments |
||
177 | * |
||
178 | * @return $this |
||
179 | */ |
||
180 | 4 | public function __call($name, $arguments) { |
|
181 | 4 | if (in_array($name, self::$propListArrayMerge)) { |
|
182 | // array_unshift($arguments, ''); |
||
183 | // $arguments[0] = &$this->$name; |
||
184 | // call_user_func_array('HelperArray::merge', $arguments); |
||
185 | 3 | HelperArray::merge($this->$name, $arguments[0], HelperArray::keyExistsOr($arguments, 1, HelperArray::MERGE_PHP)); |
|
186 | 4 | } elseif (in_array($name, self::$propListSetDefaultTrue)) { |
|
187 | 2 | $varName = lcfirst(substr($name, 3)); |
|
188 | 2 | if (!array_key_exists(0, $arguments)) { |
|
189 | 2 | $arguments[0] = true; |
|
190 | 2 | } |
|
191 | 2 | $this->$varName = $arguments[0]; |
|
192 | 2 | } |
|
193 | // TODO - make all setters protected ?? |
||
194 | // elseif(method_exists($this, $name)) { |
||
195 | // call_user_func_array(array($this, $name), $arguments); |
||
196 | // } |
||
197 | |||
198 | 4 | return $this; |
|
199 | } |
||
200 | |||
201 | /** |
||
202 | * @param int $limit |
||
203 | * |
||
204 | * @return $this |
||
205 | */ |
||
206 | 2 | public function limit($limit) { |
|
207 | 2 | $this->limit = is_numeric($limit) ? $limit : 0; |
|
208 | |||
209 | 2 | return $this; |
|
210 | } |
||
211 | |||
212 | /** |
||
213 | * @param int $offset |
||
214 | * |
||
215 | * @return $this |
||
216 | */ |
||
217 | 2 | public function offset($offset) { |
|
218 | 2 | $this->offset = is_numeric($offset) ? $offset : 0; |
|
219 | |||
220 | 2 | return $this; |
|
221 | } |
||
222 | |||
223 | /** |
||
224 | * @param string $className |
||
225 | * |
||
226 | * @return $this |
||
227 | */ |
||
228 | // TODO - rewrite |
||
229 | 2 | public function getParamsFromStaticClass($className) { |
|
230 | 2 | if (is_string($className) && $className && class_exists($className)) { |
|
231 | 2 | if(method_exists($className, 'getDb')) { |
|
232 | 2 | $this->setDb($className::getDb()); |
|
233 | 2 | } |
|
234 | 2 | $this->from($className::$_table); |
|
235 | 2 | $this->setIdField($className::$_idField); |
|
236 | 2 | } |
|
237 | |||
238 | 2 | return $this; |
|
239 | } |
||
240 | |||
241 | /** |
||
242 | * @param db_mysql|null $db |
||
243 | * @param string|object|DBStaticRecord|DbSqlAware $className |
||
244 | * |
||
245 | * @return static |
||
246 | */ |
||
247 | 1 | public static function build($db = null, $className = '') { |
|
248 | /** |
||
249 | * @var static $result |
||
250 | */ |
||
251 | 1 | $result = parent::build(null); |
|
252 | 1 | $result->getParamsFromStaticClass($className); |
|
253 | |||
254 | 1 | return $result; |
|
255 | } |
||
256 | |||
257 | /** |
||
258 | * Resets statement |
||
259 | * |
||
260 | * @param bool $full |
||
261 | * |
||
262 | * @return static |
||
263 | */ |
||
264 | 1 | protected function _reset($full = true) { |
|
265 | 1 | if ($full) { |
|
266 | 1 | $this->operation = ''; |
|
267 | 1 | $this->table = ''; |
|
268 | 1 | $this->alias = ''; |
|
269 | 1 | $this->idField = ''; |
|
270 | 1 | } |
|
271 | |||
272 | 1 | $this->fields = array(); |
|
273 | 1 | $this->where = array(); |
|
274 | 1 | $this->groupBy = array(); |
|
275 | 1 | $this->orderBy = array(); |
|
276 | 1 | $this->having = array(); |
|
277 | |||
278 | 1 | $this->limit = 0; |
|
279 | 1 | $this->offset = 0; |
|
280 | |||
281 | 1 | $this->fetchOne = false; |
|
282 | 1 | $this->forUpdate = false; |
|
283 | 1 | $this->skipLock = false; |
|
284 | |||
285 | 1 | return $this; |
|
286 | } |
||
287 | |||
288 | |||
289 | /** |
||
290 | * @param array $array |
||
291 | * |
||
292 | * @return array |
||
293 | */ |
||
294 | protected function arrayEscape(&$array) { |
||
295 | $result = array(); |
||
296 | foreach ($array as $key => &$value) { |
||
297 | $result[$key] = $this->escapeString($value); |
||
298 | } |
||
299 | |||
300 | return $result; |
||
301 | } |
||
302 | |||
303 | protected function compileOperation() { |
||
304 | if (empty($this->operation)) { |
||
305 | throw new ExceptionDbOperationEmpty(); |
||
306 | } |
||
307 | |||
308 | if (!in_array($this->operation, self::$allowedOperations)) { |
||
309 | throw new ExceptionDbOperationRestricted(); |
||
310 | } |
||
311 | |||
312 | $this->_compiledQuery[] = $this->escapeString($this->operation); |
||
313 | } |
||
314 | |||
315 | protected function compileSubject() { |
||
316 | $this->_compiledQuery[] = $this->selectFieldsToString($this->fields); |
||
317 | } |
||
318 | |||
319 | protected function compileFrom() { |
||
320 | $this->_compiledQuery[] = 'FROM `{{' . $this->escapeString($this->table) . '}}`'; |
||
321 | if (!empty($this->alias)) { |
||
322 | $this->_compiledQuery[] = 'AS `' . $this->escapeString($this->alias) . '`'; |
||
323 | } |
||
324 | } |
||
325 | |||
326 | protected function compileJoin() { |
||
327 | !empty($this->join) ? $this->_compiledQuery[] = implode(' ', $this->join) : false; |
||
328 | } |
||
329 | |||
330 | protected function compileWhere() { |
||
331 | // TODO - fields should be escaped !! |
||
332 | !empty($this->where) ? $this->_compiledQuery[] = 'WHERE ' . implode(' AND ', $this->where) : false; |
||
333 | } |
||
334 | |||
335 | protected function compileGroupBy() { |
||
340 | |||
341 | protected function compileOrderBy() { |
||
345 | |||
346 | protected function compileHaving() { |
||
350 | |||
351 | protected function compileLimit() { |
||
352 | // TODO - fields should be escaped !! |
||
353 | if ($limit = $this->fetchOne ? 1 : $this->limit) { |
||
354 | $this->_compiledQuery[] = 'LIMIT ' . $limit . (!empty($this->offset) ? ' OFFSET ' . $this->offset : ''); |
||
355 | } |
||
356 | } |
||
357 | |||
358 | protected function compileForUpdate() { |
||
359 | $this->_compiledQuery[] = |
||
360 | // forUpdate flag forces select with row locking - didn't look at skipLock flag |
||
361 | $this->forUpdate |
||
362 | || |
||
363 | // Also row locked when transaction is up and skipLock flag is not set |
||
364 | (classSupernova::db_transaction_check(false) && !$this->skipLock) ? 'FOR UPDATE' : ''; |
||
366 | |||
367 | /** |
||
368 | * @param array|mixed $fields |
||
369 | * |
||
370 | * @return string |
||
371 | * @throws ExceptionDBFieldEmpty |
||
372 | */ |
||
373 | 16 | protected function selectFieldsToString($fields) { |
|
390 | |||
391 | /** |
||
392 | * @param mixed $fieldName |
||
393 | * |
||
394 | * @return string |
||
395 | */ |
||
396 | 13 | protected function processField($fieldName) { |
|
413 | |||
414 | |||
415 | /** |
||
416 | * @return string |
||
417 | * @throws ExceptionDbOperationEmpty |
||
418 | * @throws ExceptionDbOperationRestricted |
||
419 | */ |
||
420 | 2 | public function __toString() { |
|
436 | |||
437 | /** |
||
438 | * @param bool $skip_query_check |
||
439 | * |
||
440 | * @return DbEmptyIterator|DbMysqliResultIterator |
||
441 | */ |
||
442 | public function selectIterator($skip_query_check = false) { |
||
445 | |||
446 | /** |
||
447 | * @param bool $skip_query_check |
||
448 | * |
||
449 | * @return array |
||
450 | */ |
||
451 | public function selectRow($skip_query_check = false) { |
||
456 | |||
457 | /** |
||
458 | * @param bool $skip_query_check |
||
459 | * |
||
460 | * @return mixed|null |
||
461 | */ |
||
462 | public function selectValue($skip_query_check = false) { |
||
467 | |||
468 | } |
||
469 |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.