Completed
Pull Request — master (#1036)
by Hector
03:09
created

registerJsonApiConfiguration()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 2
1
<?php
2
3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace ApiPlatform\Core\Bridge\Symfony\Bundle\DependencyInjection;
15
16
use Doctrine\Common\Annotations\Annotation;
17
use Doctrine\ORM\Version;
18
use phpDocumentor\Reflection\DocBlockFactoryInterface;
19
use Symfony\Component\Cache\Adapter\ArrayAdapter;
20
use Symfony\Component\Config\FileLocator;
21
use Symfony\Component\Config\Resource\DirectoryResource;
22
use Symfony\Component\DependencyInjection\ContainerBuilder;
23
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
24
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
25
use Symfony\Component\Finder\Finder;
26
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
27
use Symfony\Component\Validator\Validator\ValidatorInterface;
28
use Symfony\Component\Yaml\Yaml;
29
30
/**
31
 * The extension of this bundle.
32
 *
33
 * @author Kévin Dunglas <[email protected]>
34
 */
35
final class ApiPlatformExtension extends Extension implements PrependExtensionInterface
36
{
37
    /**
38
     * {@inheritdoc}
39
     */
40
    public function prepend(ContainerBuilder $container)
41
    {
42
        if (!$frameworkConfiguration = $container->getExtensionConfig('framework')) {
43
            return;
44
        }
45
46
        foreach ($frameworkConfiguration as $frameworkParameters) {
47
            if (isset($frameworkParameters['serializer'])) {
48
                $serializerConfig = $serializerConfig ?? $frameworkParameters['serializer'];
49
            }
50
51
            if (isset($frameworkParameters['property_info'])) {
52
                $propertyInfoConfig = $propertyInfoConfig ?? $frameworkParameters['property_info'];
53
            }
54
        }
55
56 View Code Duplication
        if (!isset($serializerConfig['enabled'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
57
            $container->prependExtensionConfig('framework', ['serializer' => ['enabled' => true]]);
58
        }
59
60 View Code Duplication
        if (!isset($propertyInfoConfig['enabled'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
61
            $container->prependExtensionConfig('framework', ['property_info' => ['enabled' => true]]);
62
        }
63
    }
64
65
    /**
66
     * {@inheritdoc}
67
     */
68
    public function load(array $configs, ContainerBuilder $container)
69
    {
70
        $configuration = new Configuration();
71
        $config = $this->processConfiguration($configuration, $configs);
72
        $formats = $this->getFormats($config['formats']);
73
        $errorFormats = $this->getFormats($config['error_formats']);
74
        $this->handleConfig($container, $config, $formats, $errorFormats);
75
76
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
77
        $loader->load('api.xml');
78
        $loader->load('data_provider.xml');
79
80
        if (interface_exists(ValidatorInterface::class)) {
81
            $loader->load('validator.xml');
82
        }
83
84
        $bundles = $container->getParameter('kernel.bundles');
85
        if (isset($bundles['SecurityBundle'])) {
86
            $loader->load('security.xml');
87
        }
88
89
        $this->registerMetadataConfiguration($container, $loader, $bundles, $config['loader_paths']);
90
        $this->registerOAuthConfiguration($container, $config, $loader);
91
        $this->registerSwaggerConfiguration($container, $config, $loader);
92
        $this->registerJsonApiConfiguration($formats, $loader);
93
        $this->registerJsonLdConfiguration($formats, $loader);
94
        $this->registerJsonHalConfiguration($formats, $loader);
95
        $this->registerJsonProblemConfiguration($errorFormats, $loader);
96
        $this->registerBundlesConfiguration($bundles, $config, $loader);
97
        $this->registerCacheConfiguration($container);
98
        $this->registerDoctrineExtensionConfiguration($container, $config);
99
    }
100
101
    /**
102
     * Handles configuration.
103
     *
104
     * @param ContainerBuilder $container
105
     * @param array            $config
106
     * @param array            $formats
107
     * @param array            $errorFormats
108
     */
109
    private function handleConfig(ContainerBuilder $container, array $config, array $formats, array $errorFormats)
110
    {
111
        $container->setParameter('api_platform.title', $config['title']);
112
        $container->setParameter('api_platform.description', $config['description']);
113
        $container->setParameter('api_platform.version', $config['version']);
114
        $container->setParameter('api_platform.exception_to_status', $config['exception_to_status']);
115
        $container->setParameter('api_platform.formats', $formats);
116
        $container->setParameter('api_platform.error_formats', $errorFormats);
117
        $container->setParameter('api_platform.eager_loading.enabled', $config['eager_loading']['enabled']);
118
        $container->setParameter('api_platform.eager_loading.max_joins', $config['eager_loading']['max_joins']);
119
        $container->setParameter('api_platform.eager_loading.force_eager', $config['eager_loading']['force_eager']);
120
        $container->setParameter('api_platform.collection.order', $config['collection']['order']);
121
        $container->setParameter('api_platform.collection.order_parameter_name', $config['collection']['order_parameter_name']);
122
        $container->setParameter('api_platform.collection.pagination.enabled', $config['collection']['pagination']['enabled']);
123
        $container->setParameter('api_platform.collection.pagination.client_enabled', $config['collection']['pagination']['client_enabled']);
124
        $container->setParameter('api_platform.collection.pagination.client_items_per_page', $config['collection']['pagination']['client_items_per_page']);
125
        $container->setParameter('api_platform.collection.pagination.items_per_page', $config['collection']['pagination']['items_per_page']);
126
        $container->setParameter('api_platform.collection.pagination.maximum_items_per_page', $config['collection']['pagination']['maximum_items_per_page']);
127
        $container->setParameter('api_platform.collection.pagination.page_parameter_name', $config['collection']['pagination']['page_parameter_name']);
128
        $container->setParameter('api_platform.collection.pagination.enabled_parameter_name', $config['collection']['pagination']['enabled_parameter_name']);
129
        $container->setParameter('api_platform.collection.pagination.items_per_page_parameter_name', $config['collection']['pagination']['items_per_page_parameter_name']);
130
131
        $container->setAlias('api_platform.operation_path_resolver.default', $config['default_operation_path_resolver']);
132
133
        if ($config['name_converter']) {
134
            $container->setAlias('api_platform.name_converter', $config['name_converter']);
135
        }
136
    }
137
138
    /**
139
     * Registers metadata configuration.
140
     *
141
     * @param ContainerBuilder $container
142
     * @param XmlFileLoader    $loader
143
     * @param string[]         $bundles
144
     */
145
    private function registerMetadataConfiguration(ContainerBuilder $container, XmlFileLoader $loader, array $bundles, array $loaderPaths)
146
    {
147
        $loader->load('metadata/metadata.xml');
148
        $loader->load('metadata/xml.xml');
149
150
        list($xmlResources, $yamlResources) = $this->getResourcesToWatch($container, $bundles, $loaderPaths);
151
        $container->getDefinition('api_platform.metadata.extractor.xml')->addArgument($xmlResources);
152
153
        if (class_exists(Annotation::class)) {
154
            $loader->load('metadata/annotation.xml');
155
        }
156
157
        if (class_exists(Yaml::class)) {
158
            $loader->load('metadata/yaml.xml');
159
            $container->getDefinition('api_platform.metadata.extractor.yaml')->addArgument($yamlResources);
160
        }
161
162
        if (interface_exists(DocBlockFactoryInterface::class)) {
163
            $loader->load('metadata/php_doc.xml');
164
        }
165
    }
166
167
    private function getResourcesToWatch(ContainerBuilder $container, array $bundles, array $loaderPaths): array
168
    {
169
        $resourceClassDirectories = $loaderPaths['annotation'];
170
        $xmlResources = $loaderPaths['xml'];
171
        $yamlResources = $loaderPaths['yaml'];
172
173
        foreach ($bundles as $bundle) {
174
            $bundleDirectory = dirname((new \ReflectionClass($bundle))->getFileName());
175
            list($newXmlResources, $newYamlResources) = $this->findResources($bundleDirectory);
176
177
            $xmlResources = array_merge($xmlResources, $newXmlResources);
178
            $yamlResources = array_merge($yamlResources, $newYamlResources);
179
180
            if (file_exists($entityDirectory = $bundleDirectory.'/Entity')) {
181
                $resourceClassDirectories[] = $entityDirectory;
182
                $container->addResource(new DirectoryResource($entityDirectory, '/\.php$/'));
183
            }
184
        }
185
186
        $container->setParameter('api_platform.resource_class_directories', $resourceClassDirectories);
187
188
        return [$xmlResources, $yamlResources];
189
    }
190
191
    private function findResources(string $bundleDirectory): array
192
    {
193
        $xmlResources = $yamlResources = [];
194
195
        try {
196
            foreach (Finder::create()->files()->in($bundleDirectory.'/Resources/config/')->path('api_resources')->name('*.{yml,yaml,xml}') as $file) {
197
                if ('xml' === $file->getExtension()) {
198
                    $xmlResources[] = $file->getRealPath();
199
                } else {
200
                    $yamlResources[] = $file->getRealPath();
201
                }
202
            }
203
        } catch (\InvalidArgumentException $e) {
204
            // Ignore invalid paths
205
        }
206
207
        return [$xmlResources, $yamlResources];
208
    }
209
210
    /**
211
     * Registers the OAuth configuration.
212
     *
213
     * @param ContainerBuilder $container
214
     * @param array            $config
215
     * @param XmlFileLoader    $loader
216
     */
217
    private function registerOAuthConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader)
0 ignored issues
show
Unused Code introduced by
The parameter $loader is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
218
    {
219
        if (!$config['oauth']) {
220
            return;
221
        }
222
223
        $container->setParameter('api_platform.oauth.enabled', $config['oauth']['enabled']);
224
        $container->setParameter('api_platform.oauth.clientId', $config['oauth']['clientId']);
225
        $container->setParameter('api_platform.oauth.clientSecret', $config['oauth']['clientSecret']);
226
        $container->setParameter('api_platform.oauth.type', $config['oauth']['type']);
227
        $container->setParameter('api_platform.oauth.flow', $config['oauth']['flow']);
228
        $container->setParameter('api_platform.oauth.tokenUrl', $config['oauth']['tokenUrl']);
229
        $container->setParameter('api_platform.oauth.authorizationUrl', $config['oauth']['authorizationUrl']);
230
        $container->setParameter('api_platform.oauth.scopes', $config['oauth']['scopes']);
231
    }
232
233
    /**
234
     * Registers the Swagger and Swagger UI configuration.
235
     *
236
     * @param ContainerBuilder $container
237
     * @param array            $config
238
     * @param XmlFileLoader    $loader
239
     */
240
    private function registerSwaggerConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader)
241
    {
242
        if (!$config['enable_swagger']) {
243
            return;
244
        }
245
246
        $loader->load('swagger.xml');
247
248
        if ($config['enable_swagger_ui']) {
249
            $loader->load('swagger-ui.xml');
250
            $container->setParameter('api_platform.enable_swagger_ui', $config['enable_swagger_ui']);
251
        }
252
253
        $container->setParameter('api_platform.enable_swagger', $config['enable_swagger']);
254
    }
255
256
    /**
257
     * Registers the JSON-LD and Hydra configuration.
258
     *
259
     * @param array         $formats
260
     * @param XmlFileLoader $loader
261
     */
262
    private function registerJsonLdConfiguration(array $formats, XmlFileLoader $loader)
263
    {
264
        if (!isset($formats['jsonld'])) {
265
            return;
266
        }
267
268
        $loader->load('jsonld.xml');
269
        $loader->load('hydra.xml');
270
    }
271
272
    /**
273
     * Registers the HAL configuration.
274
     *
275
     * @param array         $formats
276
     * @param XmlFileLoader $loader
277
     */
278
    private function registerJsonHalConfiguration(array $formats, XmlFileLoader $loader)
279
    {
280
        if (!isset($formats['jsonhal'])) {
281
            return;
282
        }
283
284
        $loader->load('hal.xml');
285
    }
286
287
    /**
288
     * Registers the JsonApi configuration.
289
     *
290
     * @param array         $formats
291
     * @param XmlFileLoader $loader
292
     */
293
    private function registerJsonApiConfiguration(array $formats, XmlFileLoader $loader)
294
    {
295
        if (!isset($formats['jsonapi'])) {
296
            return;
297
        }
298
299
        $loader->load('jsonapi.xml');
300
    }
301
302
    /**
303
     * Registers the JSON Problem configuration.
304
     *
305
     * @param array         $errorFormats
306
     * @param XmlFileLoader $loader
307
     */
308
    private function registerJsonProblemConfiguration(array $errorFormats, XmlFileLoader $loader)
309
    {
310
        if (!isset($errorFormats['jsonproblem'])) {
311
            return;
312
        }
313
314
        $loader->load('problem.xml');
315
    }
316
317
    /**
318
     * Registers configuration for integration with third-party bundles.
319
     *
320
     * @param string[]      $bundles
321
     * @param array         $config
322
     * @param XmlFileLoader $loader
323
     */
324
    private function registerBundlesConfiguration(array $bundles, array $config, XmlFileLoader $loader)
325
    {
326
        // Doctrine ORM support
327
        if (isset($bundles['DoctrineBundle']) && class_exists(Version::class)) {
328
            $loader->load('doctrine_orm.xml');
329
        }
330
331
        // FOSUser support
332
        if (isset($bundles['FOSUserBundle']) && $config['enable_fos_user']) {
333
            $loader->load('fos_user.xml');
334
        }
335
336
        // NelmioApiDoc support
337
        if (isset($bundles['NelmioApiDocBundle']) && $config['enable_nelmio_api_doc']) {
338
            $loader->load('nelmio_api_doc.xml');
339
        }
340
    }
341
342
    /**
343
     * Registers the cache configuration.
344
     *
345
     * @param ContainerBuilder $container
346
     */
347
    private function registerCacheConfiguration(ContainerBuilder $container)
348
    {
349
        // Don't use system cache pool in dev
350
        if (!$container->getParameter('kernel.debug')) {
351
            return;
352
        }
353
354
        $container->register('api_platform.cache.metadata.property', ArrayAdapter::class);
355
        $container->register('api_platform.cache.metadata.resource', ArrayAdapter::class);
356
        $container->register('api_platform.cache.route_name_resolver', ArrayAdapter::class);
357
    }
358
359
    /**
360
     * Manipulate doctrine extension services according to the configuration.
361
     *
362
     * @param ContainerBuilder $container
363
     * @param array            $config
364
     */
365
    private function registerDoctrineExtensionConfiguration(ContainerBuilder $container, array $config)
366
    {
367
        if (false === $config['eager_loading']['enabled']) {
368
            $container->removeDefinition('api_platform.doctrine.orm.query_extension.eager_loading');
369
            $container->removeDefinition('api_platform.doctrine.orm.query_extension.filter_eager_loading');
370
        }
371
    }
372
373
    /**
374
     * Normalizes the format from config to the one accepted by Symfony HttpFoundation.
375
     *
376
     * @param array $configFormats
377
     *
378
     * @return array
379
     */
380
    private function getFormats(array $configFormats): array
381
    {
382
        $formats = [];
383
        foreach ($configFormats as $format => $value) {
384
            foreach ($value['mime_types'] as $mimeType) {
385
                $formats[$format][] = $mimeType;
386
            }
387
        }
388
389
        return $formats;
390
    }
391
}
392