Completed
Pull Request — master (#394)
by Jonas
21:37
created

ImportService::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 21
nc 1
nop 10
dl 0
loc 23
rs 9.0856
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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 Doctrine\DBAL\Connection;
11
use Shopware\Bundle\AttributeBundle\Service\DataPersister;
12
use Shopware\Components\Model\CategoryDenormalization;
13
use ShopwarePlugins\Connect\Components\CategoryResolver\AutoCategoryResolver;
14
use Shopware\Components\Model\ModelManager;
15
use Shopware\Components\MultiEdit\Resource\ResourceInterface;
16
use Shopware\CustomModels\Connect\ProductToRemoteCategoryRepository;
17
use Shopware\CustomModels\Connect\RemoteCategoryRepository;
18
use Shopware\Models\Article\Repository as ArticleRepository;
19
use Shopware\Models\Category\Repository as CategoryRepository;
20
21
class ImportService
22
{
23
    /**
24
     * @var ModelManager
25
     */
26
    private $manager;
27
28
    /**
29
     * @var \Shopware\Components\MultiEdit\Resource\ResourceInterface
30
     */
31
    private $productResource;
32
33
    /**
34
     * @var \Shopware\Models\Category\Repository
35
     */
36
    private $categoryRepository;
37
38
    /**
39
     * @var ArticleRepository
40
     */
41
    private $articleRepository;
42
43
    /**
44
     * @var RemoteCategoryRepository
45
     */
46
    private $remoteCategoryRepository;
47
48
    /**
49
     * @var ProductToRemoteCategoryRepository
50
     */
51
    private $productToRemoteCategoryRepository;
52
53
    /**
54
     * @var CategoryResolver
55
     */
56
    private $autoCategoryResolver;
57
58
    /**
59
     * @var CategoryExtractor
60
     */
61
    private $categoryExtractor;
62
63
    /**
64
     * @var CategoryDenormalization
65
     */
66
    private $categoryDenormalization;
67
68
    /**
69
     * @var DataPersister
70
     */
71
    private $dataPersister;
72
73
    public function __construct(
74
        ModelManager $manager,
75
        ResourceInterface $productResource,
76
        CategoryRepository $categoryRepository,
77
        ArticleRepository$articleRepository,
78
        RemoteCategoryRepository $remoteCategoryRepository,
79
        ProductToRemoteCategoryRepository $productToRemoteCategoryRepository,
80
        AutoCategoryResolver $categoryResolver,
81
        CategoryExtractor $categoryExtractor,
82
        CategoryDenormalization $categoryDenormalization,
83
        DataPersister $dataPersister
84
    ) {
85
        $this->manager = $manager;
86
        $this->productResource = $productResource;
87
        $this->categoryRepository = $categoryRepository;
88
        $this->articleRepository = $articleRepository;
89
        $this->remoteCategoryRepository = $remoteCategoryRepository;
90
        $this->productToRemoteCategoryRepository = $productToRemoteCategoryRepository;
91
        $this->autoCategoryResolver = $categoryResolver;
92
        $this->categoryExtractor = $categoryExtractor;
93
        $this->categoryDenormalization = $categoryDenormalization;
94
        $this->dataPersister = $dataPersister;
95
    }
96
97
    public function findBothArticlesType($categoryId, $query = '', $showOnlyConnectArticles = true, $limit = 10, $offset = 0)
98
    {
99
        if ($categoryId == 0) {
100
            return [];
101
        }
102
103
        return $this->productResource->filter($this->getAst($categoryId, $query, $showOnlyConnectArticles), $offset, $limit);
104
    }
105
106
    /**
107
     * @param $categoryId
108
     * @return bool
109
     */
110
    public function hasCategoryChildren($categoryId)
111
    {
112
        return (bool) $this->categoryRepository->getChildrenCountList($categoryId);
113
    }
114
115
    public function assignCategoryToArticles($categoryId, array $articleIds)
116
    {
117
        $articles = $this->articleRepository->findBy(['id' => $articleIds]);
118
119
        if (empty($articles)) {
120
            throw new \RuntimeException('Invalid article ids');
121
        }
122
123
        /** @var \Shopware\Models\Category\Category $category */
124
        $category = $this->categoryRepository->find($categoryId);
125
        if (!$category) {
126
            throw new \RuntimeException('Invalid category id');
127
        }
128
129
        /** @var \Shopware\Models\Article\Article $article */
130
        foreach ($articles as $article) {
131
            $article->addCategory($category);
132
            $this->manager->persist($article);
133
            /** @var \Shopware\Models\Article\Detail $detail */
134
            foreach ($article->getDetails() as $detail) {
135
                $attribute = $detail->getAttribute();
136
                $attribute->setConnectMappedCategory(true);
137
                $this->manager->persist($attribute);
138
            }
139
        }
140
141
        $this->manager->flush();
142
    }
143
144
    /**
145
     * Unassign categories from given article ids
146
     * for the given categoryId and all childcategories
147
     * or for all categories if $categoryId is null
148
     * Set connect_mapped_category flag in article
149
     * attributes to NULL
150
     *
151
     * @param array $articleIds
152
     * @param int|null $categoryId
153
     * @throws \Doctrine\DBAL\ConnectionException
154
     * @throws \Exception
155
     */
156
    public function unAssignArticleCategories(array $articleIds, $categoryId = null)
157
    {
158
        if (!empty($articleIds)) {
159
            // cast all items in $articleIds to int
160
            // before use them in WHERE IN clause
161
            foreach ($articleIds as $key => $articleId) {
162
                $articleIds[$key] = (int) $articleId;
163
            }
164
165
            $connection = $this->manager->getConnection();
166
            $connection->beginTransaction();
167
168
            try {
169
                if ($categoryId !== null) {
170
                    $this->unAssignArticlesFromCategory($articleIds, $categoryId);
171
                } else {
172
                    $this->unAssignArticlesFromAllCategories($articleIds);
173
                }
174
175
                $connection->commit();
176
            } catch (\Exception $e) {
177
                $connection->rollBack();
178
                throw new \Exception($e->getMessage());
179
            }
180
        }
181
    }
182
183
    /**
184
     * Collect remote article ids by given category id
185
     *
186
     * @param int $localCategoryId
187
     * @return array
188
     */
189
    public function findRemoteArticleIdsByCategoryId($localCategoryId)
190
    {
191
        $connection = $this->manager->getConnection();
192
        $sql = 'SELECT sac.articleID
193
            FROM s_articles_categories sac
194
            LEFT JOIN s_articles_attributes saa ON sac.articleID = saa.articleID
195
            WHERE sac.categoryID = :categoryId AND saa.connect_mapped_category = 1';
196
        $rows = $connection->fetchAll($sql, [':categoryId' => $localCategoryId]);
197
198
        return array_map(function ($row) {
199
            return $row['articleID'];
200
        }, $rows);
201
    }
202
203
    /**
204
     * @param array $categoryIds
205
     * @return int
206
     */
207
    public function deactivateLocalCategoriesByIds(array $categoryIds)
208
    {
209
        $builder = $this->manager->getConnection()->createQueryBuilder();
210
        $rowCount = $builder->update('s_categories', 'c')
211
            ->set('c.active', 0)
212
            ->where('c.id IN (:categoryIds)')
213
            ->setParameter('categoryIds', $categoryIds, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY)
214
            ->execute();
215
216
        return $rowCount;
217
    }
218
219
    /**
220
     * Collect all child categories by given
221
     * remote category key and create same
222
     * categories structure as Shopware Connect structure.
223
     * Find all remote products which belong to these categories
224
     * and assign them.
225
     *
226
     * @param int $localCategoryId
227
     * @param string $remoteCategoryKey
228
     * @param string $remoteCategoryLabel
229
     * @return void
230
     */
231
    public function importRemoteCategory($localCategoryId, $remoteCategoryKey, $remoteCategoryLabel)
232
    {
233
        /** @var \Shopware\Models\Category\Category $localCategory */
234
        $localCategory = $this->categoryRepository->find((int) $localCategoryId);
235
        if (!$localCategory) {
236
            throw new \RuntimeException('Local category not found!');
237
        }
238
239
        /** @var \Shopware\CustomModels\Connect\RemoteCategory $remoteCategory */
240
        $remoteCategory = $this->remoteCategoryRepository->findOneBy(['categoryKey' => $remoteCategoryKey]);
241
        if (!$remoteCategory) {
242
            throw new \RuntimeException('Remote category not found!');
243
        }
244
245
        // collect his child categories and
246
        // generate remote category tree by given remote category
247
        $remoteCategoryChildren = $this->categoryExtractor->getRemoteCategoriesTree($remoteCategoryKey, true);
248
        $remoteCategoryNodes = [
249
            [
250
                'name' => $remoteCategoryLabel,
251
                'categoryId' => $remoteCategoryKey,
252
                'leaf' => empty($remoteCategoryChildren) ? true : false,
253
                'children' => $remoteCategoryChildren,
254
            ]
255
        ];
256
257
        // create same category structure as Shopware Connect structure
258
        $categories = $this->autoCategoryResolver->convertTreeToKeys($remoteCategoryNodes, $localCategory->getId(), false);
259
260
        foreach ($categories as $category) {
261
            $articleIds = $this->productToRemoteCategoryRepository->findArticleIdsByRemoteCategory($category['remoteCategory']);
262
            foreach ($articleIds as $articleId) {
263
                $this->categoryDenormalization->addAssignment($articleId, $category['categoryKey']);
264
                $this->categoryDenormalization->removeAssignment($articleId, $category['parentId']);
265
                $this->manager->getConnection()->executeQuery(
266
                    'INSERT IGNORE INTO `s_articles_categories` (`articleID`, `categoryID`) VALUES (?, ?)',
267
                    [$articleId,  $category['categoryKey']]
268
                );
269
                $this->manager->getConnection()->executeQuery(
270
                    'DELETE FROM `s_articles_categories` WHERE `articleID` = :articleID AND `categoryID` = :categoryID',
271
                    [
272
                        ':articleID' => $articleId,
273
                        ':categoryID' => $category['parentId']
274
                    ]
275
                );
276
                $detailId = $this->manager->getConnection()->fetchColumn(
277
                    'SELECT id FROM `s_articles_details` WHERE `articleID` = :articleID',
278
                    ['articleID' => $articleId]
279
                );
280
                $this->manager->getConnection()->executeQuery(
281
                    'INSERT  INTO `s_articles_attributes` (`articleID`, `articledetailsID`, `connect_mapped_category`) 
282
                        VALUES (?, ?, 1)
283
                        ON DUPLICATE KEY UPDATE `connect_mapped_category` = 1
284
                    ',
285
                    [$articleId,  $detailId]
286
                );
287
            }
288
        }
289
    }
290
291
    /**
292
     * @param array $articleIds
293
     */
294
    public function activateArticles(array $articleIds)
295
    {
296
        $articleBuilder = $this->manager->createQueryBuilder();
297
        $articleBuilder->update('\Shopware\Models\Article\Article', 'a')
298
            ->set('a.active', 1)
299
            ->where('a.id IN (:articleIds)')
300
            ->setParameter(':articleIds', $articleIds, Connection::PARAM_STR_ARRAY);
301
302
        $articleBuilder->getQuery()->execute();
303
304
        $detailBuilder = $this->manager->createQueryBuilder();
305
        $detailBuilder->update('\Shopware\Models\Article\Detail', 'd')
306
            ->set('d.active', 1)
307
            ->where('d.articleId IN (:articleIds)')
308
            ->setParameter(':articleIds', $articleIds, Connection::PARAM_STR_ARRAY);
309
310
        $detailBuilder->getQuery()->execute();
311
    }
312
313
    /**
314
     * Store remote categories in Connect tables
315
     * and add relations between categories and products.
316
     *
317
     * @param array $remoteItems
318
     *
319
     * @throws \Exception
320
     */
321
    public function storeRemoteCategories(array $remoteItems)
322
    {
323
        $connection = $this->manager->getConnection();
324
325
        $connection->beginTransaction();
326
        try {
327
            foreach ($remoteItems as $articleId => $categories) {
328
                foreach ($categories as $categoryKey => $category) {
329
                    $connection->executeQuery(
330
                        'INSERT IGNORE INTO `s_plugin_connect_categories` (`category_key`, `label`) VALUES (?, ?)',
331
                        [$categoryKey, $category]
332
                    );
333
334
                    $connection->executeQuery(
335
                        'INSERT IGNORE INTO `s_plugin_connect_product_to_categories` (`connect_category_id`, `articleID`) VALUES ((SELECT c.id FROM s_plugin_connect_categories c WHERE c.category_key = ?), ?)',
336
                        [$categoryKey, $articleId]
337
                    );
338
                }
339
            }
340
            $connection->commit();
341
        } catch (\Exception $e) {
342
            $connection->rollBack();
343
344
            throw $e;
345
        }
346
    }
347
348
    /**
349
     * Fetch remote (Connect) categories by given article ids
350
     * @param array $articleIds
351
     * @return array
352
     */
353
    public function fetchRemoteCategoriesByArticleIds(array $articleIds)
354
    {
355
        $remoteCategoryIds = [];
356
        while ($currentIdBatch = array_splice($articleIds, 0, 500)) {
357
            $sql = 'SELECT sac.categoryID
358
            FROM s_articles_categories sac
359
            LEFT JOIN s_categories_attributes attr ON sac.categoryID = attr.categoryID
360
            WHERE attr.connect_imported_category = 1 AND sac.articleID IN (' . implode(', ', $currentIdBatch) . ') GROUP BY sac.categoryID';
361
            $rows = $this->manager->getConnection()->fetchAll($sql);
362
363
            $remoteCategoryIds = array_merge($remoteCategoryIds, array_map(function ($row) {
364
                return $row['categoryID'];
365
            }, $rows));
366
        }
367
368
        return array_unique($remoteCategoryIds);
369
    }
370
371
    /**
372
     * Fetch all articles where categories are auto imported
373
     * and there isn't record in s_plugin_connect_product_to_categories for them.
374
     * Returned array contains key = articleId and value = array of categories
375
     *
376
     * @return array
377
     */
378
    public function getArticlesWithAutoImportedCategories()
379
    {
380
        $statement = $this->manager->getConnection()->prepare(
381
            'SELECT b.article_id, b.category
382
            FROM s_plugin_connect_items b
383
            LEFT JOIN s_plugin_connect_product_to_categories a ON b.article_id = a.articleID
384
            WHERE b.shop_id > 0 AND a.connect_category_id IS NULL GROUP BY b.article_id'
385
        );
386
        $statement->execute();
387
388
        $remoteItems = [];
389
        foreach ($statement->fetchAll(\PDO::FETCH_ASSOC) as $item) {
390
            $categories = json_decode($item['category'], true);
391
            if (is_array($categories) && count($categories) > 0) {
392
                $articleId = $item['article_id'];
393
                $remoteItems[$articleId] = $categories;
394
            }
395
        }
396
397
        return $remoteItems;
398
    }
399
400
    /**
401
     * Helper function to create filter values
402
     * @param int $categoryId
403
     * @param bool $showOnlyConnectArticles
404
     * @param string $query
405
     * @return array
406
     */
407
    private function getAst($categoryId, $query = '', $showOnlyConnectArticles = true)
408
    {
409
        $ast = [
410
            [
411
                'type' => 'nullaryOperators',
412
                'token' => 'ISMAIN',
413
            ]
414
        ];
415
416
        if (trim($query) !== '') {
417
            $queryArray = [
418
                [
419
                    'type' => 'boolOperators',
420
                    'token' => 'AND',
421
                ],
422
                [
423
                    'type' => 'subOperators',
424
                    'token' => '(',
425
                ],
426
                [
427
                    'type' => 'attribute',
428
                    'token' => 'ARTICLE.NAME'
429
                ],
430
                [
431
                    'type' => 'binaryOperator',
432
                    'token' => '~'
433
                ],
434
                [
435
                    'type' => 'values',
436
                    'token' => '"' . $query . '"'
437
                ],
438
                [
439
                    'type' => 'boolOperators',
440
                    'token' => 'OR',
441
                ],
442
                [
443
                    'type' => 'attribute',
444
                    'token' => 'SUPPLIER.NAME'
445
                ],
446
                [
447
                    'type' => 'binaryOperator',
448
                    'token' => '~'
449
                ],
450
                [
451
                    'type' => 'values',
452
                    'token' => '"' . $query . '"'
453
                ],
454
                [
455
                    'type' => 'boolOperators',
456
                    'token' => 'OR',
457
                ],
458
                [
459
                    'type' => 'attribute',
460
                    'token' => 'DETAIL.NUMBER'
461
                ],
462
                [
463
                    'type' => 'binaryOperator',
464
                    'token' => '~'
465
                ],
466
                [
467
                    'type' => 'values',
468
                    'token' => '"' . $query . '"'
469
                ],
470
                [
471
                    'type' => 'subOperators',
472
                    'token' => ')',
473
                ]
474
            ];
475
            $ast = array_merge($ast, $queryArray);
476
        }
477
478
        $categoryArray = [
479
            [
480
            'type' => 'boolOperators',
481
            'token' => 'AND',
482
            ],
483
            [
484
                'type' => 'subOperators',
485
                'token' => '(',
486
            ],
487
            [
488
                'type' => 'attribute',
489
                'token' => 'CATEGORY.PATH',
490
            ],
491
            [
492
                'type' => 'binaryOperators',
493
                'token' => '=',
494
            ],
495
            [
496
                'type' => 'values',
497
                'token' => '"%|' . $categoryId . '|%"',
498
            ],
499
            [
500
                'type' => 'boolOperators',
501
                'token' => 'OR',
502
            ],
503
            [
504
                'type' => 'attribute',
505
                'token' => 'CATEGORY.ID',
506
            ],
507
            [
508
                'type' => 'binaryOperators',
509
                'token' => '=',
510
            ],
511
            [
512
                'type' => 'values',
513
                'token' => $categoryId,
514
            ],
515
            [
516
                'type' => 'subOperators',
517
                'token' => ')',
518
            ]
519
        ];
520
521
        $ast = array_merge($ast, $categoryArray);
522
523
        if ($showOnlyConnectArticles === true) {
524
            $ast = array_merge($ast, [
525
                [
526
                    'type' => 'boolOperators',
527
                    'token' => 'AND',
528
                ],
529
                [
530
                    'type' => 'attribute',
531
                    'token' => 'ATTRIBUTE.CONNECTMAPPEDCATEGORY',
532
                ],
533
                [
534
                    'type' => 'binaryOperators',
535
                    'token' => '!=',
536
                ],
537
                [
538
                    'type' => 'values',
539
                    'token' => 'NULL',
540
                ],
541
            ]);
542
        }
543
544
        return $ast;
545
    }
546
547
    /**
548
     * @param array $articleIds
549
     * @param $categoryId
550
     */
551
    private function unAssignArticlesFromCategory(array $articleIds, $categoryId)
552
    {
553
        $categories = [];
554
        $categories[] = $categoryId;
555
        $childCategories = $this->manager->getConnection()->executeQuery('SELECT id FROM s_categories WHERE path LIKE ?',
556
            ["%|$categoryId|%"]);
557
558
        while ($childCategory = $childCategories->fetchColumn()) {
559
            $categories[] = (int) $childCategory;
560
        }
561
562
        $categoriesStatement = $this->manager->getConnection()->prepare('DELETE FROM s_articles_categories WHERE articleID IN (' . implode(', ', $articleIds) . ') AND categoryID IN (' . implode(', ', $categories) . ')');
563
        $categoriesStatement->execute();
564
565
        $categoriesStatement = $this->manager->getConnection()->prepare('DELETE FROM s_articles_categories_ro WHERE articleID IN (' . implode(', ', $articleIds) . ') AND parentCategoryID IN (' . implode(', ', $categories) . ')');
566
        $categoriesStatement->execute();
567
568
        $attributeStatement = $this->manager->getConnection()->prepare('
569
                    UPDATE s_articles_attributes 
570
                    SET connect_mapped_category = NULL 
571
                    WHERE articleID IN (' . implode(', ', $articleIds) . ') 
572
                      AND 
573
                        (SELECT COUNT(*) FROM s_articles_categories 
574
                            INNER JOIN s_categories_attributes ON s_articles_categories.categoryID = s_categories_attributes.categoryID
575
                            WHERE s_articles_categories.articleID = s_articles_attributes.articleID 
576
                                AND s_categories_attributes.connect_imported_category = 1
577
                        ) = 0
578
                ');
579
        $attributeStatement->execute();
580
    }
581
582
    /**
583
     * Unassign all categories from given article ids
584
     * @param array $articleIds
585
     * @throws \Doctrine\DBAL\ConnectionException
586
     * @throws \Exception
587
     */
588
    private function unAssignArticlesFromAllCategories(array $articleIds)
589
    {
590
        $attributeStatement = $this->manager->getConnection()->prepare(
591
            'UPDATE s_articles_attributes SET connect_mapped_category = NULL WHERE articleID IN (' . implode(', ', $articleIds) . ')'
592
        );
593
        $attributeStatement->execute();
594
        $categoriesStatement = $this->manager->getConnection()->prepare('DELETE FROM s_articles_categories WHERE articleID IN (' . implode(', ', $articleIds) . ')');
595
        $categoriesStatement->execute();
596
        $categoryLogStatement = $this->manager->getConnection()->prepare('DELETE FROM s_articles_categories_ro WHERE articleID IN (' . implode(', ', $articleIds) . ')');
597
        $categoryLogStatement->execute();
598
    }
599
}
600