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 DbRepository 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 DbRepository, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
10 | class DbRepository extends ManagedRepository implements RepositoryInterface |
||
11 | { |
||
12 | /** |
||
13 | * @var \Anax\Database\DatabaseQueryBuilder Database service. |
||
14 | */ |
||
15 | protected $db; |
||
16 | |||
17 | /** |
||
18 | * @var string Database table name. |
||
19 | */ |
||
20 | protected $table; |
||
21 | |||
22 | /** |
||
23 | * @var string Model class name. |
||
24 | */ |
||
25 | protected $modelClass; |
||
26 | |||
27 | /** |
||
28 | * @var string Primary key column. |
||
29 | */ |
||
30 | protected $key; |
||
31 | |||
32 | |||
33 | /** |
||
34 | * Constructor. |
||
35 | * |
||
36 | * @param \Anax\Database\DatabaseQueryBuilder $db Database service. |
||
37 | * @param string $table Database table name. |
||
38 | * @param string $modelClass Model class name. |
||
39 | * @param string $key Primary key column. |
||
40 | */ |
||
41 | 30 | public function __construct($db, $table, $modelClass, $key = 'id') |
|
48 | |||
49 | |||
50 | /** |
||
51 | * Return the name of the database table represented by the repository. |
||
52 | */ |
||
53 | 7 | public function getCollectionName() |
|
57 | |||
58 | |||
59 | /** |
||
60 | * Return the class of the model handled by the repository. |
||
61 | */ |
||
62 | 14 | public function getModelClass() |
|
66 | |||
67 | |||
68 | /** |
||
69 | * Find and return first entry by key. |
||
70 | * |
||
71 | * @param string|null $column Key column name (pass null to use registered primary key). |
||
72 | * @param mixed $value Key value. |
||
73 | * |
||
74 | * @return mixed Model instance. |
||
75 | */ |
||
76 | 18 | public function find($column, $value) |
|
80 | |||
81 | |||
82 | /** |
||
83 | * Retrieve first entry, optionally filtered by search criteria. |
||
84 | * |
||
85 | * @param string $conditions Where conditions. |
||
86 | * @param array $values Array of condition values to bind. |
||
87 | * @param array $options Query options. |
||
88 | * |
||
89 | * @return mixed Model instance. |
||
90 | */ |
||
91 | 19 | View Code Duplication | public function getFirst($conditions = null, $values = [], $options = []) |
106 | |||
107 | |||
108 | /** |
||
109 | * Retrieve all entries, optionally filtered by search criteria. |
||
110 | * |
||
111 | * @param string $conditions Where conditions. |
||
112 | * @param array $values Array of condition values to bind. |
||
113 | * @param array $options Query options. |
||
114 | * |
||
115 | * @return array Array of all matching entries. |
||
116 | */ |
||
117 | 4 | View Code Duplication | public function getAll($conditions = null, $values = [], $options = []) |
136 | |||
137 | |||
138 | /** |
||
139 | * Save entry by inserting if ID is missing and updating if ID exists. |
||
140 | * |
||
141 | * @param mixed $model Model instance. |
||
142 | * |
||
143 | * @return void |
||
144 | */ |
||
145 | 5 | public function save($model) |
|
153 | |||
154 | |||
155 | /** |
||
156 | * Delete entry. |
||
157 | * |
||
158 | * @param mixed $model Model instance. |
||
159 | */ |
||
160 | 1 | public function delete($model) |
|
168 | |||
169 | |||
170 | /** |
||
171 | * Count entries, optionally filtered by search criteria. |
||
172 | * |
||
173 | * @param string $conditions Where conditions. |
||
174 | * @param array $values Array of condition values to bind. |
||
175 | * |
||
176 | * @return int Number of entries. |
||
177 | */ |
||
178 | 4 | public function count($conditions = null, $values = []) |
|
184 | |||
185 | |||
186 | /** |
||
187 | * Execute query for selection methods. |
||
188 | * |
||
189 | * @param string $select Selection criteria. |
||
190 | * @param string $conditions Where conditions. |
||
191 | * @param array $values Array of where condition values to bind. |
||
192 | * @param array $options Query options. |
||
193 | * |
||
194 | * @return \Anax\Database\DatabaseQueryBuilder Database service instance with executed internal query. |
||
195 | */ |
||
196 | 26 | protected function executeQuery($select = null, $conditions = null, $values = [], $options = []) |
|
221 | |||
222 | |||
223 | |||
224 | /** |
||
225 | * Populate model instance including retrieved references from join query result. |
||
226 | * |
||
227 | * @param object $result Query result. |
||
228 | * |
||
229 | * @return mixed Populated model instance. |
||
230 | */ |
||
231 | 6 | protected function populateModelFromJoin($result) |
|
266 | |||
267 | |||
268 | /** |
||
269 | * Set up join query for reference retrieval. |
||
270 | * |
||
271 | * @param \Anax\Database\DatabaseQueryBuilder $query Database service instance with initialized query. |
||
272 | * @param string $select Selection criteria. |
||
273 | * @param string $conditions Where conditions. |
||
274 | * @param string $order Order by clause. |
||
275 | * |
||
276 | * @return \Anax\Database\DatabaseQueryBuilder Database service instance with prepared join query. |
||
277 | * |
||
278 | * @SuppressWarnings(PHPMD.CyclomaticComplexity) |
||
279 | * @SuppressWarnings(PHPMD.NPathComplexity) |
||
280 | */ |
||
281 | 6 | private function setupJoin($query, $select, $conditions, $order = null) |
|
337 | |||
338 | |||
339 | /** |
||
340 | * Prefix model attributes with the associated table name. |
||
341 | * |
||
342 | * @param string $input Input string. |
||
343 | * @param object $model Model instance. |
||
344 | * |
||
345 | * @return string String with table-prefixed attributes. |
||
346 | */ |
||
347 | 6 | private function prefixModelAttributes($input, $model) |
|
354 | |||
355 | |||
356 | /** |
||
357 | * Create new entry. |
||
358 | * |
||
359 | * @param mixed $model Model instance. |
||
360 | */ |
||
361 | 3 | private function create($model) |
|
370 | |||
371 | |||
372 | /** |
||
373 | * Update entry. |
||
374 | * |
||
375 | * @param mixed $model Model instance. |
||
376 | */ |
||
377 | 5 | private function update($model) |
|
388 | |||
389 | |||
390 | /** |
||
391 | * Get mutable model attributes. |
||
392 | * |
||
393 | * @param object $model Model instance. |
||
394 | * |
||
395 | * @return array Array of attributes. |
||
396 | */ |
||
397 | 5 | private function getMutableAttributes($model) |
|
411 | } |
||
412 |
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.