Completed
Pull Request — master (#412)
by Jonas
02:51
created

Helper::getArticleIdsByNumber()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 11

Duplication

Lines 15
Ratio 100 %

Importance

Changes 0
Metric Value
dl 15
loc 15
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 11
nc 1
nop 1
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 View Code Duplication
    public function checkIfConnectCategoriesHaveToBeRecreated()
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...
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
        if ($result === 0) {
254
            return true;
255
        }
256
257
        return false;
258
    }
259
260
    /**
261
     * Returns wether shopId has to be added to ConnectCategories
262
     * @return bool
263
     */
264 View Code Duplication
    public function checkIfShopIdHasToBeAddedToConnectCategories()
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...
265
    {
266
        $configComponent = ConfigFactory::getConfigInstance();
267
        $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...
268
        if ($result === 0) {
269
            return true;
270
        }
271
272
        return false;
273
    }
274
275
    /**
276
     * Returns a remote connectProduct e.g. for checkout maniputlations
277
     *
278
     * @param array $ids
279
     * @param int $shopId
280
     * @return array
281
     */
282
    public function getRemoteProducts(array $ids, $shopId)
283
    {
284
        return $this->connectProductQuery->getRemote($ids, $shopId);
285
    }
286
287
    /**
288
     * Returns a local connectProduct for export
289
     *
290
     * @param array $sourceIds
291
     * @return Product[]
292
     */
293
    public function getLocalProduct(array $sourceIds)
294
    {
295
        return $this->connectProductQuery->getLocal($sourceIds);
296
    }
297
298
    /**
299
     * Does the current basket contain connect products?
300
     *
301
     * @param $session
302
     * @return bool
303
     */
304
    public function hasBasketConnectProducts($session, $userId = null)
305
    {
306
        $connection = $this->manager->getConnection();
307
        $sql = 'SELECT ob.articleID
308
309
            FROM s_order_basket ob
310
311
            INNER JOIN s_plugin_connect_items bi
312
            ON bi.article_id = ob.articleID
313
            AND bi.shop_id IS NOT NULL
314
315
            WHERE ob.sessionID=?
316
            ';
317
        $whereClause = [$session];
318
319
        if ($userId > 0) {
320
            $sql .= ' OR userID=?';
321
            $whereClause[] = $userId;
322
        }
323
324
        $sql .= ' LIMIT 1';
325
326
        $result = $connection->fetchArray($sql, $whereClause);
327
328
        return !empty($result);
329
    }
330
331
    /**
332
     * Will return the connectAttribute for a given model. The model can be an Article\Article or Article\Detail
333
     *
334
     * @param $model ProductModel|ProductDetail
335
     * @return ConnectAttribute
336
     */
337
    public function getConnectAttributeByModel($model)
338
    {
339
        if (!$model->getId()) {
340
            return false;
341
        }
342
        $repository = $this->manager->getRepository('Shopware\CustomModels\Connect\Attribute');
343
344
        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...
345
            if (!$model->getMainDetail()) {
346
                return false;
347
            }
348
349
            return $repository->findOneBy(['articleDetailId' => $model->getMainDetail()->getId()]);
350
        } 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...
351
            return $repository->findOneBy(['articleDetailId' => $model->getId()]);
352
        }
353
354
        return false;
355
    }
356
357
    /**
358
     * Returns connectAttributes for all article details by given article object
359
     *
360
     * @param ProductModel $article
361
     * @return \Shopware\CustomModels\Connect\Attribute[]
362
     */
363
    public function getConnectAttributesByArticle(ProductModel $article)
364
    {
365
        $builder = $this->manager->createQueryBuilder();
366
        $builder->select(['connectAttribute', 'detail']);
367
        $builder->from('Shopware\CustomModels\Connect\Attribute', 'connectAttribute');
368
        $builder->innerJoin('connectAttribute.articleDetail', 'detail');
369
370
        $builder->where('connectAttribute.articleId = :articleId');
371
        $query = $builder->getQuery();
372
373
        $query->setParameter('articleId', $article->getId());
374
375
        return $query->getResult();
376
    }
377
378
    /**
379
     * Returns true when product is exported to Connect
380
     *
381
     * @param Attribute $connectAttribute
382
     * @return bool
383
     */
