Complex classes like SqlSelect 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 SqlSelect, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
15 | class SqlSelect extends SqlActions |
||
16 | { |
||
17 | /** |
||
18 | * @var string $returnType PHP Type used for return result |
||
19 | */ |
||
20 | protected $returnType = ''; |
||
21 | |||
22 | /** |
||
23 | * @var object $mainTable Informations about main table. Used for FROM part |
||
24 | */ |
||
25 | protected $mainTable; |
||
26 | |||
27 | /** |
||
28 | * @var array $subQueries All sub-queries |
||
29 | */ |
||
30 | protected $subQueries = []; |
||
31 | |||
32 | /** |
||
33 | * @var array $join List of all INNER JOIN |
||
34 | */ |
||
35 | protected $join = []; |
||
36 | |||
37 | /** |
||
38 | * @var array $joinLeft List of all LEFT JOIN |
||
39 | */ |
||
40 | protected $joinLeft = []; |
||
41 | |||
42 | /** |
||
43 | * @var array $joinRight List of all RIGHT JOIN |
||
44 | */ |
||
45 | protected $joinRight = []; |
||
46 | |||
47 | /** |
||
48 | * @var string[] $order All columns used for ORDER BY part |
||
49 | */ |
||
50 | protected $order = []; |
||
51 | |||
52 | /** |
||
53 | * @var string $limit The LIMIT part |
||
54 | */ |
||
55 | protected $limit = ''; |
||
56 | |||
57 | /** |
||
58 | * @var string[] $group The GROUP BY part |
||
59 | */ |
||
60 | protected $group = []; |
||
61 | |||
62 | /** |
||
63 | * Constructor |
||
64 | * |
||
65 | * @param \BfwSql\SqlConnect $sqlConnect Instance of SGBD connexion |
||
66 | * @param string $returnType PHP type used for return result |
||
67 | */ |
||
68 | public function __construct(SqlConnect $sqlConnect, $returnType) |
||
69 | { |
||
70 | parent::__construct($sqlConnect); |
||
71 | $this->returnType = $returnType; |
||
72 | } |
||
73 | |||
74 | /** |
||
75 | * Define object used to save informations about a table |
||
76 | * This object will be used to write query |
||
77 | * |
||
78 | * @param string|array $table Tables informations |
||
79 | * |
||
80 | * @return \stdClass |
||
81 | */ |
||
82 | protected function obtainTableInfos($table) |
||
83 | { |
||
84 | if (!is_array($table) && !is_string($table)) { |
||
85 | throw new Exception('Table information is not in the right format.'); |
||
86 | } |
||
87 | |||
88 | if (is_array($table)) { |
||
89 | $tableName = reset($table); |
||
90 | $shortcut = key($table); |
||
91 | } else { |
||
92 | $tableName = $table; |
||
93 | $shortcut = null; |
||
94 | } |
||
95 | |||
96 | $prefix = $this->sqlConnect->getConnectionInfos()->tablePrefix; |
||
97 | |||
98 | return (object) [ |
||
99 | 'tableName' => $prefix.$tableName, |
||
100 | 'shortcut' => $shortcut |
||
101 | ]; |
||
102 | } |
||
103 | |||
104 | /** |
||
105 | * Add columns for select |
||
106 | * |
||
107 | * @param array|string $columns Columns to add |
||
108 | * @param string $tableName Table name for will be columns added |
||
109 | * |
||
110 | * @return void |
||
111 | */ |
||
112 | protected function addColumnsForSelect($columns, $tableName) |
||
113 | { |
||
114 | if (!is_array($columns)) { |
||
115 | $columns = (array) $columns; |
||
116 | } |
||
117 | |||
118 | foreach ($columns as $columnShortcut => $columnName) { |
||
119 | //If value is a sql function or keyword, not add quote |
||
120 | if ( |
||
121 | strpos($columnName, ' ') === false |
||
122 | && strpos($columnName, '(') === false |
||
123 | ) { |
||
124 | //Add quote only if a column has been declared |
||
125 | if ($columnName !== '*') { |
||
126 | $columnName = '`'.$columnName.'`'; |
||
127 | } |
||
128 | |||
129 | $columnName = '`'.$tableName.'`.'.$columnName; |
||
130 | } |
||
131 | |||
132 | //If a column shortcut is declared |
||
133 | if (is_string($columnShortcut)) { |
||
134 | $this->columns[] = (object) [ |
||
135 | 'column' => $columnName, |
||
136 | 'shortcut' => $columnShortcut |
||
137 | ]; |
||
138 | } else { |
||
139 | $this->columns[] = (object) [ |
||
140 | 'column' => $columnName, |
||
141 | 'shortcut' => null |
||
142 | ]; |
||
143 | } |
||
144 | } |
||
145 | } |
||
146 | |||
147 | /** |
||
148 | * Declare information for FROM part and column will be get for main table |
||
149 | * |
||
150 | * @param string|array $table Table name. |
||
151 | * It can be an array if a table shortcut is declared. |
||
152 | * In array mode, the format is ['asValue' => 'tableName'] |
||
153 | * @param string|array $columns (default: "*") Columns will be get for |
||
154 | * the table declared in first argument |
||
155 | * |
||
156 | * @return \BfwSql\SqlSelect |
||
157 | */ |
||
158 | public function from($table, $columns = '*') |
||
159 | { |
||
160 | $this->mainTable = $this->obtainTableInfos($table); |
||
161 | |||
162 | $tableName = $this->mainTable->tableName; |
||
163 | if ($this->mainTable->shortcut !== null) { |
||
164 | $tableName = $this->mainTable->shortcut; |
||
165 | } |
||
166 | |||
167 | $this->addColumnsForSelect($columns, $tableName); |
||
168 | |||
169 | return $this; |
||
170 | } |
||
171 | |||
172 | /** |
||
173 | * Add a sub-query in the SELECT part on the request |
||
174 | * |
||
175 | * @param \BfwSql\SqlActions|string $subRequest The sub-request |
||
176 | * @param string $shortcut The shortcut to use for |
||
177 | * this query in SELECT part |
||
178 | * |
||
179 | * @return \BfwSql\SqlSelect |
||
180 | */ |
||
181 | public function subQuery($subRequest, $shortcut) |
||
182 | { |
||
183 | if ( |
||
184 | is_object($subRequest) |
||
185 | && $subRequest instanceof \BfwSql\SqlActions |
||
186 | ) { |
||
187 | $subQuery = $subRequest->assemble(); |
||
188 | } elseif (is_string($subRequest)) { |
||
189 | $subQuery = $subRequest; |
||
190 | } else { |
||
191 | throw new Exception( |
||
192 | 'subRequest passed in parameters must be an instance of ' |
||
193 | .'BfwSql System or a string.' |
||
194 | ); |
||
195 | } |
||
196 | |||
197 | $this->subQueries[] = (object) [ |
||
198 | 'query' => $subQuery, |
||
199 | 'shortcut' => $shortcut |
||
200 | ]; |
||
201 | |||
202 | return $this; |
||
203 | } |
||
204 | |||
205 | /** |
||
206 | * Add a (inner|left|right) join to the request |
||
207 | * |
||
208 | * @param string $joinPropertyName The name of the property in this |
||
209 | * class where the join is add |
||
210 | * @param string|array $table Name of the table concerned by |
||
211 | * the join. Or an array with the table shortcut in key. |
||
212 | * @param string $joinOn SQL part "ON" for this join |
||
213 | * @param string|array $joinColumns Columns from the table joined to |
||
214 | * add in the SELECT part of the request |
||
215 | * |
||
216 | * @return \BfwSql\SqlSelect |
||
217 | */ |
||
218 | protected function createJoin( |
||
219 | $joinPropertyName, |
||
220 | $table, |
||
221 | $joinOn, |
||
222 | $joinColumns |
||
223 | ) { |
||
224 | $tableInfos = $this->obtainTableInfos($table); |
||
225 | $tableInfos->on = $joinOn; |
||
226 | |||
227 | $tableName = $tableInfos->tableName; |
||
228 | if ($tableInfos->shortcut !== null) { |
||
229 | $tableName = $tableInfos->shortcut; |
||
230 | } |
||
231 | |||
232 | $this->{$joinPropertyName}[] = $tableInfos; |
||
233 | $this->addColumnsForSelect($joinColumns, $tableName); |
||
234 | |||
235 | return $this; |
||
236 | } |
||
237 | |||
238 | /** |
||
239 | * Add a INNER JOIN to the request |
||
240 | * |
||
241 | * @param string|array $table Name of the table concerned by the |
||
242 | * join. Or an array with the table shortcut in key. |
||
243 | * @param string $joinOn SQL part "ON" for this join |
||
244 | * @param string|array $joinColumns Columns from the table joined to add |
||
245 | * in the SELECT part of the request |
||
246 | * |
||
247 | * @return \BfwSql\SqlSelect |
||
248 | */ |
||
249 | public function join($table, $joinOn, $joinColumns = '*') |
||
250 | { |
||
251 | return $this->createJoin('join', $table, $joinOn, $joinColumns); |
||
252 | } |
||
253 | |||
254 | /** |
||
255 | * Add a LEFT JOIN to the request |
||
256 | * |
||
257 | * @param string|array $table Name of the table concerned by the |
||
258 | * join. Or an array with the table shortcut in key. |
||
259 | * @param string $joinOn SQL part "ON" for this join |
||
260 | * @param string|array $joinColumns Columns from the table joined to add |
||
261 | * in the SELECT part of the request |
||
262 | * |
||
263 | * @return \BfwSql\SqlSelect |
||
264 | */ |
||
265 | public function joinLeft($table, $joinOn, $joinColumns = '*') |
||
266 | { |
||
267 | return $this->createJoin('joinLeft', $table, $joinOn, $joinColumns); |
||
268 | } |
||
269 | |||
270 | /** |
||
271 | * Add a RIGHT JOIN to the request |
||
272 | * |
||
273 | * @param string|array $table Name of the table concerned by the |
||
274 | * join. Or an array with the table shortcut in key. |
||
275 | * @param string $joinOn SQL part "ON" for this join |
||
276 | * @param string|array $joinColumns Columns from the table joined to add |
||
277 | * in the SELECT part of the request |
||
278 | * |
||
279 | * @return \BfwSql\SqlSelect |
||
280 | */ |
||
281 | public function joinRight($table, $joinOn, $joinColumns = '*') |
||
282 | { |
||
283 | return $this->createJoin('joinRight', $table, $joinOn, $joinColumns); |
||
284 | } |
||
285 | |||
286 | /** |
||
287 | * Add a order condition to the request for the ORDER BY part |
||
288 | * |
||
289 | * @param string $condition The new condition |
||
290 | * |
||
291 | * @return \BfwSql\SqlSelect |
||
292 | */ |
||
293 | public function order($condition) |
||
294 | { |
||
295 | $this->order[] = (string) $condition; |
||
296 | return $this; |
||
297 | } |
||
298 | |||
299 | /** |
||
300 | * Add information about the LIMIT part in request |
||
301 | * |
||
302 | * @param array|integer $limit If it's a integer, the number of row to |
||
303 | * return. If an array, the format is [numberToStart, numberOfRowToReturn] |
||
304 | * |
||
305 | * @return \BfwSql\SqlSelect |
||
306 | */ |
||
307 | public function limit($limit) |
||
308 | { |
||
309 | if (!is_array($limit)) { |
||
310 | $limit = (array) $limit; |
||
311 | } |
||
312 | |||
313 | if (isset($limit[1])) { |
||
314 | $this->limit = $limit[0].', '.$limit[1]; |
||
315 | } else { |
||
316 | $this->limit = (string) $limit[0]; |
||
317 | } |
||
318 | |||
319 | return $this; |
||
320 | } |
||
321 | |||
322 | /** |
||
323 | * Add a GROUP BY part to the request |
||
324 | * |
||
325 | * @param string $condition The condition to use in GROUP BY |
||
326 | * |
||
327 | * @return \BfwSql\SqlSelect |
||
328 | */ |
||
329 | public function group($condition) |
||
330 | { |
||
331 | $this->group[] = $condition; |
||
332 | return $this; |
||
333 | } |
||
334 | |||
335 | /** |
||
336 | * Return the PDO constant for the returnType declared |
||
337 | * |
||
338 | * @return integer |
||
339 | */ |
||
340 | protected function obtainPdoFetchType() |
||
341 | { |
||
342 | if ($this->returnType === 'object') { |
||
343 | return PDO::FETCH_OBJ; |
||
344 | } |
||
345 | |||
346 | return PDO::FETCH_ASSOC; |
||
347 | } |
||
348 | |||
349 | /** |
||
350 | * Fetch one row of the result |
||
351 | * |
||
352 | * @return mixed |
||
353 | */ |
||
354 | public function fetchRow() |
||
355 | { |
||
356 | $req = $this->execute(); |
||
357 | return $req->fetch($this->obtainPdoFetchType()); |
||
358 | } |
||
359 | |||
360 | /** |
||
361 | * Fetch all rows returned by the request |
||
362 | * |
||
363 | * @return generator |
||
364 | */ |
||
365 | public function fetchAll() |
||
366 | { |
||
367 | $request = $this->execute(); //throw an Exception if error |
||
368 | |||
369 | while ($row = $request->fetch($this->obtainPdoFetchType())) { |
||
370 | yield $row; |
||
371 | } |
||
372 | } |
||
373 | |||
374 | /** |
||
375 | * {@inheritdoc} |
||
376 | */ |
||
377 | protected function assembleRequest() |
||
391 | |||
392 | /** |
||
393 | * Write the SELECT part of the request |
||
394 | * |
||
395 | * @return string |
||
396 | */ |
||
397 | protected function generateSelect() |
||
422 | |||
423 | /** |
||
424 | * Write the FROM part of the request |
||
425 | * |
||
426 | * @return string |
||
427 | */ |
||
428 | protected function generateFrom() |
||
438 | |||
439 | /** |
||
440 | * Write a (inner|left|right) join in the request |
||
441 | * |
||
442 | * @param string $joinProperty The join property name |
||
443 | * |
||
444 | * @return string |
||
445 | */ |
||
446 | protected function generateJoin($joinProperty) |
||
472 | |||
473 | /** |
||
474 | * Write the ORDER BY part for the request |
||
475 | * |
||
476 | * @return string |
||
477 | */ |
||
478 | protected function generateOrderBy() |
||
495 | |||
496 | /** |
||
497 | * Write the GRUOP BY part for the request |
||
498 | * |
||
499 | * @return string |
||
500 | */ |
||
501 | protected function generateGroupBy() |
||
518 | |||
519 | /** |
||
520 | * Write the LIMIT part for the request |
||
521 | * |
||
522 | * @return string |
||
523 | */ |
||
524 | protected function generateLimit() |
||
533 | } |
||
534 |