Completed
Push — master ( 3474fd...81362a )
by Daniel
13s
created

ConnectExport::updateLocalConnectItem()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 36
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 36
rs 8.439
c 0
b 0
f 0
cc 6
eloc 25
nc 4
nop 2
1
<?php
2
3
namespace ShopwarePlugins\Connect\Components;
4
5
use Doctrine\DBAL\DBALException;
6
use Shopware\Components\ContainerAwareEventManager;
7
use Shopware\Connect\SDK;
8
use Shopware\CustomModels\Connect\Attribute;
9
use ShopwarePlugins\Connect\Components\Marketplace\MarketplaceGateway;
10
use ShopwarePlugins\Connect\Components\Validator\ProductAttributesValidator;
11
use Shopware\Components\Model\ModelManager;
12
use Shopware\Models\Article\Article;
13
use Shopware\Models\Article\Detail;
14
use ShopwarePlugins\Connect\Components\ProductStream\ProductStreamsAssignments;
15
use ShopwarePlugins\Connect\Components\ErrorHandler;
16
use ShopwarePlugins\Connect\Struct\ExportList;
17
use ShopwarePlugins\Connect\Struct\SearchCriteria;
18
use Enlight_Event_EventManager;
19
20
class ConnectExport
21
{
22
    const BATCH_SIZE = 200;
23
24
    /** @var
25
     * Helper
26
     */
27
    protected $helper;
28
29
    /** @var
30
     * SDK
31
     */
32
    protected $sdk;
33
34
    /** @var
35
     * ModelManager
36
     */
37
    protected $manager;
38
39
    /**
40
     * @var ProductAttributesValidator
41
     */
42
    protected $productAttributesValidator;
43
44
    /** @var
45
     * MarketplaceGateway
46
     */
47
    protected $marketplaceGateway;
48
49
    /**
50
     * @var ErrorHandler
51
     */
52
    protected $errorHandler;
53
54
    /**
55
     * @var Config
56
     */
57
    protected $configComponent;
58
59
    /**
60
     * @var Enlight_Event_EventManager
61
     */
62
    private $eventManager;
63
64
    /**
65
     * ConnectExport constructor.
66
     * @param Helper $helper
67
     * @param SDK $sdk
68
     * @param ModelManager $manager
69
     * @param ProductAttributesValidator $productAttributesValidator
70
     * @param Config $configComponent
71
     * @param \ShopwarePlugins\Connect\Components\ErrorHandler $errorHandler
72
     * @param Enlight_Event_EventManager $eventManager
73
     */
74
    public function __construct(
75
        Helper $helper,
76
        SDK $sdk,
77
        ModelManager $manager,
78
        ProductAttributesValidator $productAttributesValidator,
79
        Config $configComponent,
80
        ErrorHandler $errorHandler,
81
        Enlight_Event_EventManager $eventManager
82
    )
83
    {
84
        $this->helper = $helper;
85
        $this->sdk = $sdk;
86
        $this->manager = $manager;
87
        $this->productAttributesValidator = $productAttributesValidator;
88
        $this->configComponent = $configComponent;
89
        $this->errorHandler = $errorHandler;
90
        $this->eventManager = $eventManager;
91
    }
92
93
    /**
94
     * Load article entity
95
     *
96
     * @param $id
97
     * @return null|\Shopware\Models\Article\Article
98
     */
99
    public function getArticleModelById($id)
100
    {
101
        return $this->manager->getRepository('Shopware\Models\Article\Article')->find($id);
102
    }
103
104
    /**
105
     * Load article detail entity
106
     *
107
     * @param $id
108
     * @return null|\Shopware\Models\Article\Detail
109
     */
110
    public function getArticleDetailById($id)
111
    {
112
        return $this->manager->getRepository('Shopware\Models\Article\Detail')->find($id);
113
    }
114
115
    /**
116
     * Helper function to mark a given array of source ids for connect update
117
     *
118
     * There is a problem with flush when is called from life cycle event in php7,
119
     * this flag '$isEvent' is preventing the flush
120
     *
121
     * @param array $ids
122
     * @param ProductStreamsAssignments|null $streamsAssignments
123
     * @return array
124
     */
125
    public function export(array $ids, ProductStreamsAssignments $streamsAssignments = null)
126
    {
127
        $ids = $this->eventManager->filter(
128
            'Connect_Supplier_Get_Products_Filter_Source_IDS',
129
            $ids,
130
            [
131
                'subject' => $this
132
            ]
133
        );
134
135
        $connectItems = $this->fetchConnectItems($ids);
136
137
        $this->eventManager->notify(
138
            'Connect_Supplier_Get_All_Products_Before',
139
            [
140
                'subject' => $this,
141
                'products' => $connectItems
142
            ]
143
        );
144
145
        $this->manager->beginTransaction();
146
        $excludeInactiveProducts = $this->configComponent->getConfig('excludeInactiveProducts');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $excludeInactiveProducts is correct as $this->configComponent->...cludeInactiveProducts') (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...
147
148
        foreach ($connectItems as &$item) {
149
            $model = $this->getArticleDetailById($item['articleDetailId']);
150
            if($model === null) {
151
                continue;
152
            }
153
154
            $connectAttribute = $this->helper->getOrCreateConnectAttributeByModel($model);
155
156
            if ($excludeInactiveProducts && !$model->getActive()) {
157
                $this->updateLocalConnectItem(
158
                    $connectAttribute->getSourceId(),
159
                    array(
160
                        'export_status' => Attribute::STATUS_INACTIVE,
161
                        'exported' => false,
162
                        'export_message' =>  Shopware()->Snippets()->getNamespace('backend/connect/view/main')->get(
163
                            'export/message/error_product_is_not_active',
164
                            'Produkt ist inaktiv',
165
                            true
166
                        ),
167
                    )
168
                );
169
                $this->manager->refresh($connectAttribute);
170
                continue;
171
            }
172
173
            if (!$this->helper->isProductExported($connectAttribute)) {
174
                $status = Attribute::STATUS_INSERT;
175
            } else {
176
                $status = Attribute::STATUS_UPDATE;
177
            }
178
179
            $categories = $this->helper->getConnectCategoryForProduct($item['articleId']);
180
            if (is_string($categories)) {
181
                $categories = array($categories);
182
            }
183
            $categories = json_encode($categories);
184
185
            $this->updateLocalConnectItem(
186
                $connectAttribute->getSourceId(),
187
                array(
188
                    'export_status' => $status,
189
                    'export_message' => null,
190
                    'exported' => true,
191
                    'category' => $categories,
192
                )
193
            );
194
195
            try {
196
                $this->productAttributesValidator->validate($this->extractProductAttributes($model));
197
                if ($status == Attribute::STATUS_INSERT) {
198
                    $this->sdk->recordInsert($item['sourceId']);
199
                } else {
200
                    $this->sdk->recordUpdate($item['sourceId']);
201
                }
202
203
                if ($this->helper->isMainVariant($item['sourceId']) &&
204
                    $streamsAssignments !== null &&
205
                    $streamsAssignments->getStreamsByArticleId($item['articleId']) !== null
206
                ) {
207
                    $this->sdk->recordStreamAssignment(
208
                        $item['sourceId'],
209
                        $streamsAssignments->getStreamsByArticleId($item['articleId']),
210
                        $item['groupId']
211
                    );
212
                }
213
            } catch (\Exception $e) {
214
                if ($this->errorHandler->isPriceError($e)) {
215
                    $this->updateLocalConnectItem(
216
                        $connectAttribute->getSourceId(),
217
                        array(
218
                            'export_status' => Attribute::STATUS_ERROR_PRICE,
219
                            'export_message' => Shopware()->Snippets()->getNamespace('backend/connect/view/main')->get(
220
                                'export/message/error_price_status',
221
                                'There is an empty price field',
222
                                true
223
                            ),
224
                        )
225
                    );
226
                } else {
227
                    $this->updateLocalConnectItem(
228
                        $connectAttribute->getSourceId(),
229
                        array(
230
                            'export_status' => Attribute::STATUS_ERROR,
231
                            'export_message' => $e->getMessage() . "\n" . $e->getTraceAsString(),
232
                        )
233
                    );
234
                }
235
236
                $this->errorHandler->handle($e);
237
            }
238
            $this->manager->refresh($connectAttribute);
239
        }
240
241
        try {
242
            $this->manager->commit();
243
        } catch (\Exception $e) {
244
            $this->manager->rollback();
245
            $this->errorHandler->handle($e);
246
        }
247
248
        return $this->errorHandler->getMessages();
249
    }
250
251
    /**
252
     * Update connect attribute data
253
     *
254
     * @param string $sourceId
255
     * @param array $params
256
     */
257
    private function updateLocalConnectItem($sourceId, $params = array())
258
    {
259
        if (empty($params)) {
260
            return;
261
        }
262
        $possibleValues = array(
263
            Attribute::STATUS_DELETE,
264
            Attribute::STATUS_INSERT,
265
            Attribute::STATUS_UPDATE,
266
            Attribute::STATUS_ERROR,
267
            Attribute::STATUS_ERROR_PRICE,
268
            Attribute::STATUS_INACTIVE,
269
            Attribute::STATUS_SYNCED,
270
            null,
271
        );
272
273
        if (isset($params['export_status']) && !in_array($params['export_status'], $possibleValues)) {
274
            throw new \InvalidArgumentException('Invalid export status');
275
        }
276
277
        if (isset($params['exported']) && !is_bool($params['exported'])) {
278
            throw new \InvalidArgumentException('Parameter $exported must be boolean.');
279
        }
280
281
        $builder = $this->manager->getConnection()->createQueryBuilder();
282
        $builder->update('s_plugin_connect_items', 'ci');
283
        array_walk($params, function($param, $name) use ($builder) {
284
            $builder->set('ci.' . $name, ':' . $name)
285
                    ->setParameter($name, $param);
286
        });
287
288
        $builder->where('source_id = :sourceId')
289
                ->setParameter('sourceId', $sourceId)
290
                ->andWhere('shop_id IS NULL')
291
                ->execute();
292
    }
293
294
    /**
295
     * Fetch connect items
296
     * Default order is main variant first, after that regular variants.
297
     * This is needed, because first received variant with an unknown groupId in Connect
298
     * will be selected as main variant.
299
     *
300
     * @param array $sourceIds
301
     * @param boolean $orderByMainVariants
302
     * @return array
303
     */
304
    public function fetchConnectItems(array $sourceIds, $orderByMainVariants = true)
305
    {
306
        if (count($sourceIds) == 0) {
307
            return array();
308
        }
309
310
        $implodedIds = '"' . implode('","', $sourceIds) . '"';
311
        $query = "SELECT bi.article_id as articleId,
312
                    bi.article_detail_id as articleDetailId,
313
                    bi.export_status as exportStatus,
314
                    bi.export_message as exportMessage,
315
                    bi.source_id as sourceId,
316
                    bi.exported,
317
                    a.name as title,
318
                    IF (a.configurator_set_id IS NOT NULL, a.id, NULL) as groupId,
319
                    d.ordernumber as number
320
            FROM s_plugin_connect_items bi
321
            LEFT JOIN s_articles a ON bi.article_id = a.id
322
            LEFT JOIN s_articles_details d ON bi.article_detail_id = d.id
323
            WHERE bi.source_id IN ($implodedIds)";
324
325
        if ($orderByMainVariants === false) {
326
            $query .= ';';
327
            return Shopware()->Db()->fetchAll($query);
328
        }
329
330
        $query .= 'AND d.kind = ?;';
331
        $mainVariants = Shopware()->Db()->fetchAll($query, array(1));
332
        $regularVariants = Shopware()->Db()->fetchAll($query, array(2));
333
334
        return array_merge($mainVariants, $regularVariants);
335
    }
336
337
    /**
338
     * Helper function to return export product ids
339
     * @return array
340
     */
341
    public function getExportArticlesIds()
342
    {
343
        $builder = $this->manager->createQueryBuilder();
344
        $builder->from('Shopware\CustomModels\Connect\Attribute', 'at');
345
        $builder->join('at.article', 'a');
346
        $builder->join('a.mainDetail', 'd');
347
        $builder->leftJoin('d.prices', 'p', 'with', "p.from = 1 AND p.customerGroupKey = 'EK'");
348
        $builder->leftJoin('a.supplier', 's');
349
        $builder->leftJoin('a.tax', 't');
350
351
        $builder->select(array('a.id'));
352
353
        $builder->where("at.exportStatus = 'update' OR at.exportStatus = 'insert' OR at.exportStatus = 'error'");
354
        $builder->andWhere('at.shopId IS NULL');
355
356
        $query = $builder->getQuery();
357
        $articles = $query->getArrayResult();
358
359
        $ids = array();
360
        foreach ($articles as $article) {
361
            $ids[] = $article['id'];
362
        }
363
364
        return $ids;
365
    }
366
367
    /**
368
     * Helper function to count how many changes
369
     * are waiting to be synchronized
370
     *
371
     * @return int
372
     */
373
    public function getChangesCount()
374
    {
375
        $sql = 'SELECT COUNT(*) FROM `sw_connect_change`';
376
377
        return (int)Shopware()->Db()->fetchOne($sql);
378
    }
379
380
    /**
381
     * Mark single connect product detail for delete
382
     *
383
     * @param \Shopware\Models\Article\Detail $detail
384
     */
385
    public function syncDeleteDetail(Detail $detail)
386
    {
387
        $attribute = $this->helper->getConnectAttributeByModel($detail);
388
        // force fetching ConnectAttribute from DB
389
        // if it was update via query builder
390
        // changes are not visible, because of doctrine proxy cache
391
        $this->manager->refresh($attribute);
392
393
        if (!$this->helper->isProductExported($attribute)) {
394
            return;
395
        }
396
        $this->sdk->recordDelete($attribute->getSourceId());
397
        $attribute->setExportStatus(Attribute::STATUS_DELETE);
398
        $attribute->setExported(false);
399
        $this->manager->persist($attribute);
400
        $this->manager->flush($attribute);
401
    }
402
403
    /**
404
     * Mark all product variants for delete
405
     *
406
     * @param Article $article
407
     */
408
    public function setDeleteStatusForVariants(Article $article)
409
    {
410
        $builder = $this->manager->createQueryBuilder();
411
        $builder->select(array('at.sourceId'))
412
            ->from('Shopware\CustomModels\Connect\Attribute', 'at')
413
            ->where('at.articleId = :articleId')
414
            ->andWhere('at.exported = 1')
415
            ->setParameter(':articleId', $article->getId());
416
        $connectItems = $builder->getQuery()->getArrayResult();
417
418
        foreach($connectItems as $item) {
419
            $this->sdk->recordDelete($item['sourceId']);
420
        }
421
422
        $builder = $this->manager->createQueryBuilder();
423
        $builder->update('Shopware\CustomModels\Connect\Attribute', 'at')
424
            ->set('at.exportStatus', $builder->expr()->literal(Attribute::STATUS_DELETE))
425
            ->set('at.exported', 0)
426
            ->where('at.articleId = :articleId')
427
            ->setParameter(':articleId', $article->getId());
428
429
        $builder->getQuery()->execute();
430
    }
431
432
    /**
433
     * @param array $sourceIds
434
     * @param $status
435
     */
436
    public function updateConnectItemsStatus(array $sourceIds, $status)
437
    {
438
        if (empty($sourceIds)) {
439
            return;
440
        }
441
442
        $chunks = array_chunk($sourceIds, self::BATCH_SIZE);
443
444
        $exported = false;
445
        if ($status == Attribute::STATUS_DELETE) {
446
            $exported = true;
447
        }
448
449
        foreach ($chunks as $chunk) {
450
            $builder = $this->manager->getConnection()->createQueryBuilder();
451
            $builder->update('s_plugin_connect_items', 'ci')
452
                ->set('ci.export_status', ':status')
453
                ->set('ci.exported', ':exported')
454
                ->where('source_id IN (:sourceIds)')
455
                ->setParameter('sourceIds', $chunk, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY)
456
                ->setParameter('status', $status)
457
                ->setParameter('exported', $exported)
458
                ->execute();
459
        }
460
    }
461
462
    /**
463
     * @param SearchCriteria $criteria
464
     * @return ExportList
465
     */
466
    public function getExportList(SearchCriteria $criteria)
467
    {
468
        $customProductsTableExists = false;
469
        try {
470
            $builder = $this->manager->getConnection()->createQueryBuilder();
471
            $builder->select('id');
472
            $builder->from('s_plugin_custom_products_template');
473
            $builder->setMaxResults(1);
474
            $builder->execute()->fetch();
475
476
            $customProductsTableExists = true;
477
        } catch (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...
478
            // ignore it
479
            // custom products is not installed
480
        }
481
482
        $builder = $this->manager->getConnection()->createQueryBuilder();
483
        $builder->select(array(
484
            'a.id',
485
            'd.ordernumber as number',
486
            'd.inStock as inStock',
487
            'a.name as name',
488
            's.name as supplier',
489
            'a.active as active',
490
            't.tax as tax',
491
            'p.price * (100 + t.tax) / 100 as price',
492
            'i.category',
493
            'i.export_status as exportStatus',
494
            'i.export_message as exportMessage',
495
            'i.cron_update as cronUpdate'
496
        ))
497
            ->from('s_plugin_connect_items', 'i')
498
            ->innerJoin('i', 's_articles', 'a', 'a.id = i.article_id')
499
            ->innerJoin('a', 's_articles_details', 'd', 'a.main_detail_id = d.id')
500
            ->leftJoin('d', 's_articles_prices', 'p', 'd.id = p.articledetailsID')
501
            ->leftJoin('a', 's_core_tax', 't', 'a.taxID = t.id')
502
            ->leftJoin('a', 's_articles_supplier', 's', 'a.supplierID = s.id')
503
            ->groupBy('i.article_id')
504
            ->where('i.shop_id IS NULL');
505
506
        if ($customProductsTableExists) {
507
            $builder->addSelect("IF(spcptpr.template_id > 0, 1, 0) as customProduct")
508
                    ->leftJoin('a', 's_plugin_custom_products_template_product_relation', 'spcptpr', 'a.id = spcptpr.article_id');
509
        }
510
511
        if ($criteria->search) {
512
            $builder->andWhere('d.ordernumber LIKE :search OR a.name LIKE :search OR s.name LIKE :search')
513
                ->setParameter('search', $criteria->search);
514
        }
515
516
        if ($criteria->categoryId) {
517
518
            // Get all children categories
519
            $qBuilder = $this->manager->getConnection()->createQueryBuilder();
520
            $qBuilder->select('c.id');
521
            $qBuilder->from('s_categories', 'c');
522
            $qBuilder->where('c.path LIKE :categoryIdSearch');
523
            $qBuilder->orWhere('c.id = :categoryId');
524
            $qBuilder->setParameter(':categoryId', $criteria->categoryId);
525
            $qBuilder->setParameter(':categoryIdSearch', "%|$criteria->categoryId|%");
526
527
            $categoryIds = $qBuilder->execute()->fetchAll(\PDO::FETCH_COLUMN);
528
529
            if (count($categoryIds) === 0) {
530
                $categoryIds = array($criteria->categoryId);
531
            }
532
533
            $builder->innerJoin('a', 's_articles_categories', 'sac', 'a.id = sac.articleID')
534
                ->andWhere('sac.categoryID IN (:categoryIds)')
535
                ->setParameter('categoryIds', $categoryIds, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY);
536
        }
537
538
        if ($criteria->supplierId) {
539
            $builder->andWhere('a.supplierID = :supplierId')
540
                ->setParameter('supplierId', $criteria->supplierId);
541
        }
542
543
        if ($criteria->exportStatus) {
544
            $errorStatuses = [Attribute::STATUS_ERROR, Attribute::STATUS_ERROR_PRICE];
545
546
            if (in_array($criteria->exportStatus, $errorStatuses)) {
547
                $builder->andWhere('i.export_status IN (:status)')
548
                    ->setParameter('status', $errorStatuses, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY);
549
            } elseif ($criteria->exportStatus == Attribute::STATUS_INACTIVE) {
550
                $builder->andWhere('a.active = :status')
551
                    ->setParameter('status', false);
552
            } else {
553
                $builder->andWhere('i.export_status LIKE :status')
554
                    ->setParameter('status', $criteria->exportStatus);
555
            }
556
        }
557
558
        if ($criteria->active) {
559
            $builder->andWhere('a.active = :active')
560
                ->setParameter('active', $criteria->active);
561
        }
562
563
        if ($criteria->orderBy) {
564
            $builder->orderBy($criteria->orderBy, $criteria->orderByDirection);
565
        }
566
567
        $total = $builder->execute()->rowCount();
568
569
        $builder->setFirstResult($criteria->offset);
570
        $builder->setMaxResults($criteria->limit);
571
572
        $data = $builder->execute()->fetchAll();
573
574
        return new ExportList(array(
575
            'articles' => $data,
576
            'count' => $total,
577
        ));
578
    }
579
580
    public function clearConnectItems()
581
    {
582
        $this->deleteAllConnectProducts();
583
        $this->resetConnectItemsStatus();
584
    }
585
586
    /**
587
     * @param int $articleId
588
     */
589
    public function markArticleForCronUpdate($articleId)
590
    {
591
        $this->manager->getConnection()->update(
592
            's_plugin_connect_items',
593
            ['cron_update' => 1],
594
            ['article_id' => (int) $articleId]
595
        );
596
    }
597
598
    /**
599
     * Wrapper method
600
     *
601
     * @param string $sourceId
602
     */
603
    public function recordDelete($sourceId)
604
    {
605
        $this->sdk->recordDelete($sourceId);
606
    }
607
608
    /**
609
     * Deletes products hash
610
     */
611
    private function deleteAllConnectProducts()
612
    {
613
        $builder = $this->manager->getConnection()->createQueryBuilder();
614
        $builder->delete('sw_connect_product');
615
        $builder->execute();
616
    }
617
618
    /**
619
     * Resets all item status
620
     */
621
    private function resetConnectItemsStatus()
622
    {
623
        $builder = $this->manager->getConnection()->createQueryBuilder();
624
        $builder->update('s_plugin_connect_items', 'ci')
625
            ->set('export_status', ':exportStatus')
626
            ->set('revision', ':revision')
627
            ->set('exported', 0)
628
            ->setParameter('exportStatus', null)
629
            ->setParameter('revision', null);
630
631
        $builder->execute();
632
    }
633
634
    private function getMarketplaceGateway()
635
    {
636
        //todo@fixme: Implement better way to get MarketplaceGateway
637
        if (!$this->marketplaceGateway) {
638
            $this->marketplaceGateway = new MarketplaceGateway($this->manager);
639
        }
640
641
        return $this->marketplaceGateway;
642
    }
643
644
    /**
645
     * Extracts all marketplaces attributes from product
646
     *
647
     * @param Detail $detail
648
     * @return array
649
     */
650
    private function extractProductAttributes(Detail $detail)
651
    {
652
        $marketplaceAttributes = array();
653
        $marketplaceAttributes['purchaseUnit'] = $detail->getPurchaseUnit();
654
        $marketplaceAttributes['referenceUnit'] = $detail->getReferenceUnit();
655
656
        // marketplace attributes are available only for SEM shops
657
        if ($this->configComponent->getConfig('isDefault', true)) {
658
            return $marketplaceAttributes;
659
        }
660
661
        foreach ($this->getMarketplaceGateway()->getMappings() as $mapping) {
662
            $shopwareAttribute = $mapping['shopwareAttributeKey'];
663
            $getter = 'get' . ucfirst($shopwareAttribute);
664
665
            if (method_exists($detail->getAttribute(), $getter)) {
666
                $marketplaceAttributes[$shopwareAttribute] = $detail->getAttribute()->{$getter}();
667
            }
668
        }
669
670
        return $marketplaceAttributes;
671
    }
672
}