Passed
Push — trunk ( 2421eb...d79bc7 )
by Christian
14:27 queued 01:28
created

SnippetService   F

Complexity

Total Complexity 75

Size/Duplication

Total Lines 614
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 242
dl 0
loc 614
rs 2.4
c 0
b 0
f 0
wmc 75

24 Methods

Rating   Name   Duplication   Size   Complexity  
A getSnippetsByLocale() 0 22 4
A getRegionFilterItems() 0 34 6
A __construct() 0 14 1
A getAuthors() 0 25 4
A getList() 0 30 4
B sortSnippets() 0 40 11
A createIsoList() 0 9 2
A getUnusedThemes() 0 11 2
A mergeSnippetsComparison() 0 10 3
A fetchSnippetsFromDatabase() 0 8 1
A getSetMetaData() 0 12 2
A getSnippetFilesByIso() 0 8 2
A findSnippetSetId() 0 29 3
A getUsedThemes() 0 17 3
A getStorefrontSnippets() 0 35 3
A flatten() 0 25 5
A fillBlankSnippets() 0 27 6
A getSnippetsFromFiles() 0 24 3
A findSnippetSetInDatabase() 0 3 1
A getLocaleBySnippetSetId() 0 11 2
A getFileSnippets() 0 9 2
A getSnippetSet() 0 21 2
A findSnippetInDatabase() 0 3 1
A databaseSnippetsToArray() 0 22 2

How to fix   Complexity   

Complex Class

Complex classes like SnippetService 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.

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 SnippetService, and based on these observations, apply Extract Interface, too.

