Complex classes like ManyToManyRelation 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 ManyToManyRelation, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 36 | class ManyToManyRelation extends AbstractRelation implements \IteratorAggregate, \Countable |
||
| 37 | { |
||
| 38 | use MatchTrait, PartialTrait, LookupTrait; |
||
| 39 | |||
| 40 | /** Schema shortcuts */ |
||
| 41 | const PIVOT_TABLE = Record::PIVOT_TABLE; |
||
| 42 | const PIVOT_DATABASE = 917; |
||
| 43 | |||
| 44 | /** |
||
| 45 | * @var Table|null |
||
| 46 | */ |
||
| 47 | private $pivotTable = null; |
||
| 48 | |||
| 49 | /** |
||
| 50 | * @var \SplObjectStorage |
||
| 51 | */ |
||
| 52 | private $pivotData; |
||
| 53 | |||
| 54 | /** |
||
| 55 | * Linked records. |
||
| 56 | * |
||
| 57 | * @var RecordInterface[] |
||
| 58 | */ |
||
| 59 | private $linked = []; |
||
| 60 | |||
| 61 | /** |
||
| 62 | * Linked but not saved yet records. |
||
| 63 | * |
||
| 64 | * @var array |
||
| 65 | */ |
||
| 66 | private $scheduled = []; |
||
| 67 | |||
| 68 | /** |
||
| 69 | * Record which pivot data was updated, record must still present in linked array. |
||
| 70 | * |
||
| 71 | * @var array |
||
| 72 | */ |
||
| 73 | private $updated = []; |
||
| 74 | |||
| 75 | /** |
||
| 76 | * Records scheduled to be de-associated. |
||
| 77 | * |
||
| 78 | * @var RecordInterface[] |
||
| 79 | */ |
||
| 80 | private $unlinked = []; |
||
| 81 | |||
| 82 | /** |
||
| 83 | * {@inheritdoc} |
||
| 84 | */ |
||
| 85 | public function hasRelated(): bool |
||
| 89 | |||
| 90 | /** |
||
| 91 | * {@inheritdoc} |
||
| 92 | */ |
||
| 93 | public function withContext( |
||
| 106 | |||
| 107 | /** |
||
| 108 | * {@inheritdoc} |
||
| 109 | */ |
||
| 110 | public function setRelated($value) |
||
| 124 | |||
| 125 | /** |
||
| 126 | * @return $this |
||
| 127 | */ |
||
| 128 | public function getRelated() |
||
| 132 | |||
| 133 | /** |
||
| 134 | * Iterate over linked instances, will force pre-loading unless partial. |
||
| 135 | * |
||
| 136 | * @return \ArrayIterator |
||
| 137 | */ |
||
| 138 | public function getIterator() |
||
| 142 | |||
| 143 | /** |
||
| 144 | * @return int |
||
| 145 | */ |
||
| 146 | public function count() |
||
| 150 | |||
| 151 | /** |
||
| 152 | * Get all unlinked records. |
||
| 153 | * |
||
| 154 | * @return \ArrayIterator |
||
| 155 | */ |
||
| 156 | public function getUnlinked() |
||
| 160 | |||
| 161 | /** |
||
| 162 | * Get pivot data associated with specific instance. |
||
| 163 | * |
||
| 164 | * @param RecordInterface $record |
||
| 165 | * |
||
| 166 | * @return array |
||
| 167 | * |
||
| 168 | * @throws RelationException |
||
| 169 | */ |
||
| 170 | public function getPivot(RecordInterface $record): array |
||
| 180 | |||
| 181 | /** |
||
| 182 | * Link record with parent entity. Only record instances is accepted. |
||
| 183 | * |
||
| 184 | * @param RecordInterface $record |
||
| 185 | * @param array $pivotData |
||
| 186 | * |
||
| 187 | * @return self |
||
| 188 | * |
||
| 189 | * @throws RelationException |
||
| 190 | */ |
||
| 191 | public function link(RecordInterface $record, array $pivotData = []): self |
||
| 217 | |||
| 218 | /** |
||
| 219 | * Unlink specific entity from relation. Will load relation data! Record to delete will be |
||
| 220 | * automatically matched to a give record. |
||
| 221 | * |
||
| 222 | * @param RecordInterface $record |
||
| 223 | * |
||
| 224 | * @return self |
||
| 225 | * |
||
| 226 | * @throws RelationException When entity not linked. |
||
| 227 | */ |
||
| 228 | public function unlink(RecordInterface $record): self |
||
| 249 | |||
| 250 | /** |
||
| 251 | * Check if given query points to linked entity. |
||
| 252 | * |
||
| 253 | * Example: |
||
| 254 | * echo $post->tags->has(1); |
||
| 255 | * echo $post->tags->has(['name'=>'tag a']); |
||
| 256 | * |
||
| 257 | * @param array|RecordInterface|mixed $query Fields, entity or PK. |
||
| 258 | * |
||
| 259 | * @return bool |
||
| 260 | */ |
||
| 261 | public function has($query) |
||
| 265 | |||
| 266 | /** |
||
| 267 | * Fine one entity for a given query or return null. Method will autoload data. |
||
| 268 | * |
||
| 269 | * Example: ->matchOne(['value' => 'something', ...]); |
||
| 270 | * |
||
| 271 | * @param array|RecordInterface|mixed $query Fields, entity or PK. |
||
| 272 | * |
||
| 273 | * @return RecordInterface|null |
||
| 274 | */ |
||
| 275 | public function matchOne($query) |
||
| 285 | |||
| 286 | /** |
||
| 287 | * Return only instances matched given query, performed in memory! Only simple conditions are |
||
| 288 | * allowed. Not "find" due trademark violation. Method will autoload data. |
||
| 289 | * |
||
| 290 | * Example: ->matchMultiple(['value' => 'something', ...]); |
||
| 291 | * |
||
| 292 | * @param array|RecordInterface|mixed $query Fields, entity or PK. |
||
| 293 | * |
||
| 294 | * @return \ArrayIterator |
||
| 295 | */ |
||
| 296 | public function matchMultiple($query) |
||
| 307 | |||
| 308 | /** |
||
| 309 | * {@inheritdoc} |
||
| 310 | */ |
||
| 311 | public function queueCommands(ContextualCommandInterface $parentCommand): CommandInterface |
||
| 387 | |||
| 388 | /** |
||
| 389 | * Load related records from database. |
||
| 390 | * |
||
| 391 | * @param bool $autoload |
||
| 392 | * |
||
| 393 | * @return self |
||
| 394 | * |
||
| 395 | * @throws SelectorException |
||
| 396 | * @throws QueryException (needs wrapping) |
||
| 397 | */ |
||
| 398 | protected function loadData(bool $autoload = true): self |
||
| 417 | |||
| 418 | /** |
||
| 419 | * Fetch data from database. Lazy load. Method require a bit of love. |
||
| 420 | * |
||
| 421 | * @return array |
||
| 422 | */ |
||
| 423 | protected function loadRelated(): array |
||
| 453 | |||
| 454 | /** |
||
| 455 | * Indicates that records are not linked yet. |
||
| 456 | * |
||
| 457 | * @param RecordInterface $outer |
||
| 458 | * @param bool $strict When true will also check that keys are not empty. |
||
| 459 | * |
||
| 460 | * @return bool |
||
| 461 | */ |
||
| 462 | protected function isLinked(RecordInterface $outer, bool $strict = false) |
||
| 483 | |||
| 484 | /** |
||
| 485 | * Insane method used to properly set pivot command context (where or insert statement) based on |
||
| 486 | * parent and outer records AND/OR based on command promises. |
||
| 487 | * |
||
| 488 | * @param ContextualCommandInterface $pivotCommand |
||
| 489 | * @param RecordInterface $parent |
||
| 490 | * @param ContextualCommandInterface $parentCommand |
||
| 491 | * @param RecordInterface $outer |
||
| 492 | * @param ContextualCommandInterface $outerCommand |
||
| 493 | * |
||
| 494 | * @return ContextualCommandInterface |
||
| 495 | */ |
||
| 496 | protected function ensureContext( |
||
| 521 | |||
| 522 | /** |
||
| 523 | * @return Table |
||
| 524 | */ |
||
| 525 | protected function pivotTable() |
||
| 537 | |||
| 538 | |||
| 539 | /** |
||
| 540 | * Create query for lazy loading. |
||
| 541 | * |
||
| 542 | * @param mixed $innerKey |
||
| 543 | * |
||
| 544 | * @return SelectQuery |
||
| 545 | */ |
||
| 546 | protected function createQuery($innerKey): SelectQuery |
||
| 574 | |||
| 575 | /** |
||
| 576 | * Init relations and populate pivot map. |
||
| 577 | * |
||
| 578 | * @return ManyToManyRelation |
||
| 579 | */ |
||
| 580 | private function initInstances(): self |
||
| 602 | |||
| 603 | /** |
||
| 604 | * Make sure that pivot data in a valid format. |
||
| 605 | * |
||
| 606 | * @param array $pivotData |
||
| 607 | * |
||
| 608 | * @throws RelationException |
||
| 609 | */ |
||
| 610 | private function assertPivot(array $pivotData) |
||
| 618 | } |
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.