Completed
Pull Request — master (#377)
by Jonas
03:33
created

AutoCategoryResolver   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 231
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
dl 0
loc 231
rs 10
c 0
b 0
f 0
wmc 19
lcom 1
cbo 2

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 19 2
B generateTree() 0 33 4
B getCategoryData() 0 29 1
B resolve() 0 30 3
B convertTreeToKeys() 0 20 5
B convertNodeToEntity() 0 27 2
A checkAndCreateLocalCategory() 0 13 2
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\CategoryResolver;
9
10
use ShopwarePlugins\Connect\Components\CategoryResolver;
11
use Shopware\CustomModels\Connect\RemoteCategoryRepository;
12
use Shopware\Models\Category\Category;
13
use Shopware\Models\Category\Repository as CategoryRepository;
14
use Shopware\Components\Model\ModelManager;
15
use ShopwarePlugins\Connect\Components\Config;
16
use Shopware\CustomModels\Connect\ProductToRemoteCategoryRepository;
17
use Shopware\CustomModels\Connect\ProductToRemoteCategory;
18
19
class AutoCategoryResolver extends CategoryResolver
20
{
21
    /**
22
     * @var CategoryRepository
23
     */
24
    private $categoryRepository;
25
26
    /**
27
     * @var Config
28
     */
29
    private $config;
30
31
    /**
32
     * AutoCategoryResolver constructor.
33
     * @param ModelManager $manager
34
     * @param CategoryRepository $categoryRepository
35
     * @param RemoteCategoryRepository $remoteCategoryRepository
36
     * @param Config $config
37
     * @param ProductToRemoteCategoryRepository $productToRemoteCategoryRepository
0 ignored issues
show
Documentation introduced by
Should the type for parameter $productToRemoteCategoryRepository not be null|ProductToRemoteCategoryRepository?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
38
     */
39
    public function __construct(
40
        ModelManager $manager,
41
        CategoryRepository $categoryRepository,
42
        RemoteCategoryRepository $remoteCategoryRepository,
43
        Config $config,
44
        ProductToRemoteCategoryRepository $productToRemoteCategoryRepository = null
45
    ) {
46
        if (!$productToRemoteCategoryRepository) {
47
            $productToRemoteCategoryRepository = $manager->getRepository(ProductToRemoteCategory::class);
48
        }
49
        parent::__construct(
50
            $manager,
51
            $remoteCategoryRepository,
52
            $productToRemoteCategoryRepository
53
        );
54
55
        $this->categoryRepository = $categoryRepository;
56
        $this->config = $config;
57
    }
58
59
    /**
60
     * {@inheritdoc}
61
     */
62
    public function resolve(array $categories)
63
    {
64
        $tree = $this->generateTree($categories);
65
66
        // we need to foreach, cause we may have two main nodes
67
        // example:
68
        // Deutsch/Category/Subcategory
69
        // English/Category/Subcategory
70
        $remoteCategories = [];
71
        foreach ($tree as $node) {
72
            $mainCategory = $this->categoryRepository->findOneBy([
73
                'name' => $node['name'],
74
                'parentId' => 1,
75
            ]);
76
            // if connectTree has a Subtree starting with Spanish but MerchantShop has no mainCategory Spanish
77
            // the categories below Spanish won't be created
78
            if ($mainCategory == null) {
79
                continue;
80
            }
81
82
            $remoteCategories = $this->convertTreeToKeys($node['children'], $mainCategory->getId());
83
        }
84
85
        // Collect all, not only leaf categories. Some customers use them to assign products.
86
        // Do not fetch them from database by name as before.
87
        // it is possible to have more than one subcategory "Boots" - CON-4589
88
        return array_map(function ($category) {
89
            return $category['categoryKey'];
90
        }, $remoteCategories);
91
    }
92
93
    /**
94
     * Loop through category tree and fetch ids
95
     *
96
     * @param array $node
97
     * @param string $parentId
98
     * @param bool $returnOnlyLeafs
99
     * @param array $categories
100
     * @return array
101
     */
102
    public function convertTreeToKeys(array $node, $parentId, $returnOnlyLeafs = true, $categories = [])
103
    {
104
        foreach ($node as $category) {
105
            $categoryId = $this->checkAndCreateLocalCategory($category, $parentId);
106
107
            if ((!$returnOnlyLeafs) || (empty($category['children']))) {
108
                $categories[] = [
109
                    'categoryKey' => $categoryId,
110
                    'parentId' => $parentId,
111
                    'remoteCategory' => $category['categoryId']
112
                ];
113
            }
114
115
            if (!empty($category['children'])) {
116
                $categories = $this->convertTreeToKeys($category['children'], $categoryId, $returnOnlyLeafs, $categories);
117
            }
118
        }
119
120
        return $categories;
121
    }
122
123
    /**
124
     * @param array $category
125
     * @param string $parentId
126
     * @return Category
127
     */
128
    public function convertNodeToEntity(array $category, $parentId)
129
    {
130
        $categoryModel = new Category();
131
        $categoryModel->fromArray($this->getCategoryData($category['name']));
132
133
        $parent = $this->categoryRepository->findOneBy([
134
            'id' => $parentId
135
        ]);
136
        $categoryModel->setParent($parent);
137
138
        $this->manager->persist($categoryModel);
139
140
        $categoryAttribute = $categoryModel->getAttribute();
141
        $categoryAttribute->setConnectImportedCategory(true);
142
        $this->manager->persist($categoryAttribute);
143
144
        /** @var \Shopware\CustomModels\Connect\RemoteCategory $remoteCategory */
145
        $remoteCategory = $this->remoteCategoryRepository->findOneBy(['categoryKey' => $category['categoryId']]);
146
        if ($remoteCategory) {
147
            $remoteCategory->addLocalCategory($categoryModel);
148
            $this->manager->persist($remoteCategory);
149
        }
150
151
        $this->manager->flush();
152
153
        return $categoryModel;
154
    }
155
156
    /**
157
     * {@inheritdoc}
158
     */
159
    public function generateTree(array $categories, $idPrefix = '')
160
    {
161
        $tree = [];
162
        uksort($categories, function ($a, $b) {
163
            return strlen($a) - strlen($b);
164
        });
165
166
        if (strlen($idPrefix) > 0) {
167
            // find child categories by given prefix
168
            $childCategories = array_filter(array_keys($categories), function ($key) use ($idPrefix) {
169
                return strpos($key, $idPrefix) === 0 && strrpos($key, '/') === strlen($idPrefix);
170
            });
171
            $filteredCategories = array_intersect_key($categories, array_flip($childCategories));
172
        } else {
173
            // filter only main categories
174
            $matchedKeys = array_filter(array_keys($categories), function ($key) {
175
                return strrpos($key, '/') === 0;
176
            });
177
            $filteredCategories = array_intersect_key($categories, array_flip($matchedKeys));
178
        }
179
180
        foreach ($filteredCategories as $key => $categoryName) {
181
            $children = $this->generateTree($categories, $key);
182
            $tree[$key] = [
183
                'name' => $categoryName,
184
                'children' => $children,
185
                'categoryId' => $key,
186
                'leaf' => empty($children),
187
            ];
188
        }
189
190
        return $tree;
191
    }
192
193
    /**
194
     * Generate category data array
195
     * it's used to create category and
196
     * attribute from array
197
     *
198
     * @param string $name
199
     * @return array
200
     */
201
    private function getCategoryData($name)
202
    {
203
        return [
204
            'name' => $name,
205
            'active' => true,
206
            'childrenCount' => 0,
207
            'text' => $name,
208
            'attribute' => [
209
                    'id' => 0,
210
                    'parent' => 0,
211
                    'name' => 'Deutsch',
212
                    'position' => 0,
213
                    'active' => true,
214
                    'childrenCount' => 0,
215
                    'text' => '',
216
                    'cls' => '',
217
                    'leaf' => false,
218
                    'allowDrag' => false,
219
                    'parentId' => 0,
220
                    'categoryId' => null,
221
                    'attribute1' => null,
222
                    'attribute2' => null,
223
                    'attribute3' => null,
224
                    'attribute4' => null,
225
                    'attribute5' => null,
226
                    'attribute6' => null,
227
                ],
228
        ];
229
    }
230
231
    /**
232
     * @param array $category
233
     * @param string $parentId
234
     * @return Category
235
     */
236
    private function checkAndCreateLocalCategory($category, $parentId)
237
    {
238
        $categoryModel = $this->categoryRepository->findOneBy([
239
            'name' => $category['name'],
240
            'parentId' => $parentId
241
        ]);
242
243
        if (!$categoryModel) {
244
            $categoryModel = $this->convertNodeToEntity($category, $parentId);
245
        }
246
247
        return $categoryModel->getId();
248
    }
249
}
250