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

Article::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
namespace ShopwarePlugins\Connect\Subscribers;
4
use Shopware\CustomModels\Connect\Attribute;
5
use ShopwarePlugins\Connect\Components\Config;
6
use Shopware\Connect\Struct\Change\FromShop\MakeMainVariant;
7
use Shopware\Models\Customer\Group;
8
use Shopware\Connect\Gateway;
9
use Shopware\Components\Model\ModelManager;
10
use ShopwarePlugins\Connect\Components\ConnectExport;
11
use Shopware\Models\Article\Article as ArticleModel;
12
use ShopwarePlugins\Connect\Components\Helper;
13
use ShopwarePlugins\Connect\Components\ProductStream\ProductStreamsAssignments;
14
use ShopwarePlugins\Connect\Components\VariantRegenerator;
15
16
/**
17
 * Class Article
18
 * @package ShopwarePlugins\Connect\Subscribers
19
 */
20
class Article extends BaseSubscriber
21
{
22
    /**
23
     * @var \Shopware\Connect\Gateway\PDO
24
     */
25
    private $connectGateway;
26
27
    /**
28
     * @var \Shopware\Components\Model\ModelManager
29
     */
30
    private $modelManager;
31
32
    /**
33
     * @var \Shopware\Models\Customer\Group
34
     */
35
    private $customerGroupRepository;
36
37
    /**
38
     * @var \Shopware\Models\Article\Detail
39
     */
40
    private $detailRepository;
41
42
    /**
43
     * @var \ShopwarePlugins\Connect\Components\ConnectExport
44
     */
45
    private $connectExport;
46
47
    /**
48
     * @var VariantRegenerator
49
     */
50
    private $variantRegenerator;
51
52
    /**
53
     * @var Helper
54
     */
55
    private $helper;
56
57
    /**
58
     * @var Config
59
     */
60
    private $config;
61
62
    public function __construct(
63
        Gateway $connectGateway,
64
        ModelManager $modelManager,
65
        ConnectExport $connectExport,
66
        VariantRegenerator $variantRegenerator,
67
        Helper $helper,
68
        Config $config
69
    ) {
70
        parent::__construct();
71
        $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...
72
        $this->modelManager = $modelManager;
73
        $this->connectExport = $connectExport;
74
        $this->variantRegenerator = $variantRegenerator;
75
        $this->helper = $helper;
76
        $this->config = $config;
77
    }
78
79
    public function getSubscribedEvents()
80
    {
81
        return array(
82
            'Shopware_Controllers_Backend_Article::preparePricesAssociatedData::after' => 'enforceConnectPriceWhenSaving',
83
            'Enlight_Controller_Action_PostDispatch_Backend_Article' => 'extendBackendArticle',
84
            'Enlight_Controller_Action_PreDispatch_Backend_Article' => 'preBackendArticle',
85
            'Enlight_Controller_Action_PostDispatch_Frontend_Detail' => 'modifyConnectArticle',
86
            'Enlight_Controller_Action_PreDispatch_Frontend_Detail' => 'extendFrontendArticle'
87
        );
88
    }
89
90
    /**
91
     * @return \Shopware\Models\Article\Detail
92
     */
93
    public function getDetailRepository()
94
    {
95
        if (!$this->detailRepository) {
96
            $this->detailRepository = $this->modelManager->getRepository('Shopware\Models\Article\Detail');
97
        }
98
99
        return $this->detailRepository;
100
    }
101
102
    /**
103
     * @return \Shopware\Components\Model\ModelRepository|Group
104
     */
105
    public function getCustomerGroupRepository()
106
    {
107
        if (!$this->customerGroupRepository) {
108
            $this->customerGroupRepository = $this->modelManager->getRepository('Shopware\Models\Customer\Group');
109
        }
110
        return $this->customerGroupRepository;
111
    }
112
113
    /**
114
     * @param \Enlight_Event_EventArgs $args
115
     */
116
    public function preBackendArticle(\Enlight_Event_EventArgs $args)
117
    {
118
        /** @var $subject \Enlight_Controller_Action */
119
        $subject = $args->getSubject();
120
        $request = $subject->Request();
121
122
        switch ($request->getActionName()) {
123
            case 'saveDetail':
124
                if ($request->getParam('standard')) {
125
                    $this->generateMainVariantChange($request->getParam('id'));
126
                }
127
                break;
128
            case 'createConfiguratorVariants':
129
                if (!$articleId = $request->getParam('articleId')) {
130
                    return;
131
                }
132
133
                $this->variantRegenerator->setInitialSourceIds(
134
                    $articleId,
135
                    $this->helper->getArticleSourceIds([$articleId])
136
                );
137
                break;
138
        }
139
    }
140
141
    /**
142
     * @event Enlight_Controller_Action_PostDispatch_Backend_Article
143
     * @param \Enlight_Event_EventArgs $args
144
     */
145
    public function extendBackendArticle(\Enlight_Event_EventArgs $args)
146
    {
147
        /** @var $subject \Enlight_Controller_Action */
148
        $subject = $args->getSubject();
149
        $request = $subject->Request();
150
151
        switch ($request->getActionName()) {
152
            case 'index':
153
                $this->registerMyTemplateDir();
154
                $this->registerMySnippets();
155
                $subject->View()->extendsTemplate(
156
                    'backend/article/connect.js'
157
                );
158
                break;
159
            case 'load':
160
                $this->registerMyTemplateDir();
161
                $this->registerMySnippets();
162
                $subject->View()->extendsTemplate(
163
                    'backend/article/model/attribute_connect.js'
164
                );
165
                $subject->View()->assign('disableConnectPrice', 'true');
166
                $subject->View()->extendsTemplate(
167
                    'backend/article/view/detail/connect_tab.js'
168
                );
169
                $subject->View()->extendsTemplate(
170
                    'backend/article/view/detail/prices_connect.js'
171
                );
172
                $subject->View()->extendsTemplate(
173
                    'backend/article/controller/detail_connect.js'
174
                );
175
                $subject->View()->extendsTemplate(
176
                    'backend/article/view/detail/connect_properties.js'
177
                );
178
                break;
179
            case 'setPropertyList':
180
                // property values are saved in different ajax call then
181
                // property group and this will generate wrong Connect changes.
182
                // after the property values are saved, the temporary property group is no needed
183
                // and it will generate right Connect changes
184
                $articleId = $request->getParam('articleId', null);
185
186
                /** @var ArticleModel $article */
187
                $article = $this->modelManager->find(ArticleModel::class, $articleId);
188
189
                if (!$article) {
190
                    return;
191
                }
192
193
                if (!$article->getPropertyGroup()) {
194
                    return;
195
                }
196
197
                // Check if entity is a connect product
198
                $attribute = $this->helper->getConnectAttributeByModel($article);
199
                if (!$attribute) {
200
                    return;
201
                }
202
203
                // if article is not exported to Connect
204
                // don't need to generate changes
205
                if (!$this->helper->isProductExported($attribute) || !empty($attribute->getShopId())) {
206
                    return;
207
                }
208
209
                if (!$this->hasPriceType()) {
210
                    return;
211
                }
212
213
                $detail = $article->getMainDetail();
214
215
                if ($detail->getAttribute()->getConnectPropertyGroup()) {
216
                    $detail->getAttribute()->setConnectPropertyGroup(null);
217
                    $this->modelManager->persist($detail);
218
                    $this->modelManager->flush();
219
                }
220
221
                $sourceIds = Shopware()->Db()->fetchCol(
222
                    'SELECT source_id FROM s_plugin_connect_items WHERE article_id = ?',
223
                    array($article->getId())
224
                );
225
226
                $this->connectExport->export($sourceIds);
227
                break;
228
            case 'createConfiguratorVariants':
229
                // main detail should be updated as well, because shopware won't call lifecycle event
230
                // even postUpdate of Detail. By this way Connect will generate change for main variant,
231
                // otherwise $product->variant property is an empty array
232
                // if main detail is not changed, Connect SDK won't generate change for it.
233
                // ticket CON-3747
234
                if (!$articleId = $request->getParam('articleId')) {
235
                    return;
236
                }
237
238
                $this->regenerateChangesForArticle($articleId);
239
                break;
240
            case 'getPropertyList':
241
                $subject->View()->data = $this->addConnectFlagToProperties(
242
                    $subject->View()->data
243
                );
244
                break;
245
            case 'deleteAllVariants':
246
                if ($articleId = $request->getParam('articleId')) {
247
                    /** @var ArticleModel $article */
248
                    $article = $this->modelManager->find(ArticleModel::class, (int) $articleId);
249
                    if (!$article) {
250
                        return;
251
                    }
252
253
                    $this->deleteVariants($articleId);
254
                }
255
                break;
256
            default:
257
                break;
258
        }
259
    }
260
261
    /**
262
     * @param int $articleId
263
     */
264
    public function regenerateChangesForArticle($articleId)
265
    {
266
        $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...
267
        if ($autoUpdateProducts == Config::UPDATE_MANUAL) {
268
            return;
269
        }
270
271
        /** @var \Shopware\Models\Article\Article $article */
272
        $article = $this->modelManager->getRepository(ArticleModel::class)->find((int)$articleId);
273
        if (!$article) {
274
            return;
275
         }
276
277
        $attribute = $this->helper->getConnectAttributeByModel($article);
278
        if (!$attribute) {
279
            return;
280
        }
281
282
        // Check if entity is a connect product
283
        if (!$this->helper->isProductExported($attribute)) {
284
            return;
285
        }
286
287
        $this->variantRegenerator->setCurrentSourceIds(
288
            $articleId,
289
            $this->helper->getArticleSourceIds([$articleId])
290
        );
291
        $this->variantRegenerator->generateChanges($articleId);
292
    }
293
294
    /**
295
     * Delete all variants of given product except main one
296
     *
297
     * @param int $articleId
298
     */
299
    private function deleteVariants($articleId)
300
    {
301
        $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...
302
        if ($autoUpdateProducts == Config::UPDATE_MANUAL) {
303
            return;
304
        }
305
306
        /** @var \Shopware\Models\Article\Article $article */
307
        $article = $this->modelManager->getRepository(ArticleModel::class)->find((int)$articleId);
308
        if (!$article) {
309
            return;
310
        }
311
312
        $connectAttribute = $this->helper->getConnectAttributeByModel($article);
313
        if (!$connectAttribute) {
314
            return;
315
        }
316
317
        // Check if entity is a connect product
318
        if (!$this->helper->isProductExported($connectAttribute)) {
319
            return;
320
        }
321
322
        $mainVariantSourceId = $connectAttribute->getSourceId();
323
        $sourceIds = array_filter(
324
            $this->helper->getArticleSourceIds([$article->getId()]),
325
            function ($sourceId) use ($mainVariantSourceId) {
326
                return $sourceId != $mainVariantSourceId;
327
            }
328
        );
329
330
        foreach ($sourceIds as $sourceId) {
331
            $this->getSDK()->recordDelete($sourceId);
332
        }
333
334
        $this->connectExport->updateConnectItemsStatus($sourceIds, Attribute::STATUS_DELETE);
335
    }
336
337
    public function addConnectFlagToProperties($data)
338
    {
339
        $groups = [];
340
        foreach ($data as $group) {
341
            $options = [];
342
            foreach ($group['value'] as $value) {
343
                $element = $value;
344
                $optionId = $value['id'];
345
                $valueModel = $this->modelManager->getRepository('Shopware\Models\Property\Value')->find($optionId);
346
347
                $attribute = null;
348
                if ($valueModel) {
349
                    $attribute = $valueModel->getAttribute();
350
                }
351
352
                if ($attribute && $attribute->getConnectIsRemote()) {
353
                    $element['connect'] = true;
354
                } else {
355
                    $element['connect'] = false;
356
                }
357
                $options[] = $element;
358
            }
359
360
            $group['value'] = $options;
361
            $groups[] = $group;
362
        }
363
364
        return $groups;
365
    }
366
367
    /**
368
     * @param $detailId
369
     */
370
    public function generateMainVariantChange($detailId)
371
    {
372
        $detail = $this->getDetailRepository()->findOneBy(array('id' => $detailId));
373
374
        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...
375
            return;
376
        }
377
378
        //if it is already main variant dont generate MakeMainVariant change
379
        if ($detail->getKind() == 1) {
380
            return;
381
        }
382
383
        $attribute = $this->helper->getConnectAttributeByModel($detail);
384
385
        if (!$attribute) {
386
            return;
387
        }
388
        // Check if entity is a connect product
389
        if (!$this->helper->isProductExported($attribute)) {
390
            return;
391
        }
392
393
        if (!$this->hasPriceType()) {
394
            return;
395
        }
396
397
        $groupId = $attribute->getGroupId() ? $attribute->getGroupId() : $attribute->getArticleId();
398
399
        $mainVariant = new MakeMainVariant(array(
400
            'sourceId' => $attribute->getSourceId(),
401
            'groupId' => $groupId
402
        ));
403
404
        try {
405
            $this->getSDK()->makeMainVariant($mainVariant);
406
        } catch (\Exception $e) {
407
            // if sn is not available, proceed without exception
408
        }
409
    }