384
    public function isProductExported(Attribute $connectAttribute)
385
    {
386
        $status = $connectAttribute->getExportStatus();
387
        if ($connectAttribute->isExported()) {
388
            return true;
389
        }
390
391
        if ($status == Attribute::STATUS_INSERT) {
392
            return true;
393
        }
394
395
        if ($status == Attribute::STATUS_UPDATE) {
396
            return true;
397
        }
398
399
        if ($status == Attribute::STATUS_SYNCED) {
400
            return true;
401
        }
402
403
        return false;
404
    }
405
406
    /**
407
     * Verifies that at least one variant from
408
     * same article is exported.
409
     *
410
     * @param Attribute $connectAttribute
411
     * @return bool
412
     */
413
    public function hasExportedVariants(Attribute $connectAttribute)
414
    {
415
        $builder = $this->manager->getConnection()->createQueryBuilder();
416
        $builder->select('COUNT(spci.id)')
417
            ->from('s_plugin_connect_items', 'spci')
418
            ->where('spci.article_id = :articleId AND spci.export_status IN (:exportStatus) AND spci.shop_id IS NULL')
419
            ->setParameter('articleId', $connectAttribute->getArticleId(), \PDO::PARAM_INT)
420
            ->setParameter(
421
                ':exportStatus',
422
                [Attribute::STATUS_INSERT, Attribute::STATUS_UPDATE, Attribute::STATUS_SYNCED],
423
                \Doctrine\DBAL\Connection::PARAM_STR_ARRAY
424
            );
425
426
        return $builder->execute()->fetchColumn() > 0;
427
    }
428
429
    /**
430
     * Helper method to create a connect attribute on the fly
431
     *
432
     * @param $model
433
     * @throws \RuntimeException
434
     * @return ConnectAttribute
435
     */
436
    public function getOrCreateConnectAttributeByModel($model)
437
    {
438
        $attribute = $this->getConnectAttributeByModel($model);
439
440
        if (!$attribute) {
441
            $attribute = new ConnectAttribute();
442
            $attribute->setPurchasePriceHash('');
443
            $attribute->setOfferValidUntil('');
444
            $attribute->setStream('');
445
446
            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...
447
                $attribute->setArticle($model);
448
                $attribute->setArticleDetail($model->getMainDetail());
449
                $attribute->setSourceId(
450
                    $this->generateSourceId($model->getMainDetail())
451
                );
452
            } 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...
453
                $attribute->setArticle($model->getArticle());
454
                $attribute->setArticleDetail($model);
455
                $attribute->setSourceId(
456
                    $this->generateSourceId($model)
457
                );
458
            } else {
459
                throw new \RuntimeException('Passed model needs to be an article or an article detail');
460
            }
461
            $this->manager->persist($attribute);
462
            $this->manager->flush($attribute);
463
        }
464
465
        return $attribute;
466
    }
467
468
    /**
469
     * Returns connect attributes for article
470
     * and all variants.
471
     * If connect attribute does not exist
472
     * it will be created.
473
     *
474
     * @param ProductModel $article
475
     * @return array
476
     */
477
    public function getOrCreateConnectAttributes(ProductModel $article)
478
    {
479
        $attributes = [];
480
        /** @var \Shopware\Models\Article\Detail $detail */
481
        foreach ($article->getDetails() as $detail) {
482
            $attributes[] = $this->getOrCreateConnectAttributeByModel($detail);
483
        }
484
485
        return $attributes;
486
    }
487
488
    /**
489
     * Generate sourceId
490
     *
491
     * @param ProductDetail $detail
492
     * @return string
493
     */
494
    public function generateSourceId(ProductDetail $detail)
495
    {
496
        if ($detail->getKind() == 1) {
497
            $sourceId = (string) $detail->getArticle()->getId();
498
        } else {
499
            $sourceId = sprintf(
500
                '%s-%s',
501
                $detail->getArticle()->getId(),
502
                $detail->getId()
503
            );
504
        }
505
506
        return $sourceId;
507
    }
508
509
    /**
510
     * @param $id
511
     * @return array
512
     */
513
    public function getConnectCategoryForProduct($id)
514
    {
515
        return $this->connectCategoryQuery->getConnectCategoryForProduct($id);
516
    }
