Skip to content

Commit 502748a

Browse files
authored
Fix the bundle when soft deps aren't installed (#1058)
1 parent c8e7eb2 commit 502748a

File tree

13 files changed

+205
-155
lines changed

13 files changed

+205
-155
lines changed

composer.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,12 @@
4646
"symfony/config": "^3.2",
4747
"symfony/dependency-injection": "^2.7 || ^3.0",
4848
"symfony/doctrine-bridge": "^2.8 || ^3.0",
49-
"symfony/phpunit-bridge": "^2.7 || ^3.0",
50-
"symfony/security": "^2.7 || ^3.0",
51-
"symfony/validator": "^2.7 || ^3.0",
5249
"symfony/finder": "^2.7 || ^3.0",
5350
"symfony/framework-bundle": "^3.1",
54-
"symfony/twig-bundle": "^2.8 || ^3.1"
51+
"symfony/phpunit-bridge": "^2.7 || ^3.0",
52+
"symfony/security": "^2.7 || ^3.0",
53+
"symfony/twig-bundle": "^2.8 || ^3.1",
54+
"symfony/validator": "^2.7 || ^3.0"
5555
},
5656
"suggest": {
5757
"friendsofsymfony/user-bundle": "To use the FOSUserBundle bridge.",

features/hal/problem.feature

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Feature: Error handling valid according to RFC 7807 (application/problem+json)
33
As a client software developer
44
I need to retrieve an RFC 7807 compliant serialization of errors
55

6+
@createSchema
67
Scenario: Get an error
78
When I add "Content-Type" header equal to "application/json"
89
And I add "Accept" header equal to "application/json"
@@ -28,6 +29,7 @@ Feature: Error handling valid according to RFC 7807 (application/problem+json)
2829
}
2930
"""
3031

32+
@dropSchema
3133
Scenario: Get an error during deserialization of simple relation
3234
When I add "Content-Type" header equal to "application/json"
3335
And I add "Accept" header equal to "application/json"

src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php

Lines changed: 67 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
namespace ApiPlatform\Core\Bridge\Symfony\Bundle\DependencyInjection;
1515

16+
use Doctrine\Common\Annotations\Annotation;
1617
use Doctrine\ORM\Version;
1718
use phpDocumentor\Reflection\DocBlockFactoryInterface;
1819
use Symfony\Component\Cache\Adapter\ArrayAdapter;
@@ -23,6 +24,8 @@
2324
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
2425
use Symfony\Component\Finder\Finder;
2526
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
27+
use Symfony\Component\Validator\Validator\ValidatorInterface;
28+
use Symfony\Component\Yaml\Yaml;
2629

2730
/**
2831
* The extension of this bundle.
@@ -36,7 +39,7 @@ final class ApiPlatformExtension extends Extension implements PrependExtensionIn
3639
*/
3740
public function prepend(ContainerBuilder $container)
3841
{
39-
if (empty($frameworkConfiguration = $container->getExtensionConfig('framework'))) {
42+
if (!$frameworkConfiguration = $container->getExtensionConfig('framework')) {
4043
return;
4144
}
4245

@@ -73,15 +76,17 @@ public function load(array $configs, ContainerBuilder $container)
7376
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
7477
$loader->load('api.xml');
7578
$loader->load('data_provider.xml');
79+
if (interface_exists(ValidatorInterface::class)) {
80+
$loader->load('validator.xml');
81+
}
7682

7783
$bundles = $container->getParameter('kernel.bundles');
7884

79-
$this->registerMetadataConfiguration($container, $loader);
85+
$this->registerMetadataConfiguration($container, $loader, $bundles);
8086
$this->registerSwaggerConfiguration($container, $config, $loader);
8187
$this->registerJsonLdConfiguration($formats, $loader);
8288
$this->registerJsonHalConfiguration($formats, $loader);
8389
$this->registerJsonProblemConfiguration($errorFormats, $loader);
84-
$this->registerLoaders($container, $bundles);
8590
$this->registerBundlesConfiguration($bundles, $config, $loader);
8691
$this->registerCacheConfiguration($container);
8792
$this->registerDoctrineExtensionConfiguration($container, $config);
@@ -129,16 +134,71 @@ private function handleConfig(ContainerBuilder $container, array $config, array
129134
*
130135
* @param ContainerBuilder $container
131136
* @param XmlFileLoader $loader
137+
* @param string[] $bundles
132138
*/
133-
private function registerMetadataConfiguration(ContainerBuilder $container, XmlFileLoader $loader)
139+
private function registerMetadataConfiguration(ContainerBuilder $container, XmlFileLoader $loader, array $bundles)
134140
{
135-
$loader->load('metadata.xml');
141+
$loader->load('metadata/metadata.xml');
142+
$loader->load('metadata/xml.xml');
143+
144+
list($xmlResources, $yamlResources) = $this->getResourcesToWatch($container, $bundles);
145+
$container->getDefinition('api_platform.metadata.extractor.xml')->addArgument($xmlResources);
146+
147+
if (class_exists(Annotation::class)) {
148+
$loader->load('metadata/annotation.xml');
149+
}
150+
151+
if (class_exists(Yaml::class)) {
152+
$loader->load('metadata/yaml.xml');
153+
$container->getDefinition('api_platform.metadata.extractor.yaml')->addArgument($yamlResources);
154+
}
136155

137-
if (!interface_exists(DocBlockFactoryInterface::class)) {
138-
$container->removeDefinition('api_platform.metadata.resource.metadata_factory.php_doc');
156+
if (interface_exists(DocBlockFactoryInterface::class)) {
157+
$loader->load('metadata/php_doc.xml');
139158
}
140159
}
141160

161+
private function getResourcesToWatch(ContainerBuilder $container, array $bundles): array
162+
{
163+
$resourceClassDirectories = $yamlResources = $xmlResources = [];
164+
165+
foreach ($bundles as $bundle) {
166+
$bundleDirectory = dirname((new \ReflectionClass($bundle))->getFileName());
167+
list($newXmlResources, $newYamlResources) = $this->findResources($bundleDirectory);
168+
169+
$xmlResources = array_merge($xmlResources, $newXmlResources);
170+
$yamlResources = array_merge($yamlResources, $newYamlResources);
171+
172+
if (file_exists($entityDirectory = $bundleDirectory.'/Entity')) {
173+
$resourceClassDirectories[] = $entityDirectory;
174+
$container->addResource(new DirectoryResource($entityDirectory, '/\.php$/'));
175+
}
176+
}
177+
178+
$container->setParameter('api_platform.resource_class_directories', $resourceClassDirectories);
179+
180+
return [$xmlResources, $yamlResources];
181+
}
182+
183+
private function findResources(string $bundleDirectory): array
184+
{
185+
$xmlResources = $yamlResources = [];
186+
187+
try {
188+
foreach (Finder::create()->files()->in($bundleDirectory.'/Resources/config/')->path('api_resources')->name('*.{yml,yaml,xml}') as $file) {
189+
if ('xml' === $file->getExtension()) {
190+
$xmlResources[] = $file->getRealPath();
191+
} else {
192+
$yamlResources[] = $file->getRealPath();
193+
}
194+
}
195+
} catch (\InvalidArgumentException $e) {
196+
// Ignore invalid paths
197+
}
198+
199+
return [$xmlResources, $yamlResources];
200+
}
201+
142202
/**
143203
* Registers the Swagger and Swagger UI configuration.
144204
*
@@ -250,34 +310,6 @@ private function registerCacheConfiguration(ContainerBuilder $container)
250310
$container->register('api_platform.cache.route_name_resolver', ArrayAdapter::class);
251311
}
252312

253-
/**
254-
* Registers configuration file loaders.
255-
*
256-
* @param ContainerBuilder $container
257-
* @param string[] $bundles
258-
*/
259-
private function registerLoaders(ContainerBuilder $container, array $bundles)
260-
{
261-
$resourceClassDirectories = [];
262-
$yamlResources = [];
263-
$xmlResources = [];
264-
265-
foreach ($bundles as $bundle) {
266-
$bundleDirectory = dirname((new \ReflectionClass($bundle))->getFileName());
267-
$this->addFileResources($bundleDirectory, $xmlResources, $yamlResources);
268-
269-
if (file_exists($entityDirectory = $bundleDirectory.'/Entity')) {
270-
$resourceClassDirectories[] = $entityDirectory;
271-
$container->addResource(new DirectoryResource($entityDirectory, '/\.php$/'));
272-
}
273-
}
274-
275-
$container->setParameter('api_platform.resource_class_directories', $resourceClassDirectories);
276-
$container->getDefinition('api_platform.metadata.resource.name_collection_factory.annotation')->addArgument('%api_platform.resource_class_directories%');
277-
$container->getDefinition('api_platform.metadata.extractor.yaml')->addArgument($yamlResources);
278-
$container->getDefinition('api_platform.metadata.extractor.xml')->addArgument($xmlResources);
279-
}
280-
281313
/**
282314
* Manipulate doctrine extension services according to the configuration.
283315
*
@@ -292,28 +324,6 @@ private function registerDoctrineExtensionConfiguration(ContainerBuilder $contai
292324
}
293325
}
294326

295-
/**
296-
* Populates file resources lists.
297-
*
298-
* @param string $bundleDirectory
299-
* @param string[] $xmlResources
300-
* @param string[] $yamlResources
301-
*/
302-
private function addFileResources(string $bundleDirectory, array &$xmlResources, array &$yamlResources)
303-
{
304-
try {
305-
foreach (Finder::create()->files()->in($bundleDirectory.'/Resources/config/')->path('api_resources')->name('*.{yml,yaml,xml}') as $file) {
306-
if ('xml' === $file->getExtension()) {
307-
$xmlResources[] = $file->getRealPath();
308-
} else {
309-
$yamlResources[] = $file->getRealPath();
310-
}
311-
}
312-
} catch (\InvalidArgumentException $e) {
313-
// Ignore invalid paths
314-
}
315-
}
316-
317327
/**
318328
* Normalizes the format from config to the one accepted by Symfony HttpFoundation.
319329
*

src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
namespace ApiPlatform\Core\Bridge\Symfony\Bundle\DependencyInjection;
1515

1616
use ApiPlatform\Core\Exception\InvalidArgumentException;
17+
use Symfony\Bundle\TwigBundle\TwigBundle;
1718
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
1819
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
1920
use Symfony\Component\Config\Definition\ConfigurationInterface;
@@ -56,7 +57,7 @@ public function getConfigTreeBuilder()
5657
->booleanNode('enable_fos_user')->defaultValue(false)->info('Enable the FOSUserBundle integration.')->end()
5758
->booleanNode('enable_nelmio_api_doc')->defaultValue(false)->info('Enable the Nelmio Api doc integration.')->end()
5859
->booleanNode('enable_swagger')->defaultValue(true)->info('Enable the Swagger documentation and export.')->end()
59-
->booleanNode('enable_swagger_ui')->defaultValue(true)->info('Enable Swagger ui.')->end()
60+
->booleanNode('enable_swagger_ui')->defaultValue(class_exists(TwigBundle::class))->info('Enable Swagger ui.')->end()
6061

6162
->arrayNode('collection')
6263
->addDefaultsIfNotSet()

src/Bridge/Symfony/Bundle/Resources/config/api.xml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,6 @@
112112
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="2" />
113113
</service>
114114

115-
<service id="api_platform.listener.view.validate" class="ApiPlatform\Core\Bridge\Symfony\Validator\EventListener\ValidateListener">
116-
<argument type="service" id="validator" />
117-
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
118-
119-
<tag name="kernel.event_listener" event="kernel.view" method="onKernelView" priority="64" />
120-
</service>
121-
122115
<service id="api_platform.listener.view.serialize" class="ApiPlatform\Core\EventListener\SerializeListener">
123116
<argument type="service" id="api_platform.serializer" />
124117
<argument type="service" id="api_platform.serializer.context_builder" />
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?xml version="1.0" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
6+
7+
<services>
8+
<service id="api_platform.metadata.resource.name_collection_factory.annotation" decorates="api_platform.metadata.resource.name_collection_factory" class="ApiPlatform\Core\Metadata\Resource\Factory\AnnotationResourceNameCollectionFactory" public="false">
9+
<argument type="service" id="annotation_reader" />
10+
<argument>%api_platform.resource_class_directories%</argument>
11+
<argument type="service" id="api_platform.metadata.resource.name_collection_factory.annotation.inner" />
12+
</service>
13+
14+
<service id="api_platform.metadata.resource.metadata_factory.annotation" decorates="api_platform.metadata.resource.metadata_factory" decoration-priority="40" class="ApiPlatform\Core\Metadata\Resource\Factory\AnnotationResourceMetadataFactory" public="false">
15+
<argument type="service" id="annotation_reader" />
16+
<argument type="service" id="api_platform.metadata.resource.metadata_factory.annotation.inner" />
17+
</service>
18+
19+
<!--
20+
Not used, the PropertyInfo factory is preferred.
21+
22+
<service id="api_platform.metadata.property.name_collection_factory.annotation" class="ApiPlatform\Core\Metadata\Property\Factory\AnnotationPropertyNameCollectionFactory" public="false">
23+
<argument type="service" id="annotation_reader" />
24+
</service>
25+
-->
26+
27+
<service id="api_platform.metadata.property.metadata_factory.annotation" decorates="api_platform.metadata.property.metadata_factory" decoration-priority="40" class="ApiPlatform\Core\Metadata\Property\Factory\AnnotationPropertyMetadataFactory" public="false">
28+
<argument type="service" id="annotation_reader" />
29+
<argument type="service" id="api_platform.metadata.property.metadata_factory.annotation.inner" />
30+
</service>
31+
</services>
32+
33+
</container>

0 commit comments

Comments
 (0)