Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like OracleGrammar 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 OracleGrammar, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
9 | class OracleGrammar extends Grammar |
||
10 | { |
||
11 | use OracleReservedWords; |
||
12 | |||
13 | /** |
||
14 | * The keyword identifier wrapper format. |
||
15 | * |
||
16 | * @var string |
||
17 | */ |
||
18 | protected $wrapper = '%s'; |
||
19 | |||
20 | /** |
||
21 | * @var string |
||
22 | */ |
||
23 | protected $schema_prefix = ''; |
||
24 | |||
25 | /** |
||
26 | * Compile an exists statement into SQL. |
||
27 | * |
||
28 | * @param \Illuminate\Database\Query\Builder $query |
||
29 | * @return string |
||
30 | */ |
||
31 | public function compileExists(Builder $query) |
||
40 | |||
41 | /** |
||
42 | * Compile a select query into SQL. |
||
43 | * |
||
44 | * @param \Illuminate\Database\Query\Builder |
||
45 | * @return string |
||
46 | */ |
||
47 | public function compileSelect(Builder $query) |
||
64 | |||
65 | /** |
||
66 | * @param Builder $query |
||
67 | * @param array $components |
||
68 | * @return bool |
||
69 | */ |
||
70 | protected function isPaginationable(Builder $query, array $components) |
||
74 | |||
75 | /** |
||
76 | * Create a full ANSI offset clause for the query. |
||
77 | * |
||
78 | * @param \Illuminate\Database\Query\Builder $query |
||
79 | * @param array $components |
||
80 | * @return string |
||
81 | */ |
||
82 | protected function compileAnsiOffset(Builder $query, $components) |
||
95 | |||
96 | /** |
||
97 | * Compile the limit / offset row constraint for a query. |
||
98 | * |
||
99 | * @param \Illuminate\Database\Query\Builder $query |
||
100 | * @return string |
||
101 | */ |
||
102 | protected function compileRowConstraint($query) |
||
118 | |||
119 | /** |
||
120 | * Compile a common table expression for a query. |
||
121 | * |
||
122 | * @param string $sql |
||
123 | * @param string $constraint |
||
124 | * @param Builder $query |
||
125 | * @return string |
||
126 | */ |
||
127 | protected function compileTableExpression($sql, $constraint, $query) |
||
135 | |||
136 | /** |
||
137 | * Compile a truncate table statement into SQL. |
||
138 | * |
||
139 | * @param \Illuminate\Database\Query\Builder $query |
||
140 | * @return array |
||
141 | */ |
||
142 | public function compileTruncate(Builder $query) |
||
146 | |||
147 | /** |
||
148 | * Compile an insert and get ID statement into SQL. |
||
149 | * |
||
150 | * @param \Illuminate\Database\Query\Builder $query |
||
151 | * @param array $values |
||
152 | * @param string $sequence |
||
153 | * @return string |
||
154 | */ |
||
155 | public function compileInsertGetId(Builder $query, $values, $sequence = 'id') |
||
172 | |||
173 | /** |
||
174 | * Compile an insert statement into SQL. |
||
175 | * |
||
176 | * @param \Illuminate\Database\Query\Builder $query |
||
177 | * @param array $values |
||
178 | * @return string |
||
179 | */ |
||
180 | public function compileInsert(Builder $query, array $values) |
||
214 | |||
215 | /** |
||
216 | * Compile an insert with blob field statement into SQL. |
||
217 | * |
||
218 | * @param \Illuminate\Database\Query\Builder $query |
||
219 | * @param array $values |
||
220 | * @param array $binaries |
||
221 | * @param string $sequence |
||
222 | * @return string |
||
223 | */ |
||
224 | public function compileInsertLob(Builder $query, $values, $binaries, $sequence = 'id') |
||
255 | |||
256 | /** |
||
257 | * Compile an update statement into SQL. |
||
258 | * |
||
259 | * @param \Illuminate\Database\Query\Builder $query |
||
260 | * @param array $values |
||
261 | * @param array $binaries |
||
262 | * @param string $sequence |
||
263 | * @return string |
||
264 | */ |
||
265 | public function compileUpdateLob(Builder $query, array $values, array $binaries, $sequence = 'id') |
||
266 | { |
||
267 | $table = $this->wrapTable($query->from); |
||
268 | |||
269 | // Each one of the columns in the update statements needs to be wrapped in the |
||
270 | // keyword identifiers, also a place-holder needs to be created for each of |
||
271 | // the values in the list of bindings so we can make the sets statements. |
||
272 | $columns = []; |
||
273 | |||
274 | foreach ($values as $key => $value) { |
||
275 | $columns[] = $this->wrap($key) . ' = ' . $this->parameter($value); |
||
276 | } |
||
277 | |||
278 | $columns = implode(', ', $columns); |
||
279 | |||
280 | // set blob variables |
||
281 | if (! is_array(reset($binaries))) { |
||
282 | $binaries = [$binaries]; |
||
283 | } |
||
284 | $binaryColumns = $this->columnize(array_keys(reset($binaries))); |
||
285 | $binaryParameters = $this->parameterize(reset($binaries)); |
||
286 | |||
287 | // create EMPTY_BLOB sql for each binary |
||
288 | $binarySql = []; |
||
289 | foreach ((array) $binaryColumns as $binary) { |
||
290 | $binarySql[] = "$binary = EMPTY_BLOB()"; |
||
291 | } |
||
292 | |||
293 | // prepare binary SQLs |
||
294 | if (count($binarySql)) { |
||
295 | $binarySql = (empty($columns) ? '' : ', ') . implode(',', $binarySql); |
||
296 | } |
||
297 | |||
298 | // If the query has any "join" clauses, we will setup the joins on the builder |
||
299 | // and compile them so we can attach them to this update, as update queries |
||
300 | // can get join statements to attach to other tables when they're needed. |
||
301 | if (isset($query->joins)) { |
||
302 | $joins = ' ' . $this->compileJoins($query, $query->joins); |
||
303 | } else { |
||
304 | $joins = ''; |
||
305 | } |
||
306 | |||
307 | // Of course, update queries may also be constrained by where clauses so we'll |
||
308 | // need to compile the where clauses and attach it to the query so only the |
||
309 | // intended records are updated by the SQL statements we generate to run. |
||
310 | $where = $this->compileWheres($query); |
||
311 | |||
312 | return "update {$table}{$joins} set $columns$binarySql $where returning " . $binaryColumns . ', ' . $this->wrap($sequence) . ' into ' . $binaryParameters . ', ?'; |
||
313 | } |
||
314 | |||
315 | /** |
||
316 | * Compile the lock into SQL. |
||
317 | * |
||
318 | * @param \Illuminate\Database\Query\Builder $query |
||
319 | * @param bool|string $value |
||
320 | * @return string |
||
321 | */ |
||
322 | protected function compileLock(Builder $query, $value) |
||
323 | { |
||
324 | if (is_string($value)) { |
||
325 | return $value; |
||
326 | } |
||
327 | |||
328 | if ($value) { |
||
329 | return 'for update'; |
||
330 | } |
||
331 | |||
332 | return ''; |
||
333 | } |
||
334 | |||
335 | /** |
||
336 | * Compile the "limit" portions of the query. |
||
337 | * |
||
338 | * @param \Illuminate\Database\Query\Builder $query |
||
339 | * @param int $limit |
||
340 | * @return string |
||
341 | */ |
||
342 | protected function compileLimit(Builder $query, $limit) |
||
343 | { |
||
344 | return ''; |
||
345 | } |
||
346 | |||
347 | /** |
||
348 | * Compile the "offset" portions of the query. |
||
349 | * |
||
350 | * @param \Illuminate\Database\Query\Builder $query |
||
351 | * @param int $offset |
||
352 | * @return string |
||
353 | */ |
||
354 | protected function compileOffset(Builder $query, $offset) |
||
355 | { |
||
356 | return ''; |
||
357 | } |
||
358 | |||
359 | /** |
||
360 | * Compile a "where date" clause. |
||
361 | * |
||
362 | * @param \Illuminate\Database\Query\Builder $query |
||
363 | * @param array $where |
||
364 | * @return string |
||
365 | */ |
||
366 | protected function whereDate(Builder $query, $where) |
||
372 | |||
373 | /** |
||
374 | * Compile a date based where clause. |
||
375 | * |
||
376 | * @param string $type |
||
377 | * @param \Illuminate\Database\Query\Builder $query |
||
378 | * @param array $where |
||
379 | * @return string |
||
380 | */ |
||
381 | protected function dateBasedWhere($type, Builder $query, $where) |
||
387 | |||
388 | /** |
||
389 | * Wrap a single string in keyword identifiers. |
||
390 | * |
||
391 | * @param string $value |
||
392 | * @return string |
||
393 | */ |
||
394 | View Code Duplication | protected function wrapValue($value) |
|
402 | |||
403 | /** |
||
404 | * Return the schema prefix |
||
405 | * |
||
406 | * @return string |
||
407 | */ |
||
408 | public function getSchemaPrefix() |
||
412 | |||
413 | /** |
||
414 | * Set the shema prefix |
||
415 | * |
||
416 | * @param string $prefix |
||
417 | */ |
||
418 | public function setSchemaPrefix($prefix) |
||
422 | |||
423 | /** |
||
424 | * Wrap a table in keyword identifiers. |
||
425 | * |
||
426 | * @param \Illuminate\Database\Query\Expression|string $table |
||
427 | * @return string |
||
428 | */ |
||
429 | public function wrapTable($table) |
||
437 | } |
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.