Completed
Pull Request — master (#399)
by Christian
03:10
created

getExportAssignmentService()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 8
rs 9.4285
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
     * @var \ShopwarePlugins\Connect\Services\ExportAssignmentService
63
     */
64
    private $exportAssignmentService;
65
66
    /**
67
     * @return ModelManager
68
     */
69
    public function getModelManager()
70
    {
71
        return $this->get('models');
72
    }
73
74
    /**
75
     * @return \Shopware\Connect\SDK
76
     */
77
    public function getSDK()
78
    {
79
        if ($this->sdk === null) {
80
            $this->sdk = $this->get('ConnectSDK');
81
        }
82
83
        return $this->sdk;
84
    }
85
86
    /**
87
     * @return \ShopwarePlugins\Connect\Services\ExportAssignmentService
88
     */
89
    public function getExportAssignmentService()
90
    {
91
        if ($this->exportAssignmentService === null) {
92
            $this->exportAssignmentService = $this->get('swagconnect.export_assignment_service');
93
        }
94
95
        return $this->exportAssignmentService;
96
    }
97
98
    /**
99
     * @return \ShopwarePlugins\Connect\Components\Helper
100
     */
101
    public function getHelper()
102
    {
103
        return $this->getConnectFactory()->getHelper();
104
    }
105
106
    /**
107
     * @return ConnectFactory
108
     */
109
    public function getConnectFactory()
110
    {
111
        if ($this->factory === null) {
112
            $this->factory = new ConnectFactory();
113
        }
114
115
        return $this->factory;
116
    }
117
118
    /**
119
     * @return ArticleRepository
120
     */
121
    private function getArticleRepository()
122
    {
123
        return $this->getModelManager()->getRepository(
124
            Article::class
125
        );
126
    }
127
128
    /**
129
     * @return \Shopware\Models\Category\Repository
130
     */
131
    private function getCategoryRepository()
132
    {
133
        return $this->getModelManager()->getRepository(
134
            Category::class
135
        );
136
    }
137
138
    /**
139
     * Will return a category model for the given id. If the attribute should not exist
140
     * it will be created
141
     *
142
     * @param $id
143
     * @return null|Category
144
     */
145
    private function getCategoryModelById($id)
146
    {
147
        $categoryModel = $this->getCategoryRepository()->find($id);
148
        if (!$categoryModel || !$categoryModel->getAttribute()) {
149
            $attribute = new \Shopware\Models\Attribute\Category();
150
            $attribute->setCategory($categoryModel);
151
            $this->getModelManager()->persist($attribute);
152
            $this->getModelManager()->flush($attribute);
153
        }
154
155
        return $categoryModel;
156
    }
157
158
    /**
159
     * When the backend module is being loaded, update connect products.
160
     *
161
     * It might be considerable to move this to e.g. the lifecycle events of the products
162
     */
163
    public function indexAction()
164
    {
165
        $this->getHelper()->updateConnectProducts();
166
167
        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...
168
    }
169
170
    /**
171
     * When the backend module is being loaded, update connect products.
172
     *
173
     * It might be considerable to move this to e.g. the lifecycle events of the products
174
     */
175
    public function refreshConnectItemsAction()
176
    {
177
        $this->getHelper()->updateConnectProducts();
178
    }
179
180
    /**
181
     * If the price type is purchase or both
182
     * and shopware is 5.2 or greater
183
     * insert detailPurchasePrice in connect config table
184
     * when priceFieldForPurchasePriceExport is empty
185
     */
186
    private function updatePurchasePriceField()
187
    {
188
        $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...
189
        if ($field) {
190
            return;
191
        }
192
193
        if (!method_exists('Shopware\Models\Article\Detail', 'setPurchasePrice')) {
194
            return;
195
        }
196
197
        if ($this->getSDK()->getPriceType() == \Shopware\Connect\SDK::PRICE_TYPE_PURCHASE
198
            || $this->getSDK()->getPriceType() == \Shopware\Connect\SDK::PRICE_TYPE_BOTH
199
        ) {
200
            $this->getConfigComponent()->setConfig(
201
                'priceFieldForPurchasePriceExport',
202
                'detailPurchasePrice',
203
                null,
204
                'export'
205
            );
206
        }
207
    }
208
209
    /**
210
     * Helper function to return a QueryBuilder for creating the listing queries for the import and export listings
211
     *
212
     * @param $filter
213
     * @param $order
214
     * @return \Doctrine\ORM\QueryBuilder
215
     */
216
    private function getListQueryBuilder($filter, $order)
217
    {
218
        $builder = $this->getModelManager()->createQueryBuilder();
219
        $builder->from('Shopware\CustomModels\Connect\Attribute', 'at');
220
        $builder->join('at.article', 'a');
221
        $builder->join('a.mainDetail', 'd');
222
        $builder->leftJoin('d.prices', 'p', 'with', "p.from = 1 AND p.customerGroupKey = 'EK'");
223
        $builder->leftJoin('a.supplier', 's');
224
        $builder->leftJoin('a.tax', 't');
225
226
        $builder->select([
227
            'a.id',
228
            'd.number as number',
229
            'd.inStock as inStock',
230
            'a.name as name',
231
            's.name as supplier',
232
            'a.active as active',
233
            't.tax as tax',
234
            'p.price * (100 + t.tax) / 100 as price',
235
            'at.category'
236
        ]);
237
238
        // show only main variant in export/import lists
239
        $builder->groupBy('at.articleId');
240
241
        foreach ($filter as $key => $rule) {
242
            switch ($rule['property']) {
243
                case 'search':
244
                    $builder->andWhere('d.number LIKE :search OR a.name LIKE :search OR s.name LIKE :search')
245
                        ->setParameter('search', $rule['value']);
246
                    break;
247
                case 'categoryId':
248
                    $builder->join('a.categories', 'c', 'with', 'c.id = :categoryId OR c.path LIKE :categoryPath')
249
                        ->setParameter('categoryId', $rule['value'])
250
                        ->setParameter('categoryPath', '%|' . $rule['value'] . '|%');
251
                    break;
252
                case 'supplierId':
253
                    $builder->andWhere('a.supplierId = :supplierId')
254
                        ->setParameter('supplierId', $rule['value']);
255
                    break;
256
                case 'exportStatus':
257
                    $builder->andWhere('at.exportStatus LIKE :status')
258
                        ->setParameter('status', $rule['value']);
259
                    break;
260
                case 'active':
261
                    $builder->andWhere('a.active LIKE :active')
262
                        ->setParameter('active', $rule['value']);
263
                    break;
264
                default:
265
                    continue;
266
            }
267
        }
268
269
        $builder->addOrderBy($order);
270
271
        return $builder;
272
    }
273
274
    /**
275
     * Get all products exported to connect
276
     */
277
    public function getExportListAction()
278
    {
279
        $filter = (array) $this->Request()->getParam('filter', []);
280
        $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...
281
282
        $criteria = new SearchCriteria([
283
            'offset' => (int) $this->Request()->getParam('start'),
284
            'limit' => (int) $this->Request()->getParam('limit'),
285
            'orderBy' => $order['property'],
286
            'orderByDirection' => $order['direction'],
287
288
        ]);
289
290
        foreach ($filter as $key => $rule) {
291
            $field = $rule['property'];
292
            $criteria->{$field} = $rule['value'];
293
        }
294
295
        $exportList = $this->getConnectExport()->getExportList($criteria);
296
297
        $this->View()->assign([
298
            'success' => true,
299
            'data' => $exportList->articles,
300
            'total' => $exportList->count,
301
        ]);
302
    }
303
304
    public function getExportStatusAction()
305
    {
306
        $attrRepo = $this->getModelManager()->getRepository('Shopware\CustomModels\Connect\Attribute');
307
308
        $syncedItems = $attrRepo->countStatus([
309
            Attribute::STATUS_SYNCED,
310
        ]);
311
312
        $totalItems = $attrRepo->countStatus([
313
            Attribute::STATUS_INSERT,
314
            Attribute::STATUS_UPDATE,
315
            Attribute::STATUS_SYNCED,
316
        ]);
317
318
        $this->View()->assign([
319
            'success' => true,
320
            'data' => $syncedItems,
321
            'total' => $totalItems,
322
        ]);
323
    }
324
325
    /**
326
     * Get all products imported from connect
327
     */
328
    public function getImportListAction()
329
    {
330
        $filter = (array) $this->Request()->getParam('filter', []);
331
        $sort = $this->Request()->getParam('sort', []);
332
333
        foreach ($sort as $key => $currentSorter) {
334
            if ($currentSorter['property'] == 'category') {
335
                unset($sort[$key]);
336
            }
337
        }
338
339
        $builder = $this->getListQueryBuilder(
340
            $filter,
341
            $sort
342
        );
343
        $builder->addSelect([
344
            'at.shopId',
345
            'at.sourceId',
346
            'at.exportStatus as connectStatus',
347
        ]);
348
        $builder->andWhere('at.shopId IS NOT NULL');
349
350
        $builder->addOrderBy('at.category', 'ASC');
351
352
        $query = $builder->getQuery();
353
354
        $query->setFirstResult($this->Request()->getParam('start'));
355
        $query->setMaxResults($this->Request()->getParam('limit'));
356
357
        $countResult = array_map('current', $builder->select(['COUNT(DISTINCT at.articleId) as current'])->orderBy('current')->getQuery()->getScalarResult());
358
        $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...
359
        $total = array_sum($countResult);
360
        // todo@sb: find better solution. getQueryCount method counts s_plugin_connect_items.id like they are not grouped by article id
361
//        $total = Shopware()->Models()->getQueryCount($query);
362
        $data = $query->getArrayResult();
363
364
        $this->View()->assign([
365
            'success' => true,
366
            'data' => $data,
367
            'total' => $total
368
        ]);
369
    }
370
371
    /**
372
     * Import parts of the connect category tree to shopware
373
     */
374
    public function importConnectCategoriesAction()
375
    {
376
        $fromCategory = $this->Request()->getParam('fromCategory');
377
        $toCategory = $this->Request()->getParam('toCategory');
378
379
        $entityManager = $this->getModelManager();
380
        $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...
381
382
        // Make sure that the target category exists
383
        $toCategoryModel = $this->getCategoryRepository()->find($toCategory);
384
        if (!$toCategoryModel) {
385
            throw new \RuntimeException("Category with id  {$toCategory} not found");
386
        }
387
388
        // The user might have changed the mapping without saving and then hit the "importCategories"
389
        // button. So we save the parent category's mapping first
390
        $parentCategory = $this->getCategoryModelById($toCategory);
391
        $parentCategory->getAttribute()->setConnectImportMapping($fromCategory);
392
        $entityManager->flush();
393
394
        try {
395
            $entityManager->getConnection()->beginTransaction();
396
            $this->importConnectCategories($fromCategory, $toCategory);
397
            $entityManager->getConnection()->commit();
398
        } catch (\Exception $e) {
399
            $entityManager->getConnection()->rollback();
400
            throw new \RuntimeException('Could not import categories', 0, $e);
401
        }
402
    }
403
404
    /**
405
     * Will import a connect category tree into shopware.
406
     *
407
     * @param $fromCategory
408
     * @param $toCategory
409
     */
410
    public function importConnectCategories($fromCategory, $toCategory)
411
    {
412
        $categoriesToImport = $this->getFlatConnectCategories($fromCategory);
413
        $toCategoryModel = $this->getCategoryRepository()->find($toCategory);
414
        $entityManager = $this->getModelManager();
415
416
        /*
417
         * The import string allows to identify categories, which have already been imported for
418
         * this exact import. This does not prevent the user from importing the same sub-tree
419
         * into multiple shopware categories. But it does prevent him from importing the same sub-tree
420
         * into the same category multiple times
421
         */
422
        $importString = $fromCategory . '-' . $toCategory;
423
        $currentLevel = 1;
424
        $mappings = [];
425
426
        foreach ($categoriesToImport as $id => $category) {
427
            $name = $category['name'];
428
            $parent = $category['parent'];
429
            $level = $category['level'];
430
431
            // Only flush after the level changed - this speeds up the import
432
            if ($currentLevel != $level) {
433
                Shopware()->Models()->flush();
434
            }
435
            $currentLevel = $level;
436
437
            /** @var Category $parentModel */
438
            if (!$parent) {
439
                // Top category level - use toCategoryModel
440
                $parentModel = $toCategoryModel;
441
            } else {
442
                // Parent was created before and is referenced in $mappings
443
                $parentModel = $mappings[$parent];
444
            }
445
446
            // Check if there is already a category attribute for this import
447
            $categoryAttributes = $entityManager->getRepository('\Shopware\Models\Attribute\Category')->findBy(
448
                ['connectImported' => $importString, 'connectImportMapping' => $id],
449
                null,
450
                1
451
            );
452
453
            if (!empty($categoryAttributes)) {
454
                /** @var \Shopware\Models\Attribute\Category $categoryAttribute */
455
                $categoryAttribute = array_pop($categoryAttributes);
456
                $category = $categoryAttribute->getCategory();
457
            } else {
458
                // Create category and attribute model
459
                $category = new Category();
460
                $category->setName($name);
461
                $category->setParent($parentModel);
462
463
                $attribute = new \Shopware\Models\Attribute\Category();
464
                $attribute->setConnectImportMapping($id);
465
                $attribute->setConnectImported($importString);
466
                $category->setAttribute($attribute);
467
468
                Shopware()->Models()->persist($category);
469
                Shopware()->Models()->persist($attribute);
470
            }
471
472
473
            // Store the new category model in out $mappings array
474
            $mappings[$id] = $category;
475
        }
476
477
        Shopware()->Models()->flush();
478
    }
479
480
    public function initParamsAction()
481
    {
482
        $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...
483
        $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...
484
        $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...
485
        $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...
486
        $isFixedPriceAllowed = 0;
487
        $priceType = $this->getSDK()->getPriceType();
488
        if ($priceType === SDK::PRICE_TYPE_BOTH ||
489
            $priceType === SDK::PRICE_TYPE_RETAIL) {
490
            $isFixedPriceAllowed = 1;
491
        }
492
        $marketplaceIncomingIcon = ($marketplaceName == Connect::MARKETPLACE_NAME ? Connect::MARKETPLACE_GREEN_ICON : $marketplaceIcon);
493
        $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...
494
        $purchasePriceInDetail = method_exists('Shopware\Models\Article\Detail', 'setPurchasePrice') ? 1 : 0;
495
496
        $this->View()->assign([
497
            'success' => true,
498
            'data' => [
499
                'marketplaceName' => $marketplaceName,
500
                'marketplaceNetworkUrl' => $marketplaceNetworkUrl,
501
                'marketplaceIcon' => $marketplaceIcon,
502
                'defaultMarketplace' => $defaultMarketplace,
503
                'isFixedPriceAllowed' => $isFixedPriceAllowed,
504
                'marketplaceIncomingIcon' => $marketplaceIncomingIcon,
505
                'marketplaceLogo' => $marketplaceLogo,
506
                'purchasePriceInDetail' => $purchasePriceInDetail,
507
            ]
508
        ]);
509
    }
510
511
    /**
512
     * Returns a flat array of connect categories
513
     *
514
     * @param $rootCategory
515
     * @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...
516
     *      string => array('id' => string, 'name' => string, 'level' => int, 'parent' => string|null)
517
     * )
518
     */
519
    private function getFlatConnectCategories($rootCategory)
520
    {
521
        $sdk = $this->getSDK();
522
        $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...
523
524
        $categoriesToImport = [];
525
        foreach ($connectCategories as $id => $name) {
526
            // Skip all entries which do not start with the parent or do not have it at all
527
            if (strpos($id, $rootCategory) !== 0) {
528
                continue;
529
            }
530
531
            $level = substr_count(preg_replace("#^{$rootCategory}#", '', $id), '/');
532
533
            // Skip the root category
534
            if ($level == 0) {
535
                continue;
536
            }
537
538
            $categoriesToImport[$id] = [
539
                'id' => $id,
540
                'name' => $name,
541
                'level' => $level,
542
                'parent' => $level == 1 ? null : implode('/', array_slice(explode('/', $id), 0, -1))
543
            ];
544
        }
545
546
        // Sort the categories ascending by their level, so parent categories can be imported first
547
        uasort(
548
            $categoriesToImport,
549
            function ($a, $b) {
550
                $a = $a['level'];
551
                $b = $b['level'];
552
                if ($a == $b) {
553
                    return 0;
554
                }
555
556
                return ($a < $b) ? -1 : 1;
557
            }
558
        );
559
560
        return $categoriesToImport;
561
    }
562
563
    /**
564
     * Save a given mapping of a given category to all subcategories
565
     */
566
    public function applyMappingToChildrenAction()
567
    {
568
        $categoryId = $this->Request()->getParam('category');
569
        $mapping = $this->Request()->getParam('mapping');
570
571
        $entityManager = $this->getModelManager();
572
573
        try {
574
            $entityManager->getConnection()->beginTransaction();
575
            $this->applyMappingToChildren($mapping, $categoryId);
576
            $entityManager->getConnection()->commit();
577
            $this->View()->assign([
578
                'success' => true
579
            ]);
580
        } catch (\Exception $e) {
581
            $entityManager->getConnection()->rollback();
582
            $this->View()->assign([
583
                'message' => $e->getMessage(),
584
                'success' => false
585
            ]);
586
        }
587
    }
588
589
    /**
590
     * Returns success true if user could be logged in, or false if something went wrong
591
     *
592
     * @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...
593
     *      string => bool
594
     * )
595
     */
596
    public function loginAction()
597
    {
598
        /** @var \Shopware\Components\HttpClient\GuzzleHttpClient $client */
599
        $client = $this->get('http_client');
600
601
        $shopwareId = $this->Request()->getParam('shopwareId');
602
        $password = $this->Request()->getParam('password');
603
        $loginUrl = $this->getHost() . '/sdk/pluginCommunication/login';
604
605
        // Try to login into connect
606
        $response = $client->post(
607
            $loginUrl,
608
            [
609
                'content-type' => 'application/x-www-form-urlencoded'
610
            ],
611
            [
612
                'username' => urlencode($shopwareId),
613
                'password' => urlencode($password)
614
            ]
615
        );
616
617
        $responseObject = json_decode($response->getBody());
618
619
        if (!$responseObject->success) {
620
            $message = $responseObject->reason;
621
622
            if ($responseObject->reason == SDK::WRONG_CREDENTIALS_MESSAGE) {
623
                $snippets = Shopware()->Snippets()->getNamespace('backend/connect/view/main');
624
                $message = $snippets->get(
625
                    'error/wrong_credentials',
626
                    SDK::WRONG_CREDENTIALS_MESSAGE,
627
                    true
628
                );
629
            }
630
631
            $this->View()->assign([
632
                'success' => false,
633
                'message' => $message
634
            ]);
635
636
            return;
637
        }
638
639
        try {
640
            // set apiKey in Connect config table
641
            // after that create completely new SDK instance,
642
            // because correct apiKey should be used during creation
643
            $this->getConfigComponent()->setConfig('apiKey', $responseObject->apiKey, null, 'general');
644
            $sdk = $this->getConnectFactory()->createSDK();
645
            $sdk->verifySdk();
646
            $this->getConfigComponent()->setConfig('apiKeyVerified', true);
647
            $this->getConfigComponent()->setConfig('shopwareId', $shopwareId, null, 'general');
648
            $this->removeConnectMenuEntry();
649
            $marketplaceSettings = $sdk->getMarketplaceSettings();
650
            $this->getMarketplaceApplier()->apply(new MarketplaceSettings($marketplaceSettings));
651
        } catch (\Exception $e) {
652
            $this->getConfigComponent()->setConfig('apiKey', null, null, 'general');
653
654
            $this->View()->assign([
655
                'success' => false,
656
                'message' => $e->getMessage()
657
            ]);
658
659
            return;
660
        }
661
662
        $this->View()->assign([
663
            'success' => true
664
        ]);
665
    }
666
667
    /**
668
     * Returns success true if user could be logged in, or false if something went wrong
669
     *
670
     * @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...
671
     *      string => bool
672
     * )
673
     */
674
    public function registerAction()
675
    {
676
        /** @var \Shopware\Components\HttpClient\GuzzleHttpClient $client */
677
        $client = $this->get('http_client');
678
679
        $shopwareId = $this->Request()->getParam('shopwareID');
680
        $password = $this->Request()->getParam('password');
681
        $email = $this->Request()->getParam('email');
682
683
        // Enter the valid production url here
684
        $host = $this->getHost();
685
686
        $loginUrl = $host . '/sdk/pluginCommunication/register';
687
688
        $response = $client->post(
689
            $loginUrl,
690
            [
691
                'content-type' => 'application/x-www-form-urlencoded'
692
            ],
693
            [
694
                'username'  => urlencode($shopwareId),
695
                'password'  => urlencode($password),
696
                'email'     => urlencode($email)
697
            ]
698
        );
699
700
        $responseObject = json_decode($response->getBody());
701
702
        if (!$responseObject->success) {
703
            $this->View()->assign([
704
                'success' => false,
705
                'message' => $responseObject->reason
706
            ]);
707
708
            return;
709
        }
710
711
        try {
712
            // set apiKey in Connect config table
713
            // after that create completely new SDK instance,
714
            // because correct apiKey should be used during creation
715
            $this->getConfigComponent()->setConfig('apiKey', $responseObject->apiKey, null, 'general');
716
            $sdk = $this->getConnectFactory()->createSDK();
717
            $sdk->verifySdk();
718
            $this->getConfigComponent()->setConfig('apiKeyVerified', true);
719
            $this->getConfigComponent()->setConfig('shopwareId', $shopwareId, null, 'general');
720
            $this->removeConnectMenuEntry();
721
            $marketplaceSettings = $sdk->getMarketplaceSettings();
722
            $this->getMarketplaceApplier()->apply(new MarketplaceSettings($marketplaceSettings));
723
        } catch (\Exception $e) {
724
            $this->getConfigComponent()->setConfig('apiKey', null, null, 'general');
725
726
            $this->View()->assign([
727
                'success' => false,
728
                'message' => $e->getMessage()
729
            ]);
730
731
            return;
732
        }
733
734
        $this->View()->assign([
735
            'success' => true
736
        ]);
737
    }
738
739
    /**
740
     * Redirects and auto login to SN system
741
     */
742
    public function autoLoginAction()
743
    {
744
        return $this->redirect('http://' . $this->getHost() . '/login');
745
    }
746
747
    /**
748
     * @param bool $loggedIn
749
     * @throws Zend_Db_Adapter_Exception
750
     */
751
    private function removeConnectMenuEntry($loggedIn = true)
752
    {
753
        /** @var Enlight_Components_Db_Adapter_Pdo_Mysql $db */
754
        $db = Shopware()->Db();
755
756
        $result = $db->fetchAssoc("SELECT id, parent, pluginID FROM s_core_menu WHERE controller = 'connect' AND name = 'Register' ORDER BY id DESC LIMIT 1");
757
        if (empty($result)) {
758
            return;
759
        }
760
        $row = current($result);
761
762
        $db->exec('DELETE FROM s_core_menu WHERE id = ' . $row['id']);
763
764
        $insertSql = "INSERT INTO s_core_menu (
765
            parent,
766
            name,
767
            class,
768
            pluginID,
769
            controller,
770
            action,
771
            onclick,
772
            active
773
          ) VALUES (
774
            '#parent#',
775
            '#name#',
776
            '#class#',
777
            #pluginID#,
778
            '#controller#',
779
            '#action#',
780
            '#onclick#',
781
            1
782
          )";
783
784
        $db->exec(strtr($insertSql, [
785
            '#parent#' => $row['parent'],
786
            '#name#' => 'Import',
787
            '#class#' => 'sc-icon-import',
788
            '#pluginID#' => $row['pluginID'],
789
            '#controller#' => 'Connect',
790
            '#onclick#' => '',
791
            '#action#' => 'Import'
792
        ]));
793
794
        $db->exec(strtr($insertSql, [
795
            '#parent#' => $row['parent'],
796
            '#name#' => 'Export',
797
            '#class#' => 'sc-icon-export',
798
            '#pluginID#' => $row['pluginID'],
799
            '#controller#' => 'Connect',
800
            '#onclick#' => '',
801
            '#action#' => 'Export'
802
        ]));
803
804
        $db->exec(strtr($insertSql, [
805
            '#parent#' => $row['parent'],
806
            '#name#' => 'Settings',
807
            '#class#' => 'sprite-gear',
808
            '#pluginID#' => $row['pluginID'],
809
            '#controller#' => 'Connect',
810
            '#onclick#' => '',
811
            '#action#' => 'Settings'
812
        ]));
813
814
        $db->exec(strtr($insertSql, [
815
            '#parent#' => $row['parent'],
816
            '#name#' => 'OpenConnect',
817
            '#class#' => 'connect-icon',
818
            '#pluginID#' => $row['pluginID'],
819
            '#controller#' => 'Connect',
820
            '#onclick#' => 'window.open("connect/autoLogin")',
821
            '#action#' => 'OpenConnect'
822
        ]));
823
    }
