Completed
Pull Request — master (#412)
by Jonas
04:21
created

Helper   D

Complexity

Total Complexity 85

Size/Duplication

Total Lines 944
Duplicated Lines 11.12 %

Coupling/Cohesion

Components 3
Dependencies 8

Importance

Changes 0
Metric Value
dl 105
loc 944
rs 4.4444
c 0
b 0
f 0
wmc 85
lcom 3
cbo 8

45 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A getDefaultCustomerGroup() 0 6 1
B getArticleModelByProduct() 24 24 2
B getArticleDetailModelByProduct() 26 26 2
A getDetailByNumber() 0 4 1
B getConnectArticleModel() 0 26 2
A getArticleIdsByNumber() 15 15 1
A getConnectArticleDetailModel() 0 6 1
B updateConnectProducts() 0 35 1
A checkIfConnectCategoriesHaveToBeRecreated() 0 6 1
A checkIfShopIdHasToBeAddedToConnectCategories() 0 6 1
A getRemoteProducts() 0 4 1
A getLocalProduct() 0 4 1
B hasBasketConnectProducts() 0 26 2
B getConnectAttributeByModel() 0 19 5
A getConnectAttributesByArticle() 0 14 1
B isProductExported() 0 21 5
A hasExportedVariants() 0 15 1
B getOrCreateConnectAttributeByModel() 0 31 4
A getOrCreateConnectAttributes() 0 10 2
A generateSourceId() 0 14 2
A getConnectCategoryForProduct() 0 4 1
A getMostRelevantConnectCategory() 0 12 1
A getUpdateFlags() 0 4 1
A getUnit() 0 6 1
A clearArticleCache() 0 7 1
B prepareConnectUnit() 0 24 5
A clearConnectReservation() 0 4 1
A getArticleSourceIds() 0 14 2
A getShopProductId() 0 13 1
A isRemoteArticleDetail() 0 17 3
A isRemoteArticleDetailDBAL() 0 11 1
A explodeArticleId() 0 12 3
A createProductModel() 0 10 1
B getArticleByRemoteProduct() 25 26 2
A getSourceIdsFromArticleId() 0 11 1
A updateUnitInRelatedProducts() 0 12 1
A isMainVariant() 0 16 2
A getLocalArticleCount() 0 4 1
C recreateConnectCategories() 7 43 7
A getProductCountForCategoryRecovery() 0 8 1
B addShopIdToConnectCategories() 0 25 4
A addShopIdToConnectCategory() 0 13 2
A createCategoryWithShopId() 8 18 2
A assignLocalCategories() 0 12 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

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 Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Helper 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 Helper, and based on these observations, apply Extract Interface, too.

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\Components;
9
10
use Shopware\Connect\Struct\Product;
11
use Shopware\CustomModels\Connect\AttributeRepository;
12
use Shopware\Models\Article\Article as ProductModel;
13
use Shopware\Components\Model\ModelManager;
14
use Doctrine\ORM\Query;
15
use Shopware\CustomModels\Connect\Attribute as ConnectAttribute;
16
use Shopware\CustomModels\Connect\Attribute;
17
use Shopware\Models\Article\Detail as ProductDetail;
18
use Shopware\Models\Article\Unit;
19
use Shopware\Models\Customer\Group;
20
use ShopwarePlugins\Connect\Components\Utils\UnitMapper;
21
use ShopwarePlugins\Connect\Struct\ShopProductId;
22
23
/**
24
 * @category  Shopware
25
 * @package   Shopware\Plugins\SwagConnect
26
 */
27
class Helper
28
{
29
    /**
30
     * @var ModelManager
31
     */
32
    private $manager;
33
34
    /**
35
     * @var CategoryQuery
36
     */
37
    private $connectCategoryQuery;
38
39
    /**
40
     * @var ProductQuery
41
     */
42
    private $connectProductQuery;
43
44
    /**
45
     * @param ModelManager $manager
46
     * @param CategoryQuery
47
     * @param ProductQuery
48
     */
49
    public function __construct(
50
        ModelManager $manager,
51
        CategoryQuery $connectCategoryQuery,
52
        ProductQuery $connectProductQuery
53
    ) {
54
        $this->manager = $manager;
55
        $this->connectCategoryQuery = $connectCategoryQuery;
56
        $this->connectProductQuery = $connectProductQuery;
57
    }
58
59
    /**
60
     * @return Group
61
     */
62
    public function getDefaultCustomerGroup()
63
    {
64
        $repository = $this->manager->getRepository('Shopware\Models\Customer\Group');
65
66
        return $repository->findOneBy(['key' => 'EK']);
67
    }
68
69
    /**
70
     * Returns an article model for a given (sdk) product.
71
     *
72
     * @param Product $product
73
     * @param int $mode
74
     * @return null|ProductModel
75
     */
76 View Code Duplication
    public function getArticleModelByProduct(Product $product, $mode = Query::HYDRATE_OBJECT)
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...
77
    {
78
        $builder = $this->manager->createQueryBuilder();
79
        $builder->select(['ba', 'a']);
80
        $builder->from('Shopware\CustomModels\Connect\Attribute', 'ba');
81
        $builder->join('ba.article', 'a');
82
83
        $builder->where('ba.shopId = :shopId AND ba.sourceId = :sourceId');
84
        $query = $builder->getQuery();
85
86
        $query->setParameter('shopId', $product->shopId);
87
        $query->setParameter('sourceId', (string) $product->sourceId);
88
        $result = $query->getResult(
89
            $mode
90
        );
91
92
        if (isset($result[0])) {
93
            $attribute = $result[0];
94
95
            return $attribute->getArticle();
96
        }
97
98
        return null;
99
    }
100
101
    /**
102
     * @param Product $product
103
     * @param int $mode
104
     * @return null|ProductDetail
105
     */
106 View Code Duplication
    public function getArticleDetailModelByProduct(Product $product, $mode = Query::HYDRATE_OBJECT)
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...
107
    {
108
        $builder = $this->manager->createQueryBuilder();
109
        $builder->select(['ba', 'd']);
110
        $builder->from('Shopware\CustomModels\Connect\Attribute', 'ba');
111
        $builder->join('ba.articleDetail', 'd');
112
        $builder->leftJoin('d.attribute', 'at');
113
        $builder->where('ba.shopId = :shopId AND ba.sourceId = :sourceId');
114
115
        $query = $builder->getQuery();
116
        $query->setParameter('shopId', $product->shopId);
117
        $query->setParameter('sourceId', (string) $product->sourceId);
118
119
        $result = $query->getResult(
120
            $mode
121
        );
122
123
        if (isset($result[0])) {
124
            /** @var \Shopware\CustomModels\Connect\Attribute $attribute */
125
            $attribute = $result[0];
126
127
            return $attribute->getArticleDetail();
128
        }
129
130
        return null;
131
    }
132
133
    /**
134
     * Get article detail by his number
135
     *
136
     * @param string $number
137
     * @return null|ProductDetail
138
     */
139
    public function getDetailByNumber($number)
140
    {
141
        return $this->manager->getRepository(ProductDetail::class)->findOneBy(['number' => $number]);
142
    }
143
144
    public function getConnectArticleModel($sourceId, $shopId)
145
    {
146
        $builder = $this->manager->createQueryBuilder();
147
        $builder->select(['ba', 'a']);
148
        $builder->from('Shopware\CustomModels\Connect\Attribute', 'ba');
149
        $builder->join('ba.article', 'a');
150
        $builder->join('a.mainDetail', 'd');
151
        $builder->leftJoin('d.attribute', 'at');
152
153
        $builder->where('ba.shopId = :shopId AND ba.sourceId = :sourceId');
154
        $query = $builder->getQuery();
155
156
        $query->setParameter('shopId', $shopId);
157
        $query->setParameter('sourceId', (string) $sourceId);
158
        $result = $query->getResult(
159
            $query::HYDRATE_OBJECT
160
        );
161
162
        if (isset($result[0])) {
163
            $attribute = $result[0];
164
165
            return $attribute->getArticle();
166
        }
167
168
        return null;
169
    }
170
171
    /**
172
     * @param array $orderNumbers
173
     * @return array
174
     */
175 View Code Duplication
    public function getArticleIdsByNumber(array $orderNumbers)
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...
176
    {
177
        $builder = $this->manager->getConnection()->createQueryBuilder();
178
179
        $rows = $builder->select('d.articleID as articleId')
180
            ->from('s_articles_details', 'd')
181
            ->where('d.ordernumber IN (:orderNumbers)')
182
            ->setParameter('orderNumbers', $orderNumbers, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY)
183
            ->execute()
184
            ->fetchAll();
185
186
        return array_map(function ($row) {
187
            return $row['articleId'];
188
        }, $rows);
189
    }
190
191
    /**
192
     * Returns article detail model by
193
     * given sourceId and shopId
194
     *
195
     * @param string $sourceId
196
     * @param int $shopId
197
     * @return null|ProductDetail
198
     */
199
    public function getConnectArticleDetailModel($sourceId, $shopId)
200
    {
201
        $product = new Product(['sourceId' => $sourceId, 'shopId' => $shopId]);
202
203
        return $this->getArticleDetailModelByProduct($product);
204
    }
205
206
    /**
207
     * Helper to update the connect_items table
208
     */
209
    public function updateConnectProducts()
210
    {
211
        // Insert new articles
212
        $sql = "
213
        INSERT INTO `s_plugin_connect_items` (article_id, article_detail_id, source_id)
214
        SELECT a.id, ad.id, IF(ad.kind = 1, a.id, CONCAT(a.id, '-', ad.id)) as sourceID
215
216
        FROM s_articles a
217
218
        LEFT JOIN `s_articles_details` ad
219
        ON a.id = ad.articleId
220
221
        LEFT JOIN `s_plugin_connect_items` bi
222
        ON bi.article_detail_id = ad.id
223
224
225
        WHERE a.id IS NOT NULL
226
        AND ad.id IS NOT NULL
227
        AND bi.id IS NULL
228
        ";
229
230
        $this->manager->getConnection()->exec($sql);
231
232
        // Delete removed articles from s_plugin_connect_items
233
        $sql = '
234
        DELETE bi FROM `s_plugin_connect_items`  bi
235
236
        LEFT JOIN `s_articles_details` ad
237
        ON ad.id = bi.article_detail_id
238
239
        WHERE ad.id IS NULL
240
        ';
241
242
        $this->manager->getConnection()->exec($sql);
243
    }
244
245
    /**
246
     * Returns wether connect categories have to be recreated or not
247
     * @return bool
248
     */
249
    public function checkIfConnectCategoriesHaveToBeRecreated()
250
    {
251
        $configComponent = ConfigFactory::getConfigInstance();
252
        $result = $configComponent->getConfig('recreateConnectCategories');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $result is correct as $configComponent->getCon...eateConnectCategories') (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...
253
        return $result === 0;
254
    }
255
256
    /**
257
     * Returns wether shopId has to be added to ConnectCategories
258
     * @return bool
259
     */
260
    public function checkIfShopIdHasToBeAddedToConnectCategories()
261
    {
262
        $configComponent = ConfigFactory::getConfigInstance();
263
        $result = $configComponent->getConfig('addShopIdToConnectCategories');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $result is correct as $configComponent->getCon...IdToConnectCategories') (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...
264
        return $result === 0;
265
    }
266
267
    /**
268
     * Returns a remote connectProduct e.g. for checkout maniputlations
269
     *
270
     * @param array $ids
271
     * @param int $shopId
272
     * @return array
273
     */
274
    public function getRemoteProducts(array $ids, $shopId)
275
    {
276
        return $this->connectProductQuery->getRemote($ids, $shopId);
277
    }
278
279
    /**
280
     * Returns a local connectProduct for export
281
     *
282
     * @param array $sourceIds
283
     * @return Product[]
284
     */
285
    public function getLocalProduct(array $sourceIds)
286
    {
287
        return $this->connectProductQuery->getLocal($sourceIds);
288
    }
289
290
    /**
291
     * Does the current basket contain connect products?
292
     *
293
     * @param $session
294
     * @return bool
295
     */
296
    public function hasBasketConnectProducts($session, $userId = null)
297
    {
298
        $connection = $this->manager->getConnection();
299
        $sql = 'SELECT ob.articleID
300
301
            FROM s_order_basket ob
302
303
            INNER JOIN s_plugin_connect_items bi
304
            ON bi.article_id = ob.articleID
305
            AND bi.shop_id IS NOT NULL
306
307
            WHERE ob.sessionID=?
308
            ';
309
        $whereClause = [$session];
310
311
        if ($userId > 0) {
312
            $sql .= ' OR userID=?';
313
            $whereClause[] = $userId;
314
        }
315
316
        $sql .= ' LIMIT 1';
317
318
        $result = $connection->fetchArray($sql, $whereClause);
319
320
        return !empty($result);
321
    }
322
323
    /**
324
     * Will return the connectAttribute for a given model. The model can be an Article\Article or Article\Detail
325
     *
326
     * @param $model ProductModel|ProductDetail
327
     * @return ConnectAttribute
328
     */
329
    public function getConnectAttributeByModel($model)
330
    {
331
        if (!$model->getId()) {
332
            return false;
333
        }
334
        $repository = $this->manager->getRepository('Shopware\CustomModels\Connect\Attribute');
335
336
        if ($model instanceof ProductModel) {
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...
337
            if (!$model->getMainDetail()) {
338
                return false;
339
            }
340
341
            return $repository->findOneBy(['articleDetailId' => $model->getMainDetail()->getId()]);
342
        } elseif ($model instanceof ProductDetail) {
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...
343
            return $repository->findOneBy(['articleDetailId' => $model->getId()]);
344
        }
345
346
        return false;
347
    }
348
349
    /**
350
     * Returns connectAttributes for all article details by given article object
351
     *
352
     * @param ProductModel $article
353
     * @return \Shopware\CustomModels\Connect\Attribute[]
354
     */
355
    public function getConnectAttributesByArticle(ProductModel $article)
356
    {
357
        $builder = $this->manager->createQueryBuilder();
358
        $builder->select(['connectAttribute', 'detail']);
359
        $builder->from('Shopware\CustomModels\Connect\Attribute', 'connectAttribute');
360
        $builder->innerJoin('connectAttribute.articleDetail', 'detail');
361
362
        $builder->where('connectAttribute.articleId = :articleId');
363
        $query = $builder->getQuery();
364
365
        $query->setParameter('articleId', $article->getId());
366
367
        return $query->getResult();
368
    }
369
370
    /**
371
     * Returns true when product is exported to Connect
372
     *
373
     * @param Attribute $connectAttribute
374
     * @return bool
375
     */
376
    public function isProductExported(Attribute $connectAttribute)
377
    {
378
        $status = $connectAttribute->getExportStatus();
379
        if ($connectAttribute->isExported()) {
380
            return true;
381
        }
382
383
        if ($status == Attribute::STATUS_INSERT) {
384
            return true;
385
        }
386
387
        if ($status == Attribute::STATUS_UPDATE) {
388
            return true;
389
        }
390
391
        if ($status == Attribute::STATUS_SYNCED) {
392
            return true;
393
        }
394
395
        return false;
396
    }
397
398
    /**
399
     * Verifies that at least one variant from
400
     * same article is exported.
401
     *
402
     * @param Attribute $connectAttribute
403
     * @return bool
404
     */
405
    public function hasExportedVariants(Attribute $connectAttribute)
406
    {
407
        $builder = $this->manager->getConnection()->createQueryBuilder();
408
        $builder->select('COUNT(spci.id)')
409
            ->from('s_plugin_connect_items', 'spci')
410
            ->where('spci.article_id = :articleId AND spci.export_status IN (:exportStatus) AND spci.shop_id IS NULL')
411
            ->setParameter('articleId', $connectAttribute->getArticleId(), \PDO::PARAM_INT)
412
            ->setParameter(
413
                ':exportStatus',
414
                [Attribute::STATUS_INSERT, Attribute::STATUS_UPDATE, Attribute::STATUS_SYNCED],
415
                \Doctrine\DBAL\Connection::PARAM_STR_ARRAY
416
            );
417
418
        return $builder->execute()->fetchColumn() > 0;
419
    }
420
421
    /**
422
     * Helper method to create a connect attribute on the fly
423
     *
424
     * @param $model
425
     * @throws \RuntimeException
426
     * @return ConnectAttribute
427
     */
428
    public function getOrCreateConnectAttributeByModel($model)
429
    {
430
        $attribute = $this->getConnectAttributeByModel($model);
431
432
        if (!$attribute) {
433
            $attribute = new ConnectAttribute();
434
            $attribute->setPurchasePriceHash('');
435
            $attribute->setOfferValidUntil('');
436
            $attribute->setStream('');
437
438
            if ($model instanceof ProductModel) {
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...
439
                $attribute->setArticle($model);
440
                $attribute->setArticleDetail($model->getMainDetail());
441
                $attribute->setSourceId(
442
                    $this->generateSourceId($model->getMainDetail())
443
                );
444
            } elseif ($model instanceof ProductDetail) {
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...
445
                $attribute->setArticle($model->getArticle());
446
                $attribute->setArticleDetail($model);
447
                $attribute->setSourceId(
448
                    $this->generateSourceId($model)
449
                );
450
            } else {
451
                throw new \RuntimeException('Passed model needs to be an article or an article detail');
452
            }
453
            $this->manager->persist($attribute);
454
            $this->manager->flush($attribute);
455
        }
456
457
        return $attribute;
458
    }
459
460
    /**
461
     * Returns connect attributes for article
462
     * and all variants.
463
     * If connect attribute does not exist
464
     * it will be created.
465
     *
466
     * @param ProductModel $article
467
     * @return array
468
     */
469
    public function getOrCreateConnectAttributes(ProductModel $article)
470
    {
471
        $attributes = [];
472
        /** @var \Shopware\Models\Article\Detail $detail */
473
        foreach ($article->getDetails() as $detail) {
474
            $attributes[] = $this->getOrCreateConnectAttributeByModel($detail);
475
        }
476
477
        return $attributes;
478
    }
479
480
    /**
481
     * Generate sourceId
482
     *
483
     * @param ProductDetail $detail
484
     * @return string
485
     */
486
    public function generateSourceId(ProductDetail $detail)
487
    {
488
        if ($detail->getKind() == 1) {
489
            $sourceId = (string) $detail->getArticle()->getId();
490
        } else {
491
            $sourceId = sprintf(
492
                '%s-%s',
493
                $detail->getArticle()->getId(),
494
                $detail->getId()
495
            );
496
        }
497
498
        return $sourceId;
499
    }
500
501
    /**
502
     * @param $id
503
     * @return array
504
     */
505
    public function getConnectCategoryForProduct($id)
506
    {
507
        return $this->connectCategoryQuery->getConnectCategoryForProduct($id);
508
    }
509
510
    public function getMostRelevantConnectCategory($categories)
511
    {
512
        usort(
513
            $categories,
514
            [
515
                $this->connectCategoryQuery->getRelevanceSorter(),
516
                'sortConnectCategoriesByRelevance'
517
            ]
518
        );
519
520
        return array_pop($categories);
521
    }
522
523
    /**
524
     * Defines the update flags
525
     *
526
     * @return array
527
     */
528
    public function getUpdateFlags()
529
    {
530
        return [2 => 'shortDescription', 4 => 'longDescription', 8 => 'name', 16 => 'image', 32 => 'price', 64 => 'imageInitialImport', 128 => 'additionalDescription'];
531
    }
532
533
    /**
534
     * Returns shopware unit entity
535
     *
536
     * @param $unitKey
537
     * @return \Shopware\Models\Article\Unit
538
     */
539
    public function getUnit($unitKey)
540
    {
541
        $repository = $this->manager->getRepository('Shopware\Models\Article\Unit');
542
543
        return $repository->findOneBy(['unit' => $unitKey]);
544
    }
545
546
    /**
547
     * Clear article cache
548
     */
549
    public function clearArticleCache($articleId)
550
    {
551
        Shopware()->Events()->notify(
552
            'Shopware_Plugins_HttpCache_InvalidateCacheId',
553
            ['cacheId' => 'a' . $articleId]
554
        );
555
    }
556
557
    /**
558
     * Replace unit and ref quantity
559
     * @param $products
560
     * @return mixed
561
     */
562
    public function prepareConnectUnit($products)
563
    {
564
        foreach ($products as &$p) {
565
            if ($p->attributes['unit']) {
566
                $configComponent = ConfigFactory::getConfigInstance();
567
                /** @var \ShopwarePlugins\Connect\Components\Utils\UnitMapper $unitMapper */
568
                $unitMapper = new UnitMapper(
569
                    $configComponent,
570
                    $this->manager
571
                );
572
573
                $p->attributes['unit'] = $unitMapper->getConnectUnit($p->attributes['unit']);
574
            }
575
576
            if ($p->attributes['ref_quantity']) {
577
                $intRefQuantity = (int) $p->attributes['ref_quantity'];
578
                if ($p->attributes['ref_quantity'] - $intRefQuantity <= 0.0001) {
579
                    $p->attributes['ref_quantity'] = $intRefQuantity;
580
                }
581
            }
582
        }
583
584
        return $products;
585
    }
586
587
    /**
588
     * Removes connect reservation from session
589
     */
590
    public function clearConnectReservation()
591
    {
592
        Shopware()->Session()->connectReservation = null;
593
    }
594
595
    /**
596
     * Collect sourceIds by given article ids
597
     *
598
     * @param array $articleIds
599
     * @return array
600
     */
601
    public function getArticleSourceIds(array $articleIds)
602
    {
603
        if (empty($articleIds)) {
604
            return [];
605
        }
606
607
        /** @var AttributeRepository $repo */
608
        $repo = $this->manager->getRepository(ConnectAttribute::class);
609
610
        return array_merge(
611
            $repo->findSourceIds($articleIds, 1),
612
            $repo->findSourceIds($articleIds, 2)
613
        );
614
    }
615
616
    /**
617
     * Get ShopProductId struct by given article detail id
618
     * It contains product sourceId and shopId.
619
     * If $articleDetailId is local product, $shopProductId->shopId will be null.
620
     *
621
     * @param int $articleDetailId
622
     * @return ShopProductId
623
     */
624
    public function getShopProductId($articleDetailId)
625
    {
626
        $articleDetailId = (int) $articleDetailId;
627
        $builder = $this->manager->getConnection()->createQueryBuilder();
628
        $builder->select('items.source_id as sourceId, items.shop_id as shopId')
629
            ->from('s_plugin_connect_items', 'items')
630
            ->where('items.article_detail_id = :articleDetailIds')
631
            ->setParameter(':articleDetailIds', $articleDetailId);
632
633
        $result = $builder->execute()->fetch(\PDO::FETCH_ASSOC);
634
635
        return new ShopProductId($result);
636
    }
637
638
    /**
639
     * Check if given articleDetailId is remote product
640
     *
641
     * @param int $articleDetailId
642
     * @return bool
643
     */
644
    public function isRemoteArticleDetail($articleDetailId)
645
    {
646
        $articleDetailId = (int) $articleDetailId;
647
        $articleDetailRepository = $this->manager->getRepository('Shopware\Models\Article\Detail');
648
        /** @var \Shopware\Models\Article\Detail $detail */
649
        $detail = $articleDetailRepository->find($articleDetailId);
650
        if (!$detail) {
651
            return false;
652
        }
653
654
        $connectAttribute = $this->getConnectAttributeByModel($detail);
655
        if (!$connectAttribute) {
656
            return false;
657
        }
658
659
        return ($connectAttribute->getShopId() != null);
660
    }
661
662
    /**
663
     * Check if given articleDetailId is remote product
664
     *
665
     * @param int $articleDetailId
666
     * @return bool
667
     */
668
    public function isRemoteArticleDetailDBAL($articleDetailId)
669
    {
670
        $articleDetailId = (int) $articleDetailId;
671
        $builder = $this->manager->getConnection()->createQueryBuilder();
672
        $builder->select('items.shop_id')
673
            ->from('s_plugin_connect_items', 'items')
674
            ->where('items.article_detail_id = :articleDetailId')
675
            ->setParameter(':articleDetailId', $articleDetailId);
676
677
        return (bool) $builder->execute()->fetchColumn();
678
    }
679
680
    /**
681
     * Extract article ID and detail ID
682
     * from source ID
683
     *
684
     * @param $sourceId
685
     * @return array
686
     */
687
    public function explodeArticleId($sourceId)
688
    {
689
        $articleId = explode('-', $sourceId);
690
691
        if (isset($articleId[1]) && isset($articleId[1])) {
692
            return $articleId;
693
        }
694
695
        return [
696
            $articleId[0]
697
        ];
698
    }
699
700
    /**
701
     * Creates Shopware product model
702
     *
703
     * @param Product $product
704
     * @return ProductModel
705
     */
706
    public function createProductModel(Product $product)
707
    {
708
        //todo@sb: Add test
709
        $model = new ProductModel();
710
        $model->setActive(false);
711
        $model->setName($product->title);
712
        $this->manager->persist($model);
713
714
        return $model;
715
    }
716
717
    /**
718
     * Returns main article detail by given groupId
719
     *
720
     * @param $product
721
     * @param int $mode
722
     * @return null|ProductModel
723
     */
724 View Code Duplication
    public function getArticleByRemoteProduct(Product $product, $mode = Query::HYDRATE_OBJECT)
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...
725
    {
726
        $builder = $this->manager->createQueryBuilder();
727
        $builder->select(['ba', 'd']);
728
        $builder->from('Shopware\CustomModels\Connect\Attribute', 'ba');
729
        $builder->join('ba.articleDetail', 'd');
730
        $builder->leftJoin('d.attribute', 'at');
731
732
        $builder->where('ba.groupId = :groupId AND ba.isMainVariant = 1 AND ba.shopId = :shopId');
733
        $query = $builder->getQuery();
734
735
        $query->setParameter('groupId', $product->groupId);
736
        $query->setParameter('shopId', $product->shopId);
737
        $result = $query->getResult(
738
            $mode
739
        );
740
741
        if (isset($result[0])) {
742
            /** @var \Shopware\CustomModels\Connect\Attribute $attribute */
743
            $attribute = $result[0];
744
745
            return $attribute->getArticle();
746
        }
747
748
        return null;
749
    }
750
751
    /**
752
     * @param int $articleId
753
     * @return array
754
     */
755
    public function getSourceIdsFromArticleId($articleId)
756
    {
757
        $rows = $this->manager->getConnection()->fetchAll(
758
            'SELECT source_id FROM s_plugin_connect_items WHERE article_id = ? AND exported = 1',
759
            [$articleId]
760
        );
761
762
        return array_map(function ($row) {
763
            return $row['source_id'];
764
        }, $rows);
765
    }
766
767
    /**
768
     * @param Unit $localUnit
769
     * @param string $remoteUnit
770
     */
771
    public function updateUnitInRelatedProducts(Unit $localUnit, $remoteUnit)
772
    {
773
        $statement = $this->manager->getConnection()->prepare('UPDATE s_articles_details sad
774
            LEFT JOIN s_articles_attributes saa ON sad.id = saa.articledetailsID
775
            SET sad.unitID = :unitId
776
            WHERE saa.connect_remote_unit = :remoteUnit');
777
778
        $statement->bindValue(':unitId', $localUnit->getId(), \PDO::PARAM_INT);
779
        $statement->bindValue(':remoteUnit', $remoteUnit, \PDO::PARAM_STR);
780
781
        $statement->execute();
782
    }
783
784
    /**
785
     * Checks whether given sourceId is main variant.
786
     * Works only with local products.
787
     * SourceIds pattern is articleId-variantId (58-142)
788
     *
789
     * For remote product check is_main_variant flag in
790
     * s_plugin_connect_items
791
     *
792
     * @param string $sourceId
793
     * @return bool
794
     */
795
    public function isMainVariant($sourceId)
796
    {
797
        $isMainVariant = $this->manager->getConnection()->fetchColumn(
798
            'SELECT d.kind
799
              FROM s_plugin_connect_items spci
800
              LEFT JOIN s_articles_details d ON spci.article_detail_id = d.id
801
              WHERE source_id = ?',
802
            [$sourceId]
803
        );
804
805
        if ($isMainVariant != 1) {
806
            return false;
807
        }
808
809
        return true;
810
    }
811
812
    public function getLocalArticleCount()
813
    {
814
        return $this->manager->getRepository(ConnectAttribute::class)->getLocalArticleCount();
815
    }
816
817
    /**
818
     * Recreates ConnectCategories wit the specified offset and batchsize
819
     * @param int $offset
820
     * @param int $batchsize
821
     */
822
    public function recreateConnectCategories($offset, $batchsize)
823
    {
824
        $result = $this->manager->getConnection()->executeQuery('SELECT `article_id`, `category`, `shop_id` FROM `s_plugin_connect_items` WHERE shop_id IS NOT NULL GROUP BY `article_id` ORDER BY `id` LIMIT ? OFFSET ?',
825
            [$batchsize, $offset],
826
            [\PDO::PARAM_INT, \PDO::PARAM_INT]
827
        );
828
829
        while ($row = $result->fetch()) {
830
            $categories = json_decode($row['category'], true);
831
            $countAssignedCategories = $this->manager->getConnection()->executeQuery('SELECT COUNT(`connect_category_id`) AS categories_count FROM s_plugin_connect_product_to_categories WHERE articleID = ?',
832
                [$row['article_id']]
833
            )->fetchColumn();
834
835
            if (count($categories) != $countAssignedCategories) {
836
                $shopId = $row['shop_id'];
837
                foreach ($categories as $categoryKey => $category) {
838
                    $selectedCategory = $this->manager->getConnection()->executeQuery('SELECT `id` FROM s_plugin_connect_categories WHERE category_key = ? AND shop_id = ?',
839
                        [$categoryKey, $shopId]);
840 View Code Duplication
                    if (!($res = $selectedCategory->fetch())) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
841
                        $this->manager->getConnection()->executeQuery('INSERT INTO s_plugin_connect_categories (category_key, label, shop_id) VALUES (?, ?, ?)',
842
                            [$categoryKey, $category, $shopId]);
843
                        $categoryId = (int) $this->manager->getConnection()->lastInsertId();
844
                    } else {
845
                        $categoryId = (int) $res['id'];
846
                    }
847
                    $selectedProductToCategory = $this->manager->getConnection()->executeQuery('SELECT COUNT(*) FROM s_plugin_connect_product_to_categories WHERE connect_category_id = ? AND articleID = ?',
848
                        [$categoryId, (int) $row['article_id']]
849
                    )->fetchColumn();
850
                    if ((int) $selectedProductToCategory === 0) {
851
                        $this->manager->getConnection()->executeQuery('INSERT INTO s_plugin_connect_product_to_categories (connect_category_id, articleID) VALUES (?, ?)',
852
                            [$categoryId, (int) $row['article_id']]
853
                            );
854
                    }
855
                }
856
            }
857
        }
858
859
        $totalCount = $this->getProductCountForCategoryRecovery();
860
        if ($batchsize + $offset >= $totalCount) {
861
            $configComponent = ConfigFactory::getConfigInstance();
862
            $configComponent->setConfig('recreateConnectCategories', 1);
863
        }
864
    }
865
866
    /**
867
     * @return int
868
     */
869
    public function getProductCountForCategoryRecovery()
870
    {
871
        return (int) $this->manager->getConnection()->executeQuery('
872
          SELECT COUNT(*) 
873
          FROM (
874
            SELECT COUNT(*) FROM `s_plugin_connect_items` WHERE shop_id IS NOT NULL GROUP BY `article_id`
875
          ) AS Z')->fetchColumn();
876
    }
877
878
    /**
879
     * adds the shopId to the ConnectCategoriesTable
880
     * @param int $offset
881
     * @param int $batchsize
882
     */
883
    public function addShopIdToConnectCategories($offset, $batchsize)
884
    {
885
        $result = $this->manager->getConnection()->executeQuery('SELECT `article_id`, `category`, `shop_id` FROM `s_plugin_connect_items` WHERE shop_id IS NOT NULL GROUP BY `article_id` ORDER BY `id` LIMIT ? OFFSET ?',
886
            [$batchsize, $offset],
887
            [\PDO::PARAM_INT, \PDO::PARAM_INT]
888
        );
889
890
        while ($row = $result->fetch()) {
891
            $categories = json_decode($row['category'], true);
892
            $this->manager->getConnection()->executeQuery('DELETE FROM s_plugin_connect_product_to_categories WHERE articleID = ?',
893
                [$row['article_id']]
894
            );
895
896
            $shopId = (int) $row['shop_id'];
897
            foreach ($categories as $categoryKey => $category) {
898
                $this->addShopIdToConnectCategory($categoryKey, $shopId, $category, $row);
899
            }
900
        }
901
902
        $totalCount = $this->getProductCountForCategoryRecovery();
903
        if ($batchsize + $offset >= $totalCount) {
904
            $configComponent = ConfigFactory::getConfigInstance();
905
            $configComponent->setConfig('addShopIdToConnectCategories', 1);
906
        }
907
    }
908
909
    /**
910
     * @param $categoryKey
911
     * @param $shopId
912
     * @param $category
913
     * @param $row
914
     */
915
    private function addShopIdToConnectCategory($categoryKey, $shopId, $category, $row)
916
    {
917
        $selectedCategory = $this->manager->getConnection()->executeQuery('SELECT `id` FROM s_plugin_connect_categories WHERE category_key = ? AND shop_id = ?',
918
            [$categoryKey, $shopId]);
919
        if (!($res = $selectedCategory->fetch())) {
920
            $categoryId = $this->createCategoryWithShopId($categoryKey, $shopId, $category);
921
        } else {
922
            $categoryId = (int)$res['id'];
923
        }
924
        $this->manager->getConnection()->executeQuery('INSERT INTO s_plugin_connect_product_to_categories (articleID, connect_category_id) VALUES (?, ?)',
925
            [$row['article_id'], $categoryId]
926
        );
927
    }
928
929
    /**
930
     * @param string $categoryKey
931
     * @param int $shopId
932
     * @param string $category
933
     * @return int
934
     */
935
    private function createCategoryWithShopId($categoryKey, $shopId, $category)
936
    {
937
        $selectedCategory = $this->manager->getConnection()->executeQuery('SELECT `id` FROM s_plugin_connect_categories WHERE category_key = ? AND shop_id IS NULL',
938
            [$categoryKey]);
939 View Code Duplication
        if (!($res = $selectedCategory->fetch())) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
940
            $this->manager->getConnection()->executeQuery('INSERT INTO s_plugin_connect_categories (category_key, label, shop_id) VALUES (?, ?, ?)',
941
                [$categoryKey, $category, $shopId]);
942
            $createdCategoryId = (int) $this->manager->getConnection()->lastInsertId();
943
            $this->assignLocalCategories($createdCategoryId, $categoryKey);
944
945
            return $createdCategoryId;
946
        }
947
948
        $this->manager->getConnection()->executeQuery('UPDATE s_plugin_connect_categories SET shop_id = ? WHERE id = ?',
949
            [$shopId, $res['id']]);
950
951
        return (int) $res['id'];
952
    }
953
954
    /**
955
     * @param int $createdCategoryId
956
     * @param string $categoryKey
957
     */
958
    private function assignLocalCategories($createdCategoryId, $categoryKey)
959
    {
960
        $originalCategoryId = $this->manager->getConnection()->fetchColumn('SELECT `id` FROM s_plugin_connect_categories WHERE category_key = ? AND id <> ?',
961
            [$categoryKey, $createdCategoryId]);
962
        $localCategories = $this->manager->getConnection()->executeQuery('SELECT `local_category_id` FROM s_plugin_connect_categories_to_local_categories WHERE remote_category_id = ?',
963
            [$originalCategoryId])->fetchAll(\PDO::FETCH_COLUMN);
964
965
        foreach ($localCategories as $localCategoryId) {
966
            $this->manager->getConnection()->executeQuery('INSERT INTO s_plugin_connect_categories_to_local_categories (remote_category_id, local_category_id) VALUES (?, ?)',
967
                [$createdCategoryId, $localCategoryId]);
968
        }
969
    }
970
}
971