pairRemainingProductAbstractPageSearchEntities()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 7
nc 1
nop 2
dl 0
loc 12
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
5
 * Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
6
 */
7
8
namespace Spryker\Zed\ProductPageSearch\Business\Publisher;
9
10
use Generated\Shared\Transfer\ProductPageLoadTransfer;
11
use Generated\Shared\Transfer\ProductPageSearchTransfer;
12
use Generated\Shared\Transfer\ProductPayloadTransfer;
13
use Orm\Zed\ProductPageSearch\Persistence\SpyProductAbstractPageSearch;
14
use Spryker\Shared\ProductPageSearch\ProductPageSearchConfig as SharedProductPageSearchConfig;
15
use Spryker\Zed\Kernel\Persistence\EntityManager\InstancePoolingTrait;
16
use Spryker\Zed\ProductPageSearch\Business\Exception\PluginNotFoundException;
17
use Spryker\Zed\ProductPageSearch\Business\Mapper\ProductPageSearchMapperInterface;
18
use Spryker\Zed\ProductPageSearch\Business\Model\ProductPageSearchWriterInterface;
19
use Spryker\Zed\ProductPageSearch\Business\Reader\AddToCartSkuReaderInterface;
20
use Spryker\Zed\ProductPageSearch\Dependency\Facade\ProductPageSearchToStoreFacadeInterface;
21
use Spryker\Zed\ProductPageSearch\Persistence\ProductPageSearchQueryContainerInterface;
22
use Spryker\Zed\ProductPageSearch\Persistence\ProductPageSearchRepositoryInterface;
23
use Spryker\Zed\ProductPageSearch\ProductPageSearchConfig;
24
25
class ProductAbstractPagePublisher implements ProductAbstractPagePublisherInterface
26
{
27
    use InstancePoolingTrait;
28
29
    /**
30
     * @var array<int>
31
     */
32
    protected static array $publishedProductAbstractIds = [];
33
34
    /**
35
     * @var array<int>
36
     */
37
    protected static array $unpublishedProductAbstractIds = [];
38
39
    /**
40
     * @var string
41
     */
42
    public const PRODUCT_ABSTRACT_LOCALIZED_ENTITY = 'PRODUCT_ABSTRACT_LOCALIZED_ENTITY';
43
44
    /**
45
     * @var string
46
     */
47
    public const PRODUCT_ABSTRACT_PAGE_SEARCH_ENTITY = 'PRODUCT_ABSTRACT_PAGE_SEARCH_ENTITY';
48
49
    /**
50
     * @var string
51
     */
52
    public const STORE_NAME = 'STORE_NAME';
53
54
    /**
55
     * @var string
56
     */
57
    public const LOCALE_NAME = 'LOCALE_NAME';
58
59
    /**
60
     * @var array<int, array<string>>
61
     */
62
    protected static $localesByIdStoreMap;
63
64
    /**
65
     * @var array<int, int>
66
     */
67
    protected array $productAbstractIdTimestampMapToSave = [];
68
69
    /**
70
     * @param \Spryker\Zed\ProductPageSearch\Persistence\ProductPageSearchRepositoryInterface $repository
71
     * @param \Spryker\Zed\ProductPageSearch\Persistence\ProductPageSearchQueryContainerInterface $queryContainer
72
     * @param array<\Spryker\Zed\ProductPageSearch\Dependency\Plugin\ProductPageDataExpanderInterface> $pageDataExpanderPlugins
73
     * @param array<\Spryker\Zed\ProductPageSearchExtension\Dependency\Plugin\ProductPageDataLoaderPluginInterface> $productPageDataLoaderPlugins
74
     * @param array<\Spryker\Zed\ProductPageSearchExtension\Dependency\Plugin\ProductPageSearchCollectionFilterPluginInterface> $productPageSearchCollectionFilterPlugins
75
     * @param \Spryker\Zed\ProductPageSearch\Business\Mapper\ProductPageSearchMapperInterface $productPageSearchMapper
76
     * @param \Spryker\Zed\ProductPageSearch\Business\Model\ProductPageSearchWriterInterface $productPageSearchWriter
77
     * @param \Spryker\Zed\ProductPageSearch\ProductPageSearchConfig $productPageSearchConfig
78
     * @param \Spryker\Zed\ProductPageSearch\Dependency\Facade\ProductPageSearchToStoreFacadeInterface $storeFacade
79
     * @param \Spryker\Zed\ProductPageSearch\Business\Reader\AddToCartSkuReaderInterface $addToCartSkuReader
80
     */
81
    public function __construct(
82
        protected ProductPageSearchRepositoryInterface $repository,
83
        protected ProductPageSearchQueryContainerInterface $queryContainer,
84
        protected array $pageDataExpanderPlugins,
85
        protected array $productPageDataLoaderPlugins,
86
        protected array $productPageSearchCollectionFilterPlugins,
87
        protected ProductPageSearchMapperInterface $productPageSearchMapper,
88
        protected ProductPageSearchWriterInterface $productPageSearchWriter,
89
        protected ProductPageSearchConfig $productPageSearchConfig,
90
        protected ProductPageSearchToStoreFacadeInterface $storeFacade,
91
        protected AddToCartSkuReaderInterface $addToCartSkuReader
92
    ) {
93
    }
94
95
    /**
96
     * @param array<int, int> $productAbstractIdTimestampMap
97
     *
98
     * @return void
99
     */
100
    public function publishWithTimestamps(array $productAbstractIdTimestampMap): void
101
    {
102
        // Filters IDs if it had been processed in the current process
103
        $productAbstractIds = array_values(array_unique(array_diff(array_keys($productAbstractIdTimestampMap), static::$publishedProductAbstractIds)));
104
        // Exclude IDs if they were processed in current process
105
        $productAbstractIdTimestampMap = array_intersect_key($productAbstractIdTimestampMap, array_flip($productAbstractIds));
106
        // Filters IDs if it had been processed in parallel processes
107
        $this->productAbstractIdTimestampMapToSave = $this->repository->getRelevantProductAbstractIdsToUpdate($productAbstractIdTimestampMap);
108
109
        if ($this->productAbstractIdTimestampMapToSave) {
110
            $this->publish(array_keys($this->productAbstractIdTimestampMapToSave));
111
        }
112
113
        static::$publishedProductAbstractIds = array_merge(static::$publishedProductAbstractIds, $productAbstractIds);
114
    }
115
116
    /**
117
     * @param array<int> $productAbstractIds
118
     *
119
     * @return void
120
     */
121
    public function publish(array $productAbstractIds)
122
    {
123
        $productAbstractIdsChunks = array_chunk(
124
            $productAbstractIds,
125
            $this->productPageSearchConfig->getProductAbstractPagePublishChunkSize(),
126
        );
127
128
        foreach ($productAbstractIdsChunks as $productAbstractIdsChunk) {
129
            $this->publishEntities($productAbstractIdsChunk, [], false);
130
        }
131
    }
132
133
    /**
134
     * @param array<int> $productAbstractIds
135
     * @param array<string> $pageDataExpanderPluginNames
136
     *
137
     * @return void
138
     */
139
    public function refresh(array $productAbstractIds, array $pageDataExpanderPluginNames = [])
140
    {
141
        $isPoolingStateChanged = $this->disableInstancePooling();
142
143
        $productAbstractIdsChunks = array_chunk(
144
            array_unique($productAbstractIds),
145
            $this->productPageSearchConfig->getProductAbstractPagePublishChunkSize(),
146
        );
147
148
        foreach ($productAbstractIdsChunks as $productAbstractIdsChunk) {
149
            $this->publishEntities($productAbstractIdsChunk, $pageDataExpanderPluginNames, true);
150
        }
151
152
        if ($isPoolingStateChanged) {
153
            $this->enableInstancePooling();
154
        }
155
    }
156
157
    /**
158
     * @param array<int, int> $productAbstractIdTimestampMap
159
     *
160
     * @return void
161
     */
162
    public function unpublishWithTimestamps(array $productAbstractIdTimestampMap)
163
    {
164
        $productAbstractIds = array_values(array_unique(array_diff(array_keys($productAbstractIdTimestampMap), static::$unpublishedProductAbstractIds)));
165
        if ($productAbstractIds) {
166
            $this->unpublish($productAbstractIds);
167
        }
168
        static::$unpublishedProductAbstractIds = array_merge(static::$unpublishedProductAbstractIds, $productAbstractIds);
169
    }
170
171
    /**
172
     * @param array<int> $productAbstractIds
173
     *
174
     * @return void
175
     */
176
    public function unpublish(array $productAbstractIds)
177
    {
178
        $productAbstractPageSearchEntities = $this->findProductAbstractPageSearchEntities($productAbstractIds);
179
        $this->productPageSearchWriter->deleteProductAbstractPageSearchEntities($productAbstractPageSearchEntities);
180
        $this->productPageSearchWriter->commitRemaining();
181
    }
182
183
    /**
184
     * @param array<int> $productAbstractIds
185
     * @param array<string> $pageDataExpanderPluginNames
186
     * @param bool $isRefresh
187
     *
188
     * @return void
189
     */
190
    protected function publishEntities(array $productAbstractIds, array $pageDataExpanderPluginNames, $isRefresh = false)
191
    {
192
        $pageDataExpanderPlugins = $this->getPageDataExpanderPlugins($pageDataExpanderPluginNames);
193
194
        $payloadTransfers = [];
195
        foreach ($productAbstractIds as $productAbstractId) {
196
            $payloadTransfers[$productAbstractId] = (new ProductPayloadTransfer())->setIdProductAbstract($productAbstractId);
197
        }
198
199
        $productPageLoadTransfer = (new ProductPageLoadTransfer())
200
            ->setProductAbstractIds($productAbstractIds)
201
            ->setPayloadTransfers($payloadTransfers);
202
203
        foreach ($this->productPageDataLoaderPlugins as $productPageDataLoaderPlugin) {
204
            $productPageLoadTransfer = $productPageDataLoaderPlugin->expandProductPageDataTransfer($productPageLoadTransfer);
205
        }
206
207
        $productAbstractLocalizedEntities = $this->findProductAbstractLocalizedEntities($productAbstractIds);
208
        $productCategories = $this->getProductCategoriesByProductAbstractIds($productAbstractIds);
209
        $productAbstractLocalizedEntities = $this->hydrateProductAbstractLocalizedEntitiesWithProductCategories($productCategories, $productAbstractLocalizedEntities);
210
211
        $productAbstractPageSearchEntities = $this->findProductAbstractPageSearchEntities($productAbstractIds);
212
        if (!$productAbstractLocalizedEntities) {
213
            $this->productPageSearchWriter->deleteProductAbstractPageSearchEntities($productAbstractPageSearchEntities);
214
            $this->productPageSearchWriter->commitRemaining();
215
216
            return;
217
        }
218
219
        if ($this->productPageSearchConfig->isProductAbstractAddToCartEnabled()) {
220
            $productAbstractLocalizedEntities = $this->hydrateProductAbstractLocalizedEntitiesWithProductAbstractAddToCartSku(
221
                $productAbstractLocalizedEntities,
222
                $productAbstractIds,
223
            );
224
        }
225
226
        $this->storeData(
227
            $productAbstractLocalizedEntities,
228
            $productAbstractPageSearchEntities,
229
            $pageDataExpanderPlugins,
230
            $productPageLoadTransfer,
231
            $isRefresh,
232
        );
233
234
        $this->productPageSearchWriter->commitRemaining();
235
    }
236
237
    /**
238
     * @param array<array<string, mixed>> $productAbstractLocalizedEntities
239
     * @param array<\Orm\Zed\ProductPageSearch\Persistence\SpyProductAbstractPageSearch> $productAbstractPageSearchEntities
240
     * @param array<\Spryker\Zed\ProductPageSearch\Dependency\Plugin\ProductPageDataExpanderInterface> $pageDataExpanderPlugins
241
     * @param \Generated\Shared\Transfer\ProductPageLoadTransfer $productPageLoadTransfer
242
     * @param bool $isRefresh
243
     *
244
     * @return void
245
     */
246
    protected function storeData(
247
        array $productAbstractLocalizedEntities,
248
        array $productAbstractPageSearchEntities,
249
        array $pageDataExpanderPlugins,
250
        ProductPageLoadTransfer $productPageLoadTransfer,
251
        $isRefresh = false
252
    ) {
253
        $pairedEntities = $this->pairProductAbstractLocalizedEntitiesWithProductAbstractPageSearchEntities(
254
            $productAbstractLocalizedEntities,
255
            $productAbstractPageSearchEntities,
256
            $productPageLoadTransfer,
257
        );
258
259
        $productPageSearchTransfers = $this->mapPairedEntitiesToProductPageSearchTransfers(
260
            $pairedEntities,
261
            $isRefresh,
262
        );
263
        $productPageSearchTransfers = $this->executeProductPageSearchCollectionFilterPlugins($productPageSearchTransfers);
264
        $productPageSearchTransfersIndexedByLocaleAndIdProductAbstract = $this->getProductPageSearchTransfersIndexedByLocaleAndIdProductAbstract(
265
            $productPageSearchTransfers,
266
        );
267
268
        foreach ($pairedEntities as $pairedEntity) {
269
            /** @var array|null $productAbstractLocalizedEntity */
270
            $productAbstractLocalizedEntity = $pairedEntity[static::PRODUCT_ABSTRACT_LOCALIZED_ENTITY];
271
            /** @var \Orm\Zed\ProductPageSearch\Persistence\SpyProductAbstractPageSearch $productAbstractPageSearchEntity */
272
            $productAbstractPageSearchEntity = $pairedEntity[static::PRODUCT_ABSTRACT_PAGE_SEARCH_ENTITY];
273
            $store = $pairedEntity[static::STORE_NAME];
274
            $locale = $pairedEntity[static::LOCALE_NAME];
275
276
            if ($productAbstractLocalizedEntity === null || !$this->isActual($productAbstractLocalizedEntity)) {
277
                $this->deleteProductAbstractPageSearchEntity($productAbstractPageSearchEntity);
278
279
                continue;
280
            }
281
282
            $idProductAbstract = $productAbstractLocalizedEntity['fk_product_abstract'];
283
            $productPageSearchTransfer = $this->findProductPageSearchTransferFromIndexedList(
284
                $productPageSearchTransfersIndexedByLocaleAndIdProductAbstract,
285
                $idProductAbstract,
286
                $locale,
287
                $store,
288
            );
289
290
            if ($productPageSearchTransfer === null) {
291
                $this->deleteProductAbstractPageSearchEntity($productAbstractPageSearchEntity);
292
293
                continue;
294
            }
295
296
            $this->storeProductAbstractPageSearchEntity(
297
                $productAbstractLocalizedEntity,
298
                $productAbstractPageSearchEntity,
299
                $productPageSearchTransfer,
300
                $store,
301
                $locale,
302
                $pageDataExpanderPlugins,
303
            );
304
        }
305
    }
306
307
    /**
308
     * @param \Orm\Zed\ProductPageSearch\Persistence\SpyProductAbstractPageSearch $productAbstractPageSearchEntity
309
     *
310
     * @return void
311
     */
312
    protected function deleteProductAbstractPageSearchEntity(SpyProductAbstractPageSearch $productAbstractPageSearchEntity)
313
    {
314
        if (!$productAbstractPageSearchEntity->isNew()) {
315
            $productAbstractPageSearchEntity->delete();
316
        }
317
    }
318
319
    /**
320
     * @param array $productAbstractLocalizedEntity
321
     * @param \Orm\Zed\ProductPageSearch\Persistence\SpyProductAbstractPageSearch $productAbstractPageSearchEntity
322
     * @param \Generated\Shared\Transfer\ProductPageSearchTransfer $productPageSearchTransfer
323
     * @param string $storeName
324
     * @param string $localeName
325
     * @param array<\Spryker\Zed\ProductPageSearch\Dependency\Plugin\ProductPageDataExpanderInterface> $pageDataExpanderPlugins
326
     *
327
     * @return void
328
     */
329
    protected function storeProductAbstractPageSearchEntity(
330
        array $productAbstractLocalizedEntity,
331
        SpyProductAbstractPageSearch $productAbstractPageSearchEntity,
332
        ProductPageSearchTransfer $productPageSearchTransfer,
333
        string $storeName,
334
        string $localeName,
335
        array $pageDataExpanderPlugins
336
    ) {
337
        if (!empty($this->productAbstractIdTimestampMapToSave[$productAbstractPageSearchEntity->getFkProductAbstract()])) {
338
            $productPageSearchTransfer->setTimestamp($this->productAbstractIdTimestampMapToSave[$productAbstractPageSearchEntity->getFkProductAbstract()]);
339
        }
340
        $productPageSearchTransfer->setStore($storeName);
341
        $productPageSearchTransfer->setLocale($localeName);
342
343
        $this->expandPageSearchTransferWithPlugins($pageDataExpanderPlugins, $productAbstractLocalizedEntity, $productPageSearchTransfer);
344
345
        $searchDocument = $this->productPageSearchMapper->mapToSearchData($productPageSearchTransfer);
346
347
        $this->productPageSearchWriter->save($productPageSearchTransfer, $searchDocument, $productAbstractPageSearchEntity);
348
    }
349
350
    /**
351
     * @param array $productAbstractLocalizedEntity
352
     *
353
     * @return bool
354
     */
355
    protected function isActual(array $productAbstractLocalizedEntity): bool
356
    {
357
        foreach ($productAbstractLocalizedEntity['SpyProductAbstract']['SpyProducts'] as $spyProduct) {
358
            if ($spyProduct['is_active'] && $this->isSearchable($spyProduct, $productAbstractLocalizedEntity['fk_locale'])) {
359
                return true;
360
            }
361
        }
362
363
        return false;
364
    }
365
366
    /**
367
     * @param array $spyProduct
368
     * @param int $idLocale
369
     *
370
     * @return bool
371
     */
372
    protected function isSearchable(array $spyProduct, int $idLocale): bool
373
    {
374
        foreach ($spyProduct['SpyProductSearches'] as $spyProductSearch) {
375
            if ($spyProductSearch['fk_locale'] === $idLocale && $spyProductSearch['is_searchable'] === true) {
376
                return true;
377
            }
378
        }
379
380
        return false;
381
    }
382
383
    /**
384
     * Retrieves the ProductPageSearchTransfer from the storage entity (if it existed already) or populates it from the localized entity.
385
     *
386
     * @param array $productAbstractLocalizedEntity
387
     * @param \Orm\Zed\ProductPageSearch\Persistence\SpyProductAbstractPageSearch $productAbstractPageSearchEntity
388
     * @param bool $isRefresh
389
     *
390
     * @return \Generated\Shared\Transfer\ProductPageSearchTransfer
391
     */
392
    protected function getProductPageSearchTransfer(
393
        array $productAbstractLocalizedEntity,
394
        SpyProductAbstractPageSearch $productAbstractPageSearchEntity,
395
        $isRefresh = false
396
    ): ProductPageSearchTransfer {
397
        if ($isRefresh && !$productAbstractPageSearchEntity->isNew()) {
398
            return $this->refreshProductPageSearchTransfer($productAbstractPageSearchEntity);
399
        }
400
401
        return $this->productPageSearchMapper->mapToProductPageSearchTransfer($productAbstractLocalizedEntity);
402
    }
403
404
    /**
405
     * @param \Orm\Zed\ProductPageSearch\Persistence\SpyProductAbstractPageSearch $productAbstractPageSearchEntity
406
     *
407
     * @return \Generated\Shared\Transfer\ProductPageSearchTransfer
408
     */
409
    protected function refreshProductPageSearchTransfer(
410
        SpyProductAbstractPageSearch $productAbstractPageSearchEntity
411
    ): ProductPageSearchTransfer {
412
        return $this->productPageSearchMapper->mapToProductPageSearchTransferFromJson($productAbstractPageSearchEntity->getStructuredData());
413
    }
414
415
    /**
416
     * @param array<string> $pageDataExpanderPluginNames
417
     *
418
     * @return array<\Spryker\Zed\ProductPageSearch\Dependency\Plugin\ProductPageDataExpanderInterface>
419
     */
420
    protected function getPageDataExpanderPlugins(array $pageDataExpanderPluginNames)
421
    {
422
        if (!$pageDataExpanderPluginNames) {
423
            return $this->pageDataExpanderPlugins;
424
        }
425
426
        $selectedExpanderPlugins = [];
427
        foreach ($pageDataExpanderPluginNames as $pageDataExpanderPluginName) {
428
            $this->assertPageDataExpanderPluginName($pageDataExpanderPluginName);
429
430
            $selectedExpanderPlugins[] = $this->pageDataExpanderPlugins[$pageDataExpanderPluginName];
431
        }
432
433
        return $selectedExpanderPlugins;
434
    }
435
436
    /**
437
     * @param string $pageDataExpanderPluginName
438
     *
439
     * @throws \Spryker\Zed\ProductPageSearch\Business\Exception\PluginNotFoundException
440
     *
441
     * @return void
442
     */
443
    protected function assertPageDataExpanderPluginName($pageDataExpanderPluginName)
444
    {
445
        if (!isset($this->pageDataExpanderPlugins[$pageDataExpanderPluginName])) {
446
            throw new PluginNotFoundException(sprintf('The plugin with this name: %s is not found', $pageDataExpanderPluginName));
447
        }
448
    }
449
450
    /**
451
     * @param array<\Spryker\Zed\ProductPageSearch\Dependency\Plugin\ProductPageDataExpanderInterface> $pageDataExpanderPlugins
452
     * @param array<string, mixed> $productAbstractLocalizedEntity
453
     * @param \Generated\Shared\Transfer\ProductPageSearchTransfer $productPageSearchTransfer
454
     *
455
     * @return void
456
     */
457
    protected function expandPageSearchTransferWithPlugins(
458
        array $pageDataExpanderPlugins,
459
        array $productAbstractLocalizedEntity,
460
        ProductPageSearchTransfer $productPageSearchTransfer
461
    ) {
462
        foreach ($pageDataExpanderPlugins as $pageDataExpanderPlugin) {
463
            $pageDataExpanderPlugin->expandProductPageData($productAbstractLocalizedEntity, $productPageSearchTransfer);
464
        }
465
    }
466
467
    /**
468
     * - Returns a paired array with all provided entities.
469
     * - ProductAbstractLocalizedEntities without ProductAbstractPageSearchEntity are paired with a newly created ProductAbstractPageSearchEntity.
470
     * - ProductAbstractPageSearchEntity without ProductAbstractLocalizedEntities (left outs) are paired with NULL.
471
     * - ProductAbstractLocalizedEntities are paired multiple times per store.
472
     *
473
     * @param array<array<string, mixed>> $productAbstractLocalizedEntities
474
     * @param array<\Orm\Zed\ProductPageSearch\Persistence\SpyProductAbstractPageSearch> $productAbstractPageSearchEntities
475
     * @param \Generated\Shared\Transfer\ProductPageLoadTransfer $productPageLoadTransfer
476
     *
477
     * @return array
478
     */
479
    protected function pairProductAbstractLocalizedEntitiesWithProductAbstractPageSearchEntities(
480
        array $productAbstractLocalizedEntities,
481
        array $productAbstractPageSearchEntities,
482
        ProductPageLoadTransfer $productPageLoadTransfer
483
    ) {
484
        $mappedProductAbstractPageSearchEntities = $this->mapProductAbstractPageSearchEntities($productAbstractPageSearchEntities);
485
486
        $pairs = [];
487
        $productPayloadTransfers = $productPageLoadTransfer->getPayloadTransfers();
488
        foreach ($productAbstractLocalizedEntities as $productAbstractLocalizedEntity) {
489
            [$pairs, $mappedProductAbstractPageSearchEntities] = $this->pairProductAbstractLocalizedEntityWithProductAbstractPageSearchEntityByStoresAndLocale(
490
                $productAbstractLocalizedEntity['fk_product_abstract'],
491
                $productAbstractLocalizedEntity['Locale']['locale_name'],
492
                $productPayloadTransfers[$productAbstractLocalizedEntity['fk_product_abstract']],
493
                $productAbstractLocalizedEntity['SpyProductAbstract']['SpyProductAbstractStores'],
494
                $productAbstractLocalizedEntity,
495
                $mappedProductAbstractPageSearchEntities,
496
                $pairs,
497
            );
498
        }
499
500
        $pairs = $this->pairRemainingProductAbstractPageSearchEntities($mappedProductAbstractPageSearchEntities, $pairs);
501
502
        return $pairs;
503
    }
504
505
    /**
506
     * @param array<int> $productAbstractIds
507
     *
508
     * @return array
509
     */
510
    protected function findProductAbstractLocalizedEntities(array $productAbstractIds)
511
    {
512
        $localesByIdStoreMap = $this->getLocalesByIdStoreMap();
513
        /** @var array<int> $allStoreIds */
514
        $allStoreIds = array_keys($localesByIdStoreMap);
515
        $allLocaleIsoCodes = array_merge(...array_values($localesByIdStoreMap));
516
517
        $productAbstractLocalizedAttributeDataCollection = $this
518
            ->queryContainer
519
            ->queryProductAbstractLocalizedAttributesByProductAbstractIdsAndStoreIdsAndLocaleIds(
520
                $productAbstractIds,
521
                $allStoreIds,
522
                $allLocaleIsoCodes,
523
            )
524
            ->find()
525
            ->getData();
526
527
        $productConcreteEntities = $this->queryContainer
528
            ->queryProductConcretesByAbstractProductIdsAndLocaleIsoCodes($productAbstractIds, $allLocaleIsoCodes)
529
            ->find()
530
            ->getData();
531
532
        $productConcreteIds = array_column($productConcreteEntities, 'id_product');
533
534
        $productSearchEntities = $this->queryContainer
535
            ->queryProductSearchByProductConcreteIdsAndLocaleIsoCodes($productConcreteIds, $allLocaleIsoCodes)
536
            ->find()
537
            ->getData();
538
539
        $productConcreteEntities = $this->hydrateProductConcreteEntitiesWithProductSearchEntities(
540
            $productSearchEntities,
541
            $productConcreteEntities,
542
        );
543
544
        return $this->hydrateProductAbstractLocalizedEntitiesWithProductConcreteEntities(
545
            $productConcreteEntities,
546
            $productAbstractLocalizedAttributeDataCollection,
547
        );
548
    }
549
550
    /**
551
     * @param array $productSearchEntities
552
     * @param array $productConcreteEntities
553
     *
554
     * @return array
555
     */
556
    protected function hydrateProductConcreteEntitiesWithProductSearchEntities(array $productSearchEntities, array $productConcreteEntities): array
557
    {
558
        $productSearchByProductConcreteId = [];
559
560
        foreach ($productSearchEntities as $productSearch) {
561
            $productSearchByProductConcreteId[$productSearch['fk_product']][] = $productSearch;
562
        }
563
564
        foreach ($productConcreteEntities as $key => $productConcreteEntity) {
565
            $productConcreteId = (int)$productConcreteEntity['id_product'];
566
            $productConcreteEntities[$key]['SpyProductSearches'] = $productSearchByProductConcreteId[$productConcreteId] ?? [];
567
        }
568
569
        return $productConcreteEntities;
570
    }
571
572
    /**
573
     * @param array $productCategories
574
     * @param array $productAbstractLocalizedEntities
575
     *
576
     * @return array
577
     */
578
    protected function hydrateProductAbstractLocalizedEntitiesWithProductCategories(array $productCategories, array $productAbstractLocalizedEntities)
579
    {
580
        $productCategoriesByProductAbstractId = [];
581
582
        foreach ($productCategories as $productCategory) {
583
            $productCategoriesByProductAbstractId[$productCategory['fk_product_abstract']][] = $productCategory;
584
        }
585
586
        foreach ($productAbstractLocalizedEntities as $key => $productAbstractLocalizedEntity) {
587
            $productAbstractId = (int)$productAbstractLocalizedEntity['fk_product_abstract'];
588
            $productAbstractLocalizedEntities[$key]['SpyProductAbstract']['SpyProductCategories']
589
                = $productCategoriesByProductAbstractId[$productAbstractId] ?? [];
590
        }
591
592
        return $productAbstractLocalizedEntities;
593
    }
594
595
    /**
596
     * @param array $productConcreteData
597
     * @param array $productAbstractLocalizedEntities
598
     *
599
     * @return array
600
     */
601
    protected function hydrateProductAbstractLocalizedEntitiesWithProductConcreteEntities(
602
        array $productConcreteData,
603
        array $productAbstractLocalizedEntities
604
    ): array {
605
        $productConcretesByProductAbstractId = [];
606
        foreach ($productConcreteData as $productConcrete) {
607
            $productConcretesByProductAbstractId[$productConcrete['fk_product_abstract']][] = $productConcrete;
608
        }
609
610
        foreach ($productAbstractLocalizedEntities as $key => $productAbstractLocalizedEntity) {
611
            $productAbstractId = (int)$productAbstractLocalizedEntity['fk_product_abstract'];
612
            $productAbstractLocalizedEntities[$key]['SpyProductAbstract']['SpyProducts'] = $productConcretesByProductAbstractId[$productAbstractId] ?? [];
613
        }
614
615
        return $productAbstractLocalizedEntities;
616
    }
617
618
    /**
619
     * @param array<int> $productAbstractIds
620
     *
621
     * @return array
622
     */
623
    protected function getProductCategoriesByProductAbstractIds(array $productAbstractIds)
624
    {
625
        return $this->queryContainer->queryAllProductCategories($productAbstractIds)->find()->getData();
626
    }
627
628
    /**
629
     * @param array<int> $productAbstractIds
630
     *
631
     * @return array<\Orm\Zed\ProductPageSearch\Persistence\SpyProductAbstractPageSearch>
632
     */
633
    protected function findProductAbstractPageSearchEntities(array $productAbstractIds)
634
    {
635
        return $this->queryContainer->queryProductAbstractSearchPageByIds($productAbstractIds)->find()->getArrayCopy();
636
    }
637
638
    /**
639
     * @param array<\Orm\Zed\ProductPageSearch\Persistence\SpyProductAbstractPageSearch> $productAbstractPageSearchEntities
640
     *
641
     * @return array
642
     */
643
    protected function mapProductAbstractPageSearchEntities(array $productAbstractPageSearchEntities)
644
    {
645
        $mappedProductAbstractPageSearchEntities = [];
646
        foreach ($productAbstractPageSearchEntities as $entity) {
647
            $mappedProductAbstractPageSearchEntities[$entity->getFkProductAbstract()][$entity->getStore()][$entity->getLocale()] = $entity;
648
        }
649
650
        return $mappedProductAbstractPageSearchEntities;
651
    }
652
653
    /**
654
     * @param array $mappedProductAbstractPageSearchEntities
655
     * @param array $pairs
656
     *
657
     * @return array
658
     */
659
    protected function pairRemainingProductAbstractPageSearchEntities(array $mappedProductAbstractPageSearchEntities, array $pairs)
660
    {
661
        array_walk_recursive($mappedProductAbstractPageSearchEntities, function (SpyProductAbstractPageSearch $productAbstractPageSearchEntity) use (&$pairs) {
662
            $pairs[] = [
663
                static::PRODUCT_ABSTRACT_LOCALIZED_ENTITY => null,
664
                static::PRODUCT_ABSTRACT_PAGE_SEARCH_ENTITY => $productAbstractPageSearchEntity,
665
                static::LOCALE_NAME => $productAbstractPageSearchEntity->getLocale(),
666
                static::STORE_NAME => $productAbstractPageSearchEntity->getStore(),
667
            ];
668
        });
669
670
        return $pairs;
671
    }
672
673
    /**
674
     * @param int $idProductAbstract
675
     * @param string $localeName
676
     * @param \Generated\Shared\Transfer\ProductPayloadTransfer $productPayloadTransfer
677
     * @param array $productAbstractStores
678
     * @param array $productAbstractLocalizedEntity
679
     * @param array $mappedProductAbstractPageSearchEntities
680
     * @param array $pairs
681
     *
682
     * @return array
683
     */
684
    protected function pairProductAbstractLocalizedEntityWithProductAbstractPageSearchEntityByStoresAndLocale(
685
        $idProductAbstract,
686
        $localeName,
687
        ProductPayloadTransfer $productPayloadTransfer,
688
        array $productAbstractStores,
689
        array $productAbstractLocalizedEntity,
690
        array $mappedProductAbstractPageSearchEntities,
691
        array $pairs
692
    ) {
693
        $localesByIdStoreMap = $this->getLocalesByIdStoreMap();
694
695
        foreach ($productAbstractStores as $productAbstractStore) {
696
            $idStore = $productAbstractStore['fk_store'];
697
            $storeName = $productAbstractStore['SpyStore']['name'];
698
699
            if (!in_array($localeName, $localesByIdStoreMap[$idStore])) {
700
                continue;
701
            }
702
703
            $productAbstractLocalizedEntity[SharedProductPageSearchConfig::PRODUCT_ABSTRACT_PAGE_LOAD_DATA] = $productPayloadTransfer;
704
705
            $searchEntity = $mappedProductAbstractPageSearchEntities[$idProductAbstract][$storeName][$localeName] ??
706
                new SpyProductAbstractPageSearch();
707
708
            unset($mappedProductAbstractPageSearchEntities[$idProductAbstract][$storeName][$localeName]);
709
710
            $pairs[] = [
711
                static::PRODUCT_ABSTRACT_LOCALIZED_ENTITY => $productAbstractLocalizedEntity,
712
                static::PRODUCT_ABSTRACT_PAGE_SEARCH_ENTITY => $searchEntity,
713
                static::LOCALE_NAME => $localeName,
714
                static::STORE_NAME => $storeName,
715
            ];
716
        }
717
718
        return [$pairs, $mappedProductAbstractPageSearchEntities];
719
    }
720
721
    /**
722
     * @param array $productAbstractLocalizedEntities
723
     * @param array<int> $productAbstractIds
724
     *
725
     * @return array
726
     */
727
    protected function hydrateProductAbstractLocalizedEntitiesWithProductAbstractAddToCartSku(
728
        array $productAbstractLocalizedEntities,
729
        array $productAbstractIds
730
    ): array {
731
        $productAbstractStoreIds = [];
732
        foreach ($productAbstractLocalizedEntities as $productAbstractLocalizedEntity) {
733
            $productAbstractId = (int)$productAbstractLocalizedEntity['fk_product_abstract'];
734
            foreach ($productAbstractLocalizedEntity['SpyProductAbstract']['SpyProductAbstractStores'] as $productAbstractStoreData) {
735
                $storeId = (int)$productAbstractStoreData['SpyStore']['id_store'];
736
                $productAbstractStoreIds[$productAbstractId][] = $storeId;
737
            }
738
        }
739
740
        $productConcreteSkuMapByIdProductAbstract = $this->addToCartSkuReader->getProductAbstractAddToCartSkus($productAbstractIds, $productAbstractStoreIds);
741
742
        foreach ($productAbstractLocalizedEntities as &$productAbstractLocalizedEntity) {
743
            $productAbstractId = (int)$productAbstractLocalizedEntity['fk_product_abstract'];
744
            $productAbstractLocalizedEntity[ProductPageSearchTransfer::ADD_TO_CART_SKU] = $productConcreteSkuMapByIdProductAbstract[$productAbstractId] ?? null;
745
        }
746
747
        return $productAbstractLocalizedEntities;
748
    }
749
750
    /**
751
     * @param array $pairedEntities
752
     * @param bool $isRefresh
753
     *
754
     * @return array
755
     */
756
    protected function mapPairedEntitiesToProductPageSearchTransfers(
757
        array $pairedEntities,
758
        bool $isRefresh
759
    ): array {
760
        $productAbstractPageSearchTransfers = [];
761
762
        foreach ($pairedEntities as $pairedEntity) {
763
            /** @var array|null $productAbstractLocalizedEntity */
764
            $productAbstractLocalizedEntity = $pairedEntity[static::PRODUCT_ABSTRACT_LOCALIZED_ENTITY] ?? null;
765
            /** @var \Orm\Zed\ProductPageSearch\Persistence\SpyProductAbstractPageSearch $productAbstractPageSearchEntity */
766
            $productAbstractPageSearchEntity = $pairedEntity[static::PRODUCT_ABSTRACT_PAGE_SEARCH_ENTITY];
767
768
            if (!$productAbstractLocalizedEntity) {
769
                continue;
770
            }
771
772
            $productAbstractPageSearchTransfers[] = $this->getProductPageSearchTransfer(
773
                $productAbstractLocalizedEntity,
774
                $productAbstractPageSearchEntity,
775
                $isRefresh,
776
            );
777
        }
778
779
        return $productAbstractPageSearchTransfers;
780
    }
781
782
    /**
783
     * @param array<\Generated\Shared\Transfer\ProductPageSearchTransfer> $productPageSearchTransfers
784
     *
785
     * @return array<\Generated\Shared\Transfer\ProductPageSearchTransfer>
786
     */
787
    protected function executeProductPageSearchCollectionFilterPlugins(array $productPageSearchTransfers): array
788
    {
789
        foreach ($this->productPageSearchCollectionFilterPlugins as $productPageSearchCollectionFilterPlugin) {
790
            $productPageSearchTransfers = $productPageSearchCollectionFilterPlugin->filter($productPageSearchTransfers);
791
        }
792
793
        return $productPageSearchTransfers;
794
    }
795
796
    /**
797
     * @param array<\Generated\Shared\Transfer\ProductPageSearchTransfer> $productPageSearchTransfers
798
     *
799
     * @return array<string, array<int, list<\Generated\Shared\Transfer\ProductPageSearchTransfer>>>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<string, array<int,...ctPageSearchTransfer>>> at position 8 could not be parsed: Expected '>' at position 8, but found 'list'.
Loading history...
800
     */
801
    protected function getProductPageSearchTransfersIndexedByLocaleAndIdProductAbstract(array $productPageSearchTransfers): array
802
    {
803
        $productPageSearchTransfersIndexedByLocaleAndIdProductAbstract = [];
804
        foreach ($productPageSearchTransfers as $productPageSearchTransfer) {
805
            $idProductAbstract = $productPageSearchTransfer->getIdProductAbstractOrFail();
806
            $locale = $productPageSearchTransfer->getLocaleOrFail();
807
            $productPageSearchTransfersIndexedByLocaleAndIdProductAbstract[$locale][$idProductAbstract][] = $productPageSearchTransfer;
808
        }
809
810
        return $productPageSearchTransfersIndexedByLocaleAndIdProductAbstract;
811
    }
812
813
    /**
814
     * @param array<string, array<int, list<\Generated\Shared\Transfer\ProductPageSearchTransfer>>> $productPageSearchTransfersIndexedByLocaleAndIdProductAbstract
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<string, array<int,...ctPageSearchTransfer>>> at position 8 could not be parsed: Expected '>' at position 8, but found 'list'.
Loading history...
815
     * @param int $idProductAbstract
816
     * @param string $locale
817
     * @param string $storeName
818
     *
819
     * @return \Generated\Shared\Transfer\ProductPageSearchTransfer|null
820
     */
821
    protected function findProductPageSearchTransferFromIndexedList(
822
        array $productPageSearchTransfersIndexedByLocaleAndIdProductAbstract,
823
        int $idProductAbstract,
824
        string $locale,
825
        string $storeName
826
    ): ?ProductPageSearchTransfer {
827
        if (!isset($productPageSearchTransfersIndexedByLocaleAndIdProductAbstract[$locale][$idProductAbstract])) {
828
            return null;
829
        }
830
831
        $productPageSearchTransfers = $productPageSearchTransfersIndexedByLocaleAndIdProductAbstract[$locale][$idProductAbstract];
832
        foreach ($productPageSearchTransfers as $productPageSearchTransfer) {
833
            if ($productPageSearchTransfer->getStore() === $storeName) {
834
                return $productPageSearchTransfer;
835
            }
836
        }
837
838
        return $productPageSearchTransfers[0] ?? null;
839
    }
840
841
    /**
842
     * @return array<int, array<string>>
843
     */
844
    protected function getLocalesByIdStoreMap(): array
845
    {
846
        if (static::$localesByIdStoreMap === null) {
0 ignored issues
show
introduced by
The condition static::localesByIdStoreMap === null is always false.
Loading history...
847
            static::$localesByIdStoreMap = [];
848
849
            foreach ($this->storeFacade->getAllStores() as $storeTransfer) {
850
                static::$localesByIdStoreMap[(int)$storeTransfer->getIdStore()] = $storeTransfer->getAvailableLocaleIsoCodes();
851
            }
852
        }
853
854
        return static::$localesByIdStoreMap;
855
    }
856
}
857