1
<?php declare(strict_types=1);
2
3
namespace Shopware\Core\System\Snippet;
4
5
use Doctrine\DBAL\ArrayParameterType;
6
use Doctrine\DBAL\Connection;
7
use Shopware\Core\Framework\Context;
8
use Shopware\Core\Framework\DataAbstractionLayer\Entity;
9
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
10
use Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Bucket\TermsAggregation;
11
use Shopware\Core\Framework\DataAbstractionLayer\Search\AggregationResult\Bucket\TermsResult;
12
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
13
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
14
use Shopware\Core\Framework\Log\Package;
15
use Shopware\Core\Framework\Uuid\Uuid;
16
use Shopware\Core\System\SalesChannel\Aggregate\SalesChannelDomain\SalesChannelDomainEntity;
0 ignored issues
show
Bug introduced by
The type Shopware\Core\System\Sal...alesChannelDomainEntity was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
17
use Shopware\Core\System\Snippet\Aggregate\SnippetSet\SnippetSetEntity;
0 ignored issues
show
Bug introduced by
The type Shopware\Core\System\Sni...petSet\SnippetSetEntity was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use Shopware\Core\System\Snippet\Files\AbstractSnippetFile;
19
use Shopware\Core\System\Snippet\Files\SnippetFileCollection;
20
use Shopware\Core\System\Snippet\Filter\SnippetFilterFactory;
21
use Shopware\Storefront\Theme\SalesChannelThemeLoader;
22
use Shopware\Storefront\Theme\StorefrontPluginConfiguration\StorefrontPluginConfiguration;
23
use Shopware\Storefront\Theme\StorefrontPluginRegistry;
24
use Symfony\Component\DependencyInjection\ContainerInterface;
25
use Symfony\Component\Translation\MessageCatalogueInterface;
26
27
#[Package('system-settings')]
28
class SnippetService
29
{
30
    /**
31
     * @internal
32
     */
33
    public function __construct(
34
        private readonly Connection $connection,
35
        private readonly SnippetFileCollection $snippetFileCollection,
36
        private readonly EntityRepository $snippetRepository,
37
        private readonly EntityRepository $snippetSetRepository,
38
        private readonly EntityRepository $salesChannelDomain,
39
        private readonly SnippetFilterFactory $snippetFilterFactory,
40
        /**
41
         * The "kernel" service is synthetic, it needs to be set at boot time before it can be used.
42
         * We need to get StorefrontPluginRegistry service from service_container lazily because it depends on kernel service.
43
         */
44
        private readonly ContainerInterface $container,
45
        private readonly ?SalesChannelThemeLoader $salesChannelThemeLoader = null
46
    ) {
47
    }
48
49
    /**
50
     * filters: [
51
     *      'isCustom' => bool,
52
     *      'isEmpty' => bool,
53
     *      'term' => string,
54
     *      'namespaces' => array,
55
     *      'authors' => array,
56
     *      'translationKeys' => array,
57
     * ]
58
     *
59
     * sort: [
60
     *      'column' => NULL || the string -> 'translationKey' || setId
61
     *      'direction' => 'ASC' || 'DESC'
62
     * ]
63
     *
64
     * @param int<1, max> $limit
65
     * @param array<string, bool|string|array<int, string>> $requestFilters
66
     * @param array<string, string> $sort
67
     *
68
     * @return array{total:int, data: array<string, array<int, array<string, string|null>>>}
69
     */
70
    public function getList(int $page, int $limit, Context $context, array $requestFilters, array $sort): array
71
    {
72
        --$page;
73
        /** @var array<string, array{iso: string, id: string}> $metaData */
74
        $metaData = $this->getSetMetaData($context);
75
76
        $isoList = $this->createIsoList($metaData);
77
        $languageFiles = $this->getSnippetFilesByIso($isoList);
78
79
        $fileSnippets = $this->getFileSnippets($languageFiles, $isoList);
80
        $dbSnippets = $this->databaseSnippetsToArray($this->findSnippetInDatabase(new Criteria(), $context), $fileSnippets);
81
82
        $snippets = array_replace_recursive($fileSnippets, $dbSnippets);
83
        $snippets = $this->fillBlankSnippets($snippets, $isoList);
84
85
        foreach ($requestFilters as $requestFilterName => $requestFilterValue) {
86
            $snippets = $this->snippetFilterFactory->getFilter($requestFilterName)->filter($snippets, $requestFilterValue);
87
        }
88
89
        $snippets = $this->sortSnippets($sort, $snippets);
90
91
        $total = 0;
92
        foreach ($snippets as &$set) {
93
            $total = $total > 0 ? $total : \count($set['snippets']);
94
            $set['snippets'] = array_chunk($set['snippets'], $limit, true)[$page] ?? [];
95
        }
96
97
        return [
98
            'total' => $total,
99
            'data' => $this->mergeSnippetsComparison($snippets),
100
        ];
101
    }
102
103
    /**
104
     * @return array<string, string>
105
     */
106
    public function getStorefrontSnippets(MessageCatalogueInterface $catalog, string $snippetSetId, ?string $fallbackLocale = null, ?string $salesChannelId = null): array
107
    {
108
        $locale = $this->getLocaleBySnippetSetId($snippetSetId);
109
110
        $snippets = [];
111
112
        $snippetFileCollection = clone $this->snippetFileCollection;
113
114
        $usingThemes = $this->getUsedThemes($salesChannelId);
115
        $unusedThemes = $this->getUnusedThemes($usingThemes);
116
        $snippetCollection = $snippetFileCollection->filter(fn (AbstractSnippetFile $snippetFile) => !\in_array($snippetFile->getTechnicalName(), $unusedThemes, true));
117
118
        $fallbackSnippets = [];
119
120
        if ($fallbackLocale !== null) {
121
            // fallback has to be the base
122
            $snippets = $fallbackSnippets = $this->getSnippetsByLocale($snippetCollection, $fallbackLocale);
123
        }
124
125
        // now override fallback with defaults in catalog
126
        $snippets = array_replace_recursive(
127
            $snippets,
128
            $catalog->all('messages')
129
        );
130
131
        // after fallback and default catalog merged, overwrite them with current locale snippets
132
        $snippets = array_replace_recursive(
133
            $snippets,
134
            $locale === $fallbackLocale ? $fallbackSnippets : $this->getSnippetsByLocale($snippetCollection, $locale)
135
        );
136
137
        // at least overwrite the snippets with the database customer overwrites
138
        return array_replace_recursive(
139
            $snippets,
140
            $this->fetchSnippetsFromDatabase($snippetSetId, $unusedThemes)
141
        );
142
    }
143
144
    /**
145
     * @return array<int, string>
146
     */
147
    public function getRegionFilterItems(Context $context): array
148
    {
149
        /** @var array<string, array{iso: string, id: string}> $metaData */
150
        $metaData = $this->getSetMetaData($context);
151
        $isoList = $this->createIsoList($metaData);
152
        $snippetFiles = $this->getSnippetFilesByIso($isoList);
153
154
        $criteria = new Criteria();
155
        $dbSnippets = $this->findSnippetInDatabase($criteria, $context);
156
157
        $result = [];
158
        foreach ($snippetFiles as $files) {
159
            foreach ($this->getSnippetsFromFiles($files, '') as $namespace => $_value) {
160
                $region = explode('.', $namespace)[0];
161
                if (\in_array($region, $result, true)) {
162
                    continue;
163
                }
164
165
                $result[] = $region;
166
            }
167
        }
168
169
        /** @var SnippetEntity $snippet */
170
        foreach ($dbSnippets as $snippet) {
171
            $region = explode('.', $snippet->getTranslationKey())[0];
172
            if (\in_array($region, $result, true)) {
173
                continue;
174
            }
175
176
            $result[] = $region;
177
        }
178
        sort($result);
179
180
        return $result;
181
    }
182
183
    /**
184
     * @return array<int, int|string>
185
     */
186
    public function getAuthors(Context $context): array
187
    {
188
        $files = $this->snippetFileCollection->toArray();
189
190
        $criteria = new Criteria();
191
        $criteria->addAggregation(new TermsAggregation('distinct_author', 'author'));
192
193
        /** @var TermsResult|null $aggregation */
194
        $aggregation = $this->snippetRepository->aggregate($criteria, $context)
195
                ->get('distinct_author');
196
197
        if (!$aggregation || empty($aggregation->getBuckets())) {
0 ignored issues
show
introduced by
$aggregation is of type Shopware\Core\Framework\...sult\Bucket\TermsResult, thus it always evaluated to true.
Loading history...
198
            $result = [];
199
        } else {
200
            $result = $aggregation->getKeys();
201
        }
202
203
        $authors = array_flip($result);
204
        foreach ($files as $file) {
205
            $authors[$file['author']] = true;
206
        }
207
        $result = array_keys($authors);
208
        sort($result);
209
210
        return $result;
211
    }
212
213
    /**
214
     * @decrecated tag:v6.6.0 - will be removed, use findSnippetSetId instead
215
     */
216
    public function getSnippetSet(string $salesChannelId, string $languageId, string $locale, Context $context): ?SnippetSetEntity
217
    {
218
        $criteria = new Criteria();
219
        $criteria->addFilter(
220
            new EqualsFilter('salesChannelId', $salesChannelId),
221
            new EqualsFilter('languageId', $languageId)
222
        );
223
        $criteria->addAssociation('snippetSet');
224
225
        /** @var SalesChannelDomainEntity|null $salesChannelDomain */
226
        $salesChannelDomain = $this->salesChannelDomain->search($criteria, $context)->first();
227
228
        if ($salesChannelDomain === null) {
229
            $criteria = new Criteria();
230
            $criteria->addFilter(new EqualsFilter('iso', $locale));
231
            $snippetSet = $this->snippetSetRepository->search($criteria, $context)->first();
232
        } else {
233
            $snippetSet = $salesChannelDomain->getSnippetSet();
234
        }
235
236
        return $snippetSet;
237
    }
238
239
    public function findSnippetSetId(string $salesChannelId, string $languageId, string $locale): string
240
    {
241
        $snippetSetId = $this->connection->fetchOne(
242
            'SELECT LOWER(HEX(`snippet_set`.`id`))
243
            FROM `sales_channel_domain`
244
            INNER JOIN `snippet_set` ON `sales_channel_domain`.`snippet_set_id` = `snippet_set`.`id`
245
            WHERE `sales_channel_domain`.`sales_channel_id` = :salesChannelId AND `sales_channel_domain`.`language_id` = :languageId
246
            LIMIT 1',
247
            [
248
                'salesChannelId' => Uuid::fromHexToBytes($salesChannelId),
249
                'languageId' => Uuid::fromHexToBytes($languageId),
250
            ]
251
        );
252
253
        if ($snippetSetId) {
254
            return $snippetSetId;
255
        }
256
257
        $sets = $this->connection->fetchAllKeyValue(
258
            'SELECT iso, LOWER(HEX(id)) FROM snippet_set WHERE iso IN (:locales) LIMIT 2',
259
            ['locales' => array_unique([$locale, 'en-GB'])],
260
            ['locales' => ArrayParameterType::STRING]
261
        );
262
263
        if (isset($sets[$locale])) {
264
            return $sets[$locale];
265
        }
266
267
        return array_pop($sets);
268
    }
269
270
    /**
271
     * @param list<string> $usingThemes
0 ignored issues
show
Bug introduced by
The type Shopware\Core\System\Snippet\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
272
     *
273
     * @return list<string>
274
     */
275
    protected function getUnusedThemes(array $usingThemes = []): array
276
    {
277
        if (!$this->container->has(StorefrontPluginRegistry::class)) {
278
            return [];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array() returns the type array which is incompatible with the documented return type Shopware\Core\System\Snippet\list.
Loading history...
279
        }
280
281
        $themeRegistry = $this->container->get(StorefrontPluginRegistry::class);
282
283
        $unusedThemes = $themeRegistry->getConfigurations()->getThemes()->filter(fn (StorefrontPluginConfiguration $theme) => !\in_array($theme->getTechnicalName(), $usingThemes, true))->map(fn (StorefrontPluginConfiguration $theme) => $theme->getTechnicalName());
284
285
        return array_values($unusedThemes);
0 ignored issues
show
Bug Best Practice introduced by
The expression return array_values($unusedThemes) returns the type array which is incompatible with the documented return type Shopware\Core\System\Snippet\list.
Loading history...
286
    }
287
288
    /**
289
     * Second parameter $unusedThemes is used for external dependencies
290
     *
291
     * @param list<string> $unusedThemes
292
     *
293
     * @return array<string, string>
294
     */
295
    protected function fetchSnippetsFromDatabase(string $snippetSetId, array $unusedThemes = []): array
296
    {
297
        /** @var array<string, string> $snippets */
298
        $snippets = $this->connection->fetchAllKeyValue('SELECT translation_key, value FROM snippet WHERE snippet_set_id = :snippetSetId', [
299
            'snippetSetId' => Uuid::fromHexToBytes($snippetSetId),
300
        ]);
301
302
        return $snippets;
303
    }
304
305
    /**
306
     * @return array<string, string>
307
     */
308
    private function getSnippetsByLocale(SnippetFileCollection $snippetFileCollection, string $locale): array
309
    {
310
        $files = $snippetFileCollection->getSnippetFilesByIso($locale);
311
        $snippets = [];
312
313
        foreach ($files as $file) {
314
            $json = json_decode(file_get_contents($file->getPath()) ?: '', true);
315
316
            $jsonError = json_last_error();
317
            if ($jsonError !== 0) {
318
                throw new \RuntimeException(sprintf('Invalid JSON in snippet file at path \'%s\' with code \'%d\'', $file->getPath(), $jsonError));
319
            }
320
321
            $flattenSnippetFileSnippets = $this->flatten($json);
322
323
            $snippets = array_replace_recursive(
324
                $snippets,
325
                $flattenSnippetFileSnippets
326
            );
327
        }
328
329
        return $snippets;
330
    }
331
332
    /**
333
     * @return list<string>
334
     */
335
    private function getUsedThemes(?string $salesChannelId = null): array
336
    {
337
        if (!$salesChannelId || $this->salesChannelThemeLoader === null) {
338
            return [StorefrontPluginRegistry::BASE_THEME_NAME];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array(Shopware\St...istry::BASE_THEME_NAME) returns the type array<integer,string> which is incompatible with the documented return type Shopware\Core\System\Snippet\list.
Loading history...
339
        }
340
341
        $saleChannelThemes = $this->salesChannelThemeLoader->load($salesChannelId);
342
343
        $usedThemes = array_filter([
344
            $saleChannelThemes['themeName'] ?? null,
345
            $saleChannelThemes['parentThemeName'] ?? null,
346
        ]);
347
348
        /** @var list<string> */
349
        return array_values(array_unique([
0 ignored issues
show
Bug Best Practice introduced by
The expression return array_values(arra...try::BASE_THEME_NAME))) returns the type array which is incompatible with the documented return type Shopware\Core\System\Snippet\list.
Loading history...
350
            ...$usedThemes,
351
            StorefrontPluginRegistry::BASE_THEME_NAME, // Storefront snippets should always be loaded
352
        ]));
