Completed
Pull Request — master (#391)
by Jonas
08:46 queued 05:29
created

getModelManager()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
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
use Shopware\Components\CSRFWhitelistAware;
9
use Shopware\Components\Model\ModelManager;
10
use Shopware\CustomModels\Connect\Attribute;
11
use Shopware\Models\Article\Repository as ArticleRepository;
12
use Shopware\Models\Category\Category;
13
use ShopwarePlugins\Connect\Components\Config;
14
use ShopwarePlugins\Connect\Components\ConnectExport;
15
use ShopwarePlugins\Connect\Components\ConnectFactory;
16
use ShopwarePlugins\Connect\Components\ErrorHandler;
17
use Shopware\Models\Article\Article;
18
use ShopwarePlugins\Connect\Components\Validator\ProductAttributesValidator\ProductsAttributesValidator;
19
use ShopwarePlugins\Connect\Components\Marketplace\MarketplaceSettingsApplier;
20
use ShopwarePlugins\Connect\Components\Marketplace\MarketplaceSettings;
21
use ShopwarePlugins\Connect\Components\ProductStream\ProductStreamService;
22
use ShopwarePlugins\Connect\Components\SnHttpClient;
23
use ShopwarePlugins\Connect\Struct\SearchCriteria;
24
use ShopwarePlugins\Connect\Subscribers\Connect;
25
use Shopware\Connect\SDK;
26
use ShopwarePlugins\Connect\Components\ConfigFactory;
27
use ShopwarePlugins\Connect\Components\ProductStream\ProductStreamsAssignments;
28
29
class Shopware_Controllers_Backend_Connect extends \Shopware_Controllers_Backend_ExtJs implements CSRFWhitelistAware
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
30
{
31
    /**
32
     * @var ConnectFactory
33
     */
34
    private $factory;
35
36
    /**
37
     * @var Config
38
     */
39
    private $configComponent;
40
41
    /**
42
     * @var MarketplaceSettingsApplier
43
     */
44
    private $marketplaceSettingsApplier;
45
46
    /**
47
     * @var \Shopware\Connect\SDK
48
     */
49
    private $sdk;
50
51
    /**
52
     * @var SnHttpClient
53
     */
54
    private $snHttpClient;
55
56
    /**
57
     * @var ProductStreamService
58
     */
59
    private $productStreamService;
60
61
    /**
62
     * @return ModelManager
63
     */
64
    public function getModelManager()
65
    {
66
        return $this->get('models');
67
    }
68
69
    /**
70
     * @return \Shopware\Connect\SDK
71
     */
72
    public function getSDK()
73
    {
74
        if ($this->sdk === null) {
75
            $this->sdk = $this->get('ConnectSDK');
76
        }
77
78
        return $this->sdk;
79
    }
80
81
    /**
82
     * @return \ShopwarePlugins\Connect\Components\Helper
83
     */
84
    public function getHelper()
85
    {
86
        return $this->getConnectFactory()->getHelper();
87
    }
88
89
    /**
90
     * @return ConnectFactory
91
     */
92
    public function getConnectFactory()
93
    {
94
        if ($this->factory === null) {
95
            $this->factory = new ConnectFactory();
96
        }
97
98
        return $this->factory;
99
    }
100
101
    /**
102
     * @return ArticleRepository
103
     */
104
    private function getArticleRepository()
105
    {
106
        return $this->getModelManager()->getRepository(
107
            Article::class
108
        );
109
    }
110
111
    /**
112
     * @return \Shopware\Models\Category\Repository
113
     */
114
    private function getCategoryRepository()
115
    {
116
        return $this->getModelManager()->getRepository(
117
            Category::class
118
        );
119
    }
120
121
    /**
122
     * Will return a category model for the given id. If the attribute should not exist
123
     * it will be created
124
     *
125
     * @param $id
126
     * @return null|Category
127
     */
128
    private function getCategoryModelById($id)
129
    {
130
        $categoryModel = $this->getCategoryRepository()->find($id);
131
        if (!$categoryModel || !$categoryModel->getAttribute()) {
132
            $attribute = new \Shopware\Models\Attribute\Category();
133
            $attribute->setCategory($categoryModel);
134
            $this->getModelManager()->persist($attribute);
135
            $this->getModelManager()->flush($attribute);
136
        }
137
138
        return $categoryModel;
139
    }
140
141
    /**
142
     * When the backend module is being loaded, update connect products.
143
     *
144
     * It might be considerable to move this to e.g. the lifecycle events of the products
145
     */
146
    public function indexAction()
147
    {
148
        $this->getHelper()->updateConnectProducts();
149
150
        parent::loadAction();
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (loadAction() instead of indexAction()). Are you sure this is correct? If so, you might want to change this to $this->loadAction().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
151
    }
152
153
    /**
154
     * When the backend module is being loaded, update connect products.
155
     *
156
     * It might be considerable to move this to e.g. the lifecycle events of the products
157
     */
158
    public function refreshConnectItemsAction()
159
    {
160
        $this->getHelper()->updateConnectProducts();
161
    }
162
163
    /**
164
     * If the price type is purchase or both
165
     * and shopware is 5.2 or greater
166
     * insert detailPurchasePrice in connect config table
167
     * when priceFieldForPurchasePriceExport is empty
168
     */
169
    private function updatePurchasePriceField()
170
    {
171
        $field = $this->getConfigComponent()->getConfig('priceFieldForPurchasePriceExport');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $field is correct as $this->getConfigComponen...orPurchasePriceExport') (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...
172
        if ($field) {
173
            return;
174
        }
175
176
        if (!method_exists('Shopware\Models\Article\Detail', 'setPurchasePrice')) {
177
            return;
178
        }
179
180
        if ($this->getSDK()->getPriceType() == \Shopware\Connect\SDK::PRICE_TYPE_PURCHASE
181
            || $this->getSDK()->getPriceType() == \Shopware\Connect\SDK::PRICE_TYPE_BOTH
182
        ) {
183
            $this->getConfigComponent()->setConfig(
184
                'priceFieldForPurchasePriceExport',
185
                'detailPurchasePrice',
186
                null,
187
                'export'
188
            );
189
        }
190
    }
191
192
    /**
193
     * Helper function to return a QueryBuilder for creating the listing queries for the import and export listings
194
     *
195
     * @param $filter
196
     * @param $order
197
     * @return \Doctrine\ORM\QueryBuilder
198
     */
199
    private function getListQueryBuilder($filter, $order)
200
    {
201
        $builder = $this->getModelManager()->createQueryBuilder();
202
        $builder->from('Shopware\CustomModels\Connect\Attribute', 'at');
203
        $builder->join('at.article', 'a');
204
        $builder->join('a.mainDetail', 'd');
205
        $builder->leftJoin('d.prices', 'p', 'with', "p.from = 1 AND p.customerGroupKey = 'EK'");
206
        $builder->leftJoin('a.supplier', 's');
207
        $builder->leftJoin('a.tax', 't');
208
209
        $builder->select([
210
            'a.id',
211
            'd.number as number',
212
            'd.inStock as inStock',
213
            'a.name as name',
214
            's.name as supplier',
215
            'a.active as active',
216
            't.tax as tax',
217
            'p.price * (100 + t.tax) / 100 as price',
218
            'at.category'
219
        ]);
220
221
        // show only main variant in export/import lists
222
        $builder->groupBy('at.articleId');
223
224
        foreach ($filter as $key => $rule) {
225
            switch ($rule['property']) {
226
                case 'search':
227
                    $builder->andWhere('d.number LIKE :search OR a.name LIKE :search OR s.name LIKE :search')
228
                        ->setParameter('search', $rule['value']);
229
                    break;
230
                case 'categoryId':
231
                    $builder->join('a.categories', 'c', 'with', 'c.id = :categoryId OR c.path LIKE :categoryPath')
232
                        ->setParameter('categoryId', $rule['value'])
233
                        ->setParameter('categoryPath', '%|' . $rule['value'] . '|%');
234
                    break;
235
                case 'supplierId':
236
                    $builder->andWhere('a.supplierId = :supplierId')
237
                        ->setParameter('supplierId', $rule['value']);
238
                    break;
239
                case 'exportStatus':
240
                    $builder->andWhere('at.exportStatus LIKE :status')
241
                        ->setParameter('status', $rule['value']);
242
                    break;
243
                case 'active':
244
                    $builder->andWhere('a.active LIKE :active')
245
                        ->setParameter('active', $rule['value']);
246
                    break;
247
                default:
248
                    continue;
249
            }
250
        }
251
252
        $builder->addOrderBy($order);
253
254
        return $builder;
255
    }
256
257
    /**
258
     * Get all products exported to connect
259
     */
260
    public function getExportListAction()
261
    {
262
        $filter = (array) $this->Request()->getParam('filter', []);
263
        $order = reset($this->Request()->getParam('sort', []));
0 ignored issues
show
Bug introduced by
$this->Request()->getParam('sort', array()) cannot be passed to reset() as the parameter $array expects a reference.
Loading history...
264
265
        $criteria = new SearchCriteria([
266
            'offset' => (int) $this->Request()->getParam('start'),
267
            'limit' => (int) $this->Request()->getParam('limit'),
268
            'orderBy' => $order['property'],
269
            'orderByDirection' => $order['direction'],
270
271
        ]);
272
273
        foreach ($filter as $key => $rule) {
274
            $field = $rule['property'];
275
            $criteria->{$field} = $rule['value'];
276
        }
277
278
        $exportList = $this->getConnectExport()->getExportList($criteria);
279
280
        $this->View()->assign([
281
            'success' => true,
282
            'data' => $exportList->articles,
283
            'total' => $exportList->count,
284
        ]);
285
    }
286
287
    public function getExportStatusAction()
288
    {
289
        $attrRepo = $this->getModelManager()->getRepository('Shopware\CustomModels\Connect\Attribute');
290
291
        $syncedItems = $attrRepo->countStatus([
292
            Attribute::STATUS_SYNCED,
293
        ]);
294
295
        $totalItems = $attrRepo->countStatus([
296
            Attribute::STATUS_INSERT,
297
            Attribute::STATUS_UPDATE,
298
            Attribute::STATUS_SYNCED,
299
        ]);
300
301
        $this->View()->assign([
302
            'success' => true,
303
            'data' => $syncedItems,
304
            'total' => $totalItems,
305
        ]);
306
    }
307
308
    /**
309
     * Get all products imported from connect
310
     */
311
    public function getImportListAction()
312
    {
313
        $filter = (array) $this->Request()->getParam('filter', []);
314
        $sort = $this->Request()->getParam('sort', []);
315
316
        foreach ($sort as $key => $currentSorter) {
317
            if ($currentSorter['property'] == 'category') {
318
                unset($sort[$key]);
319
            }
320
        }
321
322
        $builder = $this->getListQueryBuilder(
323
            $filter,
324
            $sort
325
        );
326
        $builder->addSelect([
327
            'at.shopId',
328
            'at.sourceId',
329
            'at.exportStatus as connectStatus',
330
        ]);
331
        $builder->andWhere('at.shopId IS NOT NULL');
332
333
        $builder->addOrderBy('at.category', 'ASC');
334
335
        $query = $builder->getQuery();
336
337
        $query->setFirstResult($this->Request()->getParam('start'));
338
        $query->setMaxResults($this->Request()->getParam('limit'));
339
340
        $countResult = array_map('current', $builder->select(['COUNT(DISTINCT at.articleId) as current'])->orderBy('current')->getQuery()->getScalarResult());
341
        $total = array_sum($countResult);
0 ignored issues
show
Unused Code introduced by
$total is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
342
        $total = array_sum($countResult);
343
        // todo@sb: find better solution. getQueryCount method counts s_plugin_connect_items.id like they are not grouped by article id
344
//        $total = Shopware()->Models()->getQueryCount($query);
345
        $data = $query->getArrayResult();
346
347
        $this->View()->assign([
348
            'success' => true,
349
            'data' => $data,
350
            'total' => $total
351
        ]);
352
    }
353
354
    /**
355
     * Import parts of the connect category tree to shopware
356
     */
357
    public function importConnectCategoriesAction()
358
    {
359
        $fromCategory = $this->Request()->getParam('fromCategory');
360
        $toCategory = $this->Request()->getParam('toCategory');
361
362
        $entityManager = $this->getModelManager();
363
        $helper = $this->getHelper();
0 ignored issues
show
Unused Code introduced by
$helper is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
364
365
        // Make sure that the target category exists
366
        $toCategoryModel = $this->getCategoryRepository()->find($toCategory);
367
        if (!$toCategoryModel) {
368
            throw new \RuntimeException("Category with id  {$toCategory} not found");
369
        }
370
371
        // The user might have changed the mapping without saving and then hit the "importCategories"
372
        // button. So we save the parent category's mapping first
373
        $parentCategory = $this->getCategoryModelById($toCategory);
374
        $parentCategory->getAttribute()->setConnectImportMapping($fromCategory);
375
        $entityManager->flush();
376
377
        try {
378
            $entityManager->getConnection()->beginTransaction();
379
            $this->importConnectCategories($fromCategory, $toCategory);
380
            $entityManager->getConnection()->commit();
381
        } catch (\Exception $e) {
382
            $entityManager->getConnection()->rollback();
383
            throw new \RuntimeException('Could not import categories', 0, $e);
384
        }
385
    }
386
387
    /**
388
     * Will import a connect category tree into shopware.
389
     *
390
     * @param $fromCategory
391
     * @param $toCategory
392
     */
393
    public function importConnectCategories($fromCategory, $toCategory)
394
    {
395
        $categoriesToImport = $this->getFlatConnectCategories($fromCategory);
396
        $toCategoryModel = $this->getCategoryRepository()->find($toCategory);
397
        $entityManager = $this->getModelManager();
398
399
        /*
400
         * The import string allows to identify categories, which have already been imported for
401
         * this exact import. This does not prevent the user from importing the same sub-tree
402
         * into multiple shopware categories. But it does prevent him from importing the same sub-tree
403
         * into the same category multiple times
404
         */
405
        $importString = $fromCategory . '-' . $toCategory;
406
        $currentLevel = 1;
407
        $mappings = [];
408
409
        foreach ($categoriesToImport as $id => $category) {
410
            $name = $category['name'];
411
            $parent = $category['parent'];
412
            $level = $category['level'];
413
414
            // Only flush after the level changed - this speeds up the import
415
            if ($currentLevel != $level) {
416
                Shopware()->Models()->flush();
417
            }
418
            $currentLevel = $level;
419
420
            /** @var Category $parentModel */
421
            if (!$parent) {
422
                // Top category level - use toCategoryModel
423
                $parentModel = $toCategoryModel;
424
            } else {
425
                // Parent was created before and is referenced in $mappings
426
                $parentModel = $mappings[$parent];
427
            }
428
429
            // Check if there is already a category attribute for this import
430
            $categoryAttributes = $entityManager->getRepository('\Shopware\Models\Attribute\Category')->findBy(
431
                ['connectImported' => $importString, 'connectImportMapping' => $id],
432
                null,
433
                1
434
            );
435
436
            if (!empty($categoryAttributes)) {
437
                /** @var \Shopware\Models\Attribute\Category $categoryAttribute */
438
                $categoryAttribute = array_pop($categoryAttributes);
439
                $category = $categoryAttribute->getCategory();
440
            } else {
441
                // Create category and attribute model
442
                $category = new Category();
443
                $category->setName($name);
444
                $category->setParent($parentModel);
445
446
                $attribute = new \Shopware\Models\Attribute\Category();
447
                $attribute->setConnectImportMapping($id);
448
                $attribute->setConnectImported($importString);
449
                $category->setAttribute($attribute);
450
451
                Shopware()->Models()->persist($category);
452
                Shopware()->Models()->persist($attribute);
453
            }
454
455
456
            // Store the new category model in out $mappings array
457
            $mappings[$id] = $category;
458
        }
459
460
        Shopware()->Models()->flush();
461
    }
462
463
    public function initParamsAction()
464
    {
465
        $marketplaceIcon = $this->getConfigComponent()->getConfig('marketplaceIcon', Connect::MARKETPLACE_ICON);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $marketplaceIcon is correct as $this->getConfigComponen...nect::MARKETPLACE_ICON) (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...
466
        $marketplaceName = $this->getConfigComponent()->getConfig('marketplaceName', Connect::MARKETPLACE_NAME);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $marketplaceName is correct as $this->getConfigComponen...nect::MARKETPLACE_NAME) (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...
467
        $marketplaceNetworkUrl = $this->getConfigComponent()->getConfig('marketplaceNetworkUrl', Connect::MARKETPLACE_SOCIAL_NETWORK_URL);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $marketplaceNetworkUrl is correct as $this->getConfigComponen...ACE_SOCIAL_NETWORK_URL) (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...
468
        $defaultMarketplace = $this->getConfigComponent()->getConfig('isDefault', true);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $defaultMarketplace is correct as $this->getConfigComponen...nfig('isDefault', true) (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...
469
        $isFixedPriceAllowed = 0;
470
        $priceType = $this->getSDK()->getPriceType();
471
        if ($priceType === SDK::PRICE_TYPE_BOTH ||
472
            $priceType === SDK::PRICE_TYPE_RETAIL) {
473
            $isFixedPriceAllowed = 1;
474
        }
475
        $marketplaceIncomingIcon = ($marketplaceName == Connect::MARKETPLACE_NAME ? Connect::MARKETPLACE_GREEN_ICON : $marketplaceIcon);
476
        $marketplaceLogo = $this->getConfigComponent()->getConfig('marketplaceLogo', Connect::MARKETPLACE_LOGO);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $marketplaceLogo is correct as $this->getConfigComponen...nect::MARKETPLACE_LOGO) (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...
477
        $purchasePriceInDetail = method_exists('Shopware\Models\Article\Detail', 'setPurchasePrice') ? 1 : 0;
478
479
        $this->View()->assign([
480
            'success' => true,
481
            'data' => [
482
                'marketplaceName' => $marketplaceName,
483
                'marketplaceNetworkUrl' => $marketplaceNetworkUrl,
484
                'marketplaceIcon' => $marketplaceIcon,
485
                'defaultMarketplace' => $defaultMarketplace,
486
                'isFixedPriceAllowed' => $isFixedPriceAllowed,
487
                'marketplaceIncomingIcon' => $marketplaceIncomingIcon,
488
                'marketplaceLogo' => $marketplaceLogo,
489
                'purchasePriceInDetail' => $purchasePriceInDetail,
490
            ]
491
        ]);
492
    }
493
494
    public function checkForDataMigrationAction()
495
    {
496
        $result = $this->getHelper()->checkIfConnectCategoriesHaveToBeRecreated();
497
        $this->View()->assign([
498
            'success' => true,
499
            'data' => [
500
                'recreateConnectCategories' => $result,
501
            ]
502
        ]);
503
    }
504
505
    /**
506
     * Returns a flat array of connect categories
507
     *
508
     * @param $rootCategory
509
     * @return array(
0 ignored issues
show
Documentation introduced by
The doc-type array( could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
510
     *      string => array('id' => string, 'name' => string, 'level' => int, 'parent' => string|null)
511
     * )
512
     */
513
    private function getFlatConnectCategories($rootCategory)
514
    {
515
        $sdk = $this->getSDK();
516
        $connectCategories = $sdk->getCategories();
0 ignored issues
show
Bug introduced by
The method getCategories() does not seem to exist on object<Shopware\Connect\SDK>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
517
518
        $categoriesToImport = [];
519
        foreach ($connectCategories as $id => $name) {
520
            // Skip all entries which do not start with the parent or do not have it at all
521
            if (strpos($id, $rootCategory) !== 0) {
522
                continue;
523
            }
524
525
            $level = substr_count(preg_replace("#^{$rootCategory}#", '', $id), '/');
526
527
            // Skip the root category
528
            if ($level == 0) {
529
                continue;
530
            }
531
532
            $categoriesToImport[$id] = [
533
                'id' => $id,
534
                'name' => $name,
535
                'level' => $level,
536
                'parent' => $level == 1 ? null : implode('/', array_slice(explode('/', $id), 0, -1))
537
            ];
538
        }
539
540
        // Sort the categories ascending by their level, so parent categories can be imported first
541
        uasort(
542
            $categoriesToImport,
543
            function ($a, $b) {
544
                $a = $a['level'];
545
                $b = $b['level'];
546
                if ($a == $b) {
547
                    return 0;
548
                }
549
550
                return ($a < $b) ? -1 : 1;
551
            }
552
        );
553
554
        return $categoriesToImport;
555
    }
556
557
    /**
558
     * Save a given mapping of a given category to all subcategories
559
     */
560
    public function applyMappingToChildrenAction()
561
    {
562
        $categoryId = $this->Request()->getParam('category');
563
        $mapping = $this->Request()->getParam('mapping');
564
565
        $entityManager = $this->getModelManager();
566
567
        try {
568
            $entityManager->getConnection()->beginTransaction();
569
            $this->applyMappingToChildren($mapping, $categoryId);
570
            $entityManager->getConnection()->commit();
571
            $this->View()->assign([
572
                'success' => true
573
            ]);
574
        } catch (\Exception $e) {
575
            $entityManager->getConnection()->rollback();
576
            $this->View()->assign([
577
                'message' => $e->getMessage(),
578
                'success' => false
579
            ]);
580
        }
581
    }
582
583
    /**
584
     * Returns success true if user could be logged in, or false if something went wrong
585
     *
586
     * @return array(
0 ignored issues
show
Documentation introduced by
The doc-type array( could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
587
     *      string => bool
588
     * )
589
     */
590
    public function loginAction()
591
    {
592
        /** @var \Shopware\Components\HttpClient\GuzzleHttpClient $client */
593
        $client = $this->get('http_client');
594
595
        $shopwareId = $this->Request()->getParam('shopwareId');
596
        $password = $this->Request()->getParam('password');
597
        $loginUrl = $this->getHost() . '/sdk/pluginCommunication/login';
598
599
        // Try to login into connect
600
        $response = $client->post(
601
            $loginUrl,
602
            [
603
                'content-type' => 'application/x-www-form-urlencoded'
604
            ],
605
            [
606
                'username' => urlencode($shopwareId),
607
                'password' => urlencode($password)
608
            ]
609
        );
610
611
        $responseObject = json_decode($response->getBody());
612
613
        if (!$responseObject->success) {
614
            $message = $responseObject->reason;
615
616
            if ($responseObject->reason == SDK::WRONG_CREDENTIALS_MESSAGE) {
617
                $snippets = Shopware()->Snippets()->getNamespace('backend/connect/view/main');
618
                $message = $snippets->get(
619
                    'error/wrong_credentials',
620
                    SDK::WRONG_CREDENTIALS_MESSAGE,
621
                    true
622
                );
623
            }
624
625
            $this->View()->assign([
626
                'success' => false,
627
                'message' => $message
628
            ]);
629
630
            return;
631
        }
632
633
        try {
634
            // set apiKey in Connect config table
635
            // after that create completely new SDK instance,
636
            // because correct apiKey should be used during creation
637
            $this->getConfigComponent()->setConfig('apiKey', $responseObject->apiKey, null, 'general');
638
            $sdk = $this->getConnectFactory()->createSDK();
639
            $sdk->verifySdk();
640
            $this->getConfigComponent()->setConfig('apiKeyVerified', true);
641
            $this->getConfigComponent()->setConfig('shopwareId', $shopwareId, null, 'general');
642
            $this->removeConnectMenuEntry();
643
            $marketplaceSettings = $sdk->getMarketplaceSettings();
644
            $this->getMarketplaceApplier()->apply(new MarketplaceSettings($marketplaceSettings));
645
        } catch (\Exception $e) {
646
            $this->getConfigComponent()->setConfig('apiKey', null, null, 'general');
647
648
            $this->View()->assign([
649
                'success' => false,
650
                'message' => $e->getMessage()
651
            ]);
652
653
            return;
654
        }
655
656
        $this->View()->assign([
657
            'success' => true
658
        ]);
659
    }
660
661
    /**
662
     * Returns success true if user could be logged in, or false if something went wrong
663
     *
664
     * @return array(
0 ignored issues
show
Documentation introduced by
The doc-type array( could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
665
     *      string => bool
666
     * )
667
     */
668
    public function registerAction()
669
    {
670
        /** @var \Shopware\Components\HttpClient\GuzzleHttpClient $client */
671
        $client = $this->get('http_client');
672
673
        $shopwareId = $this->Request()->getParam('shopwareID');
674
        $password = $this->Request()->getParam('password');
675
        $email = $this->Request()->getParam('email');
676
677
        // Enter the valid production url here
678
        $host = $this->getHost();
679
680
        $loginUrl = $host . '/sdk/pluginCommunication/register';
681
682
        $response = $client->post(
683
            $loginUrl,
684
            [
685
                'content-type' => 'application/x-www-form-urlencoded'
686
            ],
687
            [
688
                'username'  => urlencode($shopwareId),
689
                'password'  => urlencode($password),
690
                'email'     => urlencode($email)
691
            ]
692
        );
693
694
        $responseObject = json_decode($response->getBody());
695
696
        if (!$responseObject->success) {
697
            $this->View()->assign([
698
                'success' => false,
699
                'message' => $responseObject->reason
700
            ]);
701
702
            return;
703
        }
704
705
        try {
706
            // set apiKey in Connect config table
707
            // after that create completely new SDK instance,
708
            // because correct apiKey should be used during creation
709
            $this->getConfigComponent()->setConfig('apiKey', $responseObject->apiKey, null, 'general');
710
            $sdk = $this->getConnectFactory()->createSDK();
711
            $sdk->verifySdk();
712
            $this->getConfigComponent()->setConfig('apiKeyVerified', true);
713
            $this->getConfigComponent()->setConfig('shopwareId', $shopwareId, null, 'general');
714
            $this->removeConnectMenuEntry();
715
            $marketplaceSettings = $sdk->getMarketplaceSettings();
716
            $this->getMarketplaceApplier()->apply(new MarketplaceSettings($marketplaceSettings));
717
        } catch (\Exception $e) {
718
            $this->getConfigComponent()->setConfig('apiKey', null, null, 'general');
719
720
            $this->View()->assign([
721
                'success' => false,
722
                'message' => $e->getMessage()
723
            ]);
724
725
            return;
726
        }
727
728
        $this->View()->assign([
729
            'success' => true
730
        ]);
731
    }
732
733
    /**
734
     * Redirects and auto login to SN system
735
     */
736
    public function autoLoginAction()
737
    {
738
        return $this->redirect('http://' . $this->getHost() . '/login');
739
    }
740
741
    /**
742
     * @param bool $loggedIn
743
     * @throws Zend_Db_Adapter_Exception
744
     */
745
    private function removeConnectMenuEntry($loggedIn = true)
746
    {
747
        /** @var Enlight_Components_Db_Adapter_Pdo_Mysql $db */
748
        $db = Shopware()->Db();
749
750
        $result = $db->fetchAssoc("SELECT id, parent, pluginID FROM s_core_menu WHERE controller = 'connect' AND name = 'Register' ORDER BY id DESC LIMIT 1");
751
        if (empty($result)) {
752
            return;
753
        }
754
        $row = current($result);
755
756
        $db->exec('DELETE FROM s_core_menu WHERE id = ' . $row['id']);
757
758
        $insertSql = "INSERT INTO s_core_menu (
759
            parent,
760
            name,
761
            class,
762
            pluginID,
763
            controller,
764
            action,
765
            onclick,
766
            active
767
          ) VALUES (
768
            '#parent#',
769
            '#name#',
770
            '#class#',
771
            #pluginID#,
772
            '#controller#',
773
            '#action#',
774
            '#onclick#',
775
            1
776
          )";
777
778
        $db->exec(strtr($insertSql, [
779
            '#parent#' => $row['parent'],
780
            '#name#' => 'Import',
781
            '#class#' => 'sc-icon-import',
782
            '#pluginID#' => $row['pluginID'],
783
            '#controller#' => 'Connect',
784
            '#onclick#' => '',
785
            '#action#' => 'Import'
786
        ]));
787
788
        $db->exec(strtr($insertSql, [
789
            '#parent#' => $row['parent'],
790
            '#name#' => 'Export',
791
            '#class#' => 'sc-icon-export',
792
            '#pluginID#' => $row['pluginID'],
793
            '#controller#' => 'Connect',
794
            '#onclick#' => '',
795
            '#action#' => 'Export'
796
        ]));
797
798
        $db->exec(strtr($insertSql, [
799
            '#parent#' => $row['parent'],
800
            '#name#' => 'Settings',
801
            '#class#' => 'sprite-gear',
802
            '#pluginID#' => $row['pluginID'],
803
            '#controller#' => 'Connect',
804
            '#onclick#' => '',
805
            '#action#' => 'Settings'
806
        ]));
807
808
        $db->exec(strtr($insertSql, [
809
            '#parent#' => $row['parent'],
810
            '#name#' => 'OpenConnect',
811
            '#class#' => 'connect-icon',
812
            '#pluginID#' => $row['pluginID'],
813
            '#controller#' => 'Connect',
814
            '#onclick#' => 'window.open("connect/autoLogin")',
815
            '#action#' => 'OpenConnect'
816
        ]));
817
    }
818
819
    /**
820
     * Helper that will assign a given mapping to all children of a given category
821
     *
822
     * @param $mapping string
823
     * @param $categoryId int
824
     * @throws \Exception
825
     */
826
    private function applyMappingToChildren($mapping, $categoryId)
827
    {
828
        $helper = $this->getHelper();
0 ignored issues
show
Unused Code introduced by
$helper is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
829
        $ids = $this->getChildCategoriesIds($categoryId);
830
        $entityManager = $this->getModelManager();
831
832
833
        if (!$categoryId) {
834
            throw new \RuntimeException("Category '{$categoryId}' not found");
835
        }
836
837
        // First of all try to save the mapping for the parent category. If that fails,
838
        // it mustn't be done for the child categories
839
        $parentCategory = $this->getCategoryModelById($categoryId);
840
        $parentCategory->getAttribute()->setConnectExportMapping($mapping);
841
        $entityManager->flush();
842
843
        // Don't set the children with models in order to speed things up
844
        $builder = $entityManager->createQueryBuilder();
845
        $builder->update('\Shopware\Models\Attribute\Category', 'categoryAttribute')
846
            ->set('categoryAttribute.connectExportMapping', $builder->expr()->literal($mapping))
847
            ->where($builder->expr()->in('categoryAttribute.categoryId', $ids));
848
849
        $builder->getQuery()->execute();
850
    }
851
852
    /**
853
     * Helper function which returns the IDs of the child categories of a given parent category
854
     *
855
     * @param $parentId int
856
     * @return array
857
     */
858
    private function getChildCategoriesIds($parentId)
859
    {
860
        $query = $this->getModelManager()->createQuery('SELECT c.id from Shopware\Models\Category\Category c WHERE c.path LIKE ?1 ');
861
        $query->setParameter(1, ["%|{$parentId}|%"]);
862
        $result = $query->getResult(\Doctrine\ORM\AbstractQuery::HYDRATE_ARRAY);
863
864
        // Pop IDs from result rows
865
        return array_map(
866
            function ($row) {
867
                return array_pop($row);
868
            },
869
            $result
870
        );
871
    }
872
873
    /**
874
     * Collect all source ids by given article ids
875
     */
876
    public function getArticleSourceIdsAction()
877
    {
878
        try {
879
            $exportAll = (bool) $this->Request()->getPost('exportAll', false);
880
            $articleIds = $this->Request()->getPost('ids', []);
881
882
            if ($exportAll) {
883
                $articleIds = $this->getHelper()->getAllNonConnectArticleIds();
884
            }
885
886
            if (!is_array($articleIds)) {
887
                $articleIds = [$articleIds];
888
            }
889
890
            $sourceIds = $this->getHelper()->getArticleSourceIds($articleIds);
891
892
            $this->View()->assign([
893
                'success' => true,
894
                'sourceIds' => $sourceIds
895
            ]);
896
        } catch (\Exception $e) {
897
            $this->View()->assign([
898
                'success' => false,
899
                'message' => $e->getMessage()
900
            ]);
901
        }
902
    }
903
904
    /**
905
     * Called when ConnectCategories have to be recreated
906
     */
907
    public function applyMigrationsAction()
908
    {
909
        $batchsize = $this->Request()->getPost('batchsize');
910
        $offset = $this->Request()->getPost('offset');
911
        try {
912
            $totalCount = $this->getHelper()->applyMigrations((int) $offset, (int) $batchsize);
913
            $this->View()->assign([
914
                'success' => true,
915
                'data' => [
916
                    'totalCount' => $totalCount
917
                ]
918
            ]);
919
        } catch (Exception $e) {
920
            $this->View()->assign([
921
                'success' => false,
922
                'messages' => [ErrorHandler::TYPE_DEFAULT_ERROR => [$e->getMessage()]]
923
            ]);
924
        }
925
    }
926
927
    /**
928
     * Called when a product variants were marked for update in the connect backend module
929
     */
930
    public function insertOrUpdateProductAction()
931
    {
932
        // if priceType comes from SN and shopware version is 5.2
933
        // priceFieldForPurchasePriceExport is empty
934
        // we need to set it because there isn't customer groups
935
        // purchasePrice is stored always in article detail
936
        $this->updatePurchasePriceField();
937
        $sourceIds = $this->Request()->getPost('sourceIds');
938
        $connectExport = $this->getConnectExport();
939
940
        try {
941
            $errors = $connectExport->export($sourceIds);
942
        } catch (\RuntimeException $e) {
943
            $this->View()->assign([
944
                'success' => false,
945
                'messages' => [ErrorHandler::TYPE_DEFAULT_ERROR => [$e->getMessage()]]
946
            ]);
947
948
            return;
949
        }
950
951
        if (!empty($errors)) {
952
            $this->View()->assign([
953
                'success' => false,
954
                'messages' => $errors
955
            ]);
956
957
            return;
958
        }
959
960
        $this->View()->assign([
961
            'success' => true
962
        ]);
963
    }
964
965
    /**
966
     * Delete a product from connect export
967
     */
968
    public function deleteProductAction()
969
    {
970
        $sdk = $this->getSDK();
971
        $ids = $this->Request()->getPost('ids');
972
        foreach ($ids as $id) {
973
            /** @var \Shopware\Models\Article\Article $model */
974
            $model = $this->getConnectExport()->getArticleModelById($id);
975
            if ($model === null) {
976
                continue;
977
            }
978
            /** @var \Shopware\Models\Article\Detail $detail */
979
            foreach ($model->getDetails() as $detail) {
980
                $attribute = $this->getHelper()->getConnectAttributeByModel($detail);
981
                $sdk->recordDelete($attribute->getSourceId());
982
                $attribute->setExportStatus(Attribute::STATUS_DELETE);
983
                $attribute->setExported(false);
984
            }
985
        }
986
        Shopware()->Models()->flush();
987
    }
988
989
    /**
990
     * Verify a given api key against the connect server
991
     */
992
    public function verifyApiKeyAction()
993
    {
994
        $sdk = $this->getSDK();
995
        try {
996
            $key = $this->Request()->getPost('apiKey');
997
            $sdk->verifyKey($key);
998
            $this->View()->assign([
999
                'success' => true
1000
            ]);
1001
            $this->getConfigComponent()->setConfig('apiKeyVerified', true);
1002
            $marketplaceSettings = $sdk->getMarketplaceSettings();
1003
            $this->getMarketplaceApplier()->apply(new MarketplaceSettings($marketplaceSettings));
1004
        } catch (\Exception $e) {
1005
            $this->View()->assign([
1006
                'message' => $e->getMessage(),
1007
                'success' => false
1008
            ]);
1009
            $this->getConfigComponent()->setConfig('apiKeyVerified', false);
1010
        }
1011
1012
        $this->getModelManager()->flush();
1013
    }
1014
1015
    /**
1016
     * Returns the connectAttribute data for a given articleId
1017
     *
1018
     * @throws RuntimeException
1019
     * @throws Exception
1020
     */
1021
    public function getConnectDataAction()
1022
    {
1023
        $articleId = $this->Request()->getParam('articleId');
1024
1025
        if (!$articleId) {
1026
            throw new \Exception('Connect: ArticleId empty');
1027
        }
1028
1029
        /** @var Article $articleModel */
1030
        $articleModel = $this->getArticleRepository()->find($articleId);
1031
1032
        if (!$articleModel) {
1033
            throw new \RuntimeException("Could not find model for article with id {$articleId}");
1034
        }
1035
1036
        $data = $this->getHelper()->getOrCreateConnectAttributes($articleModel);
1037
1038
        $data = $this->getModelManager()->toArray($data);
1039
        if (isset($data['articleId'])) {
1040
            $data = [$data];
1041
        }
1042
1043
        $this->View()->assign([
1044
            'success' => true,
1045
            'data' => $data
1046
        ]);
1047
    }
1048
1049
    /**
1050
     * Save a given connect attribute
1051
     */
1052
    public function saveConnectAttributeAction()
1053
    {
1054
        $data = $this->Request()->getParams();
1055
1056
        /** @var \Shopware\CustomModels\Connect\Attribute $connectAttribute */
1057
        $connectAttribute = $this->getModelManager()->find('Shopware\CustomModels\Connect\Attribute', $data['id']);
1058
        if (!$connectAttribute) {
1059
            throw new \RuntimeException("Could not find connect attribute with id {$data['id']}");
1060
        }
1061
1062
        /** @var \Shopware\Models\Article\Detail $detail */
1063
        foreach ($connectAttribute->getArticle()->getDetails() as $detail) {
1064
            $connectAttribute = $this->getHelper()->getConnectAttributeByModel($detail);
1065
            // Only allow changes in the fixedPrice field if this is a local product
1066
            if (!$connectAttribute->getShopId()) {
1067
                $connectAttribute->setFixedPrice($data['fixedPrice']);
1068
            }
1069
            // Save the update fields
1070
            foreach ($data as $key => $value) {
1071
                if (strpos($key, 'update') === 0) {
1072
                    $setter = 'set' . ucfirst($key);
1073
                    $connectAttribute->$setter($value);
1074
                }
1075
            }
1076
            $this->getModelManager()->persist($connectAttribute);
1077
        }
1078
1079
        $this->getModelManager()->flush();
1080
1081
        $this->View()->assign(['success' => true]);
1082
    }
1083
1084
    /**
1085
     * Saves the changed "connectAllowed" attribute. Saving this attribute should be done
1086
     * by the shipping-module on its own, right now (as of SW 4.2.0) it does not do so.
1087
     *
1088
     * todo: Once the shipping module is fixed, increase the required version of this plugin
1089
     * and remove this and the unnecessary ExtJS extensions
1090
     */
1091
    public function saveShippingAttributeAction()
1092
    {
1093
        $shippingId = $this->Request()->getParam('shippingId');
1094
        $connectAllowed = $this->Request()->getParam('connectAllowed', true);
1095
1096
        if (!$shippingId) {
1097
            return;
1098
        }
1099
1100
        $shippingRepo = $this->getModelManager()->getRepository('\Shopware\Models\Dispatch\Dispatch');
1101
        /** @var \Shopware\Models\Dispatch\Dispatch $shipping */
1102
        $shipping = $shippingRepo->find($shippingId);
1103
1104
        if (!$shipping) {
1105
            return;
1106
        }
1107
1108
        $attribute = $shipping->getAttribute();
1109
1110
        if (!$attribute) {
1111
            $attribute = new \Shopware\Models\Attribute\Dispatch();
1112
            $attribute->setDispatch($shipping);
1113
            $shipping->setAttribute($attribute);
1114
            $this->getModelManager()->persist($attribute);
1115
        }
1116
1117
        $attribute->setConnectAllowed($connectAllowed);
1118
1119
        $this->getModelManager()->flush();
1120
1121
        $this->View()->assign('success', true);
1122
    }
1123
1124
    /**
1125
     * Lists all logs
1126
     */
1127
    public function getLogsAction()
1128
    {
1129
        $params = $this->Request()->getParams();
1130
        $order = $this->Request()->getParam('sort', [['property' => 'time', 'direction' => 'DESC']]);
1131
        $filters = $this->Request()->getParam('filter');
1132
1133
        $commandFilters = [];
1134
        foreach ($params as $key => $param) {
1135
            if (strpos($key, 'commandFilter_') !== false && $param == 'true') {
1136
                $commandFilters[] = str_replace('commandFilter_', '', $key);
1137
            }
1138
        }
1139
1140
        if (empty($commandFilters)) {
1141
            return;
1142
        }
1143
1144
        foreach ($order as &$rule) {
1145
            if ($rule['property'] == 'time') {
1146
                $rule['property'] = 'id';
1147
            }
1148
            $rule['property'] = 'logs.' . $rule['property'];
1149
        }
1150
1151
        $builder = $this->getModelManager()->createQueryBuilder();
1152
        $builder->select('logs');
1153
        $builder->from('Shopware\CustomModels\Connect\Log', 'logs')
1154
            ->addOrderBy($order)
1155
            ->where('logs.command IN (:commandFilter)')
1156
            ->setParameter('commandFilter', $commandFilters);
1157
1158
        foreach ($filters as $filter) {
1159
            switch ($filter['property']) {
1160
                case 'search':
1161
                    $builder->andWhere(
1162
                        'logs.request LIKE :search OR logs.response LIKE :search'
1163
                    );
1164
                    $builder->setParameter('search', $filter['value']);
1165
                    break;
1166
            }
1167
        }
1168
1169
        switch ($this->Request()->getParam('errorFilter', -1)) {
1170
            case 0:
1171
                $builder->andWhere('logs.isError = 1');
1172
                break;
1173
            case 1:
1174
                $builder->andWhere('logs.isError = 0');
1175
                break;
1176
        }
1177
1178
        $query = $builder->getQuery()
1179
            ->setFirstResult($this->Request()->getParam('start', 0))
1180
            ->setMaxResults($this->Request()->getParam('limit', 25));
1181
1182
        $total = Shopware()->Models()->getQueryCount($query);
1183
        $data = $query->getArrayResult();
1184
1185
        $this->View()->assign([
1186
            'success' => true,
1187
            'data' => $data,
1188
            'total' => $total
1189
        ]);
1190
    }
1191
1192
    /**
1193
     * Get a list of log commands
1194
     */
1195
    public function getLogCommandsAction()
1196
    {
1197
        $data = $this->getModelManager()->getConnection()->fetchAll(
1198
            'SELECT DISTINCT `command` FROM `s_plugin_connect_log`'
1199
        );
1200
1201
        $data = array_map(function ($column) {
1202
            return $column['command'];
1203
        }, $data);
1204
1205
        // Enforce these fields
1206
        foreach (['fromShop', 'toShop', 'getLastRevision', 'update', 'checkProducts', 'buy', 'reserveProducts', 'confirm'] as $value) {
1207
            if (!in_array($value, $data)) {
1208
                $data[] = $value;
1209
            }
1210
        }
1211
1212
        $this->View()->assign([
1213
            'success' => true,
1214
            'data' => $data
1215
        ]);
1216
    }
1217
1218
    /**
1219
     * Delete all log entries
1220
     */
1221
    public function clearLogAction()
1222
    {
1223
        $connection = $this->getModelManager()->getConnection();
1224
        $connection->exec('TRUNCATE `s_plugin_connect_log`;');
1225
    }
1226
1227
    /**
1228
     * @return ConnectExport
1229
     */
1230
    public function getConnectExport()
1231
    {
1232
        return new ConnectExport(
1233
            $this->getHelper(),
1234
            $this->getSDK(),
1235
            $this->getModelManager(),
1236
            new ProductsAttributesValidator(),
1237
            $this->getConfigComponent(),
1238
            new ErrorHandler(),
1239
            $this->container->get('events')
1240
        );
1241
    }
1242
1243
    /**
1244
     * @return Config
1245
     */
1246
    public function getConfigComponent()
1247
    {
1248
        if ($this->configComponent === null) {
1249
            $this->configComponent = ConfigFactory::getConfigInstance();
1250
        }
1251
1252
        return $this->configComponent;
1253
    }
1254
1255
    /**
1256
     * Saves the dynamic streams to the db.
1257
     * Cronjob will looks for those streams to process them
1258
     */
1259
    public function prepareDynamicStreamsAction()
1260
    {
1261
        $streamIds = $this->Request()->getParam('streamIds', []);
1262
1263
        try {
1264
            $streamService = $this->getProductStreamService();
1265
            $streams = $streamService->findStreams($streamIds);
1266
1267
            if (!$streams) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $streams of type Shopware\Models\ProductStream\ProductStream[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1268
                $message = Shopware()->Snippets()->getNamespace('backend/connect/view/main')->get(
1269
                    'export/message/error_no_stream_selected',
1270
                    'No streams were selected',
1271
                    true
1272
                );
1273
                throw new \Exception($message);
1274
            }
1275
1276
            $modelManager = $this->getModelManager();
1277
1278
            foreach ($streams as $stream) {
1279
                $streamAttr = $streamService->createStreamAttribute($stream);
1280
1281
                if (!$streamAttr->getExportStatus()) {
1282
                    $streamAttr->setExportStatus(ProductStreamService::STATUS_PENDING);
1283
                }
1284
1285
                $modelManager->persist($streamAttr);
1286
            }
1287
1288
            $modelManager->flush();
1289
        } catch (\Exception $e) {
1290
            $this->View()->assign([
1291
                'success' => false,
1292
                'message' => $e->getMessage()
1293
            ]);
1294
        }
1295
1296
        $this->View()->assign([
1297
            'success' => true,
1298
        ]);
1299
    }
1300
1301
    /**
1302
     * Add given category to products
1303
     */
1304
    public function assignProductsToCategoryAction()
1305
    {
1306
        $articleIds = $this->Request()->getParam('ids');
1307
        $categoryId = (int) $this->Request()->getParam('category');
1308
1309
        /** @var Category $category */
1310
        $category = $this->getCategoryModelById($categoryId);
1311
        if (!is_null($category)) {
1312
            foreach ($articleIds as $id) {
1313
                /** @var \Shopware\Models\Article\Article $article */
1314
                $article = $this->getConnectExport()->getArticleModelById($id);
1315
                if (is_null($article)) {
1316
                    continue;
1317
                }
1318
                $article->addCategory($category);
1319
                $this->getModelManager()->persist($article);
1320
            }
1321
            $this->getModelManager()->flush();
1322
        }
1323
1324
        $this->View()->assign(
1325
            [
1326
                'success' => true
1327
            ]
1328
        );
1329
    }
1330
1331
    public function getStreamListAction()
1332
    {
1333
        $productStreamService = $this->getProductStreamService();
1334
1335
        $result = $productStreamService->getList(
1336
            $this->Request()->getParam('start', 0),
1337
            $this->Request()->getParam('limit', 20)
1338
        );
1339
1340
        $this->View()->assign(
1341
            [
1342
                'success' => true,
1343
                'data' => $result['data'],
1344
                'total' => $result['total'],
1345
            ]
1346
        );
1347
    }
1348
1349
    public function getStreamProductsCountAction()
1350
    {
1351
        $streamIds = $this->request->getParam('ids', []);
1352
1353
        if (empty($streamIds)) {
1354
            $this->View()->assign([
1355
                'success' => false,
1356
                'message' => 'No stream selected'
1357
            ]);
1358
        }
1359
1360
        $sourceIdsCount = $this->getProductStreamService()->countProductsInStaticStream($streamIds);
1361
1362
        $this->View()->assign([
1363
            'success' => true,
1364
            'sourceIdsCount' => $sourceIdsCount
1365
        ]);
1366
    }
1367
1368
    public function exportStreamAction()
1369
    {
1370
        $streamIds = $this->request->getParam('streamIds', []);
1371
        $currentStreamIndex = $this->request->getParam('currentStreamIndex', 0);
1372
        $offset = $this->request->getParam('offset', 0);
1373
        $limit = $this->request->getParam('limit', 1);
1374
1375
        $streamId = $streamIds[$currentStreamIndex];
1376
1377
        $productStreamService = $this->getProductStreamService();
1378
1379
        $streamsAssignments = $this->getStreamAssignments($streamId);
1380
1381
        if (!$streamsAssignments) {
1382
            return;
1383
        }
1384
1385
        $sourceIds = $this->getHelper()->getArticleSourceIds($streamsAssignments->getArticleIds());
1386
        $sliced = array_slice($sourceIds, $offset, $limit);
1387
1388
        $exported = $this->exportStreamProducts($streamId, $sliced, $streamsAssignments);
1389
1390
        if (!$exported) {
1391
            return;
1392
        }
1393
1394
        $nextStreamIndex = $currentStreamIndex;
1395
        $newOffset = $offset + $limit;
1396
        $hasMoreIterations = true;
1397
1398
        $processedStreams = $currentStreamIndex;
1399
        $sourceIdsCount = $this->getProductStreamService()->countProductsInStaticStream($streamIds);
1400
1401
        //In this case all the products from a single stream were exported successfully but there are still more streams to be processed.
1402
        if ($newOffset > $sourceIdsCount && $currentStreamIndex + 1 <= (count($streamIds) - 1)) {
1403
            $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_EXPORT, 'Success');
1404
            $nextStreamIndex = $currentStreamIndex + 1;
1405
1406
            $streamsAssignments = $this->getStreamAssignments($streamIds[$nextStreamIndex]);
1407
1408
            if (!$streamsAssignments) {
1409
                return;
1410
            }
1411
1412
            $newOffset = 0;
1413
        }
1414
1415
        //In this case all the products from all streams were exported successfully.
1416
        if ($newOffset > $sourceIdsCount && $currentStreamIndex + 1 > (count($streamIds) - 1)) {
1417
            $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_EXPORT);
1418
            $hasMoreIterations = false;
1419
            $newOffset = $sourceIdsCount;
1420
            $processedStreams = count($streamIds);
1421
        }
1422
1423
1424
        $this->View()->assign([
1425
            'success' => true,
1426
            'nextStreamIndex' => $nextStreamIndex,
1427
            'newOffset' => $newOffset,
1428
            'hasMoreIterations' => $hasMoreIterations,
1429
            'processedStreams' => $processedStreams,
1430
        ]);
1431
    }
