Completed
Push — master ( b26934...7f62ea )
by Jonas
15s
created

Helper::getArticleModelByProduct()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 24
Code Lines 15

Duplication

Lines 24
Ratio 100 %

Importance

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