353
    }
354
355
    /**
356
     * @param array<string, string> $isoList
357
     *
358
     * @return array<string, array<int, AbstractSnippetFile>>
359
     */
360
    private function getSnippetFilesByIso(array $isoList): array
361
    {
362
        $result = [];
363
        foreach ($isoList as $iso) {
364
            $result[$iso] = $this->snippetFileCollection->getSnippetFilesByIso($iso);
365
        }
366
367
        return $result;
368
    }
369
370
    /**
371
     * @param array<int, AbstractSnippetFile> $languageFiles
372
     *
373
     * @return array<string, array<string, string|null>>
374
     */
375
    private function getSnippetsFromFiles(array $languageFiles, string $setId): array
376
    {
377
        $result = [];
378
        foreach ($languageFiles as $snippetFile) {
379
            $json = json_decode((string) file_get_contents($snippetFile->getPath()), true);
380
381
            $jsonError = json_last_error();
382
            if ($jsonError !== 0) {
383
                throw new \RuntimeException(sprintf('Invalid JSON in snippet file at path \'%s\' with code \'%d\'', $snippetFile->getPath(), $jsonError));
384
            }
385
386
            $flattenSnippetFileSnippets = $this->flatten(
387
                $json,
388
                '',
389
                ['author' => $snippetFile->getAuthor(), 'id' => null, 'setId' => $setId]
390
            );
391
392
            $result = array_replace_recursive(
393
                $result,
394
                $flattenSnippetFileSnippets
395
            );
396
        }
397
398
        return $result;
399
    }
