Completed
Branch v4 (4e54dd)
by Pieter
03:26
created

Apie::getUrlForResource()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace W2w\Lib\Apie;
4
5
use Doctrine\Common\Annotations\Reader;
6
use erasys\OpenApi\Spec\v3\Document;
7
use erasys\OpenApi\Spec\v3\Info;
8
use Psr\Cache\CacheItemPoolInterface;
9
use Symfony\Component\PropertyAccess\PropertyAccessor;
10
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
11
use Symfony\Component\Serializer\Encoder\DecoderInterface;
12
use Symfony\Component\Serializer\Encoder\EncoderInterface;
13
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
14
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
15
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
16
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
17
use W2w\Lib\Apie\Core\ApieCore;
18
use W2w\Lib\Apie\Core\ApiResourceFacade;
19
use W2w\Lib\Apie\Core\ApiResourceMetadataFactory;
20
use W2w\Lib\Apie\Core\Bridge\ChainedFrameworkConnection;
21
use W2w\Lib\Apie\Core\Bridge\FrameworkLessConnection;
22
use W2w\Lib\Apie\Core\ClassResourceConverter;
23
use W2w\Lib\Apie\Core\Encodings\ChainableFormatRetriever;
24
use W2w\Lib\Apie\Core\IdentifierExtractor;
25
use W2w\Lib\Apie\Core\PluginContainer;
26
use W2w\Lib\Apie\Core\ResourceFactories\ChainableFactory;
27
use W2w\Lib\Apie\Core\SearchFilters\SearchFilterRequest;
28
use W2w\Lib\Apie\Exceptions\BadConfigurationException;
29
use W2w\Lib\Apie\Interfaces\ApiResourceFactoryInterface;
30
use W2w\Lib\Apie\Interfaces\FormatRetrieverInterface;
31
use W2w\Lib\Apie\Interfaces\ResourceSerializerInterface;
32
use W2w\Lib\Apie\OpenApiSchema\OpenApiSchemaGenerator;
33
use W2w\Lib\Apie\OpenApiSchema\OpenApiSpecGenerator;
34
use W2w\Lib\Apie\PluginInterfaces\AnnotationReaderProviderInterface;
35
use W2w\Lib\Apie\PluginInterfaces\ApieAwareInterface;
36
use W2w\Lib\Apie\PluginInterfaces\ApieConfigInterface;
37
use W2w\Lib\Apie\PluginInterfaces\ApiResourceFactoryProviderInterface;
38
use W2w\Lib\Apie\PluginInterfaces\CacheItemPoolProviderInterface;
39
use W2w\Lib\Apie\PluginInterfaces\EncoderProviderInterface;
40
use W2w\Lib\Apie\PluginInterfaces\FrameworkConnectionInterface;
41
use W2w\Lib\Apie\PluginInterfaces\NormalizerProviderInterface;
42
use W2w\Lib\Apie\PluginInterfaces\ObjectAccessProviderInterface;
43
use W2w\Lib\Apie\PluginInterfaces\OpenApiEventProviderInterface;
44
use W2w\Lib\Apie\PluginInterfaces\OpenApiInfoProviderInterface;
45
use W2w\Lib\Apie\PluginInterfaces\ResourceProviderInterface;
46
use W2w\Lib\Apie\PluginInterfaces\SchemaProviderInterface;
47
use W2w\Lib\Apie\PluginInterfaces\SerializerProviderInterface;
48
use W2w\Lib\Apie\PluginInterfaces\SymfonyComponentProviderInterface;
49
use W2w\Lib\Apie\Plugins\Core\CorePlugin;
50
use W2w\Lib\Apie\Plugins\PrimaryKey\PrimaryKeyPlugin;
51
use W2w\Lib\ApieObjectAccessNormalizer\ObjectAccess\CachedObjectAccess;
52
use W2w\Lib\ApieObjectAccessNormalizer\ObjectAccess\GroupedObjectAccess;
53
use W2w\Lib\ApieObjectAccessNormalizer\ObjectAccess\ObjectAccess;
54
use W2w\Lib\ApieObjectAccessNormalizer\ObjectAccess\ObjectAccessInterface;
55
56
final class Apie implements SerializerProviderInterface,
57
    ResourceProviderInterface,