824
825
    /**
826
     * Helper that will assign a given mapping to all children of a given category
827
     *
828
     * @param $mapping string
829
     * @param $categoryId int
830
     * @throws \Exception
831
     */
832
    private function applyMappingToChildren($mapping, $categoryId)
833
    {
834
        $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...
835
        $ids = $this->getChildCategoriesIds($categoryId);
836
        $entityManager = $this->getModelManager();
837
838
839
        if (!$categoryId) {
840
            throw new \RuntimeException("Category '{$categoryId}' not found");
841
        }
842
843
        // First of all try to save the mapping for the parent category. If that fails,
844
        // it mustn't be done for the child categories
845
        $parentCategory = $this->getCategoryModelById($categoryId);
846
        $parentCategory->getAttribute()->setConnectExportMapping($mapping);
847
        $entityManager->flush();
848
849
        // Don't set the children with models in order to speed things up
850
        $builder = $entityManager->createQueryBuilder();
851
        $builder->update('\Shopware\Models\Attribute\Category', 'categoryAttribute')
852
            ->set('categoryAttribute.connectExportMapping', $builder->expr()->literal($mapping))
853
            ->where($builder->expr()->in('categoryAttribute.categoryId', $ids));
854
855
        $builder->getQuery()->execute();
856
    }
