Completed
Pull Request — master (#358)
by Simon
04:49
created

Lifecycle::getConnectExport()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 9
nc 1
nop 0
1
<?php
2
/**
3
 * (c) shopware AG <[email protected]>
4
 * For the full copyright and license information, please view the LICENSE
5
 * file that was distributed with this source code.
6
 */
7
8
namespace ShopwarePlugins\Connect\Subscribers;
9
10
use Enlight\Event\SubscriberInterface;
11
use Shopware\Connect\SDK;
12
use Shopware\Connect\Struct\PaymentStatus;
13
use Shopware\Components\Model\ModelManager;
14
use Shopware\CustomModels\Connect\Attribute;
15
use ShopwarePlugins\Connect\Components\Config;
16
use ShopwarePlugins\Connect\Components\Helper;
17
use ShopwarePlugins\Connect\Components\Utils;
18
use ShopwarePlugins\Connect\Components\ConnectExport;
19
use Shopware\Models\Order\Order;
20
21
/**
22
 * Handles article lifecycle events in order to automatically update/delete products to/from connect
23
 */
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(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
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)
191
    {
192
        $entity = $eventArgs->get('entity');
193
194
        $this->handleChange($entity);
195
    }
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)
203
    {
204
        if (!$entity instanceof \Shopware\Models\Article\Article
0 ignored issues
show
Bug introduced by
The class Shopware\Models\Article\Article does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
205
            && !$entity instanceof \Shopware\Models\Article\Detail
0 ignored issues
show
Bug introduced by
The class Shopware\Models\Article\Detail does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
206
        ) {
207
            return;
208
        }
209
210
        $id = $entity->getId();
211
        $className = get_class($entity);
212
        $model = $this->manager->getRepository($className)->find($id);
213
        // Check if we have a valid model
214
        if (!$model) {
215
            return;
216
        }
217
218
        // Check if entity is a connect product
219
        $attribute = $this->helper->getConnectAttributeByModel($model);
220
        if (!$attribute) {
221
            return;
222
        }
223
224
        // if article is not exported to Connect
225
        // or at least one article detail from same article is not exported
226
        // don't need to generate changes
227
        if (!$this->helper->isProductExported($attribute) || !empty($attribute->getShopId())) {
228
            if (!$this->helper->hasExportedVariants($attribute)) {
229
                return;
230
            }
231
        }
232
233
        if (!$this->hasPriceType()) {
234
            return;
235
        }
236
237
        $forceExport = false;
238
        if ($entity instanceof \Shopware\Models\Article\Detail) {
0 ignored issues
show
Bug introduced by
The class Shopware\Models\Article\Detail does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
239
            $changeSet = $this->manager->getUnitOfWork()->getEntityChangeSet($entity);
240
            // if detail number has been changed
241
            // sc plugin must generate & sync the change immediately
242
            if (array_key_exists('number', $changeSet)) {
243
                $forceExport = true;
244
            }
245
        }
246
247
        // Mark the product for connect update
248
        try {
249
            if ($model instanceof \Shopware\Models\Article\Detail) {
0 ignored issues
show
Bug introduced by
The class Shopware\Models\Article\Detail does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
250
                $this->generateChangesForDetail($model, $forceExport);
251
            } elseif ($model instanceof \Shopware\Models\Article\Article) {
0 ignored issues
show
Bug introduced by
The class Shopware\Models\Article\Article does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
252
                $this->generateChangesForArticle($model, $forceExport);
253
            }
254
        } catch (\Exception $e) {
255
            // If the update fails due to missing requirements
256
            // (e.g. category assignment), continue without error
257
        }
258
    }
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)
267
    {
268
        /** @var \Shopware\Models\Article\Detail $detail */
269
        $detail = $eventArgs->get('entity');
270
271
        /** @var \Shopware\Models\Article\Article $article */
272
        $article = $detail->getArticle();
273
        $articleAttribute = $this->helper->getConnectAttributeByModel($article);
274
        if (!$articleAttribute) {
275
            return;
276
        }
277
278
        // if article is not exported to Connect
279
        // don't need to generate changes
280
        if (!$this->helper->isProductExported($articleAttribute) || !empty($articleAttribute->getShopId())) {
281
            return;
282
        }
283
284
        if (!$this->hasPriceType()) {
285
            return;
286
        }
287
288
        // Mark the article detail for connect export
289
        try {
290
            $this->helper->getOrCreateConnectAttributeByModel($detail);
291
            $forceExport = false;
292
            $changeSet = $eventArgs->get('entityManager')->getUnitOfWork()->getEntityChangeSet($detail);
293
            // if detail number has been changed
294
            // sc plugin must generate & sync the change immediately
295
            if (array_key_exists('number', $changeSet)) {
296
                $forceExport = true;
297
            }
298
299
            $this->generateChangesForDetail($detail, $forceExport);
300
        } catch (\Exception $e) {
301
            // If the update fails due to missing requirements
302
            // (e.g. category assignment), continue without error
303
        }
304
    }
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)
312
    {
313
        /** @var \Shopware\Models\Shop\Shop $shop */
314
        $shop = $eventArgs->get('entity');
315
        $shopId = $shop->getId();
316
        $exportLanguages = $this->config->getConfig('exportLanguages');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $exportLanguages is correct as $this->config->getConfig('exportLanguages') (which targets ShopwarePlugins\Connect\...nts\Config::getConfig()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
317
        $exportLanguages = $exportLanguages ?: [];
318
319
        if (!in_array($shopId, $exportLanguages)) {
320
            return;
321
        }
322
323
        $exportLanguages = array_splice($exportLanguages, array_search($shopId, $exportLanguages), 1);
324
        $this->config->setConfig('exportLanguages', $exportLanguages, null, 'export');
325
    }
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)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
332
    {
333
        $attribute = $this->helper->getConnectAttributeByModel($detail);
334
        if (!$detail->getActive() && $this->config->getConfig('excludeInactiveProducts')) {
335
            $this->connectExport->syncDeleteDetail($detail);
336
337
            return;
338
        }
339
340
        if ($this->autoUpdateProducts == 1 || $force === true) {
341
            $this->connectExport->export(
342
                [$attribute->getSourceId()], null
343
            );
344
        } elseif ($this->autoUpdateProducts == 2) {
345
            $this->manager->getConnection()->update(
346
                's_plugin_connect_items',
347
                ['cron_update' => 1],
348
                ['article_detail_id' => $detail->getId()]
349
            );
350
        }
351
    }
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)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
358
    {
359
        if (!$article->getActive() && $this->config->getConfig('excludeInactiveProducts')) {
360
            $this->connectExport->setDeleteStatusForVariants($article);
361
362
            return;
363
        }
364
365
        if ($this->autoUpdateProducts == 1 || $force === true) {
366
            $sourceIds = $this->helper->getSourceIdsFromArticleId($article->getId());
367
368
            $this->connectExport->export($sourceIds, null);
369
        } elseif ($this->autoUpdateProducts == 2) {
370
            $this->manager->getConnection()->update(
371
                's_plugin_connect_items',
372
                ['cron_update' => 1],
373
                ['article_id' => $article->getId()]
374
            );
375
        }
376
    }
