Completed
Pull Request — master (#474)
by Christian
05:11
created

ProductToShop   F

Complexity

Total Complexity 180

Size/Duplication

Total Lines 1683
Duplicated Lines 2.97 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
dl 50
loc 1683
rs 3.9999
c 0
b 0
f 0
wmc 180
lcom 1
cbo 11

50 Methods

Rating   Name   Duplication   Size   Complexity  
A generateSKU() 0 16 4
B __construct() 0 25 1
A startTransaction() 0 4 1
A commit() 0 4 1
F insertOrUpdate() 0 173 20
C deleteDetail() 0 66 8
A getSWProductModel() 0 19 4
A generateNewDetail() 0 20 4
A checkIfMainVariant() 0 4 1
A updateConfiguratorSetTypeFromProduct() 0 7 3
A cleanUpConfiguratorSet() 0 9 3
A getOrCreateAttributeModel() 0 13 2
C getUpdateFields() 0 33 8
B isFieldUpdateAllowed() 0 36 5
C hasFieldChanged() 0 34 11
C setPropertiesForNewProducts() 0 38 8
B createSupplier() 0 28 6
C applyProductProperties() 0 79 9
A applyMarketplaceAttributes() 0 15 2
A setConnectAttributesFromProduct() 0 13 1
A updateDetailFromProduct() 0 10 1
B detailSetUnit() 0 27 4
C detailSetAttributes() 0 34 8
A connectAttributeSetLastUpdate() 0 16 1
A categoryDenormalization() 0 21 2
B getOrCreateStream() 0 27 2
A addProductToStream() 0 9 1
B setPrice() 0 38 3
B setPriceRange() 0 36 4
B setPurchasePrice() 0 28 3
A addArticleTranslations() 0 15 3
A getLocaleRepository() 0 8 2
A getShopRepository() 0 8 2
A delete() 0 12 2
A update() 0 52 2
B changeAvailability() 0 24 1
B makeMainVariant() 0 33 2
A updateOrderStatus() 0 10 3
A updateDeliveryStatus() 0 21 4
B updateTrackingNumber() 0 27 2
A combineTrackingNumbers() 0 9 1
A getTrackingNumberAsArray() 0 8 2
A saveVat() 0 18 3
A applyCrossSelling() 0 8 3
A storeCrossSellingInformationOwningSide() 0 10 3
B insertNewRelations() 0 27 4
A storeCrossSellingInformationInverseSide() 0 15 2
B deleteRemovedRelations() 50 78 3
A createPropertyGroup() 0 19 1
A removeOptions() 0 14 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like ProductToShop often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ProductToShop, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * (c) shopware AG <[email protected]>
4
 * For the full copyright and license information, please view the LICENSE
5
 * file that was distributed with this source code.
6
 */
7
8
namespace ShopwarePlugins\Connect\Components;
9
10
use Shopware\Bundle\SearchBundle\Sorting\ReleaseDateSorting;
11
use Shopware\Connect\Gateway;
12
use Shopware\Components\Model\CategoryDenormalization;
13
use Shopware\Connect\ProductToShop as ProductToShopBase;
14
use Shopware\Connect\Struct\OrderStatus;
15
use Shopware\Connect\Struct\Product;
16
use Shopware\Models\Article\Article as ProductModel;
17
use Shopware\Models\Article\Configurator\Option;
18
use Shopware\Models\Order\Status;
19
use Shopware\Models\Article\Detail as DetailModel;
20
use Shopware\Models\Attribute\Article as AttributeModel;
21
use Shopware\Components\Model\ModelManager;
22
use Shopware\Connect\Struct\PriceRange;
23
use Shopware\Connect\Struct\ProductUpdate;
24
use Shopware\CustomModels\Connect\ProductStreamAttribute;
25
use Shopware\Models\Customer\Group;
26
use Shopware\Connect\Struct\Property;
27
use Shopware\Models\ProductStream\ProductStream;
28
use Shopware\Models\Property\Group as PropertyGroup;
29
use Shopware\Models\Property\Option as PropertyOption;
30
use Shopware\Models\Property\Value as PropertyValue;
31
use ShopwarePlugins\Connect\Components\ProductStream\ProductStreamRepository;
32
use ShopwarePlugins\Connect\Components\ProductStream\ProductStreamService;
33
use ShopwarePlugins\Connect\Components\Translations\LocaleMapper;
34
use ShopwarePlugins\Connect\Components\Gateway\ProductTranslationsGateway;
35
use ShopwarePlugins\Connect\Components\Marketplace\MarketplaceGateway;
36
use ShopwarePlugins\Connect\Components\Utils\UnitMapper;
37
use Shopware\CustomModels\Connect\Attribute as ConnectAttribute;
38
use Shopware\Models\Article\Supplier;
39
use Shopware\Models\Tax\Tax;
40
use Shopware\Models\Article\Configurator\Set;
41
42
/**
43
 * The interface for products imported *from* connect *to* the local shop
44
 *
45
 * @category  Shopware
46
 * @package   Shopware\Plugins\SwagConnect
47
 */
48
class ProductToShop implements ProductToShopBase
49
{
50
    const RELATION_TYPE_RELATED = 'relationships';
51
    const RELATION_TYPE_SIMILAR = 'similar';
52
53
    /**
54
     * @var Helper
55
     */
56
    private $helper;
57
58
    /**
59
     * @var ModelManager
60
     */
61
    private $manager;
62
63
    /**
64
     * @var \ShopwarePlugins\Connect\Components\Config
65
     */
66
    private $config;
67
68
    /**
69
     * @var ImageImport
70
     */
71
    private $imageImport;
72
73
    /**
74
     * @var \ShopwarePlugins\Connect\Components\VariantConfigurator
75
     */
76
    private $variantConfigurator;
77
78
    /**
79
     * @var MarketplaceGateway
80
     */
81
    private $marketplaceGateway;
82
83
    /**
84
     * @var ProductTranslationsGateway
85
     */
86
    private $productTranslationsGateway;
87
88
    /**
89
     * @var \Shopware\Models\Shop\Repository
90
     */
91
    private $shopRepository;
92
93
    private $localeRepository;
94
95
    /**
96
     * @var CategoryResolver
97
     */
98
    private $categoryResolver;
99
100
    /**
101
     * @var \Shopware\Connect\Gateway
102
     */
103
    private $connectGateway;
104
105
    /**
106
     * @var \Enlight_Event_EventManager
107
     */
108
    private $eventManager;
109
110
    /**
111
     * @var CategoryDenormalization
112
     */
113
    private $categoryDenormalization;
114
115
    /**
116
     * @param Helper $helper
117
     * @param ModelManager $manager
118
     * @param ImageImport $imageImport
119
     * @param \ShopwarePlugins\Connect\Components\Config $config
120
     * @param VariantConfigurator $variantConfigurator
121
     * @param \ShopwarePlugins\Connect\Components\Marketplace\MarketplaceGateway $marketplaceGateway
122
     * @param ProductTranslationsGateway $productTranslationsGateway
123
     * @param CategoryResolver $categoryResolver
124
     * @param Gateway $connectGateway
125
     * @param \Enlight_Event_EventManager $eventManager
126
     * @param CategoryDenormalization $categoryDenormalization
127
     */
128
    public function __construct(
129
        Helper $helper,
130
        ModelManager $manager,
131
        ImageImport $imageImport,
132
        Config $config,
133
        VariantConfigurator $variantConfigurator,
134
        MarketplaceGateway $marketplaceGateway,
135
        ProductTranslationsGateway $productTranslationsGateway,
136
        CategoryResolver $categoryResolver,
137
        Gateway $connectGateway,
138
        \Enlight_Event_EventManager $eventManager,
139
        CategoryDenormalization $categoryDenormalization
140
    ) {
141
        $this->helper = $helper;
142
        $this->manager = $manager;
143
        $this->config = $config;
144
        $this->imageImport = $imageImport;
145
        $this->variantConfigurator = $variantConfigurator;
146
        $this->marketplaceGateway = $marketplaceGateway;
147
        $this->productTranslationsGateway = $productTranslationsGateway;
148
        $this->categoryResolver = $categoryResolver;
149
        $this->connectGateway = $connectGateway;
150
        $this->eventManager = $eventManager;
151
        $this->categoryDenormalization = $categoryDenormalization;
152
    }
153
154
    /**
155
     * Start transaction
156
     *
157
     * Starts a transaction, which includes all insertOrUpdate and delete
158
     * operations, as well as the revision updates.
159
     *
160
     * @return void
161
     */
162
    public function startTransaction()
163
    {
164
        $this->manager->getConnection()->beginTransaction();
165
    }
166
167
    /**
168
     * Commit transaction
169
     *
170
     * Commits the transactions, once all operations are queued.
171
     *
172
     * @return void
173
     */
174
    public function commit()
175
    {
176
        $this->manager->getConnection()->commit();
177
    }
178
179
    /**
180
     * Import or update given product
181
     *
182
     * Store product in your shop database as an external product. The
183
     * associated sourceId
184
     *
185
     * @param Product $product
186
     */
187
    public function insertOrUpdate(Product $product)
188
    {
189
        /** @var Product $product */
190
        $product = $this->eventManager->filter(
191
            'Connect_ProductToShop_InsertOrUpdate_Before',
192
            $product
193
        );
194
195
        // todo@dn: Set dummy values and make product inactive
196
        if (empty($product->title) || empty($product->vendor)) {
197
            return;
198
        }
199
200
        $number = $this->generateSKU($product);
201
202
        $detail = $this->helper->getArticleDetailModelByProduct($product);
203
        $detail = $this->eventManager->filter(
204
            'Connect_Merchant_Get_Article_Detail_After',
205
            $detail,
206
            [
207
                'product' => $product,
208
                'subject' => $this
209
            ]
210
        );
211
212
        $isMainVariant = false;
213
        if ($detail === null) {
214
            $active = $this->config->getConfig('activateProductsAutomatically', false) ? true : false;
215
216
            $model = $this->getSWProductModel($product, $active, $isMainVariant);
217
218
            $detail = $this->generateNewDetail($product, $model);
219
        } else {
220
            /** @var ProductModel $model */
221
            $model = $detail->getArticle();
222
            // fix for isMainVariant flag
223
            // in connect attribute table
224
            $mainDetail = $model->getMainDetail();
225
            $isMainVariant = $this->checkIfMainVariant($detail, $mainDetail);
226
            $this->variantConfigurator->configureVariantAttributes($product, $detail);
227
            $this->updateConfiguratorSetTypeFromProduct($model, $product);
228
229
            $this->cleanUpConfiguratorSet($model, $product);
230
        }
231
232
        $detail->setNumber($number);
233
234
        $detailAttribute = $this->getOrCreateAttributeModel($detail, $model);
235
236
        $connectAttribute = $this->helper->getConnectAttributeByModel($detail) ?: new ConnectAttribute;
237
        // configure main variant and groupId
238
        if ($isMainVariant === true) {
239
            $connectAttribute->setIsMainVariant(true);
240
        }
241
        $connectAttribute->setGroupId($product->groupId);
242
243
        list($updateFields, $flag) = $this->getUpdateFields($model, $detail, $connectAttribute, $product);
244
        $this->setPropertiesForNewProducts($updateFields, $model, $detailAttribute, $product);
245
246
        $this->saveVat($product, $model);
247
248
        $this->applyProductProperties($model, $product);
249
250
        $detailAttribute = $this->applyMarketplaceAttributes($detailAttribute, $product);
251
252
        $this->setConnectAttributesFromProduct($connectAttribute, $product);
253
254
        // store product categories to connect attribute
255
        $connectAttribute->setCategory($product->categories);
256
257
        $connectAttribute->setLastUpdateFlag($flag);
258
259
        $connectAttribute->setPurchasePriceHash($product->purchasePriceHash);
260
        $connectAttribute->setOfferValidUntil($product->offerValidUntil);
261
262
        $this->updateDetailFromProduct($detail, $product);
263
264
        // some shops have feature "sell not in stock",
265
        // then end customer should be able to by the product with stock = 0
266
        $shopConfiguration = $this->connectGateway->getShopConfiguration($product->shopId);
267
        if ($shopConfiguration && $shopConfiguration->sellNotInStock) {
268
            $model->setLastStock(false);
269
        } else {
270
            $model->setLastStock(true);
271
        }
272
273
        $this->detailSetUnit($detail, $product, $detailAttribute);
274
275
        $this->detailSetAttributes($detail, $product);
276
277
        $this->connectAttributeSetLastUpdate($connectAttribute, $product);
278
279
        if ($model->getMainDetail() === null) {
280
            $model->setMainDetail($detail);
281
        }
282
283
        if ($detail->getAttribute() === null) {
284
            $detail->setAttribute($detailAttribute);
285
            $detailAttribute->setArticle($model);
286
        }
287
288
        $connectAttribute->setArticle($model);
289
        $connectAttribute->setArticleDetail($detail);
290
291
        $this->eventManager->notify(
292
            'Connect_Merchant_Saving_ArticleAttribute_Before',
293
            [
294
                'subject' => $this,
295
                'connectAttribute' => $connectAttribute
296
            ]
297
        );
298
299
        //article has to be flushed
300
        $this->manager->persist($model);
301
        $this->manager->persist($connectAttribute);
302
        $this->manager->persist($detail);
303
        $this->manager->flush();
304
305
        $this->categoryResolver->storeRemoteCategories($product->categories, $model->getId(), $product->shopId);
306
        $categories = $this->categoryResolver->resolve($product->categories, $product->shopId, $product->stream);
307
        if (count($categories) > 0) {
308
            $detailAttribute->setConnectMappedCategory(true);
309
        }
310
311
        $this->manager->persist($detailAttribute);
312
        $this->manager->flush();
313
314
        $this->categoryDenormalization($model, $categories);
315
316
        $defaultCustomerGroup = $this->helper->getDefaultCustomerGroup();
317
        // Only set prices, if fixedPrice is active or price updates are configured
318
        if (count($detail->getPrices()) == 0 || $connectAttribute->getFixedPrice() || $updateFields['price']) {
319
            $this->setPrice($model, $detail, $product);
320
        }
321
        // If the price is not being update, update the purchasePrice anyway
322
        $this->setPurchasePrice($detail, $product->purchasePrice, $defaultCustomerGroup);
323
324
        $this->manager->clear();
325
326
        $this->addArticleTranslations($model, $product);
327
328
        if ($isMainVariant || $product->groupId === null) {
329
            $this->applyCrossSelling($model->getId(), $product);
330
        }
331
332
        //clear cache for that article
333
        $this->helper->clearArticleCache($model->getId());
334
335
        if ($updateFields['image']) {
336
            // Reload the model in order to not to work an the already flushed model
337
            $model = $this->helper->getArticleModelByProduct($product);
338
            // import only global images for article
339
            $this->imageImport->importImagesForArticle(array_diff($product->images, $product->variantImages), $model);
0 ignored issues
show
Bug introduced by
It seems like $model defined by $this->helper->getArticleModelByProduct($product) on line 337 can be null; however, ShopwarePlugins\Connect\...mportImagesForArticle() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
340
            if ($updateFields['mainImage'] && isset($product->images[0])) {
341
                $this->imageImport->importMainImage($product->images[0], $model->getId());
342
            }
343
            // Reload the article detail model in order to not to work an the already flushed model
344
            $detail = $this->helper->getArticleDetailModelByProduct($product);
345
            // import only specific images for variant
346
            $this->imageImport->importImagesForDetail($product->variantImages, $detail);
0 ignored issues
show
Bug introduced by
It seems like $detail defined by $this->helper->getArticl...odelByProduct($product) on line 344 can be null; however, ShopwarePlugins\Connect\...importImagesForDetail() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
347
        }
348
349
        $this->eventManager->notify(
350
            'Connect_ProductToShop_InsertOrUpdate_After',
351
            [
352
                'connectProduct' => $product,
353
                'shopArticleDetail' => $detail
354
            ]
355
        );
356
357
        $stream = $this->getOrCreateStream($product);
358
        $this->addProductToStream($stream, $model);
0 ignored issues
show
Bug introduced by
It seems like $model can be null; however, addProductToStream() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
359
    }
360
361
    /**
362
     * @param Product $product
363
     * @return string
364
     */
365
    private function generateSKU(Product $product)
366
    {
367
        if (!empty($product->sku)) {
368
            $number = 'SC-' . $product->shopId . '-' . $product->sku;
369
            $duplicatedDetail = $this->helper->getDetailByNumber($number);
370
            if ($duplicatedDetail
371
                && $this->helper->getConnectAttributeByModel($duplicatedDetail)->getSourceId() != $product->sourceId
372
            ) {
373
                $this->deleteDetail($duplicatedDetail);
374
            }
375
        } else {
376
            $number = 'SC-' . $product->shopId . '-' . $product->sourceId;
377
        }
378
379
        return $number;
380
    }
381
382
    /**
383
     * @param DetailModel $detailModel
384
     */
385
    private function deleteDetail(DetailModel $detailModel)
386
    {
387
        $this->eventManager->notify(
388
            'Connect_Merchant_Delete_Product_Before',
389
            [
390
                'subject' => $this,
391
                'articleDetail' => $detailModel
392
            ]
393
        );
394
395
        $article = $detailModel->getArticle();
396
397
        $this->removeOptions($detailModel, $article);
398
399
        // Not sure why, but the Attribute can be NULL
400
        $attribute = $this->helper->getConnectAttributeByModel($detailModel);
401
        $this->manager->remove($detailModel);
402
403
        if ($attribute) {
404
            $this->manager->remove($attribute);
405
        }
406
407
        // if removed variant is main variant
408
        // find first variant which is not main and mark it
409
        if ($detailModel->getKind() === 1) {
410
            /** @var \Shopware\Models\Article\Detail $variant */
411
            foreach ($article->getDetails() as $variant) {
412
                if ($variant->getId() != $detailModel->getId()) {
413
                    $variant->setKind(1);
414
                    $article->setMainDetail($variant);
415
                    $connectAttribute = $this->helper->getConnectAttributeByModel($variant);
416
                    if (!$connectAttribute) {
417
                        continue;
418
                    }
419
                    $connectAttribute->setIsMainVariant(true);
420
                    $this->manager->persist($connectAttribute);
421
                    $this->manager->persist($article);
422
                    $this->manager->persist($variant);
423
                    break;
424
                }
425
            }
426
        }
427
428
        if (count($details = $article->getDetails()) === 1) {
429
            $details->clear();
430
            $this->manager->remove($article);
431
        }
432
433
        //save category Ids before flush
434
        $oldCategoryIds = array_map(function ($category) {
435
            return $category->getId();
436
        }, $article->getCategories()->toArray());
437
438
        // Do not remove flush. It's needed when remove article,
439
        // because duplication of ordernumber. Even with remove before
440
        // persist calls mysql throws exception "Duplicate entry"
441
        $this->manager->flush();
442
        // always clear entity manager, because $article->getDetails() returns
443
        // more than 1 detail, but all of them were removed except main one.
444
        $this->manager->clear();
445
446
        // call this after flush because article has to be deleted that this works
447
        if (count($oldCategoryIds) > 0) {
448
            $this->categoryResolver->deleteEmptyConnectCategories($oldCategoryIds);
449
        }
450
    }
451
452
    /**
453
     * @param DetailModel $detailModel
454
     * @param $article
455
     */
456
    private function removeOptions(DetailModel $detailModel, $article)
457
    {
458
        $configSet = $article->getConfiguratorSet();
459
        $options = $detailModel->getConfiguratorOptions();
460
        if($options && $options) {
461
            $optionCollection = $configSet->getOptions();
462
            /** @var Option $option */
463
            foreach ($options as $option) {
464
                $optionCollection->removeElement($option);
465
            }
466
            $configSet->setOptions($options);
467
            $this->manager->persist($configSet);
468
        }
469
    }
470
471
    /**
472
     * @param Product $product
473
     * @param $active
474
     * @param $isMainVariant
475
     * @return null|Article
476
     */
477
    private function getSWProductModel(Product $product, $active, &$isMainVariant)
478
    {
479
        if ($product->groupId !== null) {
480
            $model = $this->helper->getArticleByRemoteProduct($product);
481
            if (!$model instanceof \Shopware\Models\Article\Article) {
0 ignored issues
show
Bug introduced by
The class Shopware\Models\Article\Article does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
482
                $model = $this->helper->createProductModel($product);
483
                $model->setActive($active);
484
                $isMainVariant = true;
485
            }
486
        } else {
487
            $model = $this->helper->getConnectArticleModel($product->sourceId, $product->shopId);
488
            if (!$model instanceof \Shopware\Models\Article\Article) {
0 ignored issues
show
Bug introduced by
The class Shopware\Models\Article\Article does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
489
                $model = $this->helper->createProductModel($product);
490
                $model->setActive($active);
491
            }
492
        }
493
494
        return $model;
495
    }
496
497
    /**
498
     * @param Product $product
499
     * @param $model
500
     * @return DetailModel
501
     */
502
    private function generateNewDetail(Product $product, $model)
503
    {
504
        $detail = new DetailModel();
505
        $detail->setActive($model->getActive());
506
        // added for 5.4 compatibility
507
        if (method_exists($detail, 'setLastStock')) {
508
            $shopConfiguration = $this->connectGateway->getShopConfiguration($product->shopId);
509
            if ($shopConfiguration && $shopConfiguration->sellNotInStock) {
510
                $detail->setLastStock(false);
511
            } else {
512
                $detail->setLastStock(true);
513
            }
514
        }
515
        $this->manager->persist($detail);
516
        $detail->setArticle($model);
517
        $model->getDetails()->add($detail);
518
        $this->variantConfigurator->configureVariantAttributes($product, $detail);
519
520
        return $detail;
521
    }
522
523
    /**
524
     * @param DetailModel $detail
525
     * @param DetailModel $mainDetail
526
     * @return bool
527
     */
528
    private function checkIfMainVariant(DetailModel $detail, DetailModel $mainDetail)
529
    {
530
        return $detail->getId() === $mainDetail->getId();
531
    }
532
533
    /**
534
     * @param ProductModel $model
535
     * @param Product $product
536
     */
537
    private function updateConfiguratorSetTypeFromProduct(ProductModel $model, Product $product)
538
    {
539
        $configSet = $model->getConfiguratorSet();
540
        if (!empty($product->variant) && $configSet instanceof Set) {
0 ignored issues
show
Bug introduced by
The class Shopware\Models\Article\Configurator\Set does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
541
            $configSet->setType($product->configuratorSetType);
542
        }
543
    }
544
545
    /**
546
     * @param ProductModel $model
547
     * @param Product $product
548
     */
549
    private function cleanUpConfiguratorSet(ProductModel $model, Product $product)
550
    {
551
        if (empty($product->variant) && $model->getConfiguratorSet()) {
552
            $this->manager->getConnection()->executeQuery(
553
                'UPDATE s_articles SET configurator_set_id = NULL WHERE id = ?',
554
                [$model->getId()]
555
            );
556
        }
557
    }
558
559
    /**
560
     * @param DetailModel $detail
561
     * @param ProductModel $model
562
     * @return AttributeModel
563
     */
564
    private function getOrCreateAttributeModel(DetailModel $detail, ProductModel $model)
565
    {
566
        $detailAttribute = $detail->getAttribute();
567
        if (!$detailAttribute) {
568
            $detailAttribute = new AttributeModel();
569
            $detail->setAttribute($detailAttribute);
570
            $model->setAttribute($detailAttribute);
571
            $detailAttribute->setArticle($model);
572
            $detailAttribute->setArticleDetail($detail);
573
        }
574
575
        return $detailAttribute;
576
    }
577
578
    /**
579
     * Get array of update info for the known fields
580
     *
581
     * @param $model
582
     * @param $detail
583
     * @param $attribute
584
     * @param $product
585
     * @return array
586
     */
587
    public function getUpdateFields($model, $detail, $attribute, $product)
588
    {
589
        // This also defines the flags of these fields
590
        $fields = $this->helper->getUpdateFlags();
591
        $flagsByName = array_flip($fields);
592
593
        $flag = 0;
594
        $output = [];
595
        foreach ($fields as $key => $field) {
596
            // Don't handle the imageInitialImport flag
597
            if ($field == 'imageInitialImport') {
598
                continue;
599
            }
600
601
            // If this is a new product
602
            if (!$model->getId() && $field == 'image' && !$this->config->getConfig(
603
                'importImagesOnFirstImport',
604
                    false
605
            )) {
606
                $output[$field] = false;
607
                $flag |= $flagsByName['imageInitialImport'];
608
                continue;
609
            }
610
611
            $updateAllowed = $this->isFieldUpdateAllowed($field, $model, $attribute);
612
            $output[$field] = $updateAllowed;
613
            if (!$updateAllowed && $this->hasFieldChanged($field, $model, $detail, $product)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $updateAllowed of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
614
                $flag |= $key;
615
            }
616
        }
617
618
        return [$output, $flag];
619
    }
620
621
    /**
622
     * Helper method to determine if a given $fields may/must be updated.
623
     * This method will check for the model->id in order to determine, if it is a new entity. Therefore
624
     * this method cannot be used after the model in question was already flushed.
625
     *
626
     * @param $field
627
     * @param $model ProductModel
628
     * @param $attribute ConnectAttribute
629
     * @throws \RuntimeException
630
     * @return bool|null
631
     */
632
    public function isFieldUpdateAllowed($field, ProductModel $model, ConnectAttribute $attribute)
633
    {
634
        $allowed = [
635
            'ShortDescription',
636
            'LongDescription',
637
            'AdditionalDescription',
638
            'Image',
639
            'Price',
640
            'Name',
641
            'MainImage',
642
        ];
643
644
        // Always allow updates for new models
645
        if (!$model->getId()) {
646
            return true;
647
        }
648
649
        $field = ucfirst($field);
650
        $attributeGetter = 'getUpdate' . $field;
651
        $configName = 'overwriteProduct' . $field;
652
653
        if (!in_array($field, $allowed)) {
654
            throw new \RuntimeException("Unknown update field {$field}");
655
        }
656
657
        $attributeValue = $attribute->$attributeGetter();
658
659
660
        // If the value is 'null' or 'inherit', the behaviour will be inherited from the global configuration
661
        // Once we have a supplier based configuration, we need to take it into account here
662
        if ($attributeValue == null || $attributeValue == 'inherit') {
663
            return $this->config->getConfig($configName, true);
664
        }
665
666
        return $attributeValue == 'overwrite';
667
    }
668
669
    /**
670
     * Determine if a given field has changed
671
     *
672
     * @param $field
673
     * @param ProductModel $model
674
     * @param DetailModel $detail
675
     * @param Product $product
676
     * @return bool
677
     */
678
    public function hasFieldChanged($field, ProductModel $model, DetailModel $detail, Product $product)
679
    {
680
        switch ($field) {
681
            case 'shortDescription':
682
                return $model->getDescription() != $product->shortDescription;
683
            case 'longDescription':
684
                return $model->getDescriptionLong() != $product->longDescription;
685
            case 'additionalDescription':
686
                return $detail->getAttribute()->getConnectProductDescription() != $product->additionalDescription;
687
            case 'name':
688
                return $model->getName() != $product->title;
689
            case 'image':
690
                return count($model->getImages()) != count($product->images);
691
            case 'mainImage':
692
                if ($product->images[0]) {
693
                    return $this->imageImport->hasMainImageChanged($product->images[0], $model->getId());
694
                }
695
696
                return false;
697
            case 'price':
698
                $prices = $detail->getPrices();
699
                if (empty($prices)) {
700
                    return true;
701
                }
702
                $price = $prices->first();
703
                if (!$price) {
704
                    return true;
705
                }
706
707
                return $prices->first()->getPrice() != $product->price;
708
        }
709
710
        throw new \InvalidArgumentException('Unrecognized field');
711
    }
712
713
    /**
714
     * @param array $updateFields
715
     * @param ProductModel $model
716
     * @param AttributeModel $detailAttribute
717
     * @param Product $product
718
     */
719
    private function setPropertiesForNewProducts(array $updateFields, ProductModel $model, AttributeModel $detailAttribute, Product $product)
720
    {
721
        /*
722
         * Make sure, that the following properties are set for
723
         * - new products
724
         * - products that have been configured to receive these updates
725
         */
726
        if ($updateFields['name']) {
727
            $model->setName($product->title);
728
        }
729
        if ($updateFields['shortDescription']) {
730
            $model->setDescription($product->shortDescription);
731
        }
732
        if ($updateFields['longDescription']) {
733
            $model->setDescriptionLong($product->longDescription);
734
        }
735
736
        if ($updateFields['additionalDescription']) {
737
            $detailAttribute->setConnectProductDescription($product->additionalDescription);
738
        }
739
740
        if ($product->vat !== null) {
741
            $repo = $this->manager->getRepository('Shopware\Models\Tax\Tax');
742
            $tax = round($product->vat * 100, 2);
743
            /** @var \Shopware\Models\Tax\Tax $tax */
744
            $tax = $repo->findOneBy(['tax' => $tax]);
745
            $model->setTax($tax);
746
        }
747
748
        if ($product->vendor !== null) {
749
            $repo = $this->manager->getRepository('Shopware\Models\Article\Supplier');
750
            $supplier = $repo->findOneBy(['name' => $product->vendor]);
751
            if ($supplier === null) {
752
                $supplier = $this->createSupplier($product->vendor);
753
            }
754
            $model->setSupplier($supplier);
755
        }
756
    }
757
758
    /**
759
     * @param $vendor
760
     * @return Supplier
761
     */
762
    private function createSupplier($vendor)
763
    {
764
        $supplier = new Supplier();
765
766
        if (is_array($vendor)) {
767
            $supplier->setName($vendor['name']);
768
            $supplier->setDescription($vendor['description']);
769
            if (array_key_exists('url', $vendor) && $vendor['url']) {
770
                $supplier->setLink($vendor['url']);
771
            }
772
773
            $supplier->setMetaTitle($vendor['page_title']);
774
775
            if (array_key_exists('logo_url', $vendor) && $vendor['logo_url']) {
776
                $this->imageImport->importImageForSupplier($vendor['logo_url'], $supplier);
777
            }
778
        } else {
779
            $supplier->setName($vendor);
780
        }
781
782
        //sets supplier attributes
783
        $attr = new \Shopware\Models\Attribute\ArticleSupplier();
784
        $attr->setConnectIsRemote(true);
785
786
        $supplier->setAttribute($attr);
787
788
        return $supplier;
789
    }
790
791
    /**
792
     * @param ProductModel $article
793
     * @param Product $product
794
     */
795
    private function applyProductProperties(ProductModel $article, Product $product)
796
    {
797
        if (empty($product->properties)) {
798
            return;
799
        }
800
801
        /** @var Property $firstProperty */
802
        $firstProperty = reset($product->properties);
803
        $groupRepo = $this->manager->getRepository(PropertyGroup::class);
804
        $group = $groupRepo->findOneBy(['name' => $firstProperty->groupName]);
805
806
        if (!$group) {
807
            $group = $this->createPropertyGroup($firstProperty);
808
        }
809
810
        $propertyValues = $article->getPropertyValues();
811
        $propertyValues->clear();
812
        $this->manager->persist($article);
813
        $this->manager->flush();
814
815
        $article->setPropertyGroup($group);
816
817
        $optionRepo = $this->manager->getRepository(PropertyOption::class);
818
        $valueRepo = $this->manager->getRepository(PropertyValue::class);
819
820
        foreach ($product->properties as $property) {
821
            $option = $optionRepo->findOneBy(['name' => $property->option]);
822
            $optionExists = $option instanceof PropertyOption;
0 ignored issues
show
Bug introduced by
The class Shopware\Models\Property\Option does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
823
            if (!$option) {
824
                $option = new PropertyOption();
825
                $option->setName($property->option);
826
                $option->setFilterable($property->filterable);
827
828
                $attribute = new \Shopware\Models\Attribute\PropertyOption();
829
                $attribute->setPropertyOption($option);
830
                $attribute->setConnectIsRemote(true);
831
                $option->setAttribute($attribute);
832
833
                $this->manager->persist($option);
834
                $this->manager->flush($option);
835
            }
836
837
            if (!$optionExists || !$value = $valueRepo->findOneBy(['option' => $option, 'value' => $property->value])) {
838
                $value = new PropertyValue($option, $property->value);
839
                $value->setPosition($property->valuePosition);
840
841
                $attribute = new \Shopware\Models\Attribute\PropertyValue();
842
                $attribute->setPropertyValue($value);
843
                $attribute->setConnectIsRemote(true);
844
                $value->setAttribute($attribute);
845
846
                $this->manager->persist($value);
847
            }
848
849
            if (!$propertyValues->contains($value)) {
850
                //add only new values
851
                $propertyValues->add($value);
852
            }
853
854
            $filters = [
855
                ['property' => 'options.name', 'expression' => '=', 'value' => $property->option],
856
                ['property' => 'groups.name', 'expression' => '=', 'value' => $property->groupName],
857
            ];
858
859
            $query = $groupRepo->getPropertyRelationQuery($filters, null, 1, 0);
860
            $relation = $query->getOneOrNullResult();
861
862
            if (!$relation) {
863
                $group->addOption($option);
864
                $this->manager->persist($group);
865
                $this->manager->flush($group);
866
            }
867
        }
868
869
        $article->setPropertyValues($propertyValues);
870
871
        $this->manager->persist($article);
872
        $this->manager->flush();
873
    }
874
875
    /**
876
     * Read product attributes mapping and set to shopware attribute model
877
     *
878
     * @param AttributeModel $detailAttribute
879
     * @param Product $product
880
     * @return AttributeModel
881
     */
882
    private function applyMarketplaceAttributes(AttributeModel $detailAttribute, Product $product)
883
    {
884
        $detailAttribute->setConnectReference($product->sourceId);
885
        $detailAttribute->setConnectArticleShipping($product->shipping);
886
        //todo@sb: check if connectAttribute matches position of the marketplace attribute
887
        array_walk($product->attributes, function ($value, $key) use ($detailAttribute) {
888
            $shopwareAttribute = $this->marketplaceGateway->findShopwareMappingFor($key);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $shopwareAttribute is correct as $this->marketplaceGatewa...hopwareMappingFor($key) (which targets ShopwarePlugins\Connect\...indShopwareMappingFor()) 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...
889
            if (strlen($shopwareAttribute) > 0) {
890
                $setter = 'set' . ucfirst($shopwareAttribute);
891
                $detailAttribute->$setter($value);
892
            }
893
        });
894
895
        return $detailAttribute;
896
    }
897
898
    /**
899
     * @param  ConnectAttribute $connectAttribute
900
     * @param Product $product
901
     */
902
    private function setConnectAttributesFromProduct(ConnectAttribute $connectAttribute, Product $product)
903
    {
904
        $connectAttribute->setShopId($product->shopId);
905
        $connectAttribute->setSourceId($product->sourceId);
906
        $connectAttribute->setExportStatus(null);
907
        $connectAttribute->setPurchasePrice($product->purchasePrice);
908
        $connectAttribute->setFixedPrice($product->fixedPrice);
909
        $connectAttribute->setStream($product->stream);
910
911
        // store purchasePriceHash and offerValidUntil
912
        $connectAttribute->setPurchasePriceHash($product->purchasePriceHash);
913
        $connectAttribute->setOfferValidUntil($product->offerValidUntil);
914
    }
915
916
    /**
917
     * @param DetailModel $detail
918
     * @param Product $product
919
     */
920
    private function updateDetailFromProduct(DetailModel $detail, Product $product)
921
    {
922
        $detail->setInStock($product->availability);
923
        $detail->setEan($product->ean);
924
        $detail->setShippingTime($product->deliveryWorkDays);
925
        $releaseDate = new \DateTime();
926
        $releaseDate->setTimestamp($product->deliveryDate);
927
        $detail->setReleaseDate($releaseDate);
928
        $detail->setMinPurchase($product->minPurchaseQuantity);
929
    }
930
931
    /**
932
     * @param DetailModel $detail
933
     * @param Product $product
934
     * @param $detailAttribute
935
     */
936
    private function detailSetUnit(DetailModel $detail, Product $product, $detailAttribute)
937
    {
938
        // if connect product has unit
939
        // find local unit with units mapping
940
        // and add to detail model
941
        if (array_key_exists('unit', $product->attributes) && $product->attributes['unit']) {
942
            $detailAttribute->setConnectRemoteUnit($product->attributes['unit']);
943
            if ($this->config->getConfig($product->attributes['unit']) == null) {
944
                $this->config->setConfig($product->attributes['unit'], '', null, 'units');
945
            }
946
947
            /** @var \ShopwarePlugins\Connect\Components\Utils\UnitMapper $unitMapper */
948
            $unitMapper = new UnitMapper($this->config, $this->manager);
949
950
            $shopwareUnit = $unitMapper->getShopwareUnit($product->attributes['unit']);
951
952
            /** @var \Shopware\Models\Article\Unit $unit */
953
            $unit = $this->helper->getUnit($shopwareUnit);
954
            $detail->setUnit($unit);
955
            $detail->setPurchaseUnit($product->attributes['quantity']);
956
            $detail->setReferenceUnit($product->attributes['ref_quantity']);
957
        } else {
958
            $detail->setUnit(null);
959
            $detail->setPurchaseUnit(null);
960
            $detail->setReferenceUnit(null);
961
        }
962
    }
963
964
    /**
965
     * @param DetailModel $detail
966
     * @param Product $product
967
     */
968
    private function detailSetAttributes(DetailModel $detail, Product $product)
969
    {
970
        // set dimension
971
        if (array_key_exists('dimension', $product->attributes) && $product->attributes['dimension']) {
972
            $dimension = explode('x', $product->attributes['dimension']);
973
            $detail->setLen($dimension[0]);
974
            $detail->setWidth($dimension[1]);
975
            $detail->setHeight($dimension[2]);
976
        } else {
977
            $detail->setLen(null);
978
            $detail->setWidth(null);
979
            $detail->setHeight(null);
980
        }
981
982
        // set weight
983
        if (array_key_exists('weight', $product->attributes) && $product->attributes['weight']) {
984
            $detail->setWeight($product->attributes['weight']);
985
        }
986
987
        //set package unit
988
        if (array_key_exists(Product::ATTRIBUTE_PACKAGEUNIT, $product->attributes)) {
989
            $detail->setPackUnit($product->attributes[Product::ATTRIBUTE_PACKAGEUNIT]);
990
        }
991
992
        //set basic unit
993
        if (array_key_exists(Product::ATTRIBUTE_BASICUNIT, $product->attributes)) {
994
            $detail->setMinPurchase($product->attributes[Product::ATTRIBUTE_BASICUNIT]);
995
        }
996
997
        //set manufacturer no.
998
        if (array_key_exists(Product::ATTRIBUTE_MANUFACTURERNUMBER, $product->attributes)) {
999
            $detail->setSupplierNumber($product->attributes[Product::ATTRIBUTE_MANUFACTURERNUMBER]);
1000
        }
1001
    }
1002
1003
    /**
1004
     * @param ConnectAttribute $connectAttribute
1005
     * @param Product $product
1006
     */
1007
    private function connectAttributeSetLastUpdate(ConnectAttribute $connectAttribute, Product $product)
1008
    {
1009
        // Whenever a product is updated, store a json encoded list of all fields that are updated optionally
1010
        // This way a customer will be able to apply the most recent changes any time later
1011
        $connectAttribute->setLastUpdate(json_encode([
1012
            'shortDescription' => $product->shortDescription,
1013
            'longDescription' => $product->longDescription,
1014
            'additionalDescription' => $product->additionalDescription,
1015
            'purchasePrice' => $product->purchasePrice,
1016
            'image' => $product->images,
1017
            'variantImages' => $product->variantImages,
1018
            'price' => $product->price * ($product->vat + 1),
1019
            'name' => $product->title,
1020
            'vat' => $product->vat
1021
        ]));
1022
    }
1023
1024
    /**
1025
     * @param ProductModel $model
1026
     * @param array $categories
1027
     */
1028
    private function categoryDenormalization(ProductModel $model, array $categories)
1029
    {
1030
        $this->categoryDenormalization->disableTransactions();
1031
        foreach ($categories as $category) {
1032
            $this->categoryDenormalization->addAssignment($model->getId(), $category);
1033
            $this->manager->getConnection()->executeQuery(
1034
                'INSERT IGNORE INTO `s_articles_categories` (`articleID`, `categoryID`) VALUES (?,?)',
1035
                [$model->getId(), $category]
1036
            );
1037
            $parentId =$this->manager->getConnection()->fetchColumn(
1038
                'SELECT parent FROM `s_categories` WHERE id = ?',
1039
                [$category]
1040
            );
1041
            $this->categoryDenormalization->removeAssignment($model->getId(), $parentId);
1042
            $this->manager->getConnection()->executeQuery(
1043
                'DELETE FROM `s_articles_categories` WHERE `articleID` = ? AND `categoryID` = ?',
1044
                [$model->getId(), $parentId]
1045
            );
1046
        }
1047
        $this->categoryDenormalization->enableTransactions();
1048
    }
1049
1050
    /**
1051
     * @param Product $product
1052
     * @return ProductStream
1053
     */
1054
    private function getOrCreateStream(Product $product)
1055
    {
1056
        /** @var ProductStreamRepository $repo */
1057
        $repo = $this->manager->getRepository(ProductStreamAttribute::class);
1058
        $stream = $repo->findConnectByName($product->stream);
1059
1060
        if (!$stream) {
1061
            $stream = new ProductStream();
1062
            $stream->setName($product->stream);
1063
            $stream->setType(ProductStreamService::STATIC_STREAM);
1064
            $stream->setSorting(json_encode(
1065
                [ReleaseDateSorting::class => ['direction' => 'desc']]
1066
            ));
1067
1068
            //add attributes
1069
            $attribute = new \Shopware\Models\Attribute\ProductStream();
1070
            $attribute->setProductStream($stream);
1071
            $attribute->setConnectIsRemote(true);
1072
            $stream->setAttribute($attribute);
1073
1074
            $this->manager->persist($attribute);
1075
            $this->manager->persist($stream);
1076
            $this->manager->flush();
1077
        }
1078
1079
        return $stream;
1080
    }
1081
1082
    /**
1083
     * @param ProductStream $stream
1084
     * @param ProductModel $article
1085
     * @throws \Doctrine\DBAL\DBALException
1086
     */
1087
    private function addProductToStream(ProductStream $stream, ProductModel $article)
1088
    {
1089
        $conn = $this->manager->getConnection();
1090
        $sql = 'INSERT INTO `s_product_streams_selection` (`stream_id`, `article_id`)
1091
                VALUES (:streamId, :articleId)
1092
                ON DUPLICATE KEY UPDATE stream_id = :streamId, article_id = :articleId';
1093
        $stmt = $conn->prepare($sql);
1094
        $stmt->execute([':streamId' => $stream->getId(), ':articleId' => $article->getId()]);
1095
    }
1096
1097
    /**
1098
     * Set detail purchase price with plain SQL
1099
     * Entity usage throws exception when error handlers are disabled
1100
     *
1101
     * @param ProductModel $article
1102
     * @param DetailModel $detail
1103
     * @param Product $product
1104
     * @throws \Doctrine\DBAL\DBALException
1105
     */
1106
    private function setPrice(ProductModel $article, DetailModel $detail, Product $product)
1107
    {
1108
        // set price via plain SQL because shopware throws exception
1109
        // undefined index: key when error handler is disabled
1110
        $customerGroup = $this->helper->getDefaultCustomerGroup();
1111
1112
        if (!empty($product->priceRanges)) {
1113
            $this->setPriceRange($article, $detail, $product->priceRanges, $customerGroup);
1114
1115
            return;
1116
        }
1117
1118
        $id = $this->manager->getConnection()->fetchColumn(
1119
            'SELECT id FROM `s_articles_prices`
1120
              WHERE `pricegroup` = ? AND `from` = ? AND `to` = ? AND `articleID` = ? AND `articledetailsID` = ?',
1121
            [$customerGroup->getKey(), 1, 'beliebig', $article->getId(), $detail->getId()]
1122
        );
1123
1124
        // todo@sb: test update prices
1125
        if ($id > 0) {
1126
            $this->manager->getConnection()->executeQuery(
1127
                'UPDATE `s_articles_prices` SET `price` = ?, `baseprice` = ? WHERE `id` = ?',
1128
                [$product->price, $product->purchasePrice, $id]
1129
            );
1130
        } else {
1131
            $this->manager->getConnection()->executeQuery(
1132
                'INSERT INTO `s_articles_prices`(`pricegroup`, `from`, `to`, `articleID`, `articledetailsID`, `price`, `baseprice`)
1133
              VALUES (?, 1, "beliebig", ?, ?, ?, ?);',
1134
                [
1135
                    $customerGroup->getKey(),
1136
                    $article->getId(),
1137
                    $detail->getId(),
1138
                    $product->price,
1139
                    $product->purchasePrice
1140
                ]
1141
            );
1142
        }
1143
    }
1144
1145
    /**
1146
     * @param ProductModel $article
1147
     * @param DetailModel $detail
1148
     * @param array $priceRanges
1149
     * @param Group $group
1150
     * @throws \Doctrine\DBAL\ConnectionException
1151
     * @throws \Exception
1152
     */
1153
    private function setPriceRange(ProductModel $article, DetailModel $detail, array $priceRanges, Group $group)
1154
    {
1155
        $this->manager->getConnection()->beginTransaction();
1156
1157
        try {
1158
            // We always delete the prices,
1159
            // because we can not know which record is update
1160
            $this->manager->getConnection()->executeQuery(
1161
                'DELETE FROM `s_articles_prices` WHERE `articleID` = ? AND `articledetailsID` = ?',
1162
                [$article->getId(), $detail->getId()]
1163
            );
1164
1165
            /** @var PriceRange $priceRange */
1166
            foreach ($priceRanges as $priceRange) {
1167
                $priceTo = $priceRange->to == PriceRange::ANY ? 'beliebig' : $priceRange->to;
1168
1169
                //todo: maybe batch insert if possible?
1170
                $this->manager->getConnection()->executeQuery(
1171
                    'INSERT INTO `s_articles_prices`(`pricegroup`, `from`, `to`, `articleID`, `articledetailsID`, `price`)
1172
                      VALUES (?, ?, ?, ?, ?, ?);',
1173
                    [
1174
                        $group->getKey(),
1175
                        $priceRange->from,
1176
                        $priceTo,
1177
                        $article->getId(),
1178
                        $detail->getId(),
1179
                        $priceRange->price
1180
                    ]
1181
                );
1182
            }
1183
            $this->manager->getConnection()->commit();
1184
        } catch (\Exception $e) {
1185
            $this->manager->getConnection()->rollBack();
1186
            throw new \Exception($e->getMessage());
1187
        }
1188
    }
1189
1190
    /**
1191
     * Set detail purchase price with plain SQL
1192
     * Entity usage throws exception when error handlers are disabled
1193
     *
1194
     * @param DetailModel $detail
1195
     * @param float $purchasePrice
1196
     * @param Group $defaultGroup
1197
     * @throws \Doctrine\DBAL\DBALException
1198
     */
1199
    private function setPurchasePrice(DetailModel $detail, $purchasePrice, Group $defaultGroup)
1200
    {
1201
        if (method_exists($detail, 'setPurchasePrice')) {
1202
            $this->manager->getConnection()->executeQuery(
1203
                'UPDATE `s_articles_details` SET `purchaseprice` = ? WHERE `id` = ?',
1204
                [$purchasePrice, $detail->getId()]
1205
            );
1206
        } else {
1207
            $id = $this->manager->getConnection()->fetchColumn(
1208
                'SELECT id FROM `s_articles_prices`
1209
              WHERE `pricegroup` = ? AND `from` = ? AND `to` = ? AND `articleID` = ? AND `articledetailsID` = ?',
1210
                [$defaultGroup->getKey(), 1, 'beliebig', $detail->getArticleId(), $detail->getId()]
1211
            );
1212
1213
            if ($id > 0) {
1214
                $this->manager->getConnection()->executeQuery(
1215
                    'UPDATE `s_articles_prices` SET `baseprice` = ? WHERE `id` = ?',
1216
                    [$purchasePrice, $id]
1217
                );
1218
            } else {
1219
                $this->manager->getConnection()->executeQuery(
1220
                    'INSERT INTO `s_articles_prices`(`pricegroup`, `from`, `to`, `articleID`, `articledetailsID`, `baseprice`)
1221
              VALUES (?, 1, "beliebig", ?, ?, ?);',
1222
                    [$defaultGroup->getKey(), $detail->getArticleId(), $detail->getId(), $purchasePrice]
1223
                );
1224
            }
1225
        }
1226
    }
1227
1228
    /**
1229
     * Adds translation record for given article
1230
     *
1231
     * @param ProductModel $article
1232
     * @param Product $sdkProduct
1233
     */
1234
    private function addArticleTranslations(ProductModel $article, Product $sdkProduct)
1235
    {
1236
        /** @var \Shopware\Connect\Struct\Translation $translation */
1237
        foreach ($sdkProduct->translations as $key => $translation) {
1238
            /** @var \Shopware\Models\Shop\Locale $locale */
1239
            $locale = $this->getLocaleRepository()->findOneBy(['locale' => LocaleMapper::getShopwareLocale($key)]);
1240
            /** @var \Shopware\Models\Shop\Shop $shop */
1241
            $shop = $this->getShopRepository()->findOneBy(['locale' => $locale]);
1242
            if (!$shop) {
1243
                continue;
1244
            }
1245
1246
            $this->productTranslationsGateway->addArticleTranslation($translation, $article->getId(), $shop->getId());
1247
        }
1248
    }
1249
1250
    /**
1251
     * dsadsa
1252
     * @return \Shopware\Components\Model\ModelRepository
1253
     */
1254
    private function getLocaleRepository()
1255
    {
1256
        if (!$this->localeRepository) {
1257
            $this->localeRepository = $this->manager->getRepository('Shopware\Models\Shop\Locale');
1258
        }
1259
1260
        return $this->localeRepository;
1261
    }
1262
1263
    private function getShopRepository()
1264
    {
1265
        if (!$this->shopRepository) {
1266
            $this->shopRepository = $this->manager->getRepository('Shopware\Models\Shop\Shop');
1267
        }
1268
1269
        return $this->shopRepository;
1270
    }
1271
1272
    /**
1273
     * Delete product or product variant with given shopId and sourceId.
1274
     *
1275
     * Only the combination of both identifies a product uniquely. Do NOT
1276
     * delete products just by their sourceId.
1277
     *
1278
     * You might receive delete requests for products, which are not available
1279
     * in your shop. Just ignore them.
1280
     *
1281
     * @param string $shopId
1282
     * @param string $sourceId
1283
     * @return void
1284
     */
1285
    public function delete($shopId, $sourceId)
1286
    {
1287
        $detail = $this->helper->getArticleDetailModelByProduct(new Product([
1288
            'shopId' => $shopId,
1289
            'sourceId' => $sourceId,
1290
        ]));
1291
        if ($detail === null) {
1292
            return;
1293
        }
1294
1295
        $this->deleteDetail($detail);
1296
    }
1297
1298
    public function update($shopId, $sourceId, ProductUpdate $product)
1299
    {
1300
        // find article detail id
1301
        $articleDetailId = $this->manager->getConnection()->fetchColumn(
1302
            'SELECT article_detail_id FROM s_plugin_connect_items WHERE source_id = ? AND shop_id = ?',
1303
            [$sourceId, $shopId]
1304
        );
1305
1306
        $this->eventManager->notify(
1307
            'Connect_Merchant_Update_GeneralProductInformation',
1308
            [
1309
                'subject' => $this,
1310
                'shopId' => $shopId,
1311
                'sourceId' => $sourceId,
1312
                'articleDetailId' => $articleDetailId
1313
            ]
1314
        );
1315
1316
        // update purchasePriceHash, offerValidUntil and purchasePrice in connect attribute
1317
        $this->manager->getConnection()->executeUpdate(
1318
            'UPDATE s_plugin_connect_items SET purchase_price_hash = ?, offer_valid_until = ?, purchase_price = ?
1319
            WHERE source_id = ? AND shop_id = ?',
1320
            [
1321
                $product->purchasePriceHash,
1322
                $product->offerValidUntil,
1323
                $product->purchasePrice,
1324
                $sourceId,
1325
                $shopId,
1326
            ]
1327
        );
1328
1329
        // update stock in article detail
1330
        // update prices
1331
        // if purchase price is stored in article detail
1332
        // update it together with stock
1333
        // since shopware 5.2
1334
        if (method_exists('Shopware\Models\Article\Detail', 'getPurchasePrice')) {
1335
            $this->manager->getConnection()->executeUpdate(
1336
                'UPDATE s_articles_details SET instock = ?, purchaseprice = ? WHERE id = ?',
1337
                [$product->availability, $product->purchasePrice, $articleDetailId]
1338
            );
1339
        } else {
1340
            $this->manager->getConnection()->executeUpdate(
1341
                'UPDATE s_articles_details SET instock = ? WHERE id = ?',
1342
                [$product->availability, $articleDetailId]
1343
            );
1344
        }
1345
        $this->manager->getConnection()->executeUpdate(
1346
            "UPDATE s_articles_prices SET price = ?, baseprice = ? WHERE articledetailsID = ? AND pricegroup = 'EK'",
1347
            [$product->price, $product->purchasePrice, $articleDetailId]
1348
        );
1349
    }
1350
1351
    public function changeAvailability($shopId, $sourceId, $availability)
1352
    {
1353
        // find article detail id
1354
        $articleDetailId = $this->manager->getConnection()->fetchColumn(
1355
            'SELECT article_detail_id FROM s_plugin_connect_items WHERE source_id = ? AND shop_id = ?',
1356
            [$sourceId, $shopId]
1357
        );
1358
1359
        $this->eventManager->notify(
1360
            'Connect_Merchant_Update_GeneralProductInformation',
1361
            [
1362
                'subject' => $this,
1363
                'shopId' => $shopId,
1364
                'sourceId' => $sourceId,
1365
                'articleDetailId' => $articleDetailId
1366
            ]
1367
        );
1368
1369
        // update stock in article detail
1370
        $this->manager->getConnection()->executeUpdate(
1371
            'UPDATE s_articles_details SET instock = ? WHERE id = ?',
1372
            [$availability, $articleDetailId]
1373
        );
1374
    }
1375
1376
    /**
1377
     * @inheritDoc
1378
     */
1379
    public function makeMainVariant($shopId, $sourceId, $groupId)
1380
    {
1381
        //find article detail which should be selected as main one
1382
        $newMainDetail = $this->helper->getConnectArticleDetailModel($sourceId, $shopId);
1383
        if (!$newMainDetail) {
1384
            return;
1385
        }
1386
1387
        /** @var \Shopware\Models\Article\Article $article */
1388
        $article = $newMainDetail->getArticle();
1389
1390
        $this->eventManager->notify(
1391
            'Connect_Merchant_Update_ProductMainVariant_Before',
1392
            [
1393
                'subject' => $this,
1394
                'shopId' => $shopId,
1395
                'sourceId' => $sourceId,
1396
                'articleId' => $article->getId(),
1397
                'articleDetailId' => $newMainDetail->getId()
1398
            ]
1399
        );
1400
1401
        // replace current main detail with new one
1402
        $currentMainDetail = $article->getMainDetail();
1403
        $currentMainDetail->setKind(2);
1404
        $newMainDetail->setKind(1);
1405
        $article->setMainDetail($newMainDetail);
1406
1407
        $this->manager->persist($newMainDetail);
1408
        $this->manager->persist($currentMainDetail);
1409
        $this->manager->persist($article);
1410
        $this->manager->flush();
1411
    }
1412
1413
    /**
1414
     * Updates the status of an Order
1415
     *
1416
     * @param string $localOrderId
1417
     * @param string $orderStatus
1418
     * @param string $trackingNumber
1419
     * @return void
1420
     */
1421
    public function updateOrderStatus($localOrderId, $orderStatus, $trackingNumber)
1422
    {
1423
        if ($this->config->getConfig('updateOrderStatus') == 1) {
1424
            $this->updateDeliveryStatus($localOrderId, $orderStatus);
1425
        }
1426
1427
        if ($trackingNumber) {
1428
            $this->updateTrackingNumber($localOrderId, $trackingNumber);
1429
        }
1430
    }
1431
1432
    /**
1433
     * @param string $localOrderId
1434
     * @param string $orderStatus
1435
     */
1436
    private function updateDeliveryStatus($localOrderId, $orderStatus)
1437
    {
1438
        $status = false;
1439
        if ($orderStatus === OrderStatus::STATE_IN_PROCESS) {
1440
            $status = Status::ORDER_STATE_PARTIALLY_DELIVERED;
1441
        } elseif ($orderStatus === OrderStatus::STATE_DELIVERED) {
1442
            $status = Status::ORDER_STATE_COMPLETELY_DELIVERED;
1443
        }
1444
1445
        if ($status) {
1446
            $this->manager->getConnection()->executeQuery(
1447
                'UPDATE s_order 
1448
                SET status = :orderStatus
1449
                WHERE ordernumber = :orderNumber',
1450
                [
1451
                    ':orderStatus' => $status,
1452
                    ':orderNumber' => $localOrderId
1453
                ]
1454
            );
1455
        }
1456
    }
1457
1458
    /**
1459
     * @param string $localOrderId
1460
     * @param string $trackingNumber
1461
     */
1462
    private function updateTrackingNumber($localOrderId, $trackingNumber)
1463
    {
1464
        $currentTrackingCode = $this->manager->getConnection()->fetchColumn(
1465
            'SELECT trackingcode
1466
            FROM s_order
1467
            WHERE ordernumber = :orderNumber',
1468
            [
1469
                ':orderNumber' => $localOrderId
1470
            ]
1471
        );
1472
1473
        if (!$currentTrackingCode) {
1474
            $newTracking = $trackingNumber;
1475
        } else {
1476
            $newTracking = $this->combineTrackingNumbers($trackingNumber, $currentTrackingCode);
1477
        }
1478
1479
        $this->manager->getConnection()->executeQuery(
1480
            'UPDATE s_order 
1481
            SET trackingcode = :trackingCode
1482
            WHERE ordernumber = :orderNumber',
1483
            [
1484
                ':trackingCode' => $newTracking,
1485
                ':orderNumber' => $localOrderId
1486
            ]
1487
        );
1488
    }
1489
1490
    /**
1491
     * @param string $newTrackingCode
1492
     * @param string $currentTrackingCode
1493
     * @return string
1494
     */
1495
    private function combineTrackingNumbers($newTrackingCode, $currentTrackingCode)
1496
    {
1497
        $currentTrackingCodes = $this->getTrackingNumberAsArray($currentTrackingCode);
1498
        $newTrackingCodes = $this->getTrackingNumberAsArray($newTrackingCode);
1499
        $newTrackingCodes = array_unique(array_merge($currentTrackingCodes, $newTrackingCodes));
1500
        $newTracking = implode(',', $newTrackingCodes);
1501
1502
        return $newTracking;
1503
    }
1504
1505
    /**
1506
     * @param string $trackingCode
1507
     * @return string[]
1508
     */
1509
    private function getTrackingNumberAsArray($trackingCode)
1510
    {
1511
        if (strpos($trackingCode, ',') !== false) {
1512
            return explode(',', $trackingCode);
1513
        }
1514
1515
        return [$trackingCode];
1516
    }
1517
1518
    /**
1519
     * @param Product $product
1520
     * @param ProductModel $model
1521
     */
1522
    private function saveVat(Product $product, ProductModel $model)
1523
    {
1524
        if ($product->vat !== null) {
1525
            $repo = $this->manager->getRepository(Tax::class);
1526
            $taxRate = round($product->vat * 100, 2);
1527
            /** @var \Shopware\Models\Tax\Tax $tax */
1528
            $tax = $repo->findOneBy(['tax' => $taxRate]);
1529
            if (!$tax) {
1530
                $tax = new Tax();
1531
                $tax->setTax($taxRate);
1532
                //this is to get rid of zeroes behind the decimal point
1533
                $name = strval(round($taxRate, 2)) . '%';
1534
                $tax->setName($name);
1535
                $this->manager->persist($tax);
1536
            }
1537
            $model->setTax($tax);
1538
        }
1539
    }
1540
1541
    /**
1542
     * @param int $articleId
1543
     * @param Product $product
1544
     */
1545
    private function applyCrossSelling($articleId, Product $product)
1546
    {
1547
        $this->deleteRemovedRelations($articleId, $product);
1548
        $this->storeCrossSellingInformationInverseSide($articleId, $product->sourceId, $product->shopId);
1549
        if ($product->similar || $product->related) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $product->similar 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...
Bug Best Practice introduced by
The expression $product->related 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...
1550
            $this->storeCrossSellingInformationOwningSide($articleId, $product);
1551
        }
1552
    }
1553
1554
    /**
1555
     * @param int $articleId
1556
     * @param Product $product
1557
     */
1558
    private function storeCrossSellingInformationOwningSide($articleId, $product)
1559
    {
1560
        foreach ($product->related as $relatedId) {
1561
            $this->insertNewRelations($articleId, $product->shopId, $relatedId, self::RELATION_TYPE_RELATED);
1562
        }
1563
1564
        foreach ($product->similar as $similarId) {
1565
            $this->insertNewRelations($articleId, $product->shopId, $similarId, self::RELATION_TYPE_SIMILAR);
1566
        }
1567
    }
1568
1569
    /**
1570
     * @param int $articleId
1571
     * @param int $shopId
1572
     * @param int $relatedId
1573
     * @param string $relationType
1574
     */
1575
    private function insertNewRelations($articleId, $shopId, $relatedId, $relationType)
1576
    {
1577
        $inserted = false;
1578
        try {
1579
            $this->manager->getConnection()->executeQuery(
1580
                'INSERT INTO s_plugin_connect_article_relations (article_id, shop_id, related_article_local_id, relationship_type) VALUES (?, ?, ?, ?)',
1581
                [$articleId, $shopId, $relatedId, $relationType]
1582
            );
1583
            $inserted = true;
1584
        } catch (\Doctrine\DBAL\DBALException $e) {
0 ignored issues
show
Bug introduced by
The class Doctrine\DBAL\DBALException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
1585
            // No problems here. Just means that the row already existed.
1586
        }
1587
1588
        //outside of try catch because we don't want to catch exceptions -> this method should not throw any
1589
        if ($inserted) {
1590
            $relatedLocalId = $this->manager->getConnection()->fetchColumn(
1591
                'SELECT article_id FROM s_plugin_connect_items WHERE shop_id = ? AND source_id = ?',
1592
                [$shopId, $relatedId]
1593
            );
1594
            if ($relatedLocalId) {
1595
                $this->manager->getConnection()->executeQuery(
1596
                    "INSERT IGNORE INTO s_articles_$relationType (articleID, relatedarticle) VALUES (?, ?)",
1597
                    [$articleId, $relatedLocalId]
1598
                );
1599
            }
1600
        }
1601
    }
1602
1603
    /**
1604
     * @param int $articleId
1605
     * @param string $sourceId
1606
     * @param int $shopId
1607
     */
1608
    private function storeCrossSellingInformationInverseSide($articleId, $sourceId, $shopId)
1609
    {
1610
        $relatedArticles = $this->manager->getConnection()->fetchAll(
1611
            'SELECT article_id, relationship_type FROM s_plugin_connect_article_relations WHERE shop_id = ? AND related_article_local_id = ?',
1612
            [$shopId, $sourceId]
1613
        );
1614
1615
        foreach ($relatedArticles as $relatedArticle) {
1616
            $relationType = $relatedArticle['relationship_type'];
1617
            $this->manager->getConnection()->executeQuery(
1618
                "INSERT IGNORE INTO s_articles_$relationType (articleID, relatedarticle) VALUES (?, ?)",
1619
                [$relatedArticle['article_id'], $articleId]
1620
            );
1621
        }
1622
    }
1623
1624
    /**
1625
     * @param $articleId
1626
     * @param $product
1627
     */
1628
    private function deleteRemovedRelations($articleId, $product)
1629
    {
1630 View Code Duplication
        if (count($product->related) > 0) {
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...
1631
            $this->manager->getConnection()->executeQuery(
1632
                'DELETE FROM s_plugin_connect_article_relations WHERE article_id = ? AND shop_id = ? AND related_article_local_id NOT IN (?) AND relationship_type = ?',
1633
                [$articleId, $product->shopId, $product->related, self::RELATION_TYPE_RELATED],
1634
                [\PDO::PARAM_INT, \PDO::PARAM_INT, \Doctrine\DBAL\Connection::PARAM_INT_ARRAY, \PDO::PARAM_STR]
1635
            );
1636
1637
            $oldRelatedIds = $this->manager->getConnection()->executeQuery(
1638
                'SELECT ar.id 
1639
                FROM s_articles_relationships AS ar
1640
                INNER JOIN s_plugin_connect_items AS ci ON ar.relatedarticle = ci.article_id
1641
                WHERE ar.articleID = ? AND ci.shop_id = ? AND ci.source_id NOT IN (?)',
1642
                [$articleId, $product->shopId, $product->related],
1643
                [\PDO::PARAM_INT, \PDO::PARAM_INT, \Doctrine\DBAL\Connection::PARAM_INT_ARRAY]
1644
            )
1645
                ->fetchAll(\PDO::FETCH_COLUMN);
1646
        } else {
1647
            $this->manager->getConnection()->executeQuery(
1648
                'DELETE FROM s_plugin_connect_article_relations WHERE article_id = ? AND shop_id = ? AND relationship_type = ?',
1649
                [$articleId, $product->shopId, self::RELATION_TYPE_RELATED]
1650
            );
1651
1652
            $oldRelatedIds = $this->manager->getConnection()->executeQuery(
1653
                'SELECT ar.id 
1654
                FROM s_articles_relationships AS ar
1655
                INNER JOIN s_plugin_connect_items AS ci ON ar.relatedarticle = ci.article_id
1656
                WHERE ar.articleID = ? AND ci.shop_id = ?',
1657
                [$articleId, $product->shopId]
1658
            )
1659
                ->fetchAll(\PDO::FETCH_COLUMN);
1660
        }
1661
1662
        $this->manager->getConnection()->executeQuery(
1663
            'DELETE FROM s_articles_relationships WHERE id IN (?)',
1664
            [$oldRelatedIds],
1665
            [\Doctrine\DBAL\Connection::PARAM_INT_ARRAY]
1666
        );
1667
1668 View Code Duplication
        if (count($product->similar) > 0) {
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...
1669
            $this->manager->getConnection()->executeQuery(
1670
                'DELETE FROM s_plugin_connect_article_relations WHERE article_id = ? AND shop_id = ? AND related_article_local_id NOT IN (?) AND relationship_type = ?',
1671
                [$articleId, $product->shopId, $product->similar, self::RELATION_TYPE_SIMILAR],
1672
                [\PDO::PARAM_INT, \PDO::PARAM_INT, \Doctrine\DBAL\Connection::PARAM_INT_ARRAY, \PDO::PARAM_STR]
1673
            );
1674
1675
            $oldSimilarIds = $this->manager->getConnection()->executeQuery(
1676
                'SELECT ar.id 
1677
                FROM s_articles_similar AS ar
1678
                INNER JOIN s_plugin_connect_items AS ci ON ar.relatedarticle = ci.article_id
1679
                WHERE ar.articleID = ? AND ci.shop_id = ? AND ci.source_id NOT IN (?)',
1680
                [$articleId, $product->shopId, $product->similar],
1681
                [\PDO::PARAM_INT, \PDO::PARAM_INT, \Doctrine\DBAL\Connection::PARAM_INT_ARRAY]
1682
            )
1683
                ->fetchAll(\PDO::FETCH_COLUMN);
1684
        } else {
1685
            $this->manager->getConnection()->executeQuery(
1686
                'DELETE FROM s_plugin_connect_article_relations WHERE article_id = ? AND shop_id = ? AND relationship_type = ?',
1687
                [$articleId, $product->shopId, self::RELATION_TYPE_SIMILAR]
1688
            );
1689
1690
            $oldSimilarIds = $this->manager->getConnection()->executeQuery(
1691
                'SELECT ar.id 
1692
                FROM s_articles_similar AS ar
1693
                INNER JOIN s_plugin_connect_items AS ci ON ar.relatedarticle = ci.article_id
1694
                WHERE ar.articleID = ? AND ci.shop_id = ?',
1695
                [$articleId, $product->shopId]
1696
            )
1697
                ->fetchAll(\PDO::FETCH_COLUMN);
1698
        }
1699
1700
        $this->manager->getConnection()->executeQuery(
1701
            'DELETE FROM s_articles_similar WHERE id IN (?)',
1702
            [$oldSimilarIds],
1703
            [\Doctrine\DBAL\Connection::PARAM_INT_ARRAY]
1704
        );
1705
    }
1706
1707
    /**
1708
     * @param Property $property
1709
     * @return PropertyGroup
1710
     */
1711
    private function createPropertyGroup(Property $property)
1712
    {
1713
        $group = new PropertyGroup();
1714
        $group->setName($property->groupName);
1715
        $group->setComparable($property->comparable);
1716
        $group->setSortMode($property->sortMode);
1717
        $group->setPosition($property->groupPosition);
1718
1719
        $attribute = new \Shopware\Models\Attribute\PropertyGroup();
1720
        $attribute->setPropertyGroup($group);
1721
        $attribute->setConnectIsRemote(true);
1722
        $group->setAttribute($attribute);
1723
1724
        $this->manager->persist($attribute);
1725
        $this->manager->persist($group);
1726
        $this->manager->flush();
1727
1728
        return $group;
1729
    }
1730
}
1731