Completed
Pull Request — master (#330)
by Stefan
04:36
created

Article::regenerateChangesForArticle()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 42
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 42
rs 8.439
cc 6
eloc 24
nc 6
nop 1
1
<?php
2
3
namespace ShopwarePlugins\Connect\Subscribers;
4
use ShopwarePlugins\Connect\Components\Config;
5
use Shopware\Connect\Struct\Change\FromShop\MakeMainVariant;
6
use Shopware\Models\Customer\Group;
7
use Shopware\Connect\Gateway;
8
use Shopware\Components\Model\ModelManager;
9
use ShopwarePlugins\Connect\Components\ConnectExport;
10
use Shopware\Models\Article\Article as ArticleModel;
11
use ShopwarePlugins\Connect\Components\Helper;
12
use ShopwarePlugins\Connect\Components\ProductStream\ProductStreamsAssignments;
13
use ShopwarePlugins\Connect\Components\ProductStream\ProductStreamService;
14
15
/**
16
 * Class Article
17
 * @package ShopwarePlugins\Connect\Subscribers
18
 */
19
class Article extends BaseSubscriber
20
{
21
    /**
22
     * @var \Shopware\Connect\Gateway\PDO
23
     */
24
    private $connectGateway;
25
26
    /**
27
     * @var \Shopware\Components\Model\ModelManager
28
     */
29
    private $modelManager;
30
31
    /**
32
     * @var \Shopware\Models\Customer\Group
33
     */
34
    private $customerGroupRepository;
35
36
    /**
37
     * @var \Shopware\Models\Article\Detail
38
     */
39
    private $detailRepository;
40
41
    /**
42
     * @var \ShopwarePlugins\Connect\Components\ConnectExport
43
     */
44
    private $connectExport;
45
46
    /**
47
     * @var ProductStreamService
48
     */
49
    private $productStreamService;
50
51
    /**
52
     * @var Helper
53
     */
54
    private $helper;
55
56
    /**
57
     * @var Config
58
     */
59
    private $config;
60
61
    public function __construct(
62
        Gateway $connectGateway,
63
        ModelManager $modelManager,
64
        ConnectExport $connectExport,
65
        ProductStreamService $productStreamService,
66
        Helper $helper,
67
        Config $config
68
    ) {
69
        parent::__construct();
70
        $this->connectGateway = $connectGateway;
0 ignored issues
show
Documentation Bug introduced by
$connectGateway is of type object<Shopware\Connect\Gateway>, but the property $connectGateway was declared to be of type object<Shopware\Connect\Gateway\PDO>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
71
        $this->modelManager = $modelManager;
72
        $this->connectExport = $connectExport;
73
        $this->productStreamService = $productStreamService;
74
        $this->helper = $helper;
75
        $this->config = $config;
76
    }
77
78
    public function getSubscribedEvents()
79
    {
80
        return array(
81
            'Shopware_Controllers_Backend_Article::preparePricesAssociatedData::after' => 'enforceConnectPriceWhenSaving',
82
            'Enlight_Controller_Action_PostDispatch_Backend_Article' => 'extendBackendArticle',
83
            'Enlight_Controller_Action_PreDispatch_Backend_Article' => 'preBackendArticle',
84
            'Enlight_Controller_Action_PostDispatch_Frontend_Detail' => 'modifyConnectArticle',
85
            'Enlight_Controller_Action_PreDispatch_Frontend_Detail' => 'extendFrontendArticle'
86
        );
87
    }
88
89
    /**
90
     * @return \Shopware\Models\Article\Detail
91
     */
92
    public function getDetailRepository()
93
    {
94
        if (!$this->detailRepository) {
95
            $this->detailRepository = $this->modelManager->getRepository('Shopware\Models\Article\Detail');
96
        }
97
98
        return $this->detailRepository;
99
    }
100
101
    /**
102
     * @return \Shopware\Components\Model\ModelRepository|Group
103
     */
104
    public function getCustomerGroupRepository()
105
    {
106
        if (!$this->customerGroupRepository) {
107
            $this->customerGroupRepository = $this->modelManager->getRepository('Shopware\Models\Customer\Group');
108
        }
109
        return $this->customerGroupRepository;
110
    }
111
112
    /**
113
     * @param \Enlight_Event_EventArgs $args
114
     */
115
    public function preBackendArticle(\Enlight_Event_EventArgs $args)
116
    {
117
        /** @var $subject \Enlight_Controller_Action */
118
        $subject = $args->getSubject();
119
        $request = $subject->Request();
120
121
        switch ($request->getActionName()) {
122
            case 'saveDetail':
123
                if ($request->getParam('standard')) {
124
                    $this->generateMainVariantChange($request->getParam('id'));
125
                }
126
                break;
127
        }
128
    }
129
130
    /**
131
     * @event Enlight_Controller_Action_PostDispatch_Backend_Article
132
     * @param \Enlight_Event_EventArgs $args
133
     */
134
    public function extendBackendArticle(\Enlight_Event_EventArgs $args)
135
    {
136
        /** @var $subject \Enlight_Controller_Action */
137
        $subject = $args->getSubject();
138
        $request = $subject->Request();
139
140
        switch ($request->getActionName()) {
141
            case 'index':
142
                $this->registerMyTemplateDir();
143
                $this->registerMySnippets();
144
                $subject->View()->extendsTemplate(
145
                    'backend/article/connect.js'
146
                );
147
                break;
148
            case 'load':
149
                $this->registerMyTemplateDir();
150
                $this->registerMySnippets();
151
                $subject->View()->extendsTemplate(
152
                    'backend/article/model/attribute_connect.js'
153
                );
154
155
//                if (\Shopware::VERSION != '__VERSION__' && version_compare(\Shopware::VERSION, '4.2.2', '<')) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
156
                $subject->View()->assign('disableConnectPrice', 'true');
157
//
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
158
//                    $subject->View()->extendsTemplate(
159
//                        'backend/article/model/price_attribute_connect.js'
160
//                    );
161
//                }
162
163
                $subject->View()->extendsTemplate(
164
                    'backend/article/view/detail/connect_tab.js'
165
                );
166
                $subject->View()->extendsTemplate(
167
                    'backend/article/view/detail/prices_connect.js'
168
                );
169
                $subject->View()->extendsTemplate(
170
                    'backend/article/controller/detail_connect.js'
171
                );
172
                $subject->View()->extendsTemplate(
173
                    'backend/article/view/detail/connect_properties.js'
174
                );
175
                break;
176
            case 'setPropertyList':
177
                // property values are saved in different ajax call then
178
                // property group and this will generate wrong Connect changes.
179
                // after the property values are saved, the temporary property group is no needed
180
                // and it will generate right Connect changes
181
                $articleId = $request->getParam('articleId', null);
182
183
                /** @var ArticleModel $article */
184
                $article = $this->modelManager->find(ArticleModel::class, $articleId);
185
186
                if (!$article) {
187
                    return;
188
                }
189
190
                if (!$article->getPropertyGroup()) {
191
                    return;
192
                }
193
194
                // Check if entity is a connect product
195
                $attribute = $this->helper->getConnectAttributeByModel($article);
196
                if (!$attribute) {
197
                    return;
198
                }
199
200
                // if article is not exported to Connect
201
                // don't need to generate changes
202
                if (!$this->helper->isProductExported($attribute) || !empty($attribute->getShopId())) {
203
                    return;
204
                }
205
206
                if (!$this->hasPriceType()) {
207
                    return;
208
                }
209
210
                $detail = $article->getMainDetail();
211
212
                if ($detail->getAttribute()->getConnectPropertyGroup()) {
213
                    $detail->getAttribute()->setConnectPropertyGroup(null);
214
                    $this->modelManager->persist($detail);
215
                    $this->modelManager->flush();
216
                }
217
218
                $sourceIds = Shopware()->Db()->fetchCol(
219
                    'SELECT source_id FROM s_plugin_connect_items WHERE article_id = ?',
220
                    array($article->getId())
221
                );
222
223
                $this->connectExport->export($sourceIds);
224
                break;
225
            case 'createConfiguratorVariants':
226
                // main detail should be updated as well, because shopware won't call lifecycle event
227
                // even postUpdate of Detail. By this way Connect will generate change for main variant,
228
                // otherwise $product->variant property is an empty array
229
                // if main detail is not changed, Connect SDK won't generate change for it.
230
                // ticket CON-3747
231
                if (!$articleId = $request->getParam('articleId')) {
232
                    return;
233
                }
234
235
                $this->regenerateChangesForArticle($articleId);
236
                break;
237
            case 'getPropertyList':
238
                $subject->View()->data = $this->addConnectFlagToProperties(
239
                    $subject->View()->data
240
                );
241
                break;
242
            case 'deleteAllVariants':
243
                if ($articleId = $request->getParam('articleId')) {
244
                    /** @var ArticleModel $article */
245
                    $article = $this->modelManager->find(ArticleModel::class, (int) $articleId);
246
                    if (!$article) {
247
                        return;
248
                    }
249
                    $this->deleteAllVariants($articleId);
250
                }
251
                break;
252
            default:
253
                break;
254
        }
255
    }
256
257
    /**
258
     * @param int $articleId
259
     */
260
    public function regenerateChangesForArticle($articleId)
261
    {
262
        $autoUpdateProducts = $this->config->getConfig('autoUpdateProducts', Config::UPDATE_AUTO);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $autoUpdateProducts is correct as $this->config->getConfig...ts\Config::UPDATE_AUTO) (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...
263
        if ($autoUpdateProducts == Config::UPDATE_MANUAL) {
264
            return;
265
        }
266
267
        /** @var \Shopware\Models\Article\Article $article */
268
        $article = $this->modelManager->getRepository(ArticleModel::class)->find((int)$articleId);
269
        if (!$article) {
270
            return;
271
        }
272
273
        $attribute = $this->helper->getConnectAttributeByModel($article);
274
        if (!$attribute) {
275
            return;
276
        }
277
278
        // Check if entity is a connect product
279
        if (!$this->helper->isProductExported($attribute)) {
280
            return;
281
        }
282
283
        $this->deleteAllVariants($article);
284
285
        if ($autoUpdateProducts == Config::UPDATE_CRON_JOB) {
286
            $this->modelManager->getConnection()->update(
287
                's_plugin_connect_items',
288
                array('cron_update' => 1),
289
                array('article_id' => $articleId)
290
            );
291
            return;
292
        }
293
294
        $sourceIds = $this->helper->getSourceIdsFromArticleId($articleId);
295
        $this->connectExport->export(
296
            $sourceIds,
297
            new ProductStreamsAssignments(
298
                ['assignments' => $this->productStreamService->collectRelatedStreamsAssignments([$articleId])]
299
            )
300
        );
301
    }
302
303
    /**
304
     * @param ArticleModel $article
305
     */
306
    private function deleteAllVariants(ArticleModel $article)
307
    {
308
        $this->connectExport->setDeleteStatusForVariants($article);
309
    }
310
311
    public function addConnectFlagToProperties($data)
312
    {
313
        $groups = [];
314
        foreach ($data as $group) {
315
            $options = [];
316
            foreach ($group['value'] as $value) {
317
                $element = $value;
318
                $optionId = $value['id'];
319
                $valueModel = $this->modelManager->getRepository('Shopware\Models\Property\Value')->find($optionId);
320
321
                $attribute = null;
322
                if ($valueModel) {
323
                    $attribute = $valueModel->getAttribute();
324
                }
325
326
                if ($attribute && $attribute->getConnectIsRemote()) {
327
                    $element['connect'] = true;
328
                } else {
329
                    $element['connect'] = false;
330
                }
331
                $options[] = $element;
332
            }
333
334
            $group['value'] = $options;
335
            $groups[] = $group;
336
        }
337
338
        return $groups;
339
    }
340
341
    /**
342
     * @param $detailId
343
     */
344
    public function generateMainVariantChange($detailId)
345
    {
346
        $detail = $this->getDetailRepository()->findOneBy(array('id' => $detailId));
347
348
        if (!$detail 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...
349
            return;
350
        }
351
352
        //if it is already main variant dont generate MakeMainVariant change
353
        if ($detail->getKind() == 1) {
354
            return;
355
        }
356
357
        $attribute = $this->helper->getConnectAttributeByModel($detail);
358
359
        if (!$attribute) {
360
            return;
361
        }
362
        // Check if entity is a connect product
363
        if (!$this->helper->isProductExported($attribute)) {
364
            return;
365
        }
366
367
        if (!$this->hasPriceType()) {
368
            return;
369
        }
370
371
        $groupId = $attribute->getGroupId() ? $attribute->getGroupId() : $attribute->getArticleId();
372
373
        $mainVariant = new MakeMainVariant(array(
374
            'sourceId' => $attribute->getSourceId(),
375
            'groupId' => $groupId
376
        ));
377
378
        try {
379
            $this->getSDK()->makeMainVariant($mainVariant);
380
        } catch (\Exception $e) {
381
            // if sn is not available, proceed without exception
382
        }
383
    }
384
385
    /**
386
     * When saving prices make sure, that the connectPrice is stored in net
387
     *
388
     * @param \Enlight_Hook_HookArgs $args
389
     */
390
    public function enforceConnectPriceWhenSaving(\Enlight_Hook_HookArgs $args)
391
    {
392
        /** @var array $prices */
393
        $prices = $args->getReturn();
394
395
        $connectCustomerGroup = $this->getConnectCustomerGroup();
396
        if (!$connectCustomerGroup) {
397
            return;
398
        }
399
        $connectCustomerGroupKey = $connectCustomerGroup->getKey();
400
        $defaultPrices = array();
401
        foreach ($prices as $key => $priceData) {
402
            if ($priceData['customerGroupKey'] == $connectCustomerGroupKey) {
403
                return;
404
            }
405
            if ($priceData['customerGroupKey'] == 'EK') {
406
                $defaultPrices[] = $priceData;
407
            }
408
        }
409
410
        foreach ($defaultPrices as $price) {
411
            $prices[] = array(
412
                'from' => $price['from'],
413
                'to' => $price['to'],
414
                'price' => $price['price'],
415
                'pseudoPrice' => $price['pseudoPrice'],
416
                'basePrice' => $price['basePrice'],
417
                'percent' => $price['percent'],
418
                'customerGroup' => $connectCustomerGroup,
419
                'article' => $price['article'],
420
                'articleDetail' => $price['articleDetail'],
421
            );
422
        }
423
424
        $args->setReturn($prices);
425
    }
426
427
    /**
428
     * @return \Shopware\Models\Customer\Group|null
429
     */
430 View Code Duplication
    public function getConnectCustomerGroup()
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...
431
    {
432
        $repo = Shopware()->Models()->getRepository('Shopware\Models\Attribute\CustomerGroup');
433
        /** @var \Shopware\Models\Attribute\CustomerGroup $model */
434
        $model = $repo->findOneBy(array('connectGroup' => true));
435
436
        $customerGroup = null;
0 ignored issues
show
Unused Code introduced by
$customerGroup is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
437
        if ($model && $model->getCustomerGroup()) {
438
            return $model->getCustomerGroup();
439
        }
440
441
        return null;
442
    }
443
444
    /**
445
     * Load article detail
446
     *
447
     * @param \Enlight_Event_EventArgs $args
448
     */
449
    public function extendFrontendArticle(\Enlight_Event_EventArgs $args)
450
    {
451
        /** @var \Enlight_Controller_Request_RequestHttp $request */
452
        $request = $args->getSubject()->Request();
453
        if ($request->getActionName() != 'index') {
454
            return;
455
        }
456
457
        $detailId = (int) $request->sArticleDetail;
458
        if ($detailId === 0) {
459
            return;
460
        }
461
462
        /** @var \Shopware\Models\Article\Detail $detailModel */
463
        $detailModel = Shopware()->Models()->getRepository('Shopware\Models\Article\Detail')->find($detailId);
464
        if (!$detailModel) {
465
            return;
466
        }
467
468
        $params = array();
469
        /** @var \Shopware\Models\Article\Configurator\Option $option */
470
        foreach ($detailModel->getConfiguratorOptions() as $option) {
471
            $groupId = $option->getGroup()->getId();
472
            $params[$groupId] = $option->getId();
473
        }
474
        $request->setPost('group', $params);
475
    }
476
477
    /**
478
     * Should be possible to buy connect products
479
     * when they're not in stock.
480
     * Depends on remote shop configuration.
481
     *
482
     * @param \Enlight_Event_EventArgs $args
483
     */
484
    public function modifyConnectArticle(\Enlight_Event_EventArgs $args)
485
    {
486
        /** @var \Enlight_Controller_Request_RequestHttp $request */
487
        $request = $args->getSubject()->Request();
488
489
        if ($request->getActionName() != 'index') {
490
            return;
491
        }
492
        $subject = $args->getSubject();
493
        $article = $subject->View()->getAssign('sArticle');
494
        if (!$article) {
495
            return;
496
        }
497
498
        // when article stock is greater than 0
499
        // we don't need to modify it.
500
        if ($article['instock'] > 0) {
501
            return;
502
        }
503
504
        $articleId = $article['articleID'];
505
        $remoteShopId = $this->getRemoteShopId($articleId);
506
        if (!$remoteShopId) {
507
            // article is not imported via Connect
508
            return;
509
        }
510
511
        /** @var \Shopware\Models\Article\Article $articleModel */
512
        $articleModel = Shopware()->Models()->getRepository('Shopware\Models\Article\Article')->find($articleId);
513
        if (!$articleModel) {
514
            return;
515
        }
516
517
        $shopConfiguration = $this->connectGateway->getShopConfiguration($remoteShopId);
0 ignored issues
show
Documentation introduced by
$remoteShopId is of type boolean|integer, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
518
        if ($shopConfiguration->sellNotInStock && !$articleModel->getLastStock()) {
519
            // if selNotInStock is = true and article getLastStock = false
520
            // we don't need to modify it
521
            return;
522
        }
523
524
        if (!$shopConfiguration->sellNotInStock && $articleModel->getLastStock()) {
525
            // if sellNotInStock is = false and article getLastStock = true
526
            // we don't need to modify it
527
            return;
528
        }
529
530
        // sellNotInStock is opposite on articleLastStock
531
        // when it's true, lastStock must be false
532
        $articleModel->setLastStock(!$shopConfiguration->sellNotInStock);
533
        Shopware()->Models()->persist($articleModel);
534
        Shopware()->Models()->flush();
535
536
        // modify assigned article
537
        if ($shopConfiguration->sellNotInStock) {
538
            $article['laststock'] = false;
539
            $article['instock'] = 100;
540
            $article['isAvailable'] = true;
541
        } else {
542
            $article['laststock'] = true;
543
        }
544
        $subject->View()->assign('sArticle', $article);
545
    }
546
547
    /**
548
     * Not using the default helper-methods here, in order to keep this small and without any dependencies
549
     * to the SDK
550
     *
551
     * @param $id
552
     * @return boolean|int
553
     */
554
    private function getRemoteShopId($id)
555
    {
556
        $sql = 'SELECT shop_id FROM s_plugin_connect_items WHERE article_id = ? AND shop_id IS NOT NULL';
557
        return Shopware()->Db()->fetchOne($sql, array($id));
558
    }
559
}