517
518
    public function getMostRelevantConnectCategory($categories)
519
    {
520
        usort(
521
            $categories,
522
            [
523
                $this->connectCategoryQuery->getRelevanceSorter(),
524
                'sortConnectCategoriesByRelevance'
525
            ]
526
        );
527
528
        return array_pop($categories);
529
    }
530
531
    /**
532
     * Defines the update flags
533
     *
534
     * @return array
535
     */
536
    public function getUpdateFlags()
537
    {
538
        return [2 => 'shortDescription', 4 => 'longDescription', 8 => 'name', 16 => 'image', 32 => 'price', 64 => 'imageInitialImport', 128 => 'additionalDescription'];
539
    }
540
541
    /**
542
     * Returns shopware unit entity
543
     *
544
     * @param $unitKey
545
     * @return \Shopware\Models\Article\Unit
546
     */
547
    public function getUnit($unitKey)
548
    {
549
        $repository = $this->manager->getRepository('Shopware\Models\Article\Unit');
550
551
        return $repository->findOneBy(['unit' => $unitKey]);
552
    }
553
554
    /**
555
     * Clear article cache
556
     */
557
    public function clearArticleCache($articleId)
558
    {
559
        Shopware()->Events()->notify(
560
            'Shopware_Plugins_HttpCache_InvalidateCacheId',
561
            ['cacheId' => 'a' . $articleId]
562
        );
563
    }
564
565
    /**
566
     * Replace unit and ref quantity
567
     * @param $products
568
     * @return mixed
569
     */
570
    public function prepareConnectUnit($products)
571
    {
572
        foreach ($products as &$p) {
573
            if ($p->attributes['unit']) {
574
                $configComponent = ConfigFactory::getConfigInstance();
575
                /** @var \ShopwarePlugins\Connect\Components\Utils\UnitMapper $unitMapper */
576
                $unitMapper = new UnitMapper(
577
                    $configComponent,
578
                    $this->manager
579
                );
580
581
                $p->attributes['unit'] = $unitMapper->getConnectUnit($p->attributes['unit']);
582
            }
583
584
            if ($p->attributes['ref_quantity']) {
585
                $intRefQuantity = (int) $p->attributes['ref_quantity'];
586
                if ($p->attributes['ref_quantity'] - $intRefQuantity <= 0.0001) {
587
                    $p->attributes['ref_quantity'] = $intRefQuantity;
588
                }
589
            }
590
        }
591
592
        return $products;
593
    }
594
595
    /**
596
     * Removes connect reservation from session
597
     */
598
    public function clearConnectReservation()
599
    {
600
        Shopware()->Session()->connectReservation = null;
601
    }
602
603
    /**
604
     * Collect sourceIds by given article ids
605
     *
606
     * @param array $articleIds
607
     * @return array
608
     */
609
    public function getArticleSourceIds(array $articleIds)
610
    {
611
        if (empty($articleIds)) {
612
            return [];
613
        }
614
615
        /** @var AttributeRepository $repo */
616
        $repo = $this->manager->getRepository(ConnectAttribute::class);
617
618
        return array_merge(
619
            $repo->findSourceIds($articleIds, 1),
620
            $repo->findSourceIds($articleIds, 2)
621
        );
622
    }
623
624
    /**
625
     * Get ShopProductId struct by given article detail id
626
     * It contains product sourceId and shopId.
627
     * If $articleDetailId is local product, $shopProductId->shopId will be null.
628
     *
629
     * @param int $articleDetailId
630
     * @return ShopProductId
631
     */
632
    public function getShopProductId($articleDetailId)
633
    {
634
        $articleDetailId = (int) $articleDetailId;
635
        $builder = $this->manager->getConnection()->createQueryBuilder();
636
        $builder->select('items.source_id as sourceId, items.shop_id as shopId')
637
            ->from('s_plugin_connect_items', 'items')
638
            ->where('items.article_detail_id = :articleDetailIds')
639
            ->setParameter(':articleDetailIds', $articleDetailId);
640
641
        $result = $builder->execute()->fetch(\PDO::FETCH_ASSOC);
642
643
        return new ShopProductId($result);
644
    }