400
401
    /**
402
     * @param array<string, array<string, array<string, array<string, string|null>>>> $sets
403
     *
404
     * @return array<string, array<int, array<string, string|null>>>
405
     */
406
    private function mergeSnippetsComparison(array $sets): array
407
    {
408
        $result = [];
409
        foreach ($sets as $snippetSet) {
410
            foreach ($snippetSet['snippets'] as $translationKey => $snippet) {
411
                $result[$translationKey][] = $snippet;
412
            }
413
        }
414
415
        return $result;
416
    }
417
418
    private function getLocaleBySnippetSetId(string $snippetSetId): string
419
    {
420
        $locale = $this->connection->fetchOne('SELECT iso FROM snippet_set WHERE id = :snippetSetId', [
421
            'snippetSetId' => Uuid::fromHexToBytes($snippetSetId),
422
        ]);
423
424
        if ($locale === false) {
425
            throw new \InvalidArgumentException(sprintf('No snippetSet with id "%s" found', $snippetSetId));
426
        }
427
428
        return (string) $locale;
429
    }
430
431
    /**
432
     * @param array<string, array<string, array<string, array<string, string|null>>>> $fileSnippets
433
     * @param array<string, string> $isoList
434
     *
435
     * @return array<string, array<string, array<string, array<string, string|null>>>>
436
     */