857
858
    /**
859
     * Helper function which returns the IDs of the child categories of a given parent category
860
     *
861
     * @param $parentId int
862
     * @return array
863
     */
864
    private function getChildCategoriesIds($parentId)
865
    {
866
        $query = $this->getModelManager()->createQuery('SELECT c.id from Shopware\Models\Category\Category c WHERE c.path LIKE ?1 ');
867
        $query->setParameter(1, ["%|{$parentId}|%"]);
868
        $result = $query->getResult(\Doctrine\ORM\AbstractQuery::HYDRATE_ARRAY);
869
870
        // Pop IDs from result rows
871
        return array_map(
872
            function ($row) {
873
                return array_pop($row);
874
            },
875
            $result
876
        );
877
    }
878
879
    /**
880
     * get the amount of products that can be exported
881
     */
882
    public function getArticleCountAction()
883
    {
884
        try {
885
            $count = $this->getExportAssignmentService()->getCountOfAllExportableArticles();
886
            $this->View()->assign([
887
                'success' => true,
888
                'count' => $count
889
            ]);
890
        } catch (\Exception $e) {
891
            $this->View()->assign([
892
                'success' => false,
893
                'message' => $e->getMessage()
894
            ]);
895
        }
896
    }
897
898
    public function exportAllProductsAction()