645
646
    /**
647
     * Check if given articleDetailId is remote product
648
     *
649
     * @param int $articleDetailId
650
     * @return bool
651
     */
652
    public function isRemoteArticleDetail($articleDetailId)
653
    {
654
        $articleDetailId = (int) $articleDetailId;
655
        $articleDetailRepository = $this->manager->getRepository('Shopware\Models\Article\Detail');
656
        /** @var \Shopware\Models\Article\Detail $detail */
657
        $detail = $articleDetailRepository->find($articleDetailId);
658
        if (!$detail) {
659
            return false;
660
        }
661
662
        $connectAttribute = $this->getConnectAttributeByModel($detail);
663
        if (!$connectAttribute) {
664
            return false;
665
        }
666
667
        return ($connectAttribute->getShopId() != null);
668
    }
669
670
    /**
671
     * Check if given articleDetailId is remote product
672
     *
673
     * @param int $articleDetailId
674
     * @return bool
675
     */
676
    public function isRemoteArticleDetailDBAL($articleDetailId)
677
    {
678
        $articleDetailId = (int) $articleDetailId;
679
        $builder = $this->manager->getConnection()->createQueryBuilder();
680
        $builder->select('items.shop_id')
681
            ->from('s_plugin_connect_items', 'items')
682
            ->where('items.article_detail_id = :articleDetailId')
683
            ->setParameter(':articleDetailId', $articleDetailId);
684
685
        return (bool) $builder->execute()->fetchColumn();
686
    }
687
688
    /**
689
     * Extract article ID and detail ID
690
     * from source ID
691
     *
692
     * @param $sourceId
693
     * @return array
694
     */
695
    public function explodeArticleId($sourceId)
696
    {
697
        $articleId = explode('-', $sourceId);
698
699
        if (isset($articleId[1]) && isset($articleId[1])) {
700
            return $articleId;
701
        }
702
703
        return [
704
            $articleId[0]
705
        ];
706
    }
707
708
    /**
709
     * Creates Shopware product model
710
     *
711
     * @param Product $product
712
     * @return ProductModel
713
     */
714
    public function createProductModel(Product $product)
715
    {
716
        //todo@sb: Add test
717
        $model = new ProductModel();
718
        $model->setActive(false);
719
        $model->setName($product->title);
720
        $this->manager->persist($model);
721
722
        return $model;
723
    }
724
725
    /**
726
     * Returns main article detail by given groupId
727
     *
728
     * @param $product
729
     * @param int $mode
730
     * @return null|ProductModel
731
     */
732 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...
733
    {
734
        $builder = $this->manager->createQueryBuilder();
735
        $builder->select(['ba', 'd']);
736
        $builder->from('Shopware\CustomModels\Connect\Attribute', 'ba');
737
        $builder->join('ba.articleDetail', 'd');
738
        $builder->leftJoin('d.attribute', 'at');
739
740
        $builder->where('ba.groupId = :groupId AND ba.isMainVariant = 1 AND ba.shopId = :shopId');
741
        $query = $builder->getQuery();
742
743
        $query->setParameter('groupId', $product->groupId);
744
        $query->setParameter('shopId', $product->shopId);
745
        $result = $query->getResult(
746
            $mode
747
        );
748
749
        if (isset($result[0])) {
750
            /** @var \Shopware\CustomModels\Connect\Attribute $attribute */
751
            $attribute = $result[0];
752
753
            return $attribute->getArticle();
754
        }
755
756
        return null;
757
    }
758
759
    /**
760
     * @param int $articleId
761
     * @return array
762
     */
763
    public function getSourceIdsFromArticleId($articleId)
764
    {
765
        $rows = $this->manager->getConnection()->fetchAll(
766
            'SELECT source_id FROM s_plugin_connect_items WHERE article_id = ? AND exported = 1',
767
            [$articleId]
768
        );
769
770
        return array_map(function ($row) {
771
            return $row['source_id'];
772
        }, $rows);
773
    }
774
775
    /**
776
     * @param Unit $localUnit
777
     * @param string $remoteUnit
778
     */
779
    public function updateUnitInRelatedProducts(Unit $localUnit, $remoteUnit)