437
    private function fillBlankSnippets(array $fileSnippets, array $isoList): array
438
    {
439
        foreach ($isoList as $setId => $_iso) {
440
            foreach ($isoList as $currentSetId => $_currentIso) {
441
                if ($setId === $currentSetId) {
442
                    continue;
443
                }
444
445
                foreach ($fileSnippets[$setId]['snippets'] as $index => $_snippet) {
446
                    if (!isset($fileSnippets[$currentSetId]['snippets'][$index])) {
447
                        $fileSnippets[$currentSetId]['snippets'][$index] = [
448
                            'value' => '',
449
                            'translationKey' => $index,
450
                            'author' => '',
451
                            'origin' => '',
452
                            'resetTo' => '',
453
                            'setId' => $currentSetId,
454
                            'id' => null,
455
                        ];
456
                    }
457
                }
458
459
                ksort($fileSnippets[$currentSetId]['snippets']);
460
            }
461
        }
462
463
        return $fileSnippets;
464
    }
465
466
    /**
467
     * @param array<string, array<int, AbstractSnippetFile>> $languageFiles
468
     * @param array<string, string> $isoList
469
     *
470
     * @return array<string, array<string, array<string, array<string, string|null>>>>
471
     */
472
    private function getFileSnippets(array $languageFiles, array $isoList): array
473
    {
474
        $fileSnippets = [];
475
476
        foreach ($isoList as $setId => $iso) {
477
            $fileSnippets[$setId]['snippets'] = $this->getSnippetsFromFiles($languageFiles[$iso], $setId);
478
        }
479
480
        return $fileSnippets;
481
    }
482
483
    /**
484
     * @param array<string, array{iso: string, id: string}> $metaData
485
     *
486
     * @return array<string, string>
487
     */
488
    private function createIsoList(array $metaData): array
489
    {
490
        $isoList = [];
491
492
        foreach ($metaData as $set) {
493
            $isoList[$set['id']] = $set['iso'];
494
        }
495
496
        return $isoList;
497
    }
498
499
    /**
500
     * @return array<string, array<mixed>>
501
     */
502
    private function getSetMetaData(Context $context): array
503
    {
504
        $queryResult = $this->findSnippetSetInDatabase(new Criteria(), $context);
505
506
        /** @var array<string, array{iso: string, id: string}> $result */
507
        $result = [];
508
        /** @var SnippetSetEntity $value */
509
        foreach ($queryResult as $key => $value) {
510
            $result[$key] = $value->jsonSerialize();
511
        }
512
513
        return $result;
514
    }
