Completed
Push — master ( 7f62ea...9f6b80 )
by Jonas
16s
created

CategoryResolver::createLocalCategory()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 29
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 17
nc 2
nop 4
dl 0
loc 29
rs 8.8571
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\CustomModels\Connect\ProductToRemoteCategory;
11
use Shopware\CustomModels\Connect\RemoteCategory;
12
use Shopware\CustomModels\Connect\RemoteCategoryRepository;
13
use Shopware\Components\Model\ModelManager;
14
use Shopware\CustomModels\Connect\ProductToRemoteCategoryRepository;
15
use Shopware\Models\Category\Repository as CategoryRepository;
16
use Shopware\Models\Category\Category;
17
use Shopware\Components\Model\CategoryDenormalization;
18
19
abstract class CategoryResolver
20
{
21
    /**
22
     * @var ModelManager
23
     */
24
    protected $manager;
25
26
    /**
27
     * @var \Shopware\CustomModels\Connect\RemoteCategoryRepository
28
     */
29
    protected $remoteCategoryRepository;
30
31
    /**
32
     * @var \Shopware\CustomModels\Connect\ProductToRemoteCategoryRepository
33
     */
34
    protected $productToRemoteCategoryRepository;
35
36
    /**
37
     * @var \Shopware\Models\Category\Repository
38
     */
39
    protected $categoryRepository;
40
41
    /**
42
     * @var CategoryDenormalization
43
     */
44
    private $categoryDenormalization;
45
46
    public function __construct(
47
        ModelManager $manager,
48
        RemoteCategoryRepository $remoteCategoryRepository,
49
        ProductToRemoteCategoryRepository $productToRemoteCategoryRepository,
50
        CategoryRepository $categoryRepository,
51
        CategoryDenormalization $categoryDenormalization
52
    ) {
53
        $this->manager = $manager;
54
        $this->remoteCategoryRepository = $remoteCategoryRepository;
55
        $this->productToRemoteCategoryRepository = $productToRemoteCategoryRepository;
56
        $this->categoryRepository = $categoryRepository;
57
        $this->categoryDenormalization = $categoryDenormalization;
58
    }
59
60
    /**
61
     * Returns array with category entities
62
     * if they don't exist will be created
63
     *
64
     * @param array $categories
65
     * @param int $shopId
66
     * @return \Shopware\Models\Category\Category[]
67
     */
68
    abstract public function resolve(array $categories, $shopId);
69
70
    /**
71
     * Generates categories tree by given array of categories
72
     *
73
     * @param array $categories
74
     * @param string $idPrefix
75
     * @return array
76
     */
77
    abstract public function generateTree(array $categories, $idPrefix = '');
78
79
    /**
80
     * Stores raw Shopware Connect categories
81
     *
82
     * @param array $categories
83
     * @param int $articleId
84
     * @param int $shopId
85
     * @return void
86
     */
87
    public function storeRemoteCategories(array $categories, $articleId, $shopId)
88
    {
89
        $remoteCategories = [];
90
        foreach ($categories as $categoryKey => $category) {
91
            $remoteCategory = $this->remoteCategoryRepository->findOneBy(['categoryKey' => $categoryKey, 'shopId' => $shopId]);
92
            if (!$remoteCategory) {
93
                $remoteCategory = new RemoteCategory();
94
                $remoteCategory->setCategoryKey($categoryKey);
95
                $remoteCategory->setShopId($shopId);
96
            }
97
            $remoteCategory->setLabel($category);
98
            $this->manager->persist($remoteCategory);
99
            $remoteCategories[] = $remoteCategory;
100
        }
101
102
        $this->manager->flush();
103
104
        $this->removeProductsFromNotAssignedRemoteCategories($remoteCategories, $articleId);
105
        $this->addProductToRemoteCategory($remoteCategories, $articleId);
106
107
        $this->manager->flush();
108
    }
109
110
    /**
111
     * @param RemoteCategory[] $remoteCategories
112
     * @param $articleId
113
     */
114
    private function addProductToRemoteCategory(array $remoteCategories, $articleId)
115
    {
116
        $productToCategories = $this->productToRemoteCategoryRepository->getRemoteCategoryIds($articleId);
117
        /** @var $remoteCategory \Shopware\CustomModels\Connect\RemoteCategory */
118
        foreach ($remoteCategories as $remoteCategory) {
119
            if (!in_array($remoteCategory->getId(), $productToCategories)) {
120
                $productToCategory = new ProductToRemoteCategory();
121
                $productToCategory->setArticleId($articleId);
122
                $productToCategory->setConnectCategory($remoteCategory);
123
                $this->manager->persist($productToCategory);
124
            }
125
        }
126
    }
127
128
    /**
129
     * @param \Shopware\CustomModels\Connect\RemoteCategory[] $assignedCategories
130
     * @param int $articleId
131
     */
132
    private function removeProductsFromNotAssignedRemoteCategories(array $assignedCategories, $articleId)
133
    {
134
        $currentProductCategoryIds = $this->productToRemoteCategoryRepository->getRemoteCategoryIds($articleId);
135
136
        $assignedCategoryIds = array_map(function (RemoteCategory $assignedCategory) {
137
            $assignedCategory->getId();
0 ignored issues
show
Unused Code introduced by
The call to the method Shopware\CustomModels\Co...RemoteCategory::getId() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
138
        }, $assignedCategories);
139
140
        /** @var int $currentProductCategoryId */
141
        foreach ($currentProductCategoryIds as $currentProductCategoryId) {
142
            if (!in_array($currentProductCategoryId, $assignedCategoryIds)) {
143
                $this->deleteAssignmentOfLocalCategories($currentProductCategoryId, $articleId);
144
                $this->productToRemoteCategoryRepository->deleteByConnectCategoryId($currentProductCategoryId, $articleId);
145
            }
146
        }
147
    }
148
149
    /**
150
     * @param int $currentProductCategoryId
151
     * @param int $articleId
152
     */
153
    private function deleteAssignmentOfLocalCategories($currentProductCategoryId, $articleId)
154
    {
155
        $localCategoriesIds = $this->manager->getConnection()->executeQuery(
156
            'SELECT local_category_id FROM s_plugin_connect_categories_to_local_categories WHERE remote_category_id = ?',
157
            [$currentProductCategoryId]
158
        )->fetchAll(\PDO::FETCH_COLUMN);
159
        if ($localCategoriesIds) {
160
            $this->manager->getConnection()->executeQuery(
161
                'DELETE FROM `s_articles_categories` WHERE `articleID` = ? AND `categoryID` IN (?)',
162
                [$articleId, $localCategoriesIds],
163
                [\PDO::PARAM_INT, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY]
164
            );
165
            foreach ($localCategoriesIds as $categoryId) {
166
                $this->categoryDenormalization->removeAssignment($articleId, $categoryId);
167
            }
168
169
            $this->deleteEmptyConnectCategories($localCategoriesIds);
170
        }
171
    }
172
173
    /**
174
     * @param int[] $categoryIds
175
     */
176
    public function deleteEmptyConnectCategories(array $categoryIds)
177
    {
178
        foreach ($categoryIds as $categoryId) {
179
            $articleCount = (int) $this->manager->getConnection()->fetchColumn(
180
                'SELECT COUNT(id) FROM s_articles_categories WHERE categoryID = ?',
181
                [$categoryId]
182
            );
183
            if ($articleCount === 0) {
184
                $this->deleteEmptyCategory($categoryId);
185
            }
186
        }
187
    }
188
189
    /**
190
     * @param int $categoryId
191
     */
192
    private function deleteEmptyCategory($categoryId)
193
    {
194
        $connectImported = $this->manager->getConnection()->fetchColumn(
195
            'SELECT connect_imported_category FROM s_categories_attributes WHERE categoryID = ?',
196
            [$categoryId]
197
        );
198
199
        if ($connectImported == 1 && $this->countChildCategories($categoryId) === 0) {
200
            $parent = (int) $this->manager->getConnection()->fetchColumn(
201
                'SELECT parent FROM s_categories WHERE `id` = ?',
202
                [$categoryId]
203
            );
204
205
            $this->manager->getConnection()->executeQuery(
206
                'DELETE FROM `s_categories` WHERE `id` = ?',
207
                [$categoryId]
208
            );
209
210
            $this->deleteEmptyCategory($parent);
211
        }
212
    }
213
214
    /**
215
     * Loop through category tree and fetch ids
216
     *
217
     * @param array $node
218
     * @param int $parentId
219
     * @param int $shopId
220
     * @param bool $returnOnlyLeafs
221
     * @param array $categories
222
     * @return array
223
     */
224
    public function convertTreeToKeys(array $node, $parentId, $shopId, $returnOnlyLeafs = true, $categories = [])
225
    {
226
        foreach ($node as $category) {
227
            $categoryId = $this->checkAndCreateLocalCategory($category['name'], $category['categoryId'], $parentId, $shopId);
228
229
            if ((!$returnOnlyLeafs) || (empty($category['children']))) {
230
                $categories[] = [
231
                    'categoryKey' => $categoryId,
232
                    'parentId' => $parentId,
233
                    'remoteCategory' => $category['categoryId']
234
                ];
235
            }
236
237
            if (!empty($category['children'])) {
238
                $categories = $this->convertTreeToKeys($category['children'], $categoryId, $shopId, $returnOnlyLeafs, $categories);
239
            }
240
        }
241
242
        return $categories;
243
    }
244
245
    /**
246
     * @param string $categoryName
247
     * @param string $categoryKey
248
     * @param int $parentId
249
     * @param int $shopId
250
     * @return int
251
     */
252
    private function checkAndCreateLocalCategory($categoryName, $categoryKey, $parentId, $shopId)
253
    {
254
        $id = $this->manager->getConnection()->fetchColumn('SELECT `id` 
255
            FROM `s_categories`
256
            WHERE `parent` = :parentId AND `description` = :description',
257
            [':parentId' => $parentId, ':description' => $categoryName]);
258
259
        if (!$id) {
260
            return $this->createLocalCategory($categoryName, $categoryKey, $parentId, $shopId);
261
        }
262
263
        return $id;
264
    }
265
266
    /**
267
     * @param string $categoryName
268
     * @param string $categoryKey
269
     * @param int $parentId
270
     * @param int $shopId
271
     * @return int
272
     */
273
    public function createLocalCategory($categoryName, $categoryKey, $parentId, $shopId)
274
    {
275
        $path = $this->manager->getConnection()->fetchColumn('SELECT `path` 
276
            FROM `s_categories`
277
            WHERE `id` = ?',
278
            [$parentId]);
279
        $suffix = ($path) ? "$parentId|" : "|$parentId|";
280
        $path = $path . $suffix;
281
        $now = new \DateTime('now');
282
        $timestamp = $now->format('Y-m-d H:i:s');
283
        $this->manager->getConnection()->executeQuery('INSERT INTO `s_categories` (`description`, `parent`, `path`, `active`, `added`, `changed`) 
284
            VALUES (?, ?, ?, 1, ?, ?)',
285
            [$categoryName, $parentId, $path, $timestamp, $timestamp]);
286
        $localCategoryId = $this->manager->getConnection()->fetchColumn('SELECT LAST_INSERT_ID()');
287
288
        $this->manager->getConnection()->executeQuery('INSERT INTO `s_categories_attributes` (`categoryID`, `connect_imported_category`) 
289
            VALUES (?, 1)',
290
            [$localCategoryId]);
291
292
        $remoteCategoryId = $this->manager->getConnection()->fetchColumn('SELECT `id` 
293
            FROM `s_plugin_connect_categories`
294
            WHERE `category_key` = ? AND `shop_id` = ?',
295
            [$categoryKey, $shopId]);
296
        $this->manager->getConnection()->executeQuery('INSERT INTO `s_plugin_connect_categories_to_local_categories` (`remote_category_id`, `local_category_id`) 
297
            VALUES (?, ?)',
298
            [$remoteCategoryId, $localCategoryId]);
299
300
        return $localCategoryId;
301
    }
302
303
    /**
304
     * @param $categoryId
305
     * @return int
306
     */
307
    private function countChildCategories($categoryId)
308
    {
309
        return (int) $this->manager->getConnection()->fetchColumn(
310
            'SELECT COUNT(id) FROM s_categories WHERE parent = ?',
311
            [$categoryId]
312
        );
313
    }
314
}
315