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.