377
378
    /**
379
     * Sends the new order status when supplier change it
380
     *
381
     * @param Order $order
382
     */
383
    private function updateOrderStatus(Order $order)
384
    {
385
        $attribute = $order->getAttribute();
386
        if (!$attribute || !$attribute->getConnectShopId()) {
387
            return;
388
        }
389
390
        $orderStatusMapper = new Utils\OrderStatusMapper();
391
        $orderStatus = $orderStatusMapper->getOrderStatusStructFromOrder($order);
392
393
        try {
394
            $this->sdk->updateOrderStatus($orderStatus);
395
        } catch (\Exception $e) {
396
            // if sn is not available, proceed without exception
397
        }
398
    }
399
400
    /**
401
     * Sends the new payment status when merchant change it
402
     *
403
     * @param Order $order
404
     */
405
    private function updatePaymentStatus(Order $order)
406
    {
407
        $orderUtil = new Utils\ConnectOrderUtil();
408
        if (!$orderUtil->hasLocalOrderConnectProducts($order->getId())) {
409
            return;
410
        }
411
412
        $paymentStatusMapper = new Utils\OrderPaymentStatusMapper();
413
        $paymentStatus = $paymentStatusMapper->getPaymentStatus($order);
414
415
        $this->generateChangeForPaymentStatus($paymentStatus);
416
    }
417
418
    /**
419
     * @param PaymentStatus $paymentStatus
420
     */
421
    private function generateChangeForPaymentStatus(PaymentStatus $paymentStatus)
422
    {
423
        try {
424
            $this->sdk->updatePaymentStatus($paymentStatus);
425
        } catch (\Exception $e) {
426
            // if sn is not available, proceed without exception
427
        }
428
    }
429
430
    /**
431
     * @return bool
432
     */
433 View Code Duplication
    private function hasPriceType()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
434
    {
435
        if ($this->sdk->getPriceType() === \Shopware\Connect\SDK::PRICE_TYPE_PURCHASE
436
            || $this->sdk->getPriceType() === \Shopware\Connect\SDK::PRICE_TYPE_RETAIL
437
            || $this->sdk->getPriceType() === \Shopware\Connect\SDK::PRICE_TYPE_BOTH
438
        ) {
439
            return true;
440
        }
441
442
        return false;
443
    }
444
}
445