• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

sulu / SuluContentBundle / 11978668947

22 Nov 2024 07:11PM UTC coverage: 97.052% (-2.9%) from 100.0%
11978668947

Pull #271

github

web-flow
Merge 85d51e611 into a23a5b050
Pull Request #271: Draft: Base implementation of website content resolving

268 of 353 new or added lines in 17 files covered. (75.92%)

2798 of 2883 relevant lines covered (97.05%)

10.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.9
/Content/Application/ContentResolver/ContentResolver.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of Sulu.
7
 *
8
 * (c) Sulu GmbH
9
 *
10
 * This source file is subject to the MIT license that is bundled
11
 * with this source code in the file LICENSE.
12
 */
13

14
namespace Sulu\Bundle\ContentBundle\Content\Application\ContentResolver;
15

16
use Sulu\Bundle\ContentBundle\Content\Application\ContentResolver\Resolver\ResolverInterface;
17
use Sulu\Bundle\ContentBundle\Content\Application\ContentResolver\Resolver\TemplateResolver;
18
use Sulu\Bundle\ContentBundle\Content\Application\ContentResolver\Value\ContentView;
19
use Sulu\Bundle\ContentBundle\Content\Application\ContentResolver\Value\ResolvableResource;
20
use Sulu\Bundle\ContentBundle\Content\Application\ResourceLoader\ResourceLoaderProvider;
21
use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentInterface;
22