899
    {
900
        try {
901
            $this->updatePurchasePriceField();
902
903
            $offset = $this->Request()->getPost('offset', []);
904
            $batchSize = $this->Request()->getPost('batchSize', []);
905
906
            $errors = $this->getExportAssignmentService()->exportBatchOfAllProducts($offset, $batchSize);
907
908 View Code Duplication
            if (!empty($errors)) {
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...
909
                $this->View()->assign([
910
                    'success' => false,
911
                    'messages' => $errors
912
                ]);
913
914
                return;
915
            }
916
        } catch (\Exception $e) {
917
            $this->View()->assign([
918
                'success' => false,
919
                'message' => $e->getMessage()
920
            ]);
921
        }
922
        $this->View()->assign([
923
            'success' => true,
924
        ]);
925
    }
926
927
    /**
928
     * Collect all source ids by given article ids
929
     */
930
    public function getArticleSourceIdsAction()
931
    {
932
        try {
933
            $articleIds = $this->Request()->getPost('ids', []);
934
935
            if (!is_array($articleIds)) {
936
                $articleIds = [$articleIds];
937
            }
938
939
            $sourceIds = $this->getHelper()->getArticleSourceIds($articleIds);
940
941
            $this->View()->assign([
942
                'success' => true,
943
                'sourceIds' => $sourceIds
944
            ]);
945
        } catch (\Exception $e) {
946
            $this->View()->assign([
947
                'success' => false,
948
                'message' => $e->getMessage()
949
            ]);
950
        }
951
    }
