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 Lifecycle 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 Lifecycle, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 24 | class Lifecycle implements SubscriberInterface |
||
| 25 | { |
||
| 26 | /** |
||
| 27 | * @var \Shopware\Components\Model\ModelManager |
||
| 28 | */ |
||
| 29 | private $manager; |
||
| 30 | |||
| 31 | /** |
||
| 32 | * @var int |
||
| 33 | */ |
||
| 34 | private $autoUpdateProducts; |
||
| 35 | |||
| 36 | /** |
||
| 37 | * @var Helper |
||
| 38 | */ |
||
| 39 | private $helper; |
||
| 40 | /** |
||
| 41 | * @var SDK |
||
| 42 | */ |
||
| 43 | private $sdk; |
||
| 44 | |||
| 45 | /** |
||
| 46 | * @var Config |
||
| 47 | */ |
||
| 48 | private $config; |
||
| 49 | |||
| 50 | /** |
||
| 51 | * @var ConnectExport |
||
| 52 | */ |
||
| 53 | private $connectExport; |
||
| 54 | |||
| 55 | /** |
||
| 56 | * @param ModelManager $modelManager |
||
| 57 | * @param int $autoUpdateProducts |
||
| 58 | * @param Helper $helper |
||
| 59 | * @param SDK $sdk |
||
| 60 | * @param Config $config |
||
| 61 | * @param ConnectExport $connectExport |
||
| 62 | */ |
||
| 63 | View Code Duplication | public function __construct( |
|
|
|
|||
| 64 | ModelManager $modelManager, |
||
| 65 | $autoUpdateProducts, |
||
| 66 | Helper $helper, |
||
| 67 | SDK $sdk, |
||
| 68 | Config $config, |
||
| 69 | ConnectExport $connectExport |
||
| 70 | ) { |
||
| 71 | $this->manager = $modelManager; |
||
| 72 | $this->autoUpdateProducts = $autoUpdateProducts; |
||
| 73 | $this->helper = $helper; |
||
| 74 | $this->sdk = $sdk; |
||
| 75 | $this->config = $config; |
||
| 76 | $this->connectExport = $connectExport; |
||
| 77 | } |
||
| 78 | |||
| 79 | public static function getSubscribedEvents() |
||
| 80 | { |
||
| 81 | return [ |
||
| 82 | 'Shopware\Models\Article\Article::preUpdate' => 'onPreUpdate', |
||
| 83 | 'Shopware\Models\Article\Article::postPersist' => 'onUpdateArticle', |
||
| 84 | 'Shopware\Models\Article\Detail::postPersist' => 'onPersistDetail', |
||
| 85 | 'Shopware\Models\Article\Article::preRemove' => 'onDeleteArticle', |
||
| 86 | 'Shopware\Models\Article\Detail::preRemove' => 'onDeleteDetail', |
||
| 87 | 'Shopware\Models\Order\Order::postUpdate' => 'onUpdateOrder', |
||
| 88 | 'Shopware\Models\Shop\Shop::preRemove' => 'onDeleteShop', |
||
| 89 | ]; |
||
| 90 | } |
||
| 91 | |||
| 92 | /** |
||
| 93 | * @param \Enlight_Event_EventArgs $eventArgs |
||
| 94 | */ |
||
| 95 | public function onPreUpdate(\Enlight_Event_EventArgs $eventArgs) |
||
| 96 | { |
||
| 97 | /** @var \Shopware\Models\Article\Article $entity */ |
||
| 98 | $entity = $eventArgs->get('entity'); |
||
| 99 | $db = Shopware()->Db(); |
||
| 100 | |||
| 101 | // Check if entity is a connect product |
||
| 102 | $attribute = $this->helper->getConnectAttributeByModel($entity); |
||
| 103 | if (!$attribute) { |
||
| 104 | return; |
||
| 105 | } |
||
| 106 | |||
| 107 | // if article is not exported to Connect |
||
| 108 | // don't need to generate changes |
||
| 109 | if (!$this->helper->isProductExported($attribute) || !empty($attribute->getShopId())) { |
||
| 110 | return; |
||
| 111 | } |
||
| 112 | |||
| 113 | if (!$this->hasPriceType()) { |
||
| 114 | return; |
||
| 115 | } |
||
| 116 | |||
| 117 | $changeSet = $eventArgs->get('entityManager')->getUnitOfWork()->getEntityChangeSet($entity); |
||
| 118 | |||
| 119 | // If product propertyGroup is changed we need to store the old one, |
||
| 120 | // because product property value are still not changed and |
||
| 121 | // this will generate wrong Connect changes |
||
| 122 | if ($changeSet['propertyGroup']) { |
||
| 123 | $filterGroupId = $db->fetchOne( |
||
| 124 | 'SELECT filtergroupID FROM s_articles WHERE id = ?', [$entity->getId()] |
||
| 125 | ); |
||
| 126 | |||
| 127 | $db->executeUpdate( |
||
| 128 | 'UPDATE `s_articles_attributes` SET `connect_property_group` = ? WHERE `articledetailsID` = ?', |
||
| 129 | [$filterGroupId, $entity->getMainDetail()->getId()] |
||
| 130 | ); |
||
| 131 | } |
||
| 132 | } |
||
| 133 | |||
| 134 | /** |
||
| 135 | * @param \Enlight_Event_EventArgs $eventArgs |
||
| 136 | */ |
||
| 137 | public function onUpdateOrder(\Enlight_Event_EventArgs $eventArgs) |
||
| 138 | { |
||
| 139 | /** @var \Shopware\Models\Order\Order $order */ |
||
| 140 | $order = $eventArgs->get('entity'); |
||
| 141 | |||
| 142 | // Compute the changeset and return, if orderStatus did not change |
||
| 143 | $changeSet = $eventArgs->get('entityManager')->getUnitOfWork()->getEntityChangeSet($order); |
||
| 144 | |||
| 145 | if (isset($changeSet['paymentStatus'])) { |
||
| 146 | $this->updatePaymentStatus($order); |
||
| 147 | } |
||
| 148 | |||
| 149 | if (isset($changeSet['orderStatus'])) { |
||
| 150 | $this->updateOrderStatus($order); |
||
| 151 | } |
||
| 152 | } |
||
| 153 | |||
| 154 | /** |
||
| 155 | * Callback function to delete an product from connect |
||
| 156 | * after it is going to be deleted locally |
||
| 157 | * |
||
| 158 | * @param \Enlight_Event_EventArgs $eventArgs |
||
| 159 | */ |
||
| 160 | public function onDeleteArticle(\Enlight_Event_EventArgs $eventArgs) |
||
| 161 | { |
||
| 162 | $entity = $eventArgs->get('entity'); |
||
| 163 | $this->connectExport->setDeleteStatusForVariants($entity); |
||
| 164 | } |
||
| 165 | |||
| 166 | /** |
||
| 167 | * Callback function to delete product detail from connect |
||
| 168 | * after it is going to be deleted locally |
||
| 169 | * |
||
| 170 | * @param \Enlight_Event_EventArgs $eventArgs |
||
| 171 | */ |
||
| 172 | public function onDeleteDetail(\Enlight_Event_EventArgs $eventArgs) |
||
| 173 | { |
||
| 174 | /** @var \Shopware\Models\Article\Detail $entity */ |
||
| 175 | $entity = $eventArgs->get('entity'); |
||
| 176 | if ($entity->getKind() !== 1) { |
||
| 177 | $attribute = $this->helper->getConnectAttributeByModel($entity); |
||
| 178 | if (!$this->helper->isProductExported($attribute)) { |
||
| 179 | return; |
||
| 180 | } |
||
| 181 | $this->connectExport->updateConnectItemsStatus([$attribute->getSourceId()], Attribute::STATUS_DELETE); |
||
| 182 | } |
||
| 183 | } |
||
| 184 | |||
| 185 | /** |
||
| 186 | * Callback method to update changed connect products |
||
| 187 | * |
||
| 188 | * @param \Enlight_Event_EventArgs $eventArgs |
||
| 189 | */ |
||
| 190 | public function onUpdateArticle(\Enlight_Event_EventArgs $eventArgs) |
||
| 196 | |||
| 197 | /** |
||
| 198 | * Generate changes for Article or Detail if necessary |
||
| 199 | * |
||
| 200 | * @param \Shopware\Models\Article\Article | \Shopware\Models\Article\Detail $entity |
||
| 201 | */ |
||
| 202 | public function handleChange($entity) |
||
| 259 | |||
| 260 | /** |
||
| 261 | * Callback method to insert new article details in Connect system |
||
| 262 | * Used when article is exported and after that variants are generated |
||
| 263 | * |
||
| 264 | * @param \Enlight_Event_EventArgs $eventArgs |
||
| 265 | */ |
||
| 266 | public function onPersistDetail(\Enlight_Event_EventArgs $eventArgs) |
||
| 305 | |||
| 306 | /** |
||
| 307 | * Callback function to shop from export languages |
||
| 308 | * |
||
| 309 | * @param \Enlight_Event_EventArgs $eventArgs |
||
| 310 | */ |
||
| 311 | public function onDeleteShop(\Enlight_Event_EventArgs $eventArgs) |
||
| 326 | |||
| 327 | /** |
||
| 328 | * @param \Shopware\Models\Article\Detail $detail |
||
| 329 | * @param bool $force |
||
| 330 | */ |
||
| 331 | View Code Duplication | private function generateChangesForDetail(\Shopware\Models\Article\Detail $detail, $force = false) |
|
| 352 | |||
| 353 | /** |
||
| 354 | * @param \Shopware\Models\Article\Article $article |
||
| 355 | * @param bool $force |
||
| 356 | */ |
||
| 357 | View Code Duplication | private function generateChangesForArticle(\Shopware\Models\Article\Article $article, $force = false) |
|
| 377 | |||
| 378 | /** |
||
| 379 | * Sends the new order status when supplier change it |
||
| 380 | * |
||
| 381 | * @param Order $order |
||
| 382 | */ |
||
| 383 | private function updateOrderStatus(Order $order) |
||
| 399 | |||
| 400 | /** |
||
| 401 | * Sends the new payment status when merchant change it |
||
| 402 | * |
||
| 403 | * @param Order $order |
||
| 404 | */ |
||
| 405 | private function updatePaymentStatus(Order $order) |
||
| 417 | |||
| 418 | /** |
||
| 419 | * @param PaymentStatus $paymentStatus |
||
| 420 | */ |
||
| 421 | private function generateChangeForPaymentStatus(PaymentStatus $paymentStatus) |
||
| 429 | |||
| 430 | /** |
||
| 431 | * @return bool |
||
| 432 | */ |
||
| 433 | View Code Duplication | private function hasPriceType() |
|
| 444 | } |
||
| 445 |
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.