515
516
    /**
517
     * @param array<string, Entity> $queryResult
518
     * @param array<string, array<string, array<string, array<string, string|null>>>> $fileSnippets
519
     *
520
     * @return array<string, array<string, array<string, array<string, string|null>>>>
521
     */
522
    private function databaseSnippetsToArray(array $queryResult, array $fileSnippets): array
523
    {
524
        $result = [];
525
        /** @var SnippetEntity $snippet */
526
        foreach ($queryResult as $snippet) {
527
            $currentSnippet = array_intersect_key(
528
                $snippet->jsonSerialize(),
529
                array_flip([
530
                    'author',
531
                    'id',
532
                    'setId',
533
                    'translationKey',
534
                    'value',
535
                ])
536
            );
537
538
            $currentSnippet['origin'] = '';
539
            $currentSnippet['resetTo'] = $fileSnippets[$snippet->getSetId()]['snippets'][$snippet->getTranslationKey()]['origin'] ?? $snippet->getValue();
540
            $result[$snippet->getSetId()]['snippets'][$snippet->getTranslationKey()] = $currentSnippet;
541
        }
542
543
        return $result;
544
    }
545
546
    /**
547
     * @return array<string, Entity>
548
     */
549
    private function findSnippetInDatabase(Criteria $criteria, Context $context): array
550
    {
551
        return $this->snippetRepository->search($criteria, $context)->getEntities()->getElements();
552
    }
553
554
    /**
555
     * @return array<string, Entity>
556
     */
557
    private function findSnippetSetInDatabase(Criteria $criteria, Context $context): array
558
    {
559
        return $this->snippetSetRepository->search($criteria, $context)->getEntities()->getElements();
560
    }
561
562
    /**
563
     * @param array<string, string> $sort
564
     * @param array<string, array<string, array<string, array<string, string|null>>>> $snippets
565
     *
566
     * @return array<string, array<string, array<string, array<string, string|null>>>>
567
     */
568
    private function sortSnippets(array $sort, array $snippets): array
569
    {
570
        if (!isset($sort['sortBy'], $sort['sortDirection'])) {
571
            return $snippets;
572
        }
573
574
        if ($sort['sortBy'] === 'translationKey' || $sort['sortBy'] === 'id') {
575
            foreach ($snippets as &$set) {
576
                if ($sort['sortDirection'] === 'ASC') {
577
                    ksort($set['snippets']);
578
                } elseif ($sort['sortDirection'] === 'DESC') {
579
                    krsort($set['snippets']);
580
                }
581
            }
582
583
            return $snippets;
584
        }
585
586
        if (!isset($snippets[$sort['sortBy']])) {
587
            return $snippets;
588
        }
589
590
        $mainSet = $snippets[$sort['sortBy']];
591
        unset($snippets[$sort['sortBy']]);
592
593
        uasort($mainSet['snippets'], static function ($a, $b) use ($sort) {
594
            $a = mb_strtolower((string) $a['value']);
595
            $b = mb_strtolower((string) $b['value']);
596
597
            return $sort['sortDirection'] !== 'DESC' ? $a <=> $b : $b <=> $a;
598
        });
599
600
        $result = [$sort['sortBy'] => $mainSet];
601
        foreach ($snippets as $setId => $set) {
602
            foreach ($mainSet['snippets'] as $currentKey => $_value) {
603
                $result[$setId]['snippets'][$currentKey] = $set['snippets'][$currentKey];
604
            }
605
        }
606
607
        return $result;
608
    }
609
610
    /**
611
     * @param array<string, string|array<string, mixed>> $array
612
     * @param array<string, string|null>|null $additionalParameters
613
     *
614
     * @return array<string, string|array<string, mixed>>
615
     */
616
    private function flatten(array $array, string $prefix = '', ?array $additionalParameters = null): array
617
    {
618
        $result = [];
619
        foreach ($array as $index => $value) {
620
            $newIndex = $prefix . (empty($prefix) ? '' : '.') . $index;
621
622
            if (\is_array($value)) {
623
                $result = [...$result, ...$this->flatten($value, $newIndex, $additionalParameters)];
624
            } else {
625
                if (!empty($additionalParameters)) {
626
                    $result[$newIndex] = array_merge([
627
                        'value' => $value,
628
                        'origin' => $value,
629
                        'resetTo' => $value,
630
                        'translationKey' => $newIndex,
631
                    ], $additionalParameters);
632
633
                    continue;
634
                }
635
636
                $result[$newIndex] = $value;
637
            }
638
        }
639
640
        return $result;
641
    }
642
}
643