952
953
    /**
954
     * Called when ConnectCategories have to be recreated
955
     */
956
    public function applyConnectCategoriesRecoveryAction()
957
    {
958
        $batchsize = $this->Request()->getPost('batchsize');
959
        $offset = $this->Request()->getPost('offset');
960
        try {
961
            $this->getHelper()->recreateConnectCategories((int) $offset, (int) $batchsize);
962
            $this->View()->assign([
963
                'success' => true,
964
            ]);
965
        } catch (Exception $e) {
966
            $this->View()->assign([
967
                'success' => false,
968
                'messages' => [ErrorHandler::TYPE_DEFAULT_ERROR => [$e->getMessage()]]
969
            ]);
970
        }
971
    }
972
973
    /**
974
     * Called when a product variants were marked for update in the connect backend module
975
     */
976
    public function insertOrUpdateProductAction()
977
    {
978
        // if priceType comes from SN and shopware version is 5.2
979
        // priceFieldForPurchasePriceExport is empty
980
        // we need to set it because there isn't customer groups
981
        // purchasePrice is stored always in article detail
982
        $this->updatePurchasePriceField();
983
        $sourceIds = $this->Request()->getPost('sourceIds');
984
        $connectExport = $this->getConnectExport();
985
986
        try {
987
            $errors = $connectExport->export($sourceIds);
988
        } catch (\RuntimeException $e) {
989
            $this->View()->assign([
990
                'success' => false,
991
                'messages' => [ErrorHandler::TYPE_DEFAULT_ERROR => [$e->getMessage()]]
992
            ]);
993
994
            return;
995
        }
996
997 View Code Duplication
        if (!empty($errors)) {
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...
998
            $this->View()->assign([
999
                'success' => false,
1000
                'messages' => $errors
1001
            ]);
1002
1003
            return;
1004
        }
1005
1006
        $this->View()->assign([
1007
            'success' => true
1008
        ]);
1009
    }
1010
1011
    /**
1012
     * Delete a product from connect export
1013
     */
1014
    public function deleteProductAction()
1015
    {
1016
        $sdk = $this->getSDK();
1017
        $ids = $this->Request()->getPost('ids');
1018
        foreach ($ids as $id) {
1019
            /** @var \Shopware\Models\Article\Article $model */
1020
            $model = $this->getConnectExport()->getArticleModelById($id);
1021
            if ($model === null) {
1022
                continue;
1023
            }
1024
            /** @var \Shopware\Models\Article\Detail $detail */
1025
            foreach ($model->getDetails() as $detail) {
1026
                $attribute = $this->getHelper()->getConnectAttributeByModel($detail);
1027
                $sdk->recordDelete($attribute->getSourceId());
1028
                $attribute->setExportStatus(Attribute::STATUS_DELETE);
1029
                $attribute->setExported(false);
1030
            }
1031
        }
1032
        Shopware()->Models()->flush();
1033
    }
1034
1035
    /**
1036
     * Verify a given api key against the connect server
1037
     */
1038
    public function verifyApiKeyAction()
1039
    {
1040
        $sdk = $this->getSDK();
1041
        try {
1042
            $key = $this->Request()->getPost('apiKey');
1043
            $sdk->verifyKey($key);
1044
            $this->View()->assign([
1045
                'success' => true
1046
            ]);
1047
            $this->getConfigComponent()->setConfig('apiKeyVerified', true);
1048
            $marketplaceSettings = $sdk->getMarketplaceSettings();
1049
            $this->getMarketplaceApplier()->apply(new MarketplaceSettings($marketplaceSettings));
1050
        } catch (\Exception $e) {
1051
            $this->View()->assign([
1052
                'message' => $e->getMessage(),
1053
                'success' => false
1054
            ]);
1055
            $this->getConfigComponent()->setConfig('apiKeyVerified', false);
1056
        }
1057
1058
        $this->getModelManager()->flush();
1059
    }
1060
1061
    /**
1062
     * Returns the connectAttribute data for a given articleId
1063
     *
1064
     * @throws RuntimeException
1065
     * @throws Exception
1066
     */
1067
    public function getConnectDataAction()
1068
    {
1069
        $articleId = $this->Request()->getParam('articleId');
1070
1071
        if (!$articleId) {
1072
            throw new \Exception('Connect: ArticleId empty');
1073
        }
1074
1075
        /** @var Article $articleModel */
1076
        $articleModel = $this->getArticleRepository()->find($articleId);
1077
1078
        if (!$articleModel) {
1079
            throw new \RuntimeException("Could not find model for article with id {$articleId}");
1080
        }
1081
1082
        $data = $this->getHelper()->getOrCreateConnectAttributes($articleModel);
1083
1084
        $data = $this->getModelManager()->toArray($data);
1085
        if (isset($data['articleId'])) {
1086
            $data = [$data];
1087
        }
1088
1089
        $this->View()->assign([
1090
            'success' => true,
1091
            'data' => $data
1092
        ]);
1093
    }
1094
1095
    /**
1096
     * Save a given connect attribute
1097
     */
1098
    public function saveConnectAttributeAction()