410
411
    /**
412
     * When saving prices make sure, that the connectPrice is stored in net
413
     *
414
     * @param \Enlight_Hook_HookArgs $args
415
     */
416
    public function enforceConnectPriceWhenSaving(\Enlight_Hook_HookArgs $args)
417
    {
418
        /** @var array $prices */
419
        $prices = $args->getReturn();
420
421
        $connectCustomerGroup = $this->getConnectCustomerGroup();
422
        if (!$connectCustomerGroup) {
423
            return;
424
        }
425
        $connectCustomerGroupKey = $connectCustomerGroup->getKey();
426
        $defaultPrices = array();
427
        foreach ($prices as $key => $priceData) {
428
            if ($priceData['customerGroupKey'] == $connectCustomerGroupKey) {
429
                return;
430
            }
431
            if ($priceData['customerGroupKey'] == 'EK') {
432
                $defaultPrices[] = $priceData;
433
            }
434
        }
435
436
        foreach ($defaultPrices as $price) {
437
            $prices[] = array(
438
                'from' => $price['from'],
439
                'to' => $price['to'],
440
                'price' => $price['price'],
441
                'pseudoPrice' => $price['pseudoPrice'],
442
                'basePrice' => $price['basePrice'],
443
                'percent' => $price['percent'],
444
                'customerGroup' => $connectCustomerGroup,
445
                'article' => $price['article'],
446
                'articleDetail' => $price['articleDetail'],
447
            );
448
        }
449
450
        $args->setReturn($prices);
451
    }
