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 RecordEntity 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 RecordEntity, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
41 | class RecordEntity extends SchematicEntity implements RecordInterface |
||
42 | { |
||
43 | use SaturateTrait; |
||
44 | |||
45 | /** |
||
46 | * We are going to inherit parent validation rules, this will let spiral translator know about |
||
47 | * it and merge i18n messages. |
||
48 | * |
||
49 | * @see TranslatorTrait |
||
50 | */ |
||
51 | const I18N_INHERIT_MESSAGES = true; |
||
52 | |||
53 | /** |
||
54 | * Field format declares how entity must process magic setters and getters. Available values: |
||
55 | * camelCase, tableize. |
||
56 | */ |
||
57 | const FIELD_FORMAT = 'tableize'; |
||
58 | |||
59 | /** |
||
60 | * ORM records are be divided by two sections: active and passive records. When record is active |
||
61 | * ORM allowed to modify associated record table using declared schema and created relations. |
||
62 | * |
||
63 | * Passive records (ACTIVE_SCHEMA = false) however can only read table schema from database and |
||
64 | * forbidden to do any schema modification either by record or by relations. |
||
65 | * |
||
66 | * You can use ACTIVE_SCHEMA = false in cases where you need to create an ActiveRecord for |
||
67 | * existed table. |
||
68 | * |
||
69 | * @see RecordSchema |
||
70 | * @see \Spiral\ORM\Entities\SchemaBuilder |
||
71 | */ |
||
72 | const ACTIVE_SCHEMA = true; |
||
73 | |||
74 | /** |
||
75 | * Indication that record were deleted. |
||
76 | */ |
||
77 | const DELETED = 900; |
||
78 | |||
79 | /** |
||
80 | * Default ORM relation types, see ORM configuration and documentation for more information, |
||
81 | * i had to remove 200 lines of comments to make record little bit smaller. |
||
82 | * |
||
83 | * @see RelationSchemaInterface |
||
84 | * @see RelationSchema |
||
85 | */ |
||
86 | const HAS_ONE = 101; |
||
87 | const HAS_MANY = 102; |
||
88 | const BELONGS_TO = 103; |
||
89 | const MANY_TO_MANY = 104; |
||
90 | |||
91 | /** |
||
92 | * Morphed relation types are usually created by inversion or equivalent of primary relation |
||
93 | * types. |
||
94 | * |
||
95 | * @see RelationSchemaInterface |
||
96 | * @see RelationSchema |
||
97 | * @see MorphedRelation |
||
98 | */ |
||
99 | const BELONGS_TO_MORPHED = 108; |
||
100 | const MANY_TO_MORPHED = 109; |
||
101 | |||
102 | /** |
||
103 | * Constants used to declare relations in record schema, used in normalized relation schema. |
||
104 | * |
||
105 | * @see RelationSchemaInterface |
||
106 | */ |
||
107 | const OUTER_KEY = 901; //Outer key name |
||
108 | const INNER_KEY = 902; //Inner key name |
||
109 | const MORPH_KEY = 903; //Morph key name |
||
110 | const PIVOT_TABLE = 904; //Pivot table name |
||
111 | const PIVOT_COLUMNS = 905; //Pre-defined pivot table columns |
||
112 | const PIVOT_DEFAULTS = 906; //Pre-defined pivot table default values |
||
113 | const THOUGHT_INNER_KEY = 907; //Pivot table options |
||
114 | const THOUGHT_OUTER_KEY = 908; //Pivot table options |
||
115 | const WHERE = 909; //Where conditions |
||
116 | const WHERE_PIVOT = 910; //Where pivot conditions |
||
117 | |||
118 | /** |
||
119 | * Additional constants used to control relation schema behaviour. |
||
120 | * |
||
121 | * @see Record::$schema |
||
122 | * @see RelationSchemaInterface |
||
123 | */ |
||
124 | const INVERSE = 1001; //Relation should be inverted to parent record |
||
125 | const CONSTRAINT = 1002; //Relation should create foreign keys (default) |
||
126 | const CONSTRAINT_ACTION = 1003; //Default relation foreign key delete/update action (CASCADE) |
||
127 | const CREATE_PIVOT = 1004; //Many-to-Many should create pivot table automatically (default) |
||
128 | const NULLABLE = 1005; //Relation can be nullable (default) |
||
129 | const CREATE_INDEXES = 1006; //Indication that relation is allowed to create required indexes |
||
130 | const MORPHED_ALIASES = 1007; //Aliases for morphed sub-relations |
||
131 | |||
132 | /** |
||
133 | * Relations marked as embedded will be automatically saved/validated with parent model. In |
||
134 | * addition such models data can be set using setFields method (only for ONE relations). |
||
135 | * |
||
136 | * @see setFields() |
||
137 | * @see save() |
||
138 | * @see validate() |
||
139 | */ |
||
140 | const EMBEDDED_RELATION = 1008; |
||
141 | |||
142 | /** |
||
143 | * Constants used to declare indexes in record schema. |
||
144 | * |
||
145 | * @see Record::$indexes |
||
146 | */ |
||
147 | const INDEX = 1000; //Default index type |
||
148 | const UNIQUE = 2000; //Unique index definition |
||
149 | |||
150 | /** |
||
151 | * Indicates that record data were loaded from database (not recently created). |
||
152 | * |
||
153 | * @var bool |
||
154 | */ |
||
155 | private $recordState = false; |
||
156 | |||
157 | /** |
||
158 | * Errors in relations and accessors. |
||
159 | * |
||
160 | * @var array |
||
161 | */ |
||
162 | private $innerErrors = []; |
||
163 | |||
164 | /** |
||
165 | * SolidState will force record data to be saved as one big update set without any generating |
||
166 | * separate update statements for changed columns. |
||
167 | * |
||
168 | * @var bool |
||
169 | */ |
||
170 | private $solidState = false; |
||
171 | |||
172 | /** |
||
173 | * Record field updates (changed values). |
||
174 | * |
||
175 | * @var array |
||
176 | */ |
||
177 | private $updates = []; |
||
178 | |||
179 | /** |
||
180 | * Constructed and pre-cached set of record relations. Relation will be in a form of data array |
||
181 | * to be created on demand. |
||
182 | * |
||
183 | * @see relation() |
||
184 | * @see __call() |
||
185 | * @see __set() |
||
186 | * @see __get() |
||
187 | * |
||
188 | * @var RelationInterface[]|array |
||
189 | */ |
||
190 | protected $relations = []; |
||
191 | |||
192 | /** |
||
193 | * @invisible |
||
194 | * |
||
195 | * @var ORMInterface|ORM |
||
196 | */ |
||
197 | protected $orm = null; |
||
198 | |||
199 | /** |
||
200 | * Schema provided by ORM component. |
||
201 | * |
||
202 | * @var array |
||
203 | */ |
||
204 | protected $ormSchema = []; |
||
205 | |||
206 | /** |
||
207 | * {@inheritdoc} |
||
208 | * |
||
209 | * @param null|array $schema |
||
210 | * |
||
211 | * @throws SugarException |
||
212 | */ |
||
213 | public function __construct( |
||
234 | |||
235 | /** |
||
236 | * Change record solid state. SolidState will force record data to be saved as one big update |
||
237 | * set without any generating separate update statements for changed columns. |
||
238 | * |
||
239 | * Attention, you have to carefully use forceUpdate flag with records without primary keys due |
||
240 | * update criteria (WHERE condition) can not be easy constructed for records with primary key. |
||
241 | * |
||
242 | * @param bool $solidState |
||
243 | * @param bool $forceUpdate Mark all fields as changed to force update later. |
||
244 | * |
||
245 | * @return $this |
||
246 | */ |
||
247 | public function solidState($solidState, $forceUpdate = false) |
||
261 | |||
262 | /** |
||
263 | * Is record is solid state? |
||
264 | * |
||
265 | * @see solidState() |
||
266 | * |
||
267 | * @return bool |
||
268 | */ |
||
269 | public function isSolid() |
||
273 | |||
274 | /** |
||
275 | * {@inheritdoc} |
||
276 | */ |
||
277 | public function recordRole() |
||
281 | |||
282 | /** |
||
283 | * {@inheritdoc} |
||
284 | */ |
||
285 | public function isLoaded() |
||
289 | |||
290 | /** |
||
291 | * {@inheritdoc} |
||
292 | */ |
||
293 | public function isDeleted() |
||
297 | |||
298 | /** |
||
299 | * {@inheritdoc} |
||
300 | */ |
||
301 | public function primaryKey() |
||
309 | |||
310 | /** |
||
311 | * {@inheritdoc} |
||
312 | * |
||
313 | * @see $fillable |
||
314 | * @see $secured |
||
315 | * @see isFillable() |
||
316 | * |
||
317 | * @param array|\Traversable $fields |
||
318 | * @param bool $all Fill all fields including non fillable. |
||
319 | * |
||
320 | * @return $this |
||
321 | * |
||
322 | * @throws AccessorExceptionInterface |
||
323 | */ |
||
324 | public function setFields($fields = [], $all = false) |
||
343 | |||
344 | /** |
||
345 | * {@inheritdoc} |
||
346 | * |
||
347 | * Must track field updates. |
||
348 | */ |
||
349 | public function setField($name, $value, $filter = true) |
||
371 | |||
372 | /** |
||
373 | * {@inheritdoc} |
||
374 | * |
||
375 | * Record will skip filtration for nullable fields. |
||
376 | */ |
||
377 | public function getField($name, $default = null, $filter = true) |
||
390 | |||
391 | /** |
||
392 | * {@inheritdoc} |
||
393 | * |
||
394 | * @see relation() |
||
395 | */ |
||
396 | public function __get($offset) |
||
405 | |||
406 | /** |
||
407 | * {@inheritdoc} |
||
408 | * |
||
409 | * @see relation() |
||
410 | */ |
||
411 | public function __set($offset, $value) |
||
422 | |||
423 | /** |
||
424 | * {@inheritdoc} |
||
425 | * |
||
426 | * @throws FieldException |
||
427 | */ |
||
428 | public function __unset($offset) |
||
432 | |||
433 | /** |
||
434 | * {@inheritdoc} |
||
435 | */ |
||
436 | public function __isset($name) |
||
444 | |||
445 | /** |
||
446 | * Direct access to relation by it's name. |
||
447 | * |
||
448 | * @see relation() |
||
449 | * |
||
450 | * @param string $method |
||
451 | * @param array $arguments |
||
452 | * |
||
453 | * @return RelationInterface|mixed|AccessorInterface |
||
454 | */ |
||
455 | public function __call($method, array $arguments) |
||
464 | |||
465 | /** |
||
466 | * Get or create record relation by it's name and pre-loaded (optional) set of data. |
||
467 | * |
||
468 | * @param string $name |
||
469 | * |
||
470 | * @return RelationInterface |
||
471 | * |
||
472 | * @throws RelationException |
||
473 | * @throws RecordException |
||
474 | */ |
||
475 | public function relation($name) |
||
489 | |||
490 | /** |
||
491 | * {@inheritdoc} |
||
492 | * |
||
493 | * @param string $field Specific field name to check for updates. |
||
494 | */ |
||
495 | public function hasUpdates($field = null) |
||
522 | |||
523 | /** |
||
524 | * {@inheritdoc} |
||
525 | */ |
||
526 | public function flushUpdates() |
||
536 | |||
537 | /** |
||
538 | * Create set of fields to be sent to UPDATE statement. |
||
539 | * |
||
540 | * @return array |
||
541 | */ |
||
542 | public function compileUpdates() |
||
575 | |||
576 | /** |
||
577 | * @return array |
||
578 | */ |
||
579 | public function __debugInfo() |
||
589 | |||
590 | /** |
||
591 | * {@inheritdoc} |
||
592 | */ |
||
593 | public function isValid() |
||
597 | |||
598 | /** |
||
599 | * {@inheritdoc} |
||
600 | */ |
||
601 | public function getErrors($reset = false) |
||
605 | |||
606 | /** |
||
607 | * Change record loaded state. |
||
608 | * |
||
609 | * @param bool|mixed $state |
||
610 | * |
||
611 | * @return $this |
||
612 | */ |
||
613 | protected function loadedState($state) |
||
619 | |||
620 | /** |
||
621 | * {@inheritdoc} |
||
622 | * |
||
623 | * Will validate every embedded relation. |
||
624 | * |
||
625 | * @param bool $reset |
||
626 | * |
||
627 | * @throws RecordException |
||
628 | */ |
||
629 | View Code Duplication | protected function validate($reset = false) |
|
648 | |||
649 | /** |
||
650 | * Get WHERE array to be used to perform record data update or deletion. Usually will include |
||
651 | * record primary key. |
||
652 | * |
||
653 | * @return array |
||
654 | */ |
||
655 | protected function stateCriteria() |
||
664 | |||
665 | /** |
||
666 | * {@inheritdoc} |
||
667 | */ |
||
668 | protected function container() |
||
676 | |||
677 | /** |
||
678 | * Check if relation is embedded. |
||
679 | * |
||
680 | * @internal |
||
681 | * |
||
682 | * @param string $relation |
||
683 | * |
||
684 | * @return bool |
||
685 | */ |
||
686 | protected function embeddedRelation($relation) |
||
690 | |||
691 | /** |
||
692 | * @param array $data |
||
693 | */ |
||
694 | private function extractRelations(array &$data) |
||
703 | |||
704 | /** |
||
705 | * @param string $name |
||
706 | * @param array|null $data |
||
707 | * @param bool $loaded |
||
708 | * @return RelationInterface|void |
||
709 | */ |
||
710 | private function initiateRelation($name, $data, $loaded) |
||
728 | |||
729 | /** |
||
730 | * {@inheritdoc} |
||
731 | * |
||
732 | * @see Component::staticContainer() |
||
733 | * |
||
734 | * @param array $fields Record fields to set, will be passed thought filters. |
||
735 | * @param ORM $orm ORM component, global container will be called if not instance provided. |
||
736 | * @event created() |
||
737 | */ |
||
738 | View Code Duplication | public static function create($fields = [], ORMInterface $orm = null) |
|
750 | } |
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.