1099
    {
1100
        $data = $this->Request()->getParams();
1101
1102
        /** @var \Shopware\CustomModels\Connect\Attribute $connectAttribute */
1103
        $connectAttribute = $this->getModelManager()->find('Shopware\CustomModels\Connect\Attribute', $data['id']);
1104
        if (!$connectAttribute) {
1105
            throw new \RuntimeException("Could not find connect attribute with id {$data['id']}");
1106
        }
1107
1108
        /** @var \Shopware\Models\Article\Detail $detail */
1109
        foreach ($connectAttribute->getArticle()->getDetails() as $detail) {
1110
            $connectAttribute = $this->getHelper()->getConnectAttributeByModel($detail);
1111
            // Only allow changes in the fixedPrice field if this is a local product
1112
            if (!$connectAttribute->getShopId()) {
1113
                $connectAttribute->setFixedPrice($data['fixedPrice']);
1114
            }
1115
            // Save the update fields
1116
            foreach ($data as $key => $value) {
1117
                if (strpos($key, 'update') === 0) {
1118
                    $setter = 'set' . ucfirst($key);
1119
                    $connectAttribute->$setter($value);
1120
                }
1121
            }
1122
            $this->getModelManager()->persist($connectAttribute);
1123
        }
1124
1125
        $this->getModelManager()->flush();
1126
1127
        $this->View()->assign(['success' => true]);
1128
    }
1129
1130
    /**
1131
     * Saves the changed "connectAllowed" attribute. Saving this attribute should be done
1132
     * by the shipping-module on its own, right now (as of SW 4.2.0) it does not do so.
1133
     *
1134
     * todo: Once the shipping module is fixed, increase the required version of this plugin
1135
     * and remove this and the unnecessary ExtJS extensions
1136
     */
1137
    public function saveShippingAttributeAction()
1138
    {
1139
        $shippingId = $this->Request()->getParam('shippingId');
1140
        $connectAllowed = $this->Request()->getParam('connectAllowed', true);
1141
1142
        if (!$shippingId) {
1143
            return;
1144
        }
1145
1146
        $shippingRepo = $this->getModelManager()->getRepository('\Shopware\Models\Dispatch\Dispatch');
1147
        /** @var \Shopware\Models\Dispatch\Dispatch $shipping */
1148
        $shipping = $shippingRepo->find($shippingId);
1149
1150
        if (!$shipping) {
1151
            return;
1152
        }
1153
1154
        $attribute = $shipping->getAttribute();
1155
1156
        if (!$attribute) {
1157
            $attribute = new \Shopware\Models\Attribute\Dispatch();
1158
            $attribute->setDispatch($shipping);
1159
            $shipping->setAttribute($attribute);
1160
            $this->getModelManager()->persist($attribute);
1161
        }
1162
1163
        $attribute->setConnectAllowed($connectAllowed);
1164
1165
        $this->getModelManager()->flush();
1166
1167
        $this->View()->assign('success', true);
1168
    }
1169
1170
    /**
1171
     * Lists all logs
1172
     */
1173
    public function getLogsAction()
1174
    {
1175
        $params = $this->Request()->getParams();
1176
        $order = $this->Request()->getParam('sort', [['property' => 'time', 'direction' => 'DESC']]);
1177
        $filters = $this->Request()->getParam('filter');
1178
1179
        $commandFilters = [];
1180
        foreach ($params as $key => $param) {
1181
            if (strpos($key, 'commandFilter_') !== false && $param == 'true') {
1182
                $commandFilters[] = str_replace('commandFilter_', '', $key);
1183
            }
1184
        }
1185
1186
        if (empty($commandFilters)) {
1187
            return;
1188
        }
1189
1190
        foreach ($order as &$rule) {
1191
            if ($rule['property'] == 'time') {
1192
                $rule['property'] = 'id';
1193
            }
1194
            $rule['property'] = 'logs.' . $rule['property'];
1195
        }
1196
1197
        $builder = $this->getModelManager()->createQueryBuilder();
1198
        $builder->select('logs');
1199
        $builder->from('Shopware\CustomModels\Connect\Log', 'logs')
1200
            ->addOrderBy($order)
1201
            ->where('logs.command IN (:commandFilter)')
1202
            ->setParameter('commandFilter', $commandFilters);
1203
1204
        foreach ($filters as $filter) {
1205
            switch ($filter['property']) {
1206
                case 'search':
1207
                    $builder->andWhere(
1208
                        'logs.request LIKE :search OR logs.response LIKE :search'
1209
                    );
1210
                    $builder->setParameter('search', $filter['value']);
1211
                    break;
1212
            }
1213
        }
1214
1215
        switch ($this->Request()->getParam('errorFilter', -1)) {
1216
            case 0:
1217
                $builder->andWhere('logs.isError = 1');
1218
                break;
1219
            case 1:
1220
                $builder->andWhere('logs.isError = 0');
1221
                break;
1222
        }
1223
1224
        $query = $builder->getQuery()
1225
            ->setFirstResult($this->Request()->getParam('start', 0))
1226
            ->setMaxResults($this->Request()->getParam('limit', 25));
1227
1228
        $total = Shopware()->Models()->getQueryCount($query);
1229
        $data = $query->getArrayResult();
1230
1231
        $this->View()->assign([
1232
            'success' => true,
1233
            'data' => $data,
1234
            'total' => $total
1235
        ]);
1236
    }
1237
1238
    /**
1239
     * Get a list of log commands
1240
     */
1241
    public function getLogCommandsAction()
1242
    {
1243
        $data = $this->getModelManager()->getConnection()->fetchAll(
1244
            'SELECT DISTINCT `command` FROM `s_plugin_connect_log`'
1245
        );
1246
1247
        $data = array_map(function ($column) {
1248
            return $column['command'];
1249
        }, $data);
1250
1251
        // Enforce these fields
1252
        foreach (['fromShop', 'toShop', 'getLastRevision', 'update', 'checkProducts', 'buy', 'reserveProducts', 'confirm'] as $value) {
1253
            if (!in_array($value, $data)) {
1254
                $data[] = $value;
1255
            }
1256
        }
1257
1258
        $this->View()->assign([
1259
            'success' => true,
1260
            'data' => $data
1261
        ]);
1262
    }
1263
1264
    /**
1265
     * Delete all log entries
1266
     */
1267
    public function clearLogAction()
1268
    {
1269
        $connection = $this->getModelManager()->getConnection();
1270
        $connection->exec('TRUNCATE `s_plugin_connect_log`;');
1271
    }
1272
1273
    /**
1274
     * @return ConnectExport
1275
     */
1276
    public function getConnectExport()
1277
    {
1278
        return new ConnectExport(
1279
            $this->getHelper(),
1280
            $this->getSDK(),
1281
            $this->getModelManager(),
1282
            new ProductsAttributesValidator(),
1283
            $this->getConfigComponent(),
1284
            new ErrorHandler(),
1285
            $this->container->get('events')
1286
        );
1287
    }
1288
1289
    /**
1290
     * @return Config
1291
     */
1292
    public function getConfigComponent()
1293
    {
1294
        if ($this->configComponent === null) {
1295
            $this->configComponent = ConfigFactory::getConfigInstance();
1296
        }
1297
1298
        return $this->configComponent;
1299
    }
1300
1301
    /**
1302
     * Saves the dynamic streams to the db.
1303
     * Cronjob will looks for those streams to process them
1304
     */