1432
1433
    public function hasManyVariantsAction()
1434
    {
1435
        $streamId = $this->request->getParam('streamId');
1436
        $articleId = $this->request->getParam('articleId');
1437
1438
        if (!$this->getProductStreamService()->isStreamExported($streamId)) {
1439
            return;
1440
        }
1441
1442
        $sourceIds = $this->getHelper()->getSourceIdsFromArticleId($articleId);
1443
1444
        $hasManyVariants = false;
1445
1446
        if (count($sourceIds) > ProductStreamService::PRODUCT_LIMIT) {
1447
            $hasManyVariants = true;
1448
        }
1449
1450
        $this->View()->assign([
1451
            'success' => true,
1452
            'hasManyVariants' => $hasManyVariants
1453
        ]);
1454
    }
1455
1456
    public function exportAllWithCronAction()
1457
    {
1458
        try {
1459
            $db = Shopware()->Db();
1460
            $this->getConfigComponent()->setConfig('autoUpdateProducts', 2, null, 'export');
1461
1462
            $db->update(
1463
                's_crontab',
1464
                ['active' => 1],
1465
                "action = 'ShopwareConnectUpdateProducts' OR action = 'Shopware_CronJob_ShopwareConnectUpdateProducts'"
1466
            );
1467
1468
            $db->update(
1469
                's_plugin_connect_items',
1470
                ['cron_update' => 1, 'export_status' => Attribute::STATUS_UPDATE],
1471
                'shop_id IS NULL'
1472
            );
1473
1474
            $this->View()->assign([
1475
                'success' => true,
1476
            ]);
1477
        } catch (\Exception $e) {
1478
            $this->View()->assign([
1479
                'success' => false,
1480
                'message' => $e->getMessage()
1481
            ]);
1482
        }
1483
    }