780
    {
781
        $statement = $this->manager->getConnection()->prepare('UPDATE s_articles_details sad
782
            LEFT JOIN s_articles_attributes saa ON sad.id = saa.articledetailsID
783
            SET sad.unitID = :unitId
784
            WHERE saa.connect_remote_unit = :remoteUnit');
785
786
        $statement->bindValue(':unitId', $localUnit->getId(), \PDO::PARAM_INT);
787
        $statement->bindValue(':remoteUnit', $remoteUnit, \PDO::PARAM_STR);
788
789
        $statement->execute();
790
    }
791
792
    /**
793
     * Checks whether given sourceId is main variant.
794
     * Works only with local products.
795
     * SourceIds pattern is articleId-variantId (58-142)
796
     *
797
     * For remote product check is_main_variant flag in
798
     * s_plugin_connect_items
799
     *
800
     * @param string $sourceId
801
     * @return bool
802
     */
803
    public function isMainVariant($sourceId)
804
    {
805
        $isMainVariant = $this->manager->getConnection()->fetchColumn(
806
            'SELECT d.kind
807
              FROM s_plugin_connect_items spci
808
              LEFT JOIN s_articles_details d ON spci.article_detail_id = d.id
809
              WHERE source_id = ?',
810
            [$sourceId]
811
        );
812
813
        if ($isMainVariant != 1) {
814
            return false;
815
        }
816
817
        return true;
818
    }
819
820
    public function getLocalArticleCount()
821
    {
822
        return $this->manager->getRepository(ConnectAttribute::class)->getLocalArticleCount();
823
    }
824
825
    /**
826
     * Recreates ConnectCategories wit the specified offset and batchsize
827
     * @param int $offset
828
     * @param int $batchsize
829
     */
830
    public function recreateConnectCategories($offset, $batchsize)
831
    {
832
        $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 ?',
833
            [$batchsize, $offset],
834
            [\PDO::PARAM_INT, \PDO::PARAM_INT]
835
        );
836
837
        while ($row = $result->fetch()) {
838
            $categories = json_decode($row['category'], true);
839
            $countAssignedCategories = $this->manager->getConnection()->executeQuery('SELECT COUNT(`connect_category_id`) AS categories_count FROM s_plugin_connect_product_to_categories WHERE articleID = ?',
840
                [$row['article_id']]
841
            )->fetchColumn();
842
843
            if (count($categories) != $countAssignedCategories) {
844
                $shopId = $row['shop_id'];
845
                foreach ($categories as $categoryKey => $category) {
846
                    $selectedCategory = $this->manager->getConnection()->executeQuery('SELECT `id` FROM s_plugin_connect_categories WHERE category_key = ? AND shop_id = ?',
847
                        [$categoryKey, $shopId]);
848 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...
849
                        $this->manager->getConnection()->executeQuery('INSERT INTO s_plugin_connect_categories (category_key, label, shop_id) VALUES (?, ?, ?)',
850
                            [$categoryKey, $category, $shopId]);
851
                        $categoryId = (int) $this->manager->getConnection()->lastInsertId();
852
                    } else {
853
                        $categoryId = (int) $res['id'];
854
                    }
855
                    $selectedProductToCategory = $this->manager->getConnection()->executeQuery('SELECT COUNT(*) FROM s_plugin_connect_product_to_categories WHERE connect_category_id = ? AND articleID = ?',
856
                        [$categoryId, (int) $row['article_id']]
857
                    )->fetchColumn();
858
                    if ((int) $selectedProductToCategory === 0) {
859
                        $this->manager->getConnection()->executeQuery('INSERT INTO s_plugin_connect_product_to_categories (connect_category_id, articleID) VALUES (?, ?)',
860
                            [$categoryId, (int) $row['article_id']]
861
                            );
862
                    }
863
                }
864
            }
865
        }
866
867
        $totalCount = $this->getProductCountForCategoryRecovery();
868
        if ($batchsize + $offset >= $totalCount) {
869
            $configComponent = ConfigFactory::getConfigInstance();
870
            $configComponent->setConfig('recreateConnectCategories', 1);
871
        }
872
    }
873
874
    /**
875
     * @return int
876
     */
877
    public function getProductCountForCategoryRecovery()