1305
    public function prepareDynamicStreamsAction()
1306
    {
1307
        $streamIds = $this->Request()->getParam('streamIds', []);
1308
1309
        try {
1310
            $streamService = $this->getProductStreamService();
1311
            $streams = $streamService->findStreams($streamIds);
1312
1313
            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...
1314
                $message = Shopware()->Snippets()->getNamespace('backend/connect/view/main')->get(
1315
                    'export/message/error_no_stream_selected',
1316
                    'No streams were selected',
1317
                    true
1318
                );
1319
                throw new \Exception($message);
1320
            }
1321
1322
            $modelManager = $this->getModelManager();
1323
1324
            foreach ($streams as $stream) {
1325
                $streamAttr = $streamService->createStreamAttribute($stream);
1326
1327
                if (!$streamAttr->getExportStatus()) {
1328
                    $streamAttr->setExportStatus(ProductStreamService::STATUS_PENDING);
1329
                }
1330
1331
                $modelManager->persist($streamAttr);
1332
            }
1333
1334
            $modelManager->flush();
1335
        } catch (\Exception $e) {
1336
            $this->View()->assign([
1337
                'success' => false,
1338
                'message' => $e->getMessage()
1339
            ]);
1340
        }
1341
1342
        $this->View()->assign([
1343
            'success' => true,
1344
        ]);
1345
    }
1346
1347
    /**
1348
     * Add given category to products
1349
     */
1350
    public function assignProductsToCategoryAction()
1351
    {
1352
        $articleIds = $this->Request()->getParam('ids');
1353
        $categoryId = (int) $this->Request()->getParam('category');
1354
1355
        /** @var Category $category */
1356
        $category = $this->getCategoryModelById($categoryId);
1357
        if (!is_null($category)) {
1358
            foreach ($articleIds as $id) {
1359
                /** @var \Shopware\Models\Article\Article $article */
1360
                $article = $this->getConnectExport()->getArticleModelById($id);
1361
                if (is_null($article)) {
1362
                    continue;
1363
                }
1364
                $article->addCategory($category);
1365
                $this->getModelManager()->persist($article);
1366
            }
1367
            $this->getModelManager()->flush();
1368
        }
1369
1370
        $this->View()->assign(
1371
            [
1372
                'success' => true
1373
            ]
1374
        );
1375
    }
1376
1377
    public function getStreamListAction()
1378
    {
1379
        $productStreamService = $this->getProductStreamService();
1380
1381
        $result = $productStreamService->getList(
1382
            $this->Request()->getParam('start', 0),
1383
            $this->Request()->getParam('limit', 20)
1384
        );
1385
1386
        $this->View()->assign(
1387
            [
1388
                'success' => true,
1389
                'data' => $result['data'],
1390
                'total' => $result['total'],
1391
            ]
1392
        );
1393
    }
1394
1395
    public function getStreamProductsCountAction()
1396
    {
1397
        $streamIds = $this->request->getParam('ids', []);
1398
1399
        if (empty($streamIds)) {
1400
            $this->View()->assign([
1401
                'success' => false,
1402
                'message' => 'No stream selected'
1403
            ]);
1404
        }
1405
1406
        $sourceIdsCount = $this->getProductStreamService()->countProductsInStaticStream($streamIds);
1407
1408
        $this->View()->assign([
1409
            'success' => true,
1410
            'sourceIdsCount' => $sourceIdsCount
1411
        ]);
1412
    }
1413
1414
    public function exportStreamAction()
1415
    {
1416
        $streamIds = $this->request->getParam('streamIds', []);
1417
        $currentStreamIndex = $this->request->getParam('currentStreamIndex', 0);
1418
        $offset = $this->request->getParam('offset', 0);
1419
        $limit = $this->request->getParam('limit', 1);
1420
1421
        $streamId = $streamIds[$currentStreamIndex];
1422
1423
        $productStreamService = $this->getProductStreamService();
1424
1425
        $streamsAssignments = $this->getStreamAssignments($streamId);
1426
1427
        if (!$streamsAssignments) {
1428
            return;
1429
        }
1430
1431
        $sourceIds = $this->getHelper()->getArticleSourceIds($streamsAssignments->getArticleIds());
1432
        $sliced = array_slice($sourceIds, $offset, $limit);
1433
1434
        $exported = $this->exportStreamProducts($streamId, $sliced, $streamsAssignments);
1435
1436
        if (!$exported) {
1437
            return;
1438
        }
1439
1440
        $nextStreamIndex = $currentStreamIndex;
1441
        $newOffset = $offset + $limit;
1442
        $hasMoreIterations = true;
1443
1444
        $processedStreams = $currentStreamIndex;
1445
        $sourceIdsCount = $this->getProductStreamService()->countProductsInStaticStream($streamIds);
1446
1447
        //In this case all the products from a single stream were exported successfully but there are still more streams to be processed.
1448
        if ($newOffset > $sourceIdsCount && $currentStreamIndex + 1 <= (count($streamIds) - 1)) {
1449
            $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_EXPORT, 'Success');
1450
            $nextStreamIndex = $currentStreamIndex + 1;
1451
1452
            $streamsAssignments = $this->getStreamAssignments($streamIds[$nextStreamIndex]);
1453
1454
            if (!$streamsAssignments) {
1455
                return;
1456
            }
1457
1458
            $newOffset = 0;
1459
        }
1460
1461
        //In this case all the products from all streams were exported successfully.
1462
        if ($newOffset > $sourceIdsCount && $currentStreamIndex + 1 > (count($streamIds) - 1)) {
1463
            $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_EXPORT);
1464
            $hasMoreIterations = false;
1465
            $newOffset = $sourceIdsCount;
1466
            $processedStreams = count($streamIds);
1467
        }
1468
1469
1470
        $this->View()->assign([
1471
            'success' => true,
1472
            'nextStreamIndex' => $nextStreamIndex,
1473
            'newOffset' => $newOffset,
1474
            'hasMoreIterations' => $hasMoreIterations,
1475
            'processedStreams' => $processedStreams,
1476
        ]);
1477
    }
1478
1479
    public function hasManyVariantsAction()
1480
    {
1481
        $streamId = $this->request->getParam('streamId');
1482
        $articleId = $this->request->getParam('articleId');
1483
1484
        if (!$this->getProductStreamService()->isStreamExported($streamId)) {
1485
            return;
1486
        }
1487
1488
        $sourceIds = $this->getHelper()->getSourceIdsFromArticleId($articleId);
1489
1490
        $hasManyVariants = false;
1491
1492
        if (count($sourceIds) > ProductStreamService::PRODUCT_LIMIT) {
1493
            $hasManyVariants = true;
1494
        }
1495
1496
        $this->View()->assign([
1497
            'success' => true,
1498
            'hasManyVariants' => $hasManyVariants
1499
        ]);
1500
    }
1501
1502
    public function exportAllWithCronAction()
