| Total Complexity | 44 |
| Total Lines | 196 |
| Duplicated Lines | 0 % |
| Changes | 0 | ||
Complex classes like DbOperationManager 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.
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 DbOperationManager, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 10 | class DbOperationManager |
||
| 11 | { |
||
| 12 | // comparators //////////////// |
||
| 13 | |||
| 14 | public $inserts = []; |
||
| 15 | |||
| 16 | /** UPDATEs of a single entity */ |
||
| 17 | public $updates = []; |
||
| 18 | |||
| 19 | /** DELETEs of a single entity */ |
||
| 20 | public $deletes = []; |
||
| 21 | |||
| 22 | /** bulk modifications (DELETE, UPDATE) on an entity collection */ |
||
| 23 | public $bulkOperations = []; |
||
| 24 | |||
| 25 | /** bulk modifications (DELETE, UPDATE) for which order of execution is important */ |
||
| 26 | public $bulkOperationsInsertionOrder = []; |
||
| 27 | |||
| 28 | public function addOperation(DbEntityOperation $newOperation): bool |
||
| 29 | { |
||
| 30 | if ($newOperation instanceof DbEntityOperation) { |
||
|
|
|||
| 31 | $clazz = get_class($newOperation); |
||
| 32 | if ($newOperation->getOperationType() == DbOperationType::INSERT) { |
||
| 33 | if (!array_key_exists($clazz, $this->inserts)) { |
||
| 34 | $this->inserts[$clazz] = []; |
||
| 35 | } |
||
| 36 | if (!in_array($newOperation, $this->inserts[$clazz])) { |
||
| 37 | $this->inserts[$clazz][] = $newOperation; |
||
| 38 | return true; |
||
| 39 | } |
||
| 40 | } elseif ($newOperation->getOperationType() == DbOperationType::DELETE) { |
||
| 41 | if (!array_key_exists($clazz, $this->deletes)) { |
||
| 42 | $this->deletes[$clazz] = []; |
||
| 43 | } |
||
| 44 | if (!in_array($newOperation, $this->deletes)) { |
||
| 45 | $this->deletes[$clazz][] = $newOperation; |
||
| 46 | return true; |
||
| 47 | } |
||
| 48 | } else {// UPDATE |
||
| 49 | if (!array_key_exists($clazz, $this->updates)) { |
||
| 50 | $this->updates[$clazz] = []; |
||
| 51 | } |
||
| 52 | if (!in_array($newOperation, $this->updates)) { |
||
| 53 | $this->updates[$clazz][] = $newOperation; |
||
| 54 | return true; |
||
| 55 | } |
||
| 56 | } |
||
| 57 | } elseif ($newOperation instanceof DbBulkOperation) { |
||
| 58 | if (!array_key_exists($clazz, $this->bulkOperations)) { |
||
| 59 | $this->bulkOperations[$clazz] = []; |
||
| 60 | } |
||
| 61 | if (!in_array($newOperation, $this->bulkOperations)) { |
||
| 62 | $this->bulkOperations[$clazz][] = $newOperation; |
||
| 63 | return true; |
||
| 64 | } |
||
| 65 | } |
||
| 66 | return false; |
||
| 67 | } |
||
| 68 | |||
| 69 | public function addOperationPreserveOrder(DbBulkOperation $newOperation): bool |
||
| 76 | } |
||
| 77 | |||
| 78 | public function calculateFlush(): array |
||
| 79 | { |
||
| 80 | $flush = []; |
||
| 81 | // first INSERTs |
||
| 82 | $this->addSortedInserts($flush); |
||
| 83 | // then UPDATEs + DELETEs |
||
| 84 | $this->addSortedModifications($flush); |
||
| 85 | $this->determineDependencies($flush); |
||
| 86 | return $flush; |
||
| 87 | } |
||
| 88 | |||
| 89 | /** Adds the insert operations to the flush (in correct order). |
||
| 90 | * @param operationsForFlush */ |
||
| 91 | protected function addSortedInserts(array &$flush): void |
||
| 100 | } |
||
| 101 | } |
||
| 102 | } |
||
| 103 | |||
| 104 | /** Adds a correctly ordered list of UPDATE and DELETE operations to the flush. |
||
| 105 | * @param flush */ |
||
| 106 | protected function addSortedModifications(array &$flush): void |
||
| 129 | } |
||
| 130 | } |
||
| 131 | |||
| 132 | protected function addSortedModificationsForType(string $type, array $preSortedOperations, array &$flush): void |
||
| 133 | { |
||
| 134 | if (!empty($preSortedOperations)) { |
||
| 135 | if (is_a($type, HasDbReferencesInterface::class, true)) { |
||
| 136 | // if this type has self references, we need to resolve the reference order |
||
| 137 | $flush = array_merge($flush, $this->sortByReferences($preSortedOperations)); |
||
| 138 | } else { |
||
| 139 | $flush = array_merge($flush, $preSortedOperations); |
||
| 140 | } |
||
| 141 | } |
||
| 142 | } |
||
| 143 | |||
| 144 | /** |
||
| 145 | * Assumptions: |
||
| 146 | * a) all operations in the set work on entities such that the entities implement {@link HasDbReferences}. |
||
| 147 | * b) all operations in the set work on the same type (ie. all operations are INSERTs or DELETEs). |
||
| 148 | * |
||
| 149 | */ |
||
| 150 | protected function sortByReferences(array $preSorted): array |
||
| 151 | { |
||
| 152 | // copy the pre-sorted set and apply final sorting to list |
||
| 153 | $opList = $preSorted; |
||
| 154 | |||
| 155 | for ($i = 0; $i < count($opList); $i += 1) { |
||
| 156 | $currentOperation = $opList[$i]; |
||
| 157 | $currentEntity = $currentOperation->getEntity(); |
||
| 158 | $currentReferences = $currentOperation->getFlushRelevantEntityReferences(); |
||
| 159 | |||
| 160 | // check whether this operation must be placed after another operation |
||
| 161 | $moveTo = $i; |
||
| 162 | for ($k = $i + 1; $k < count($opList); $k += 1) { |
||
| 163 | $otherOperation = $opList[$k]; |
||
| 164 | $otherEntity = $otherOperation->getEntity(); |
||
| 165 | $otherReferences = $otherOperation->getFlushRelevantEntityReferences(); |
||
| 166 | |||
| 167 | if ($currentOperation->getOperationType() == DbOperationType::INSERT) { |
||
| 168 | // if we reference the other entity, we need to be inserted after that entity |
||
| 169 | if (!empty($currentReferences) && in_array($otherEntity->getId(), $currentReferences)) { |
||
| 170 | $moveTo = $k; |
||
| 171 | break; // we can only reference a single entity |
||
| 172 | } |
||
| 173 | } else { // UPDATE or DELETE |
||
| 174 | // if the other entity has a reference to us, we must be placed after the other entity |
||
| 175 | if (!empty($otherReferences) && in_array($currentEntity->getId(), $otherReferences)) { |
||
| 176 | $moveTo = $k; |
||
| 177 | // cannot break, there may be another entity further to the right which also references us |
||
| 178 | } |
||
| 179 | } |
||
| 180 | } |
||
| 181 | |||
| 182 | if ($moveTo > $i) { |
||
| 183 | unset($opList[$i]); |
||
| 184 | $opList[$moveTo] = $currentOperation; |
||
| 185 | $i -= 1; |
||
| 186 | } |
||
| 187 | } |
||
| 188 | |||
| 189 | return $opList; |
||
| 190 | } |
||
| 191 | |||
| 192 | protected function determineDependencies(array $flush): void |
||
| 206 | } |
||
| 207 | } |
||
| 208 | } |
||
| 209 | } |
||
| 210 | } |
||
| 216 |