58
    NormalizerProviderInterface,
59
    EncoderProviderInterface,
60
    SymfonyComponentProviderInterface,
61
    CacheItemPoolProviderInterface,
62
    AnnotationReaderProviderInterface,
63
    ApiResourceFactoryProviderInterface,
64
    OpenApiInfoProviderInterface,
65
    ApieConfigInterface,
66
    SchemaProviderInterface,
67
    OpenApiEventProviderInterface,
68
    FrameworkConnectionInterface
69
{
70
    const VERSION = "4.0";
71
72
    /**
73
     * @var bool
74
     */
75
    private $debug;
76
77
    /**
78
     * @var string|null
79
     */
80
    private $cacheFolder;
81
82
    /**
83
     * @var PluginContainer
84
     */
85
    private $pluginContainer;
86
87
    /**
88
     * @var ApieCore
89
     */
90
    private $apieCore;
91
92
    /**
93
     * @var ChainableFactory|null
94
     */
95
    private $chainableFactory;
96
97
    /**
98
     * @var ChainableFormatRetriever|null
99
     */
100
    private $chainableFormatRetriever;
101
102
    /**
103
104
     * @param object[] $plugins
105
     * @param bool $debug
106
     * @param string|null $cacheFolder
107
     * @param bool $addCorePlugin
108
     */
109
    public function __construct(array $plugins, bool $debug = false, ?string $cacheFolder = null, bool $addCorePlugin = true)
110
    {
111
        $this->debug = $debug;
112
        $this->cacheFolder = $cacheFolder;
113
        if ($addCorePlugin) {
114
            $plugins[] = new PrimaryKeyPlugin();
115
            $plugins[] = new CorePlugin();
116
        }
117
        $this->pluginContainer = new PluginContainer($plugins);
118
        $this->pluginContainer->each(
119
            ApieAwareInterface::class,
120
            function (ApieAwareInterface $plugin) {
121
                $plugin->setApie($this);
122
            }
123
        );
124
        $this->apieCore = new ApieCore($this, $this->pluginContainer);
125
    }
126
127
    /**
128
     * Creates a new instance of Apie reusing the services/instances of the current Apie instance.
129
     *
130
     * @param array $plugins
131
     * @return Apie
132
     */
133
    public function createContext(array $plugins = []): self
134
    {
135
        $plugins[] = new PrimaryKeyPlugin();
136
        $plugins[] = $this;
137
        return new Apie($plugins, $this->debug, $this->cacheFolder, false);
138
    }
139
140
    /**
141
     * @return bool
142
     */
143
    public function isDebug(): bool
144
    {
145
        return $this->debug;
146
    }
147
148
    /**
149
     * @return string|null
150
     */
151
    public function getCacheFolder(): ?string
152
    {
153
        return $this->cacheFolder;
154
    }
155
156
    public function getPlugin(string $pluginClass): object
157
    {
158
        return $this->pluginContainer->getPlugin($pluginClass);
159
    }
160
161
    /**
162
     * @return ResourceSerializerInterface
163
     */
164
    public function getResourceSerializer(): ResourceSerializerInterface
165
    {
166
        $serializer = $this->pluginContainer->first(
167
            SerializerProviderInterface::class,
168
            new BadConfigurationException('I have no resource serializer set up')
169
        )->getResourceSerializer();
170
        return $serializer;
171
    }
172
173
    /**
174
     * Returns a list of Api resources.
175
     *
176
     * @return string[]
177
     */
178
    public function getResources(): array
179
    {
180
        return array_values(array_unique($this->pluginContainer->merge(ResourceProviderInterface::class, 'getResources')));
181
    }
182
183
    /**
184
     * @return NormalizerInterface[]|DenormalizerInterface[]
185
     */
186
    public function getNormalizers(): array
187
    {
188
        return $this->pluginContainer->merge(NormalizerProviderInterface::class, 'getNormalizers');
189
    }
190
191
    /**
192
     * @return EncoderInterface[]|DecoderInterface[]
193
     */
194
    public function getEncoders(): array
195
    {
196
        return $this->pluginContainer->merge(EncoderProviderInterface::class, 'getEncoders');
197
    }
198
199
    /**
200
     * @param string $interface
201
     * @return mixed[]
202
     */
203
    public function getPluginsWithInterface(string $interface): array
204
    {
205
        return $this->pluginContainer->getPluginsWithInterface($interface);
206
    }
207
208
    public function getClassMetadataFactory(): ClassMetadataFactoryInterface
209
    {
210
        return $this->pluginContainer->first(
211
            SymfonyComponentProviderInterface::class,
212
            new BadConfigurationException('I have no symfony component provider set up')
213
        )->getClassMetadataFactory();
214
    }
215
216
    public function getPropertyConverter(): NameConverterInterface
217
    {
218
        return $this->pluginContainer->first(
219
            SymfonyComponentProviderInterface::class,
220
            new BadConfigurationException('I have no symfony component provider set up')
221
        )->getPropertyConverter();
222
    }
223
224
    public function getPropertyAccessor(): PropertyAccessor
225
    {
226
        return $this->pluginContainer->first(
227
            SymfonyComponentProviderInterface::class,
228
            new BadConfigurationException('I have no symfony component provider set up')
229
        )->getPropertyAccessor();
230
    }
231
232
    public function getPropertyTypeExtractor(): PropertyTypeExtractorInterface
233
    {
234
        return $this->pluginContainer->first(
235
            SymfonyComponentProviderInterface::class,
236
            new BadConfigurationException('I have no symfony component provider set up')
237
        )->getPropertyTypeExtractor();
238
    }
239
240
    public function getCacheItemPool(): CacheItemPoolInterface
241
    {
242
        return $this->pluginContainer->first(
243
            CacheItemPoolProviderInterface::class,
244
            new BadConfigurationException('I have no cache item pool provider set up')
245
        )->getCacheItemPool();
246
    }
247
248
    public function getAnnotationReader(): Reader
249
    {
250
        return $this->pluginContainer->first(
251
            AnnotationReaderProviderInterface::class,
252
            new BadConfigurationException('I have no annotation reader set up')
253
        )->getAnnotationReader();
254
    }
255
256
    public function getFormatRetriever(): FormatRetrieverInterface
257
    {
258
        if (!$this->chainableFormatRetriever) {
259
            $this->chainableFormatRetriever = new ChainableFormatRetriever(
260
                iterator_to_array($this->pluginContainer->combine(EncoderProviderInterface::class, 'getFormatRetriever'))
261
            );
262
        }
263
        return $this->chainableFormatRetriever;
264
    }
265
266
    public function getIdentifierExtractor(): IdentifierExtractor
267
    {
268
        return $this->apieCore->getIdentifierExtractor();
269
    }
270
271
    public function getApiResourceMetadataFactory(): ApiResourceMetadataFactory
272
    {
273
        return $this->apieCore->getApiResourceMetadataFactory();
274
    }
275
276
    public function getApiResourceFacade(): ApiResourceFacade
277
    {
278
        return $this->apieCore->getApiResourceFacade();
279
    }
280
281
    public function getOpenApiSpecGenerator(): OpenApiSpecGenerator
282
    {
283
        return $this->apieCore->getOpenApiSpecGenerator();
284
    }
285
286
    public function getSchemaGenerator(): OpenApiSchemaGenerator
287
    {
288
        return $this->apieCore->getSchemaGenerator();
289
    }
290
291
    public function getApiResourceFactory(): ApiResourceFactoryInterface
292
    {
293
        if (!$this->chainableFactory) {
294
            $this->chainableFactory = new ChainableFactory(
295
                iterator_to_array($this->pluginContainer->combine(ApiResourceFactoryProviderInterface::class, 'getApiResourceFactory'))
296
            );
297
        }
298
        return $this->chainableFactory;
299
    }
300
301
    public function createInfo(): Info
302
    {
303
        $res = $this->pluginContainer->first(OpenApiInfoProviderInterface::class, null);
304
305
        if (empty($res)) {
306
            return new Info('Apie', Apie::VERSION);
307
        }
308
        return $res->createInfo();
309
    }
310
311
    public function getBaseUrl(): string
312
    {
313
        return $this->pluginContainer->first(ApieConfigInterface::class, new BadConfigurationException('I have no config set up'))->getBaseUrl();
314
    }
315
316
    public function getDefinedStaticData(): array
317
    {
318
        $result = [];
319
        foreach (array_reverse($this->pluginContainer->getPluginsWithInterface(SchemaProviderInterface::class)) as $schemaDefinition) {
320
            foreach ($schemaDefinition->getDefinedStaticData() as $className => $schema) {
321
                $result[$className] = $schema;
322
            }
323
        }
324
        return $result;
325
    }
326
327
    public function getDynamicSchemaLogic(): array
328
    {
329
        $result = [];
330
        foreach (array_reverse($this->pluginContainer->getPluginsWithInterface(SchemaProviderInterface::class)) as $schemaDefinition) {
331
            foreach ($schemaDefinition->getDynamicSchemaLogic() as $className => $callable) {
332
                $result[$className] = $callable;
333
            }
334
        }
335
        return $result;
336
    }
337
338
    public function getClassResourceConverter(): ClassResourceConverter
339
    {
340
        return $this->apieCore->getClassResourceConverter();
341
    }
342
343
    public function onOpenApiDocGenerated(Document $document): Document
344
    {
345
        $this->pluginContainer->each(OpenApiEventProviderInterface::class, function (OpenApiEventProviderInterface $plugin) use (&$document) {
346
            $document = $plugin->onOpenApiDocGenerated($document);
347
        });
348
        return $document;
349
    }
350
351
     public function getObjectAccess(): ObjectAccessInterface
352
    {
353
        $objectAccess = new ObjectAccess();
354
        $objectAccesses = $this->pluginContainer->getPluginsWithInterface(ObjectAccessProviderInterface::class);
355
        if (!empty($objectAccesses)) {
356
            $list = [];
357
            foreach ($objectAccesses as $objectAccessPlugin) {
358
                $list = array_merge($list, $objectAccessPlugin->getObjectAccesses());
359
            }
360
            $objectAccess = new GroupedObjectAccess($objectAccess, $list);
361
        }
362
        if (!$this->debug && $this->cacheFolder) {
363
            return new CachedObjectAccess($objectAccess, $this->getCacheItemPool());
364
        }
365
        return $objectAccess;
366
    }
367
368
    public function getFrameworkConnection(): FrameworkConnectionInterface
369
    {
370
        $res = new ChainedFrameworkConnection(
371
            $this->getPluginsWithInterface(FrameworkConnectionInterface::class),
372
            new FrameworkLessConnection($this)
373
        );
374
        return $res;
375
    }
376
377
    public function getService(string $id): object
378
    {
379
        return $this->getFrameworkConnection()->getService($id);
380
    }
381
382
    public function getUrlForResource(object $resource): ?string
383
    {
384
        return $this->getFrameworkConnection()->getUrlForResource($resource);
385
    }
386
387
    public function getOverviewUrlForResourceClass(string $resourceClass, ?SearchFilterRequest $filterRequest = null
388
    ): ?string {
389
        return $this->getFrameworkConnection()->getOverviewUrlForResourceClass($resourceClass, $filterRequest);
390
    }
391
392
    public function getAcceptLanguage(): ?string
393
    {
394
        return $this->getFrameworkConnection()->getAcceptLanguage();
395
    }
396
397
    public function getContentLanguage(): ?string
398
    {
399
        return $this->getFrameworkConnection()->getContentLanguage();
400
    }
401
402
    public function getExampleUrl(string $resourceClass): ?string
403
    {
404
        return $this->getFrameworkConnection()->getExampleUrl($resourceClass);
405
    }
406
}
407