1503
    {
1504
        try {
1505
            $db = Shopware()->Db();
1506
            $this->getConfigComponent()->setConfig('autoUpdateProducts', 2, null, 'export');
1507
1508
            $db->update(
1509
                's_crontab',
1510
                ['active' => 1],
1511
                "action = 'ShopwareConnectUpdateProducts' OR action = 'Shopware_CronJob_ShopwareConnectUpdateProducts'"
1512
            );
1513
1514
            $db->update(
1515
                's_plugin_connect_items',
1516
                ['cron_update' => 1, 'export_status' => Attribute::STATUS_UPDATE],
1517
                'shop_id IS NULL'
1518
            );
1519
1520
            $this->View()->assign([
1521
                'success' => true,
1522
            ]);
1523
        } catch (\Exception $e) {
1524
            $this->View()->assign([
1525
                'success' => false,
1526
                'message' => $e->getMessage()
1527
            ]);
1528
        }
1529
    }
1530
1531
    /**
1532
     * @param int $streamId
1533
     * @return bool|ProductStreamsAssignments
1534
     */
1535
    private function getStreamAssignments($streamId)
1536
    {
1537
        $productStreamService = $this->getProductStreamService();
1538
1539
        try {
1540
            $streamsAssignments = $productStreamService->prepareStreamsAssignments($streamId);
1541
        } catch (\Exception $e) {
1542
            $this->View()->assign([
1543
                'success' => false,
1544
                'messages' => [ErrorHandler::TYPE_DEFAULT_ERROR => [$e->getMessage()]]
1545
            ]);
1546
1547
            return false;
1548
        }
1549
1550
        return $streamsAssignments;
1551
    }
1552
1553
    /**
1554
     * @param int $streamId
1555
     * @param array $sourceIds
1556
     * @param ProductStreamsAssignments $streamsAssignments
1557
     * @return bool
1558
     */
1559
    private function exportStreamProducts($streamId, array $sourceIds, $streamsAssignments)
1560
    {
1561
        $productStreamService = $this->getProductStreamService();
1562
        $connectExport = $this->getConnectExport();
1563
1564
        try {
1565
            $errorMessages = $connectExport->export($sourceIds, $streamsAssignments);
1566
        } catch (\RuntimeException $e) {
1567
            $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_ERROR);
1568
            $this->View()->assign([
1569
                'success' => false,
1570
                'messages' => [ErrorHandler::TYPE_DEFAULT_ERROR => [$e->getMessage()]]
1571
            ]);
1572
1573
            return false;
1574
        }
1575
1576
        if (!empty($errorMessages)) {
1577
            $errorMessagesText = '';
1578
            $displayedErrorTypes = [
1579
                ErrorHandler::TYPE_DEFAULT_ERROR,
1580
                ErrorHandler::TYPE_PRICE_ERROR
1581
            ];
1582
1583
            foreach ($displayedErrorTypes as $displayedErrorType) {
1584
                $errorMessagesText .= implode('\n', $errorMessages[$displayedErrorType]);
1585
            }
1586
1587
            $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_ERROR, $errorMessagesText);
1588
1589
            $this->View()->assign([
1590
                'success' => false,
1591
                'messages' => $errorMessages
1592
            ]);
1593
1594
            return false;
1595
        }
1596
1597
        return true;
1598
    }
1599
1600
    /**
1601
     * Deletes products from connect stream export
1602
     */
1603
    public function removeStreamsAction()
1604
    {
1605
        $streamIds = $this->request->getParam('ids', []);
1606
1607
        $productStreamService = $this->getProductStreamService();
1608
        $connectExport = $this->getConnectExport();
1609
1610
        $filteredStreamIds = $productStreamService->filterExportedStreams($streamIds);
1611
1612
        foreach ($filteredStreamIds as $streamId) {
1613
            try {
1614
                $removedRecords = [];
1615
1616
                $assignments = $productStreamService->getStreamAssignments($streamId);
1617
                $sourceIds = $this->getHelper()->getArticleSourceIds($assignments->getArticleIds());
1618
                $items = $connectExport->fetchConnectItems($sourceIds, false);
1619
1620
                foreach ($items as $item) {
1621
                    if ($productStreamService->allowToRemove($assignments, $streamId, $item['articleId'])) {
1622
                        $this->getSDK()->recordDelete($item['sourceId']);
1623
                        $removedRecords[] = $item['sourceId'];
1624 View Code Duplication
                    } else {
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...
1625
                        //updates items with the new streams
1626
                        $streamCollection = $assignments->getStreamsByArticleId($item['articleId']);
1627
                        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...
1628
                            continue;
1629
                        }
1630
1631
                        //removes current stream from the collection
1632
                        unset($streamCollection[$streamId]);
1633
1634
                        $this->getSDK()->recordStreamAssignment(
1635
                            $item['sourceId'],
1636
                            $streamCollection,
1637
                            $item['groupId']
1638
                        );
1639
                    }
1640
                }
1641
1642
                $connectExport->updateConnectItemsStatus($removedRecords, Attribute::STATUS_DELETE);
1643
                $this->getSDK()->recordStreamDelete($streamId);
1644
                $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_DELETE);
1645
            } catch (\Exception $e) {
1646
                $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_ERROR);
1647
                $this->View()->assign([
1648
                    'success' => false,
1649
                    'message' => $e->getMessage()
1650
                ]);
1651
1652
                return;
1653
            }
1654
        }
1655
    }
1656
1657 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...
1658
    {
1659
        if (!$this->marketplaceSettingsApplier) {
1660
            $this->marketplaceSettingsApplier = new MarketplaceSettingsApplier(
1661
                $this->getConfigComponent(),
1662
                Shopware()->Models(),
1663
                Shopware()->Db()
1664
            );
1665
        }
1666
1667
        return $this->marketplaceSettingsApplier;
1668
    }
1669
1670
    /**
1671
     * @return SnHttpClient
1672
     */
1673 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...
1674
    {
1675
        if (!$this->snHttpClient) {
1676
            $this->snHttpClient = new SnHttpClient(
1677
                $this->get('http_client'),
1678
                new \Shopware\Connect\Gateway\PDO(Shopware()->Db()->getConnection()),
1679
                $this->getConfigComponent()
1680
            );
1681
        }
1682
1683
        return $this->snHttpClient;
1684
    }
1685
1686
    /**
1687
     * @return string
1688
     */
1689
    protected function getHost()
1690
    {
1691
        $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...
1692
        if (!$host || $host == '') {
1693
            $host = $this->getConfigComponent()->getMarketplaceUrl();
1694
        }
1695
1696
        return $host;
1697
    }
1698
1699
    /**
1700
     * @return ProductStreamService
1701
     */
1702
    protected function getProductStreamService()
1703
    {
1704
        if ($this->productStreamService === null) {
1705
            $this->productStreamService = $this->get('swagconnect.product_stream_service');
1706
        }
1707
1708
        return $this->productStreamService;
1709
    }
1710
1711
    /**
1712
     * {@inheritdoc}
1713
     */
1714
    public function getWhitelistedCSRFActions()
1715
    {
1716
        return ['login', 'autoLogin'];
1717
    }
1718
}
1719