1484
1485
    /**
1486
     * @param int $streamId
1487
     * @return bool|ProductStreamsAssignments
1488
     */
1489
    private function getStreamAssignments($streamId)
1490
    {
1491
        $productStreamService = $this->getProductStreamService();
1492
1493
        try {
1494
            $streamsAssignments = $productStreamService->prepareStreamsAssignments($streamId);
1495
        } catch (\Exception $e) {
1496
            $this->View()->assign([
1497
                'success' => false,
1498
                'messages' => [ErrorHandler::TYPE_DEFAULT_ERROR => [$e->getMessage()]]
1499
            ]);
1500
1501
            return false;
1502
        }
1503
1504
        return $streamsAssignments;
1505
    }
1506
1507
    /**
1508
     * @param int $streamId
1509
     * @param array $sourceIds
1510
     * @param ProductStreamsAssignments $streamsAssignments
1511
     * @return bool
1512
     */
1513
    private function exportStreamProducts($streamId, array $sourceIds, $streamsAssignments)
1514
    {
1515
        $productStreamService = $this->getProductStreamService();
1516
        $connectExport = $this->getConnectExport();
1517
1518
        try {
1519
            $errorMessages = $connectExport->export($sourceIds, $streamsAssignments);
1520
        } catch (\RuntimeException $e) {
1521
            $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_ERROR);
1522
            $this->View()->assign([
1523
                'success' => false,
1524
                'messages' => [ErrorHandler::TYPE_DEFAULT_ERROR => [$e->getMessage()]]
1525
            ]);
1526
1527
            return false;
1528
        }