23
class ContentResolver implements ContentResolverInterface
24
{
25
    /**
26
     * @param iterable<ResolverInterface> $contentResolvers
27
     */
28
    public function __construct(
29
        private iterable $contentResolvers,
30
        private ResourceLoaderProvider $resourceLoaderProvider
31
    ) {
32
    }
2✔
33

34
    /**
35
     * @return array{
36
     *      resource: object,
37
     *      content: mixed,
38
     *      view: mixed[]
39
     *  }
40
     */
41
    public function resolve(DimensionContentInterface $dimensionContent): array
42
    {
43
        $contentViews = [];
2✔
44
        foreach ($this->contentResolvers as $resolverKey => $contentResolver) {
2✔
45
            $contentView = $contentResolver->resolve($dimensionContent);
2✔
46

47
            if ($contentResolver instanceof TemplateResolver) {
2✔
48
                /** @var mixed[] $content */
49
                $content = $contentView->getContent();
2✔
50
                $contentViews = \array_merge($contentViews, $content);
2✔
51
                continue;
2✔
52
            }
53

54
            $contentViews[$resolverKey] = $contentView;
2✔
55
        }
56
        $resolvedContent = $this->resolveContentViews($contentViews);
2✔
57
        $resolvedResources = $this->loadAndResolveResources($resolvedContent['resolvableResources'], $dimensionContent->getLocale());
2✔
58
        $content = $this->replaceResolvableResourcesWithResolvedValues($resolvedContent['content'], $resolvedResources);
2✔
59

60
        return [
2✔
61
            'resource' => $dimensionContent->getResource(),
2✔
62
            'content' => $content,
2✔
63
            'view' => $resolvedContent['view'],
2✔
64
        ];
2✔
65
    }
66

67
    /**
68
     * @param ContentView[] $contentViews
69
     *
70
     * @return array{
71
     *     content: mixed[],
72
     *     view: mixed[],
73
     *     resolvableResources: array<string, array<ResolvableResource>>
74
     *     }
75
     */
76
    private function resolveContentViews(array $contentViews): array
77
    {
78
        $content = [];
2✔
79
        $view = [];
2✔
80
        $resolvableResources = [];
2✔
81

82
        foreach ($contentViews as $name => $contentView) {
2✔
83
            $result = $this->resolveContentView($contentView, (string) $name);
2✔
84
            $content = \array_merge($content, $result['content']);
2✔
85
            $view = \array_merge($view, $result['view']);
2✔
86
            $resolvableResources = \array_merge_recursive($resolvableResources, $result['resolvableResources']);
2✔
87
        }
88

89
        return [
2✔
90
            'content' => $content,
2✔
91
            'view' => $view,
2✔
92
            'resolvableResources' => $resolvableResources,
2✔
93
        ];
2✔
94
    }
95

96
    /**
97
     * @return array{
98
     *     content: mixed[],
99
     *     view: mixed[],
100
     *     resolvableResources: array<string, array<ResolvableResource>>
101
     *     }
102
     */
103
    private function resolveContentView(ContentView $contentView, string $name): array
104
    {
105
        $content = $contentView->getContent();
2✔
106
        $view = $contentView->getView();
2✔
107

108
        $result = [
2✔
109
            'content' => [],
2✔
110
            'view' => [],
2✔
111
            'resolvableResources' => [],
2✔
112
        ];
2✔
113
        if (\is_array($content)) {
2✔
114
            if (\count(\array_filter($content, fn ($entry) => $entry instanceof ContentView)) === \count($content)) {
2✔
115
                // resolve array of content views
116
                $resolvedContentViews = $this->resolveContentViews($content);
2✔
117
                $result['content'][$name] = $resolvedContentViews['content'];
2✔
118
                $result['view'][$name] = $resolvedContentViews['view'];
2✔
119
                $result['resolvableResources'] = \array_merge_recursive($result['resolvableResources'], $resolvedContentViews['resolvableResources']);
2✔
120

121
                return $result;
2✔
122
            }
123

124
            $resolvableResources = [];
2✔
125
            foreach ($content as $key => $entry) {
2✔
126
                // resolve array of mixed content
127
                if ($entry instanceof ContentView) {
2✔
NEW
128
                    $resolvedContentView = $this->resolveContentView($entry, $key);
×
NEW
129
                    $result['content'][$name] = \array_merge($result['content'][$name] ?? [], $resolvedContentView['content']);
×
NEW
130
                    $result['view'][$name] = \array_merge($result['view'][$name] ?? [], $resolvedContentView['view']);
×
NEW
131
                    $resolvableResources = \array_merge_recursive($resolvableResources, $resolvedContentView['resolvableResources']);
×
132

NEW
133
                    continue;
×
134
                }
135

136
                if ($entry instanceof ResolvableResource) {
2✔
NEW
137
                    $resolvableResources[$entry->getResourceLoaderKey()][] = $entry;
×
138
                }
139

140
                $result['content'][$name][$key] = $entry;
2✔
141
                $result['view'][$name][$key] = [];
2✔
142
            }
143

144
            $result['resolvableResources'] = $resolvableResources;
2✔
145

146
            return $result;
2✔
147
        }
148

149
        if ($content instanceof ResolvableResource) {
2✔
NEW
150
            $result['resolvableResources'][$content->getResourceLoaderKey()][] = $content;
×
151
        }
152

153
        $result['content'][$name] = $content;
2✔
154
        $result['view'][$name] = $view;
2✔
155

156
        return $result;
2✔
157
    }
158

159
    /**
160
     * Loads and resolves resources from various resource loaders.
161
     *
162
     * @param array<string, array<ResolvableResource>> $resourcesPerLoader Resource loaders and their associated resources to load
163
     *
164
     * @return array<string, mixed[]> Resolved resources organized by resource loader key
165
     */
166
    private function loadAndResolveResources(array $resourcesPerLoader, ?string $locale): array
167
    {
168
        $resolvedResources = [];
2✔
169

170
        foreach ($resourcesPerLoader as $loaderKey => $resourcesToLoad) {
2✔
NEW
171
            if (!$loaderKey) {
×
NEW
172
                throw new \RuntimeException(\sprintf('ResourceLoader key "%s" is invalid', $loaderKey));
×
173
            }
174

NEW
175
            $resourceLoader = $this->resourceLoaderProvider->getResourceLoader($loaderKey);
×
NEW
176
            if (!$resourceLoader) {
×
NEW
177
                throw new \RuntimeException(\sprintf('ResourceLoader with key "%s" not found', $loaderKey));
×
178
            }
179

NEW
180
            $resourceIds = \array_map(fn (ResolvableResource $resource) => $resource->getId(), $resourcesToLoad);
×
NEW
181
            $resolvedResources[$loaderKey] = $resourceLoader->load(
×
NEW
182
                $resourceIds,
×
NEW
183
                $locale
×
NEW
184
            );
×
185
        }
186

187
        return $resolvedResources;
2✔
188
    }
189

190
    /**
191
     * Replaces all instances of ResolvableResource in the given content with their resolved values.
192
     *
193
     * @param mixed[] $content The content to replace ResolvableResource instances
194
     * @param array<string, mixed[]> $resolvedResources The resolved resources, indexed by resource loader key and objectHash
195
     *
196
     * @return mixed[] The content with all ResolvableResource instances replaced with their resolved values
197
     */
198
    private function replaceResolvableResourcesWithResolvedValues(array $content, array $resolvedResources): array
199
    {
200
        \array_walk_recursive($content, function(&$value) use ($resolvedResources) {
2✔
201
            if ($value instanceof ResolvableResource) {
2✔
NEW
202
                $value = $value->executeResourceCallback(
×
NEW
203
                    $resolvedResources[$value->getResourceLoaderKey()][$value->getId()]
×
NEW
204
                );
×
205
            }
206
        });
2✔
207

208
        return $content;
2✔
209
    }
210
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc