Complex classes like ActiveRecordArray 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 ActiveRecordArray, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
31 | class ActiveRecordArray extends \ArrayObject implements ActiveRecordParentalInterface, ActiveRecordReadOnlyInterface, ActiveRecordSaveAllInterface |
||
32 | { |
||
33 | |||
34 | use ActionErrors; |
||
35 | use ActiveRecordParentalTrait; |
||
36 | use ActiveRecordReadOnlyTrait; |
||
37 | |||
38 | /** |
||
39 | * @var string|object|false|null object class for use by $this->newElement() |
||
40 | */ |
||
41 | protected $defaultObjectClass = false; |
||
42 | |||
43 | /** |
||
44 | * @var boolean can elements automatically be created in the array when |
||
45 | * a new key is used e.g. $myObject['key1]->myVar1 = 'myVal'; without first setting up $myObject['key1] |
||
46 | * if false an exception will be thrown |
||
47 | */ |
||
48 | protected $autoCreateObjectOnNewKey = true; |
||
49 | |||
50 | /** |
||
51 | * @event ModelEvent an event that is triggered before saveAll() |
||
52 | * You may set [[ModelEvent::isValid]] to be false to stop the update. |
||
53 | */ |
||
54 | const EVENT_BEFORE_SAVE_ALL = 'beforeSaveAll'; |
||
55 | |||
56 | /** |
||
57 | * @event Event an event that is triggered after saveAll() has completed |
||
58 | */ |
||
59 | const EVENT_AFTER_SAVE_ALL = 'afterSaveAll'; |
||
60 | |||
61 | /** |
||
62 | * @event Event an event that is triggered after saveAll() has failed |
||
63 | */ |
||
64 | const EVENT_AFTER_SAVE_ALL_FAILED = 'afterSaveAllFailed'; |
||
65 | |||
66 | /** |
||
67 | * @event ModelEvent an event that is triggered before saveAll() |
||
68 | * You may set [[ModelEvent::isValid]] to be false to stop the update. |
||
69 | */ |
||
70 | const EVENT_BEFORE_DELETE_FULL = 'beforeDeleteFull'; |
||
71 | |||
72 | /** |
||
73 | * @event Event an event that is triggered after saveAll() has completed |
||
74 | */ |
||
75 | const EVENT_AFTER_DELETE_FULL = 'afterDeleteFull'; |
||
76 | |||
77 | /** |
||
78 | * @event Event an event that is triggered after saveAll() has failed |
||
79 | */ |
||
80 | const EVENT_AFTER_DELETE_FULL_FAILED = 'afterDeleteFullFailed'; |
||
81 | |||
82 | /** |
||
83 | * Construct |
||
84 | * |
||
85 | * @param string|null|array $input |
||
86 | * @param integer $flags |
||
87 | * @param string $iterator_class |
||
88 | */ |
||
89 | public function __construct($input = null, $flags = 0, $iterator_class = 'ArrayIterator') |
||
100 | |||
101 | |||
102 | /** |
||
103 | * Appends the value |
||
104 | * |
||
105 | * @param ActiveRecord|ActiveAttributeRecord $value |
||
106 | * @return void |
||
107 | */ |
||
108 | public function append($value) |
||
112 | |||
113 | |||
114 | /** |
||
115 | * Appends the value but with a specific key value |
||
116 | * |
||
117 | * @param ActiveRecord|ActiveAttributeRecord $value |
||
118 | * @param mixed $key |
||
119 | * @return void |
||
120 | */ |
||
121 | public function appendWithKey($value, $key) |
||
125 | |||
126 | |||
127 | /** |
||
128 | * (non-PHPdoc) |
||
129 | * |
||
130 | * @see ArrayObject::offsetSet() |
||
131 | * @throws ActiveRecordArrayException |
||
132 | */ |
||
133 | public function offsetSet($key, $value) |
||
169 | |||
170 | |||
171 | /** |
||
172 | * (non-PHPdoc) |
||
173 | * @see ArrayObject::offsetGet() |
||
174 | * @throws ActiveRecordArrayException |
||
175 | */ |
||
176 | public function offsetGet($key) |
||
189 | |||
190 | |||
191 | /** |
||
192 | * Returns the value at the specified key |
||
193 | * |
||
194 | * @param mixed $key |
||
195 | * @return ActiveRecord ActiveAttributeRecord |
||
196 | */ |
||
197 | public function get($key) |
||
201 | |||
202 | |||
203 | /** |
||
204 | * Returns the value at the specified key |
||
205 | * |
||
206 | * @param mixed $key |
||
207 | * @return ActiveRecord|ActiveAttributeRecord |
||
208 | */ |
||
209 | public function row($key) |
||
213 | |||
214 | |||
215 | /** |
||
216 | * Create a new entry of the relevant object in the array and return the |
||
217 | * temp array key to that new object |
||
218 | * |
||
219 | * @param mixed $key |
||
220 | * [OPTIONAL] specify the temp key to be used for the new entry |
||
221 | * if it already exists then return false. default to assigning a temp key |
||
222 | * @param mixed $value |
||
223 | * [OPTIONAL] add a specific object rather than creating one |
||
224 | * based on $this->defaultObjectClass |
||
225 | * @return string false success return the key used to create the new element |
||
226 | */ |
||
227 | public function newElement($key = null, $value = null) |
||
253 | |||
254 | |||
255 | /** |
||
256 | * Remove existing element |
||
257 | * |
||
258 | * @param mixed $index |
||
259 | * specify index/key of the array element to remove - this will not delete data from the db |
||
260 | */ |
||
261 | public function removeElement($index) |
||
265 | |||
266 | /** |
||
267 | * Delete existing element if it exists in the db and then Remove existing element from array |
||
268 | * |
||
269 | * @param mixed $index |
||
270 | * specify index/key of the array element to remove - this will not delete data from the db |
||
271 | * @return boolean Success |
||
272 | */ |
||
273 | public function deleteOne($index) |
||
283 | |||
284 | |||
285 | /** |
||
286 | * Perform a saveAll() call but push the request down the model map including |
||
287 | * models that are not currently loaded (perhaps because child models need to |
||
288 | * pick up new values from parents |
||
289 | * |
||
290 | * @param boolean $runValidation |
||
291 | * should validations be executed on all models before allowing saveAll() |
||
292 | * @return boolean |
||
293 | * did saveAll() successfully process |
||
294 | */ |
||
295 | public function push($runValidation = true) |
||
299 | |||
300 | |||
301 | /** |
||
302 | * This method is called at the beginning of a saveAll() request |
||
303 | * |
||
304 | * @param boolean $runValidation |
||
305 | * should validations be executed on all models before allowing saveAll() |
||
306 | * @param boolean $hasParentModel |
||
307 | * whether this method was called from the top level or by a parent |
||
308 | * If false, it means the method was called at the top level |
||
309 | * @param boolean $push |
||
310 | * is saveAll being pushed onto lazy (un)loaded models as well |
||
311 | * @return boolean whether the saveAll() method call should continue |
||
312 | * If false, saveAll() will be cancelled. |
||
313 | */ |
||
314 | public function beforeSaveAllInternal($runValidation = true, $hasParentModel = false, $push = false) |
||
420 | |||
421 | |||
422 | /** |
||
423 | * Saves all models in the array but also loops through defined |
||
424 | * relationships (if appropriate) to save those as well |
||
425 | * |
||
426 | * @param boolean $runValidation |
||
427 | * should validations be executed on all models before allowing saveAll() |
||
428 | * @param boolean $hasParentModel |
||
429 | * whether this method was called from the top level or by a parent |
||
430 | * If false, it means the method was called at the top level |
||
431 | * @param boolean $push |
||
432 | * is saveAll being pushed onto lazy (un)loaded models as well |
||
433 | * @return boolean |
||
434 | * did saveAll() successfully process |
||
435 | */ |
||
436 | public function saveAll($runValidation = true, $hasParentModel = false, $push = false) |
||
576 | |||
577 | |||
578 | /** |
||
579 | * This method is called at the end of a successful saveAll() |
||
580 | * The default implementation will trigger an [[EVENT_AFTER_SAVE_ALL]] event |
||
581 | * When overriding this method, make sure you call the parent implementation so that |
||
582 | * the event is triggered. |
||
583 | * |
||
584 | * @param boolean $hasParentModel |
||
585 | * whether this method was called from the top level or by a parent |
||
586 | * If false, it means the method was called at the top level |
||
587 | */ |
||
588 | public function afterSaveAllInternal($hasParentModel = false) |
||
619 | |||
620 | |||
621 | /** |
||
622 | * This method is called at the end of a failed saveAll() |
||
623 | * The default implementation will trigger an [[EVENT_AFTER_SAVE_ALL_FAILED]] event |
||
624 | * When overriding this method, make sure you call the parent implementation so that |
||
625 | * the event is triggered. |
||
626 | * |
||
627 | * @param boolean $hasParentModel |
||
628 | * whether this method was called from the top level or by a parent |
||
629 | * If false, it means the method was called at the top level |
||
630 | */ |
||
631 | public function afterSaveAllFailedInternal($hasParentModel = false) |
||
672 | |||
673 | |||
674 | /** |
||
675 | * Loops through the current array of objects and delete them |
||
676 | * |
||
677 | * @see \yii\db\BaseActiveRecord::delete() |
||
678 | */ |
||
679 | public function deleteFull($hasParentModel = false) |
||
750 | |||
751 | |||
752 | /** |
||
753 | * This method is called at the beginning of a deleteFull() request on a record array |
||
754 | * |
||
755 | * @param boolean $hasParentModel |
||
756 | * whether this method was called from the top level or by a parent |
||
757 | * If false, it means the method was called at the top level |
||
758 | * @return boolean whether the deleteFull() method call should continue |
||
759 | * If false, deleteFull() will be cancelled. |
||
760 | */ |
||
761 | public function beforeDeleteFullInternal($hasParentModel = false) |
||
854 | |||
855 | |||
856 | /** |
||
857 | * This method is called at the end of a successful deleteFull() |
||
858 | * |
||
859 | * @param boolean $hasParentModel |
||
860 | * whether this method was called from the top level or by a parent |
||
861 | * If false, it means the method was called at the top level |
||
862 | */ |
||
863 | public function afterDeleteFullInternal($hasParentModel = false) |
||
898 | |||
899 | |||
900 | /** |
||
901 | * This method is called at the end of a failed deleteFull() |
||
902 | * |
||
903 | * @param boolean $hasParentModel |
||
904 | * whether this method was called from the top level or by a parent |
||
905 | * If false, it means the method was called at the top level |
||
906 | */ |
||
907 | public function afterDeleteFullFailedInternal($hasParentModel = false) |
||
950 | |||
951 | |||
952 | /** |
||
953 | * Return all objects in the array as arrays but do not load children |
||
954 | * |
||
955 | * @return array |
||
956 | */ |
||
957 | public function toArray(array $fields = [], array $expand = [], $recursive = true) |
||
971 | |||
972 | |||
973 | /** |
||
974 | * Return all objects in the array as arrays including their children |
||
975 | * if applicable |
||
976 | * |
||
977 | * @param boolean $loadedOnly [OPTIONAL] only show populated relations default is false |
||
978 | * @param boolean $excludeNewAndBlankRelations [OPTIONAL] exclude new blank records, default true |
||
979 | * @return array |
||
980 | */ |
||
981 | public function allToArray($loadedOnly=false, $excludeNewAndBlankRelations=true) |
||
1011 | |||
1012 | |||
1013 | /** |
||
1014 | * Set the default object class for use by $this->newElement() |
||
1015 | * |
||
1016 | * @param string $class |
||
1017 | */ |
||
1018 | public function setDefaultObjectClass($class) |
||
1022 | |||
1023 | |||
1024 | /** |
||
1025 | * Set parent model |
||
1026 | * |
||
1027 | * @param ActiveRecord|YiiActiveRecord $parentModel |
||
1028 | */ |
||
1029 | public function setParentModel($parentModel) |
||
1042 | |||
1043 | |||
1044 | /** |
||
1045 | * Set the read only value |
||
1046 | * |
||
1047 | * @param boolean $value [OPTIONAL] default true |
||
1048 | */ |
||
1049 | public function setReadOnly($value=true) |
||
1062 | |||
1063 | |||
1064 | /** |
||
1065 | * Set the can delete value |
||
1066 | * |
||
1067 | * @param boolean $value [OPTIONAL] default true |
||
1068 | */ |
||
1069 | public function setCanDelete($value=true) |
||
1082 | |||
1083 | |||
1084 | /** |
||
1085 | * Set if a new object should be created when an array element does not yes exist |
||
1086 | * |
||
1087 | * @param boolean $value [OPTIONAL] default true (which is also the default if the method is not used) |
||
1088 | */ |
||
1089 | public function setAutoCreateObjectOnNewKey($value=true) |
||
1093 | |||
1094 | |||
1095 | /** |
||
1096 | * Set attribute in each of the models within the array |
||
1097 | * |
||
1098 | * @param string $attributeName |
||
1099 | * name of attribute to be updated |
||
1100 | * @param mixed $value |
||
1101 | * value to be set |
||
1102 | * @param boolean $onlyIfChanged |
||
1103 | * limit setting the value to records that have changes already |
||
1104 | * helps avoid saving new otherwise empty records to the db |
||
1105 | * @param boolean $onlyIfNew |
||
1106 | * limit setting the value to new records only |
||
1107 | */ |
||
1108 | public function setAttribute($attributeName, $value, $onlyIfChanged = false, $onlyIfNew = false) |
||
1146 | |||
1147 | |||
1148 | /** |
||
1149 | * Determine if any of the models in the array have any unsaved changed |
||
1150 | * |
||
1151 | * @param boolean $checkRelations should changes in relations be checked as well |
||
1152 | * @return boolean |
||
1153 | */ |
||
1154 | public function hasChanges($checkRelations=false) |
||
1172 | |||
1173 | |||
1174 | /** |
||
1175 | * Call a function on each of the models in the array collection |
||
1176 | * |
||
1177 | * @param string $methodName |
||
1178 | * @param mixed $parameters |
||
1179 | * @param boolean $asArray |
||
1180 | * call method with $parameters as the first parameter rather than as one parameter per array element |
||
1181 | */ |
||
1182 | public function callMethodOnEachObjectInArray($methodName, $parameters = false, $asArray = true) |
||
1209 | |||
1210 | |||
1211 | /** |
||
1212 | * Call the debugTest method on all objects in the model map (used for testing) |
||
1213 | * |
||
1214 | * @param boolean $loadedOnly [OPTIONAL] only include populated relations default is false |
||
1215 | * @param boolean $excludeNewAndBlankRelations [OPTIONAL] exclude new blank records, default true |
||
1216 | * @return array |
||
1217 | */ |
||
1218 | public function callDebugTestOnAll($loadedOnly=false, $excludeNewAndBlankRelations=true) |
||
1248 | |||
1249 | |||
1250 | /** |
||
1251 | * Allows use of array_* functions on this object array |
||
1252 | * <code> |
||
1253 | * <?php |
||
1254 | * $yourObject->array_keys(); |
||
1255 | * ?> |
||
1256 | * </code> |
||
1257 | * |
||
1258 | * @param string $func |
||
1259 | * @param mixed[] $argv |
||
1260 | * @throws ActiveRecordArrayException |
||
1261 | * @return mixed |
||
1262 | */ |
||
1263 | public function __call($func, $argv) |
||
1270 | |||
1271 | |||
1272 | /** |
||
1273 | * Required to meet the needs of ActiveRecordSaveAllInterface but not used at this level |
||
1274 | * @see ActiveRecordSaveAllInterface |
||
1275 | */ |
||
1276 | public function save($runValidation = true, $attributes = null, $hasParentModel = false, $fromSaveAll = false) |
||
1280 | |||
1281 | |||
1282 | /** |
||
1283 | * Required to meet the needs of ActiveRecordSaveAllInterface but not used at this level |
||
1284 | * @see ActiveRecordSaveAllInterface |
||
1285 | */ |
||
1286 | public function beforeSaveAll() |
||
1290 | |||
1291 | |||
1292 | /** |
||
1293 | * Required to meet the needs of ActiveRecordSaveAllInterface but not used at this level |
||
1294 | * @see ActiveRecordSaveAllInterface |
||
1295 | */ |
||
1296 | public function afterSaveAll() |
||
1300 | |||
1301 | |||
1302 | /** |
||
1303 | * Required to meet the needs of ActiveRecordSaveAllInterface but not used at, |
||
1304 | * this level, each object in the array will be processed by afterSaveAllFailedInternal() |
||
1305 | */ |
||
1306 | public function afterSaveAllFailed() |
||
1310 | |||
1311 | |||
1312 | /** |
||
1313 | * Obtain data required to reset current record to state before saveAll() was called in the event |
||
1314 | * that saveAll() fails |
||
1315 | * @return array array of data required to rollback the current model |
||
1316 | */ |
||
1317 | public function getResetDataForFailedSave() |
||
1321 | |||
1322 | |||
1323 | /** |
||
1324 | * Required to meet the needs of ActiveRecordSaveAllInterface but not used at this level |
||
1325 | * @see ActiveRecordSaveAllInterface |
||
1326 | */ |
||
1327 | public function resetOnFailedSave($data) |
||
1331 | |||
1332 | /** |
||
1333 | * Required to meet the needs of ActiveRecordSaveAllInterface but not used at this level |
||
1334 | * @see ActiveRecordSaveAllInterface |
||
1335 | */ |
||
1336 | public function delete($hasParentModel = false, $fromDeleteFull = false) |
||
1340 | |||
1341 | /** |
||
1342 | * Required to meet the needs of ActiveRecordSaveAllInterface but not used at this level |
||
1343 | * @see ActiveRecordSaveAllInterface |
||
1344 | */ |
||
1345 | public function beforeDeleteFull() |
||
1349 | |||
1350 | |||
1351 | /** |
||
1352 | * Required to meet the needs of ActiveRecordSaveAllInterface but not used at this level |
||
1353 | * @see ActiveRecordSaveAllInterface |
||
1354 | */ |
||
1355 | public function afterDeleteFull() |
||
1359 | |||
1360 | |||
1361 | /** |
||
1362 | * Required to meet the needs of ActiveRecordSaveAllInterface but not used at, |
||
1363 | * this level, each object in the array will be processed by afterSaveAllFailedInternal() |
||
1364 | */ |
||
1365 | public function afterDeleteFullFailed() |
||
1369 | |||
1370 | } |
||
1371 |
This check marks calls to methods that do not seem to exist on an object.
This is most likely the result of a method being renamed without all references to it being renamed likewise.