1529
1530
        if (!empty($errorMessages)) {
1531
            $errorMessagesText = '';
1532
            $displayedErrorTypes = [
1533
                ErrorHandler::TYPE_DEFAULT_ERROR,
1534
                ErrorHandler::TYPE_PRICE_ERROR
1535
            ];
1536
1537
            foreach ($displayedErrorTypes as $displayedErrorType) {
1538
                $errorMessagesText .= implode('\n', $errorMessages[$displayedErrorType]);
1539
            }
1540
1541
            $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_ERROR, $errorMessagesText);
1542
1543
            $this->View()->assign([
1544
                'success' => false,
1545
                'messages' => $errorMessages
1546
            ]);
1547
1548
            return false;
1549
        }
1550
1551
        return true;
1552
    }
1553
1554
    /**
1555
     * Deletes products from connect stream export
1556
     */
1557
    public function removeStreamsAction()
1558
    {
1559
        $streamIds = $this->request->getParam('ids', []);
1560
1561
        $productStreamService = $this->getProductStreamService();
1562
        $connectExport = $this->getConnectExport();
1563
1564
        $filteredStreamIds = $productStreamService->filterExportedStreams($streamIds);
1565
1566
        foreach ($filteredStreamIds as $streamId) {
1567
            try {
1568
                $removedRecords = [];
1569
1570
                $assignments = $productStreamService->getStreamAssignments($streamId);
1571
                $sourceIds = $this->getHelper()->getArticleSourceIds($assignments->getArticleIds());
1572
                $items = $connectExport->fetchConnectItems($sourceIds, false);
1573
1574 View Code Duplication
                foreach ($items as $item) {
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...
1575
                    if ($productStreamService->allowToRemove($assignments, $streamId, $item['articleId'])) {
1576
                        $this->getSDK()->recordDelete($item['sourceId']);
1577
                        $removedRecords[] = $item['sourceId'];
1578
                    } else {
1579
                        //updates items with the new streams
1580
                        $streamCollection = $assignments->getStreamsByArticleId($item['articleId']);
1581
                        if (!$this->getHelper()->isMainVariant($item['sourceId']) || !$streamCollection) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $streamCollection of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1582
                            continue;
1583
                        }
1584
1585
                        //removes current stream from the collection
1586
                        unset($streamCollection[$streamId]);
1587
1588
                        $this->getSDK()->recordStreamAssignment(
1589
                            $item['sourceId'],
1590
                            $streamCollection,
1591
                            $item['groupId']
1592
                        );
1593
                    }
1594
                }
1595
1596
                $connectExport->updateConnectItemsStatus($removedRecords, Attribute::STATUS_DELETE);
1597
                $this->getSDK()->recordStreamDelete($streamId);
1598
                $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_DELETE);
1599
            } catch (\Exception $e) {
1600
                $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_ERROR);
