Skip to content

Commit 2e4da92

Browse files
committed
Merge branch '2.1'
2 parents 3b66e65 + d034ede commit 2e4da92

File tree

11 files changed

+81
-7
lines changed

11 files changed

+81
-7
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
* Fix the Doctrine query generated to retrieve nested subresources
3232
* Fix several bugs in the automatic eager loading support
3333
* Fix a bug occurring when passing nor an IRI nor an array in an embedded relation
34-
* Allow to pass request `0` items per page in collections
34+
* Allow to request `0` items per page in collections
3535
* Also copy the `Host` from the Symfony Router
3636
* `Paginator::getLastPage()` now always returns a `float`
3737
* Minor performance improvements

src/Bridge/Doctrine/Orm/Extension/OrderExtension.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGenerator
5959
$field = $order;
6060
$order = 'ASC';
6161
}
62-
if (false === ($pos = strpos($field, '.'))) {
62+
63+
if (false === ($pos = \strpos($field, '.'))
64+
|| isset($classMetaData->embeddedClasses[\substr($field, 0, $pos)])) {
6365
// Configure default filter with property
6466
$field = "{$rootAlias}.{$field}";
6567
} else {

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,12 @@ private function registerMetadataConfiguration(ContainerBuilder $container, arra
197197

198198
list($xmlResources, $yamlResources) = $this->getResourcesToWatch($container, $config['mapping']['paths']);
199199

200+
if (isset($config['resource_class_directories']) && $config['resource_class_directories']) {
201+
$container->setParameter('api_platform.resource_class_directories', array_merge(
202+
$config['resource_class_directories'], $container->getParameter('api_platform.resource_class_directories')
203+
));
204+
}
205+
200206
$container->getDefinition('api_platform.metadata.extractor.xml')->addArgument($xmlResources);
201207

202208
if (class_exists(Annotation::class)) {

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ public function getConfigTreeBuilder()
187187
->end()
188188
->end()
189189

190+
->arrayNode('resource_class_directories')
191+
->prototype('scalar')->end()
192+
->end()
193+
190194
->arrayNode('http_cache')
191195
->addDefaultsIfNotSet()
192196
->children()
@@ -258,7 +262,7 @@ private function addExceptionToStatusSection(ArrayNodeDefinition $rootNode)
258262
}
259263

260264
if (\defined($httpStatusCodeConstant = sprintf('%s::%s', Response::class, $httpStatusCode))) {
261-
@trigger_error(sprintf('Using a string "%s" as a constant of the "%s" class is deprecated since API Platform 2.1 and will not be possible anymore in API Platform 3. Use the Symfony\'s custom YAML extension for PHP constants instead (i.e. "!php/const %s").', $httpStatusCode, Response::class, $httpStatusCodeConstant), E_USER_DEPRECATED);
265+
@trigger_error(sprintf('Using a string "%s" as a constant of the "%s" class is deprecated since API Platform 2.1 and will not be possible anymore in API Platform 3. Use the Symfony\'s custom YAML extension for PHP constants instead (i.e. "!php/const:%s").', $httpStatusCode, Response::class, $httpStatusCodeConstant), E_USER_DEPRECATED);
262266

263267
$httpStatusCode = \constant($httpStatusCodeConstant);
264268
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
<tag name="routing.loader" />
4545
</service>
4646

47+
<service id="ApiPlatform\Core\Api\UrlGeneratorInterface" alias="api_platform.router" />
48+
4749
<service id="api_platform.router" class="ApiPlatform\Core\Bridge\Symfony\Routing\Router" public="false">
4850
<argument type="service" id="router" />
4951
</service>

src/Documentation/Action/DocumentationAction.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
use ApiPlatform\Core\Documentation\Documentation;
1717
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface;
18+
use Symfony\Component\HttpFoundation\Request;
1819

1920
/**
2021
* Generates the API documentation.
@@ -38,8 +39,10 @@ public function __construct(ResourceNameCollectionFactoryInterface $resourceName
3839
$this->formats = $formats;
3940
}
4041

41-
public function __invoke(): Documentation
42+
public function __invoke(Request $request): Documentation
4243
{
44+
$request->attributes->set('_api_normalization_context', $request->attributes->get('_api_normalization_context', []) + ['base_url' => $request->getBaseUrl()]);
45+
4346
return new Documentation($this->resourceNameCollectionFactory->create(), $this->title, $this->description, $this->version, $this->formats);
4447
}
4548
}

src/EventListener/SerializeListener.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ private function serializeRawData(GetResponseForControllerResultEvent $event, Re
9191
}
9292

9393
if (\is_object($controllerResult)) {
94-
$event->setControllerResult($this->serializer->serialize($controllerResult, $request->getRequestFormat()));
94+
$event->setControllerResult($this->serializer->serialize($controllerResult, $request->getRequestFormat(), $request->attributes->get('_api_normalization_context', [])));
9595

9696
return;
9797
}

tests/Bridge/Doctrine/Orm/Extension/OrderExtensionTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
1919
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
2020
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Dummy;
21+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\EmbeddedDummy;
2122
use Doctrine\ORM\EntityManager;
2223
use Doctrine\ORM\Mapping\ClassMetadata;
2324
use Doctrine\ORM\QueryBuilder;
@@ -139,4 +140,27 @@ public function testApplyToCollectionWithOrderOverriddenWithAssociation()
139140
$orderExtensionTest = new OrderExtension('asc', $resourceMetadataFactoryProphecy->reveal());
140141
$orderExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class);
141142
}
143+
144+
public function testApplyToCollectionWithOrderOverriddenWithEmbeddedAssociation()
145+
{
146+
$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
147+
$queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
148+
$queryBuilderProphecy->getRootAliases()->willReturn(['o']);
149+
$queryBuilderProphecy->addOrderBy('o.embeddedDummy.dummyName', 'DESC')->shouldBeCalled();
150+
151+
$classMetadataProphecy = $this->prophesize(ClassMetadata::class);
152+
$classMetadataProphecy->getIdentifier()->shouldBeCalled()->willReturn(['id']);
153+
$classMetadataProphecy->embeddedClasses = ['embeddedDummy' => []];
154+
155+
$resourceMetadataFactoryProphecy->create(EmbeddedDummy::class)->shouldBeCalled()->willReturn(new ResourceMetadata(null, null, null, null, null, ['order' => ['embeddedDummy.dummyName' => 'DESC']]));
156+
157+
$emProphecy = $this->prophesize(EntityManager::class);
158+
$emProphecy->getClassMetadata(EmbeddedDummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
159+
160+
$queryBuilderProphecy->getEntityManager()->shouldBeCalled()->willReturn($emProphecy->reveal());
161+
162+
$queryBuilder = $queryBuilderProphecy->reveal();
163+
$orderExtensionTest = new OrderExtension('asc', $resourceMetadataFactoryProphecy->reveal());
164+
$orderExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), EmbeddedDummy::class);
165+
}
142166
}

tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
use ApiPlatform\Core\Api\FilterInterface;
1717
use ApiPlatform\Core\Api\IriConverterInterface;
18+
use ApiPlatform\Core\Api\UrlGeneratorInterface;
1819
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\EagerLoadingExtension;
1920
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\FilterEagerLoadingExtension;
2021
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\FilterExtension;
@@ -274,6 +275,28 @@ public function testEnableSecurity()
274275
$this->extension->load(self::DEFAULT_CONFIG, $containerBuilder);
275276
}
276277

278+
public function testAddResourceClassDirectories()
279+
{
280+
$containerBuilderProphecy = $this->getBaseContainerBuilderProphecy();
281+
$containerBuilderProphecy->getParameter('api_platform.resource_class_directories')->shouldBeCalled()->willReturn([]);
282+
$i = 0;
283+
// it's called once from getResourcesToWatch and then if the configuration exists
284+
$containerBuilderProphecy->setParameter('api_platform.resource_class_directories', Argument::that(function ($arg) use ($i) {
285+
if (0 === $i++) {
286+
return $arg;
287+
}
288+
289+
if (!in_array('foobar', $arg, true)) {
290+
throw new \Exception('"foobar" should be in "resource_class_directories"');
291+
}
292+
293+
return $arg;
294+
}))->shouldBeCalled();
295+
$containerBuilder = $containerBuilderProphecy->reveal();
296+
297+
$this->extension->load(array_merge_recursive(self::DEFAULT_CONFIG, ['api_platform' => ['resource_class_directories' => ['foobar']]]), $containerBuilder);
298+
}
299+
277300
/**
278301
* @expectedException \ApiPlatform\Core\Exception\RuntimeException
279302
* @expectedExceptionMessageRegExp /Unsupported mapping type in ".+", supported types are XML & Yaml\./
@@ -509,6 +532,7 @@ private function getPartialContainerBuilderProphecy($test = false)
509532
'api_platform.property_info' => 'property_info',
510533
'api_platform.serializer' => 'serializer',
511534
IriConverterInterface::class => 'api_platform.iri_converter',
535+
UrlGeneratorInterface::class => 'api_platform.router',
512536
SerializerContextBuilderInterface::class => 'api_platform.serializer.context_builder',
513537
CollectionDataProviderInterface::class => 'api_platform.collection_data_provider',
514538
ItemDataProviderInterface::class => 'api_platform.item_data_provider',

tests/Bridge/Symfony/Bundle/DependencyInjection/ConfigurationTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,13 @@ public function testDefaultConfig()
134134
'public' => null,
135135
],
136136
'allow_plain_identifiers' => false,
137+
'resource_class_directories' => [],
137138
], $config);
138139
}
139140

140141
/**
141142
* @group legacy
142-
* @expectedDeprecation Using a string "HTTP_INTERNAL_SERVER_ERROR" as a constant of the "Symfony\Component\HttpFoundation\Response" class is deprecated since API Platform 2.1 and will not be possible anymore in API Platform 3. Use the Symfony's custom YAML extension for PHP constants instead (i.e. "!php/const Symfony\Component\HttpFoundation\Response::HTTP_INTERNAL_SERVER_ERROR").
143+
* @expectedDeprecation Using a string "HTTP_INTERNAL_SERVER_ERROR" as a constant of the "Symfony\Component\HttpFoundation\Response" class is deprecated since API Platform 2.1 and will not be possible anymore in API Platform 3. Use the Symfony's custom YAML extension for PHP constants instead (i.e. "!php/const:Symfony\Component\HttpFoundation\Response::HTTP_INTERNAL_SERVER_ERROR").
143144
*/
144145
public function testLegacyExceptionToStatusConfig()
145146
{

tests/Documentation/Action/DocumentationActionTest.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface;
1919
use ApiPlatform\Core\Metadata\Resource\ResourceNameCollection;
2020
use PHPUnit\Framework\TestCase;
21+
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
22+
use Symfony\Component\HttpFoundation\Request;
2123

2224
/**
2325
* @author Amrouche Hamza <[email protected]>
@@ -26,9 +28,15 @@ class DocumentationActionTest extends TestCase
2628
{
2729
public function testDocumentationAction()
2830
{
31+
$requestProphecy = $this->prophesize(Request::class);
32+
$attributesProphecy = $this->prophesize(ParameterBagInterface::class);
2933
$resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class);
3034
$resourceNameCollectionFactoryProphecy->create()->willReturn(new ResourceNameCollection(['dummies']));
35+
$requestProphecy->attributes = $attributesProphecy->reveal();
36+
$requestProphecy->getBaseUrl()->willReturn('/api')->shouldBeCalledTimes(1);
37+
$attributesProphecy->get('_api_normalization_context', [])->willReturn(['foo' => 'bar'])->shouldBeCalledTimes(1);
38+
$attributesProphecy->set('_api_normalization_context', ['foo' => 'bar', 'base_url' => '/api'])->shouldBeCalledTimes(1);
3139
$documentation = new DocumentationAction($resourceNameCollectionFactoryProphecy->reveal(), 'My happy hippie api', 'lots of chocolate', '1.0.0', ['formats' => ['jsonld' => 'application/ld+json']]);
32-
$this->assertEquals(new Documentation(new ResourceNameCollection(['dummies']), 'My happy hippie api', 'lots of chocolate', '1.0.0', ['formats' => ['jsonld' => 'application/ld+json']]), $documentation());
40+
$this->assertEquals(new Documentation(new ResourceNameCollection(['dummies']), 'My happy hippie api', 'lots of chocolate', '1.0.0', ['formats' => ['jsonld' => 'application/ld+json']]), $documentation($requestProphecy->reveal()));
3341
}
3442
}

0 commit comments

Comments
 (0)