Skip to content

Commit 3dd281d

Browse files
committed
Release 1.0.1
1 parent 8a53b69 commit 3dd281d

File tree

2 files changed

+151
-82
lines changed

2 files changed

+151
-82
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Change Log for Sidworks Product Labels plugin
22
All notable changes to this module will be documented in this file.
33

4-
## 1.0.1 [07-07-2025]
4+
## 1.0.1 [08-07-2025]
55
### Fixed
66
- Make sure to use SalesChannelContext for products
77

src/Service/LabelStreamService.php

Lines changed: 150 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,20 @@
77
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
88
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
99
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\ContainsFilter;
10+
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
1011
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
11-
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\NotFilter;
12-
use Shopware\Core\Framework\DataAbstractionLayer\Search\Grouping\FieldGrouping;
13-
use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
14-
use Shopware\Core\Framework\Struct\ArrayEntity;
1512
use Shopware\Core\System\SalesChannel\Entity\SalesChannelRepository;
1613
use Shopware\Core\System\SalesChannel\SalesChannelContext;
17-
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
14+
use Shopware\Core\Framework\Struct\ArrayEntity;
1815
use Sidworks\ProductLabels\Core\Content\ProductLabels\ProductLabelsEntity;
1916

2017
class LabelStreamService
2118
{
2219
private const SIDWORKS_PRODUCT_LABELS_EXTENSION = 'sidworksProductLabels';
2320

2421
private array $labelCache = [];
22+
private array $variantMappingCache = [];
23+
private array $streamMatchCache = [];
2524

2625
public function __construct(
2726
private readonly EntityRepository $productLabelsRepository,
@@ -31,151 +30,210 @@ public function __construct(
3130

3231
public function getProductLabelStreamProducts(array $productIds, SalesChannelContext $context): array
3332
{
34-
$cacheKey = md5(implode(',', $productIds) . $context->getSalesChannelId());
35-
33+
$cacheKey = $this->getCacheKey($productIds, $context->getSalesChannelId());
3634
if (isset($this->labelCache[$cacheKey])) {
3735
return $this->labelCache[$cacheKey];
3836
}
3937

4038
$productLabels = $this->fetchActiveProductLabels($context->getContext());
41-
4239
$productLabelStreamItems = [];
4340

41+
if (!$productLabels) {
42+
return [];
43+
}
44+
45+
// Pre-fetch variant mapping once for all labels
46+
$variantToParent = $this->getVariantToParentMapping($productIds, $context);
47+
4448
foreach ($productLabels as $productLabel) {
4549
if (!$this->shouldShowProductLabel($productLabel)) {
4650
continue;
4751
}
4852

49-
$matchedIds = $this->getMatchedProductIds($productLabel, $productIds, $context);
50-
53+
$matchedIds = $this->getMatchedProductIds($productLabel, $productIds, $context, $variantToParent);
5154
if (empty($matchedIds)) {
5255
continue;
5356
}
5457

5558
$productLabelStreamItems[] = [
5659
'label' => $productLabel,
57-
'matchedProductIds' => array_values(array_unique($matchedIds)),
60+
'matchedProductIds' => $matchedIds,
5861
];
5962
}
6063

6164
$this->labelCache[$cacheKey] = $productLabelStreamItems;
62-
6365
return $productLabelStreamItems;
6466
}
6567

6668
public function applyLabelsToProduct($product, array $productLabelsStreamProducts): void
6769
{
68-
foreach ($productLabelsStreamProducts as $productLabelsStreamProduct) {
69-
$matchedIds = $productLabelsStreamProduct['matchedProductIds'] ?? [];
70-
$label = $productLabelsStreamProduct['label'];
71-
$labelId = $label->getId();
70+
$productId = $product->getId();
71+
$sidworksProductLabels = $product->getExtension(self::SIDWORKS_PRODUCT_LABELS_EXTENSION) ?? new ArrayEntity();
7272

73-
$matchedMap = array_flip($matchedIds);
74-
if (!isset($matchedMap[$product->getId()])) {
73+
foreach ($productLabelsStreamProducts as $productLabelsStreamProduct) {
74+
if (!in_array($productId, $productLabelsStreamProduct['matchedProductIds'], true)) {
7575
continue;
7676
}
7777

78-
/** @var ArrayEntity $sidworksProductLabels */
79-
$sidworksProductLabels = $product->getExtension(self::SIDWORKS_PRODUCT_LABELS_EXTENSION) ?? new ArrayEntity();
80-
$sidworksProductLabels->set($labelId, $label);
78+
$label = $productLabelsStreamProduct['label'];
79+
$sidworksProductLabels->set($label->getId(), $label);
80+
}
8181

82+
if ($sidworksProductLabels->all()) {
8283
$product->addExtension(self::SIDWORKS_PRODUCT_LABELS_EXTENSION, $sidworksProductLabels);
8384
}
8485
}
8586

8687
private function fetchActiveProductLabels(Context $context): iterable
8788
{
89+
$salesChannelId = $context->getSource()->getSalesChannelId();
90+
$cacheKey = "active_labels_{$salesChannelId}";
91+
92+
if (isset($this->labelCache[$cacheKey])) {
93+
return $this->labelCache[$cacheKey];
94+
}
95+
8896
$criteria = new Criteria();
8997
$criteria->addFilter(new EqualsFilter('active', 1));
90-
$criteria->addFilter(new ContainsFilter('salesChannelIds', $context->getSource()->getSalesChannelId()));
98+
$criteria->addFilter(new ContainsFilter('salesChannelIds', $salesChannelId));
9199

92-
return $this->productLabelsRepository->search($criteria, $context)->getEntities();
100+
$result = $this->productLabelsRepository->search($criteria, $context)->getEntities();
101+
$this->labelCache[$cacheKey] = $result;
102+
103+
return $result;
93104
}
94105

95-
private function getMatchedProductIds(ProductLabelsEntity $productLabel, array $productIds, SalesChannelContext $context): array
96-
{
106+
private function getMatchedProductIds(
107+
ProductLabelsEntity $productLabel,
108+
array $productIds,
109+
SalesChannelContext $context,
110+
array $variantToParent
111+
): array {
97112
$matchedIds = [];
98113

99-
$selectedProducts = $productLabel->getSelectedProducts() ?? [];
100-
$selectedMatches = array_intersect($productIds, $selectedProducts);
101-
if (!empty($selectedMatches)) {
102-
$matchedIds = array_flip($selectedMatches);
114+
// Handle selected products
115+
if ($selectedProducts = $productLabel->getSelectedProducts()) {
116+
$matchedIds = array_flip(array_intersect($productIds, $selectedProducts));
103117
}
104118

105-
$filters = null;
106-
if ($productLabel->getProductStreamId()) {
107-
$filters = $this->productStreamBuilder->buildFilters(
108-
$productLabel->getProductStreamId(),
109-
$context->getContext()
110-
);
119+
// Handle product stream
120+
if ($streamId = $productLabel->getProductStreamId()) {
121+
$streamMatches = $this->matchProductsByStream($streamId, $productIds, $context);
122+
$matchedIds = array_merge($matchedIds, $streamMatches);
123+
}
111124

112-
$productIds[] = '18e3f68e6de24264b6e837098d16ac1c';
113-
$criteria = new Criteria($productIds);
114-
$criteria->addFilter(...$filters);
115-
$criteria->addFields(['id']);
125+
// Handle variants
126+
if ($this->shouldProcessVariants($selectedProducts, $productLabel->getProductStreamId())) {
127+
$variantMatches = $this->processVariantMatches($selectedProducts, $streamId, $variantToParent, $context);
128+
$matchedIds = array_merge($matchedIds, $variantMatches);
129+
}
130+
131+
return array_keys($matchedIds);
132+
}
116133

117-
$streamProducts = $this->productRepository->search($criteria, $context);
118-
$streamProductIds = $streamProducts->getEntities()->getIds();
134+
private function processVariantMatches(
135+
?array $selectedProducts,
136+
?string $streamId,
137+
array $variantToParent,
138+
SalesChannelContext $context
139+
): array {
140+
$matchedIds = [];
119141

120-
foreach ($streamProductIds as $id) {
121-
$matchedIds[$id] = true;
142+
if (!empty($selectedProducts)) {
143+
foreach (array_intersect(array_keys($variantToParent), $selectedProducts) as $variantId) {
144+
$matchedIds[$variantToParent[$variantId]] = true;
122145
}
123146
}
124147

125-
if ($this->shouldProcessVariants($selectedProducts, $productLabel->getProductStreamId())) {
126-
$variantToParent = $this->getVariantToParentMapping($productIds, $context);
127-
128-
if (!empty($variantToParent)) {
129-
if (!empty($selectedProducts)) {
130-
$variantMatches = array_intersect(array_keys($variantToParent), $selectedProducts);
131-
foreach ($variantMatches as $variantId) {
132-
$matchedIds[$variantToParent[$variantId]] = true;
133-
}
134-
}
135-
136-
if ($filters !== null) {
137-
$variantIds = array_keys($variantToParent);
138-
$variantStreamCriteria = new Criteria($variantIds);
139-
$variantStreamCriteria->addFilter(...$filters);
140-
$variantStreamCriteria->addFields(['id']);
141-
142-
$variantStreamProducts = $this->productRepository->search($variantStreamCriteria, $context);
143-
144-
foreach ($variantStreamProducts->getIds() as $variantId) {
145-
$matchedIds[$variantToParent[$variantId]] = true;
146-
}
147-
}
148+
if ($streamId && !empty($variantToParent)) {
149+
$streamVariantMatches = $this->matchVariantsByStream($streamId, array_keys($variantToParent), $variantToParent, $context);
150+
$matchedIds = array_merge($matchedIds, $streamVariantMatches);
151+
}
152+
153+
return $matchedIds;
154+
}
155+
156+
private function matchProductsByStream(string $streamId, array $productIds, SalesChannelContext $context): array
157+
{
158+
$cacheKey = $this->getStreamCacheKey($streamId, $productIds, $context->getSalesChannelId());
159+
160+
if (isset($this->streamMatchCache[$cacheKey])) {
161+
return $this->streamMatchCache[$cacheKey];
162+
}
163+
164+
$filters = $this->productStreamBuilder->buildFilters($streamId, $context->getContext());
165+
$criteria = new Criteria($this->ensureMinProductIds($productIds));
166+
$criteria->addFilter(...$filters)->addFields(['id']);
167+
168+
$matchedIds = [];
169+
foreach ($this->productRepository->search($criteria, $context)->getIds() as $id) {
170+
$matchedIds[$id] = true;
171+
}
172+
173+
$this->streamMatchCache[$cacheKey] = $matchedIds;
174+
return $matchedIds;
175+
}
176+
177+
private function matchVariantsByStream(string $streamId, array $variantIds, array $variantToParent, SalesChannelContext $context): array
178+
{
179+
$cacheKey = $this->getStreamCacheKey($streamId, $variantIds, $context->getSalesChannelId()) . '_variants';
180+
181+
if (isset($this->streamMatchCache[$cacheKey])) {
182+
return $this->streamMatchCache[$cacheKey];
183+
}
184+
185+
$filters = $this->productStreamBuilder->buildFilters($streamId, $context->getContext());
186+
$criteria = new Criteria($variantIds);
187+
$criteria->addFilter(...$filters)->addFields(['id']);
188+
189+
$matchedIds = [];
190+
foreach ($this->productRepository->search($criteria, $context)->getIds() as $variantId) {
191+
if (isset($variantToParent[$variantId])) {
192+
$matchedIds[$variantToParent[$variantId]] = true;
148193
}
149194
}
150195

151-
return array_keys($matchedIds);
196+
$this->streamMatchCache[$cacheKey] = $matchedIds;
197+
return $matchedIds;
152198
}
153199

154-
private function shouldProcessVariants(array $selectedProducts, ?string $productStreamId): bool
200+
private function shouldProcessVariants(?array $selectedProducts, ?string $productStreamId): bool
155201
{
156202
return !empty($selectedProducts) || $productStreamId !== null;
157203
}
158204

159205
private function getVariantToParentMapping(array $productIds, SalesChannelContext $context): array
160206
{
161-
$variantCriteria = new Criteria();
162-
$variantCriteria->addFilter(new EqualsAnyFilter('parentId', $productIds));
163-
$variantCriteria->addFields(['id', 'parentId']);
207+
if (empty($productIds)) {
208+
return [];
209+
}
164210

165-
$variantProducts = $this->productRepository->search($variantCriteria, $context);
211+
$cacheKey = $this->getCacheKey($productIds, $context->getSalesChannelId()) . '_variants';
166212

167-
if ($variantProducts->count() === 0) {
168-
return [];
213+
if (isset($this->variantMappingCache[$cacheKey])) {
214+
return $this->variantMappingCache[$cacheKey];
169215
}
170216

171-
$variantToParent = [];
217+
$criteria = new Criteria();
218+
$criteria->addFilter(new EqualsAnyFilter('parentId', $productIds));
219+
$criteria->addFields(['id', 'parentId']);
220+
221+
$variantProducts = $this->productRepository->search($criteria, $context);
222+
223+
$mapping = [];
172224
foreach ($variantProducts->getEntities() as $variant) {
173-
$variantId = $variant->get('id');
174-
$parentId = $variant->get('parentId');
175-
$variantToParent[$variantId] = $parentId;
225+
$mapping[$variant->get('id')] = $variant->get('parentId');
176226
}
177227

178-
return $variantToParent;
228+
$this->variantMappingCache[$cacheKey] = $mapping;
229+
return $mapping;
230+
}
231+
232+
private function ensureMinProductIds(array $productIds): array
233+
{
234+
return count($productIds) === 1
235+
? array_merge($productIds, ['00000000000000000000000000000000'])
236+
: $productIds;
179237
}
180238

181239
public function shouldShowProductLabel(ProductLabelsEntity $productLabel): bool
@@ -191,8 +249,19 @@ public function shouldShowProductLabel(ProductLabelsEntity $productLabel): bool
191249
return match (true) {
192250
$from && $from > $now => false,
193251
$to && $to < $now => false,
194-
$from && $from <= $now && (!$to || $to >= $now) => true,
195-
default => $productLabel->getActive()
252+
default => true,
196253
};
197254
}
255+
256+
private function getCacheKey(array $productIds, string $salesChannelId): string
257+
{
258+
sort($productIds); // Ensure consistent ordering for cache keys
259+
return md5(implode(',', $productIds) . $salesChannelId);
260+
}
261+
262+
private function getStreamCacheKey(string $streamId, array $productIds, string $salesChannelId): string
263+
{
264+
sort($productIds);
265+
return md5($streamId . implode(',', $productIds) . $salesChannelId);
266+
}
198267
}

0 commit comments

Comments
 (0)