878
    {
879
        return (int) $this->manager->getConnection()->executeQuery('
880
          SELECT COUNT(*) 
881
          FROM (
882
            SELECT COUNT(*) FROM `s_plugin_connect_items` WHERE shop_id IS NOT NULL GROUP BY `article_id`
883
          ) AS Z')->fetchColumn();
884
    }
885
886
    /**
887
     * adds the shopId to the ConnectCategoriesTable
888
     * @param int $offset
889
     * @param int $batchsize
890
     */
891
    public function addShopIdToConnectCategories($offset, $batchsize)
892
    {
893
        $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 ?',
894
            [$batchsize, $offset],
895
            [\PDO::PARAM_INT, \PDO::PARAM_INT]
896
        );
897
898
        while ($row = $result->fetch()) {
899
            $categories = json_decode($row['category'], true);
900
            $this->manager->getConnection()->executeQuery('DELETE FROM s_plugin_connect_product_to_categories WHERE articleID = ?',
901
                [$row['article_id']]
902
            );
903
904
            $shopId = (int) $row['shop_id'];
905
            foreach ($categories as $categoryKey => $category) {
906
                $selectedCategory = $this->manager->getConnection()->executeQuery('SELECT `id` FROM s_plugin_connect_categories WHERE category_key = ? AND shop_id = ?',
907
                    [$categoryKey, $shopId]);
908
                if (!($res = $selectedCategory->fetch())) {
909
                    $categoryId = $this->createCategoryWithShopId($categoryKey, $shopId, $category);
910
                } else {
911
                    $categoryId = (int) $res['id'];
912
                }
913
                $this->manager->getConnection()->executeQuery('INSERT INTO s_plugin_connect_product_to_categories (articleID, connect_category_id) VALUES (?, ?)',
914
                    [$row['article_id'], $categoryId]
915
                );
916
            }
917
        }
918
919
        $totalCount = $this->getProductCountForCategoryRecovery();
920
        if ($batchsize + $offset >= $totalCount) {
921
            $configComponent = ConfigFactory::getConfigInstance();
922
            $configComponent->setConfig('addShopIdToConnectCategories', 1);
923
        }
924
    }
925
926
    /**
927
     * @param string $categoryKey
928
     * @param int $shopId
929
     * @param string $category
930
     * @return int
931
     */
932
    private function createCategoryWithShopId($categoryKey, $shopId, $category)
933
    {
934
        $selectedCategory = $this->manager->getConnection()->executeQuery('SELECT `id` FROM s_plugin_connect_categories WHERE category_key = ? AND shop_id IS NULL',
935
            [$categoryKey]);
936 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...
937
            $this->manager->getConnection()->executeQuery('INSERT INTO s_plugin_connect_categories (category_key, label, shop_id) VALUES (?, ?, ?)',
938
                [$categoryKey, $category, $shopId]);
939
            $createdCategoryId = (int) $this->manager->getConnection()->lastInsertId();
940
            $this->assignLocalCategories($createdCategoryId, $categoryKey);
941
942
            return $createdCategoryId;
943
        }
944
945
        $this->manager->getConnection()->executeQuery('UPDATE s_plugin_connect_categories SET shop_id = ? WHERE id = ?',
946
            [$shopId, $res['id']]);
947
948
        return (int) $res['id'];
949
    }
950
951
    /**
952
     * @param int $createdCategoryId
953
     * @param string $categoryKey
954
     */
955
    private function assignLocalCategories($createdCategoryId, $categoryKey)
956
    {
957
        $originalCategoryId = $this->manager->getConnection()->fetchColumn('SELECT `id` FROM s_plugin_connect_categories WHERE category_key = ? AND id <> ?',
958
            [$categoryKey, $createdCategoryId]);
959
        $localCategories = $this->manager->getConnection()->executeQuery('SELECT `local_category_id` FROM s_plugin_connect_categories_to_local_categories WHERE remote_category_id = ?',
960
            [$originalCategoryId])->fetchAll(\PDO::FETCH_COLUMN);
961
962
        foreach ($localCategories as $localCategoryId) {
963
            $this->manager->getConnection()->executeQuery('INSERT INTO s_plugin_connect_categories_to_local_categories (remote_category_id, local_category_id) VALUES (?, ?)',
964
                [$createdCategoryId, $localCategoryId]);
965
        }
966
    }
967
}
968