452
453
    /**
454
     * @return \Shopware\Models\Customer\Group|null
455
     */
456 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...
457
    {
458
        $repo = Shopware()->Models()->getRepository('Shopware\Models\Attribute\CustomerGroup');
459
        /** @var \Shopware\Models\Attribute\CustomerGroup $model */
460
        $model = $repo->findOneBy(array('connectGroup' => true));
461
462
        $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...
463
        if ($model && $model->getCustomerGroup()) {
464
            return $model->getCustomerGroup();
465
        }
466
467
        return null;
468
    }
469
470
    /**
471
     * Load article detail
472
     *
473
     * @param \Enlight_Event_EventArgs $args
474
     */
475
    public function extendFrontendArticle(\Enlight_Event_EventArgs $args)
476
    {
477
        /** @var \Enlight_Controller_Request_RequestHttp $request */
478
        $request = $args->getSubject()->Request();
479
        if ($request->getActionName() != 'index') {
480
            return;
481
        }
482
483
        $detailId = (int) $request->sArticleDetail;
484
        if ($detailId === 0) {
485
            return;
486
        }
487
488
        /** @var \Shopware\Models\Article\Detail $detailModel */
489
        $detailModel = Shopware()->Models()->getRepository('Shopware\Models\Article\Detail')->find($detailId);
490
        if (!$detailModel) {
491
            return;
492
        }
493
494
        $params = array();
495
        /** @var \Shopware\Models\Article\Configurator\Option $option */
496
        foreach ($detailModel->getConfiguratorOptions() as $option) {
497
            $groupId = $option->getGroup()->getId();
498
            $params[$groupId] = $option->getId();
499
        }
500
        $request->setPost('group', $params);
501
    }
