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 SchemaBuilder 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 SchemaBuilder, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
28 | class SchemaBuilder extends Component |
||
29 | { |
||
30 | /** |
||
31 | * @var RecordSchema[] |
||
32 | */ |
||
33 | private $records = []; |
||
34 | |||
35 | /** |
||
36 | * @var AbstractTable[] |
||
37 | */ |
||
38 | private $tables = []; |
||
39 | |||
40 | /** |
||
41 | * @var ORMConfig |
||
42 | */ |
||
43 | protected $config = null; |
||
44 | |||
45 | /** |
||
46 | * @invisible |
||
47 | * @var ORM |
||
48 | */ |
||
49 | protected $orm = null; |
||
50 | |||
51 | /** |
||
52 | * @param ORMConfig $config |
||
53 | * @param ORM $orm |
||
54 | * @param ClassLocatorInterface $locator |
||
55 | */ |
||
56 | public function __construct(ORMConfig $config, ORM $orm, ClassLocatorInterface $locator) |
||
57 | { |
||
58 | $this->config = $config; |
||
59 | $this->orm = $orm; |
||
60 | |||
61 | //Locating all models and sources |
||
62 | $this->locateRecords($locator)->locateSources($locator); |
||
63 | |||
64 | //Casting relations |
||
65 | $this->castSchemas(); |
||
66 | } |
||
67 | |||
68 | /** |
||
69 | * Check if Record class known to schema builder. |
||
70 | * |
||
71 | * @param string $class |
||
72 | * @return bool |
||
73 | */ |
||
74 | public function hasRecord($class) |
||
78 | |||
79 | /** |
||
80 | * Instance of RecordSchema associated with given class name. |
||
81 | * |
||
82 | * @param string $class |
||
83 | * @return RecordSchema |
||
84 | * @throws SchemaException |
||
85 | * @throws RecordSchemaException |
||
86 | */ |
||
87 | public function record($class) |
||
100 | |||
101 | /** |
||
102 | * @return RecordSchema[] |
||
103 | */ |
||
104 | public function getRecords() |
||
108 | |||
109 | /** |
||
110 | * Check if given table was declared by one of record or relation. |
||
111 | * |
||
112 | * @param string $database Table database. |
||
113 | * @param string $table Table name without prefix. |
||
114 | * @return bool |
||
115 | */ |
||
116 | public function hasTable($database, $table) |
||
120 | |||
121 | /** |
||
122 | * Request table schema. Every non empty table schema will be synchronized with it's databases |
||
123 | * when executeSchema() method will be called. |
||
124 | * |
||
125 | * Attention, every declared table will be synced with database if their initiator allows such |
||
126 | * operation. |
||
127 | * |
||
128 | * @param string $database Table database. |
||
129 | * @param string $table Table name without prefix. |
||
130 | * @return AbstractTable |
||
131 | */ |
||
132 | public function declareTable($database, $table) |
||
144 | |||
145 | /** |
||
146 | * Perform schema reflection to database(s). All declared tables will created or altered. Only |
||
147 | * tables linked to non abstract records and record with active schema parameter will be |
||
148 | * executed. |
||
149 | * |
||
150 | * SchemaBuilder will not allow (SchemaException) to create or alter tables columns declared |
||
151 | * by abstract or records with ACTIVE_SCHEMA constant set to false. ActiveSchema still can |
||
152 | * declare foreign keys and indexes (most of relations automatically request index or foreign |
||
153 | * key), but they are going to be ignored. |
||
154 | * |
||
155 | * Due principals of database schemas and ORM component logic no data or columns will ever be |
||
156 | * removed from database. In addition column renaming will cause creation of another column. |
||
157 | * |
||
158 | * Use database migrations to solve more complex database questions. Or disable ACTIVE_SCHEMA |
||
159 | * and live like normal people. |
||
160 | * |
||
161 | * @throws SchemaException |
||
162 | * @throws \Spiral\Database\Exceptions\SchemaException |
||
163 | * @throws \Spiral\Database\Exceptions\QueryException |
||
164 | * @throws \Spiral\Database\Exceptions\DriverException |
||
165 | */ |
||
166 | public function synchronizeSchema() |
||
173 | |||
174 | /** |
||
175 | * Resolve real database name using it's alias. |
||
176 | * |
||
177 | * @see DatabaseProvider |
||
178 | * @param string|null $alias |
||
179 | * @return string |
||
180 | */ |
||
181 | public function resolveDatabase($alias) |
||
185 | |||
186 | /** |
||
187 | * Get all mutators associated with field type. |
||
188 | * |
||
189 | * @param string $type Field type. |
||
190 | * @return array |
||
191 | */ |
||
192 | public function getMutators($type) |
||
196 | |||
197 | /** |
||
198 | * Get mutator alias if presented. Aliases used to simplify schema (accessors) definition. |
||
199 | * |
||
200 | * @param string $alias |
||
201 | * @return string|array |
||
202 | */ |
||
203 | public function mutatorAlias($alias) |
||
207 | |||
208 | /** |
||
209 | * Normalize record schema in lighter structure to be saved in ORM component memory. |
||
210 | * |
||
211 | * @return array |
||
212 | * @throws SchemaException |
||
213 | */ |
||
214 | public function normalizeSchema() |
||
244 | |||
245 | /** |
||
246 | * Create appropriate instance of RelationSchema based on it's definition provided by ORM Record |
||
247 | * or manually. Due internal format first definition key will be stated as definition type and |
||
248 | * key value as record/entity definition relates too. |
||
249 | * |
||
250 | * @param RecordSchema $record |
||
251 | * @param string $name |
||
252 | * @param array $definition |
||
253 | * @return RelationInterface |
||
254 | * @throws SchemaException |
||
255 | */ |
||
256 | public function relationSchema(RecordSchema $record, $name, array $definition) |
||
278 | |||
279 | /** |
||
280 | * Get list of tables to be updated, method must automatically check if table actually allowed |
||
281 | * to be updated. |
||
282 | * |
||
283 | * @return AbstractTable[] |
||
284 | */ |
||
285 | public function getTables() |
||
309 | |||
310 | /** |
||
311 | * Locate every available Record class. |
||
312 | * |
||
313 | * @param ClassLocatorInterface $locator |
||
314 | * @return $this |
||
315 | * @throws SchemaException |
||
316 | */ |
||
317 | protected function locateRecords(ClassLocatorInterface $locator) |
||
350 | |||
351 | /** |
||
352 | * Locate ORM entities sources. |
||
353 | * |
||
354 | * @param ClassLocatorInterface $locator |
||
355 | * @return $this |
||
356 | */ |
||
357 | View Code Duplication | protected function locateSources(ClassLocatorInterface $locator) |
|
374 | |||
375 | /** |
||
376 | * SchemaBuilder will request every located RecordSchema to declare it's schemas and relations. |
||
377 | * In addition this methods will create inversed set of relations. |
||
378 | * |
||
379 | * @throws SchemaException |
||
380 | * @throws RelationSchemaException |
||
381 | * @throws RecordSchemaException |
||
382 | */ |
||
383 | protected function castSchemas() |
||
416 | |||
417 | /** |
||
418 | * Find record related to given table. This operation is required to catch if some |
||
419 | * relation/schema declared values in passive (no altering) table. Might return if no records |
||
420 | * find (pivot or user specified tables). |
||
421 | * |
||
422 | * @param AbstractTable $table |
||
423 | * @return RecordSchema|null |
||
424 | */ |
||
425 | private function findRecord(AbstractTable $table) |
||
436 | |||
437 | /** |
||
438 | * Normalize and pack every declared record relation schema. |
||
439 | * |
||
440 | * @param RecordSchema $record |
||
441 | * @return array |
||
442 | */ |
||
443 | private function packRelations(RecordSchema $record) |
||
452 | } |
||
453 |
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.