1601
                $this->View()->assign([
1602
                    'success' => false,
1603
                    'message' => $e->getMessage()
1604
                ]);
1605
1606
                return;
1607
            }
1608
        }
1609
    }
1610
1611 View Code Duplication
    private function getMarketplaceApplier()
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...
1612
    {
1613
        if (!$this->marketplaceSettingsApplier) {
1614
            $this->marketplaceSettingsApplier = new MarketplaceSettingsApplier(
1615
                $this->getConfigComponent(),
1616
                Shopware()->Models(),
1617
                Shopware()->Db()
1618
            );
1619
        }
1620
1621
        return $this->marketplaceSettingsApplier;
1622
    }
1623
1624
    /**
1625
     * @return SnHttpClient
1626
     */
1627 View Code Duplication
    protected function getSnHttpClient()
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...
1628
    {
1629
        if (!$this->snHttpClient) {
1630
            $this->snHttpClient = new SnHttpClient(
1631
                $this->get('http_client'),
1632
                new \Shopware\Connect\Gateway\PDO(Shopware()->Db()->getConnection()),
1633
                $this->getConfigComponent()
1634
            );
1635
        }
1636
1637
        return $this->snHttpClient;
1638
    }
1639
1640
    /**
1641
     * @return string
1642
     */
1643
    protected function getHost()
1644
    {
1645
        $host = $this->getConfigComponent()->getConfig('connectDebugHost');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $host is correct as $this->getConfigComponen...fig('connectDebugHost') (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...
1646
        if (!$host || $host == '') {
1647
            $host = $this->getConfigComponent()->getMarketplaceUrl();
1648
        }
1649
1650
        return $host;
1651
    }
1652
1653
    /**
1654
     * @return ProductStreamService
1655
     */
1656
    protected function getProductStreamService()
1657
    {
1658
        if ($this->productStreamService === null) {
1659
            $this->productStreamService = $this->get('swagconnect.product_stream_service');
1660
        }
1661
1662
        return $this->productStreamService;
1663
    }
1664
1665
    /**
1666
     * {@inheritdoc}
1667
     */
1668
    public function getWhitelistedCSRFActions()
1669
    {
1670
        return ['login', 'autoLogin'];
1671
    }
1672
}
1673