Completed
Pull Request — master (#394)
by Jonas
02:59
created

ImportService::activateArticles()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 13
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 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) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $categoryId of type integer|null against null; this is ambiguous if the integer can be zero. Consider using a strict comparison !== instead.
Loading history...
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[] = $categoryId;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$categories was never initialized. Although not strictly required by PHP, it is generally a good practice to add $categories = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
554
        $childCategories = $this->manager->getConnection()->executeQuery('SELECT id FROM s_categories WHERE path LIKE ?',
555
            ["%|$categoryId|%"]);
556
557
        while ($childCategory = $childCategories->fetchColumn()) {
558
            $categories[] = (int) $childCategory;
559
        }
560
561
        $categoriesStatement = $this->manager->getConnection()->prepare('DELETE FROM s_articles_categories WHERE articleID IN (' . implode(', ', $articleIds) . ') AND categoryID IN (' . implode(', ', $categories) . ')');
562
        $categoriesStatement->execute();
563
564
        //Deletes all entries in s_articles_categories_ro that are not in the Path to a assigned category
565
        //need the nested select because mysql got an error otherwise
566
        //see: https://stackoverflow.com/questions/45494/mysql-error-1093-cant-specify-target-table-for-update-in-from-clause
567
        $this->manager->getConnection()->executeQuery('
568
                  DELETE FROM s_articles_categories_ro
569
                  WHERE id NOT IN 
570
                    (SELECT id FROM 
571
                      (SELECT DISTINCT s_articles_categories_ro.id AS id FROM s_articles_categories_ro 
572
                        INNER JOIN s_articles_categories ON s_articles_categories.articleID = s_articles_categories_ro.articleID
573
                        INNER JOIN s_categories ON s_articles_categories.categoryID = s_categories.id
574
                        WHERE (s_categories.path LIKE CONCAT("%|", s_articles_categories_ro.categoryID, "|%") OR s_categories.id = s_articles_categories_ro.categoryID) 
575
                      ) AS temp
576
                    )
577
                  ');
578
579
        //we have to check the parent category too, because we can have following entries in s_articles_category_id:
580
        //|articleID|categoryID|parentCategoryID
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
581
        //|        1|         3|              5
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
582
        //|        1|         3|              6
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
583
        // -> category 6 got deleted from article but isn't delete in previous statement because categoryID is valid
584
        $this->manager->getConnection()->executeQuery('
585
                  DELETE FROM s_articles_categories_ro
586
                  WHERE parentCategoryID NOT IN 
587
                    (SELECT categoryID
588
                      FROM s_articles_categories
589
                      WHERE s_articles_categories.articleID = s_articles_categories_ro.articleID
590
                    )
591
                  ');
592
593
594
        $attributeStatement = $this->manager->getConnection()->prepare('
595
                    UPDATE s_articles_attributes 
596
                    SET connect_mapped_category = NULL 
597
                    WHERE articleID IN (' . implode(', ', $articleIds) . ') 
598
                      AND articleID NOT IN 
599
                        (SELECT DISTINCT articleID FROM s_articles_categories)
600
                ');
601
        $attributeStatement->execute();
602
    }
603
604
    /**
605
     * Unassign all categories from given article ids
606
     * @param array $articleIds
607
     * @throws \Doctrine\DBAL\ConnectionException
608
     * @throws \Exception
609
     */
610
    private function unAssignArticlesFromAllCategories(array $articleIds)
611
    {
612
        $attributeStatement = $this->manager->getConnection()->prepare(
613
            'UPDATE s_articles_attributes SET connect_mapped_category = NULL WHERE articleID IN (' . implode(', ', $articleIds) . ')'
614
        );
615
        $attributeStatement->execute();
616
        $categoriesStatement = $this->manager->getConnection()->prepare('DELETE FROM s_articles_categories WHERE articleID IN (' . implode(', ', $articleIds) . ')');
617
        $categoriesStatement->execute();
618
        $categoryLogStatement = $this->manager->getConnection()->prepare('DELETE FROM s_articles_categories_ro WHERE articleID IN (' . implode(', ', $articleIds) . ')');
619
        $categoryLogStatement->execute();
620
    }
621
}
622