502
503
    /**
504
     * Should be possible to buy connect products
505
     * when they're not in stock.
506
     * Depends on remote shop configuration.
507
     *
508
     * @param \Enlight_Event_EventArgs $args
509
     */
510
    public function modifyConnectArticle(\Enlight_Event_EventArgs $args)
511
    {
512
        /** @var \Enlight_Controller_Request_RequestHttp $request */
513
        $request = $args->getSubject()->Request();
514
515
        if ($request->getActionName() != 'index') {
516
            return;
517
        }
518
        $subject = $args->getSubject();
519
        $article = $subject->View()->getAssign('sArticle');
520
        if (!$article) {
521
            return;
522
        }
523
524
        // when article stock is greater than 0
525
        // we don't need to modify it.
526
        if ($article['instock'] > 0) {
527
            return;
528
        }
529
530
        $articleId = $article['articleID'];
531
        $remoteShopId = $this->getRemoteShopId($articleId);
532
        if (!$remoteShopId) {
533
            // article is not imported via Connect
534
            return;
535
        }
536
537
        /** @var \Shopware\Models\Article\Article $articleModel */
538
        $articleModel = Shopware()->Models()->getRepository('Shopware\Models\Article\Article')->find($articleId);
539
        if (!$articleModel) {
540
            return;
541
        }
542
543
        $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...
544
        if ($shopConfiguration->sellNotInStock && !$articleModel->getLastStock()) {
545
            // if selNotInStock is = true and article getLastStock = false
546
            // we don't need to modify it
547
            return;
548
        }
549
550
        if (!$shopConfiguration->sellNotInStock && $articleModel->getLastStock()) {
551
            // if sellNotInStock is = false and article getLastStock = true
552
            // we don't need to modify it
553
            return;
554
        }
555
556
        // sellNotInStock is opposite on articleLastStock
557
        // when it's true, lastStock must be false
558
        $articleModel->setLastStock(!$shopConfiguration->sellNotInStock);
559
        Shopware()->Models()->persist($articleModel);
560
        Shopware()->Models()->flush();
561
562
        // modify assigned article
563
        if ($shopConfiguration->sellNotInStock) {
564
            $article['laststock'] = false;
565
            $article['instock'] = 100;
566
            $article['isAvailable'] = true;
567
        } else {
568
            $article['laststock'] = true;
569
        }
570
        $subject->View()->assign('sArticle', $article);
571
    }
572
573
    /**
574
     * Not using the default helper-methods here, in order to keep this small and without any dependencies
575
     * to the SDK
576
     *
577
     * @param $id
578
     * @return boolean|int
579
     */
580
    private function getRemoteShopId($id)
581
    {
582
        $sql = 'SELECT shop_id FROM s_plugin_connect_items WHERE article_id = ? AND shop_id IS NOT NULL';
583
        return Shopware()->Db()->fetchOne($sql, array($id));
584
    }
585
}