Skip to content

Commit bf867e0

Browse files
authored
Merge pull request #2557 from soyuka/merge
Merge 2.4 onto master
2 parents d531495 + a58b37c commit bf867e0

File tree

63 files changed

+659
-305
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+659
-305
lines changed

features/hydra/collection.feature

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,6 @@ Feature: Collections support
396396
}
397397
"""
398398

399-
@!mongodb
400399
@createSchema
401400
Scenario: Allow passing 0 to `itemsPerPage`
402401
When I send a "GET" request to "/dummies?itemsPerPage=0"

features/main/crud_abstract.feature

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,20 +80,20 @@ Feature: Create-Retrieve-Update-Delete on abstract resource
8080
"""
8181

8282
Scenario: Update a concrete resource
83-
When I add "Content-Type" header equal to "application/ld+json"
84-
And I send a "PUT" request to "/concrete_dummies/1" with body:
83+
When I add "Content-Type" header equal to "application/ld+json"
84+
And I send a "PUT" request to "/concrete_dummies/1" with body:
8585
"""
8686
{
8787
"@id": "/concrete_dummies/1",
8888
"instance": "Become real",
8989
"name": "A nice dummy"
9090
}
9191
"""
92-
Then the response status code should be 200
93-
And the response should be in JSON
94-
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
95-
And the header "Content-Location" should be equal to "/concrete_dummies/1"
96-
And the JSON should be equal to:
92+
Then the response status code should be 200
93+
And the response should be in JSON
94+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
95+
And the header "Content-Location" should be equal to "/concrete_dummies/1"
96+
And the JSON should be equal to:
9797
"""
9898
{
9999
"@context": "/contexts/ConcreteDummy",
@@ -105,7 +105,61 @@ Feature: Create-Retrieve-Update-Delete on abstract resource
105105
}
106106
"""
107107

108+
Scenario: Update a concrete resource using abstract resource uri
109+
When I add "Content-Type" header equal to "application/ld+json"
110+
And I send a "PUT" request to "/abstract_dummies/1" with body:
111+
"""
112+
{
113+
"@id": "/concrete_dummies/1",
114+
"instance": "Become surreal",
115+
"name": "A nicer dummy"
116+
}
117+
"""
118+
Then the response status code should be 200
119+
And the response should be in JSON
120+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
121+
And the header "Content-Location" should be equal to "/concrete_dummies/1"
122+
And the JSON should be equal to:
123+
"""
124+
{
125+
"@context": "/contexts/ConcreteDummy",
126+
"@id": "/concrete_dummies/1",
127+
"@type": "ConcreteDummy",
128+
"instance": "Become surreal",
129+
"id": 1,
130+
"name": "A nicer dummy"
131+
}
132+
"""
133+
108134
Scenario: Delete a resource
109135
When I send a "DELETE" request to "/abstract_dummies/1"
110136
Then the response status code should be 204
111137
And the response should be empty
138+
139+
@createSchema
140+
Scenario: Create a concrete resource with discriminator
141+
When I add "Content-Type" header equal to "application/ld+json"
142+
And I send a "POST" request to "/abstract_dummies" with body:
143+
"""
144+
{
145+
"discr": "concrete",
146+
"instance": "Concrete",
147+
"name": "My Dummy"
148+
}
149+
"""
150+
Then the response status code should be 201
151+
And the response should be in JSON
152+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
153+
And the header "Content-Location" should be equal to "/concrete_dummies/1"
154+
And the header "Location" should be equal to "/concrete_dummies/1"
155+
And the JSON should be equal to:
156+
"""
157+
{
158+
"@context": "/contexts/ConcreteDummy",
159+
"@id": "/concrete_dummies/1",
160+
"@type": "ConcreteDummy",
161+
"instance": "Concrete",
162+
"id": 1,
163+
"name": "My Dummy"
164+
}
165+
"""

phpstan.neon

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,5 @@ parameters:
4949
- '#Method ApiPlatform\\Core\\Bridge\\Doctrine\\MongoDbOdm\\Filter\\(Abstract|Boolean|Date|Exists|Numeric|Order|Range|Search)Filter::isPropertyNested\(\) invoked with 2 parameters, 1 required\.#'
5050
- '#Method ApiPlatform\\Core\\Bridge\\Doctrine\\MongoDbOdm\\Filter\\(Abstract|Boolean|Date|Exists|Numeric|Order|Range|Search)Filter::splitPropertyParts\(\) invoked with 2 parameters, 1 required\.#'
5151
- '#Method ApiPlatform\\Core\\DataProvider\\CollectionDataProviderInterface::getCollection\(\) invoked with 3 parameters, 1-2 required\.#'
52+
- '#Method Symfony\\Component\\Serializer\\NameConverter\\NameConverterInterface::normalize\(\) invoked with 3 parameters, 1 required\.#'
53+
- '#Method Symfony\\Component\\Serializer\\NameConverter\\NameConverterInterface::normalize\(\) invoked with 4 parameters, 1 required\.#'

src/Bridge/Doctrine/MongoDbOdm/Extension/PaginationExtension.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,18 @@ public function applyToCollection(Builder $aggregationBuilder, string $resourceC
6464
throw new RuntimeException(sprintf('The repository for "%s" must be an instance of "%s".', $resourceClass, DocumentRepository::class));
6565
}
6666

67+
$resultsAggregationBuilder = $repository->createAggregationBuilder()->skip($offset);
68+
if ($limit > 0) {
69+
$resultsAggregationBuilder->limit($limit);
70+
} else {
71+
// Results have to be 0 but MongoDB does not support a limit equal to 0.
72+
$resultsAggregationBuilder->match()->field(Paginator::LIMIT_ZERO_MARKER_FIELD)->equals(Paginator::LIMIT_ZERO_MARKER);
73+
}
74+
6775
$aggregationBuilder
6876
->facet()
6977
->field('results')->pipeline(
70-
$repository->createAggregationBuilder()
71-
->skip($offset)
72-
->limit($limit)
78+
$resultsAggregationBuilder
7379
)
7480
->field('count')->pipeline(
7581
$repository->createAggregationBuilder()

src/Bridge/Doctrine/MongoDbOdm/Paginator.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
*/
2929
final class Paginator implements \IteratorAggregate, PaginatorInterface
3030
{
31+
public const LIMIT_ZERO_MARKER_FIELD = '___';
32+
public const LIMIT_ZERO_MARKER = 'limit0';
33+
3134
/**
3235
* @var Iterator
3336
*/
@@ -76,7 +79,7 @@ public function __construct(Iterator $mongoDbOdmIterator, UnitOfWork $unitOfWork
7679
* skip/limit parameters of the query, the values set in the facet stage are used instead.
7780
*/
7881
$this->firstResult = $this->getStageInfo($resultsFacetInfo, '$skip');
79-
$this->maxResults = $this->getStageInfo($resultsFacetInfo, '$limit');
82+
$this->maxResults = $this->hasLimitZeroStage($resultsFacetInfo) ? 0 : $this->getStageInfo($resultsFacetInfo, '$limit');
8083
$this->totalItems = $mongoDbOdmIterator->toArray()[0]['count'][0]['count'] ?? 0;
8184
}
8285

@@ -85,6 +88,10 @@ public function __construct(Iterator $mongoDbOdmIterator, UnitOfWork $unitOfWork
8588
*/
8689
public function getCurrentPage(): float
8790
{
91+
if (0 >= $this->maxResults) {
92+
return 1.;
93+
}
94+
8895
return floor($this->firstResult / $this->maxResults) + 1.;
8996
}
9097

@@ -93,6 +100,10 @@ public function getCurrentPage(): float
93100
*/
94101
public function getLastPage(): float
95102
{
103+
if (0 >= $this->maxResults) {
104+
return 1.;
105+
}
106+
96107
return ceil($this->totalItems / $this->maxResults) ?: 1.;
97108
}
98109

@@ -161,4 +172,15 @@ private function getStageInfo(array $resultsFacetInfo, string $stage): int
161172

162173
throw new InvalidArgumentException("$stage stage was not applied to the facet stage of the aggregation pipeline.");
163174
}
175+
176+
private function hasLimitZeroStage(array $resultsFacetInfo): bool
177+
{
178+
foreach ($resultsFacetInfo as $resultFacetInfo) {
179+
if (self::LIMIT_ZERO_MARKER === ($resultFacetInfo['$match'][self::LIMIT_ZERO_MARKER_FIELD] ?? null)) {
180+
return true;
181+
}
182+
}
183+
184+
return false;
185+
}
164186
}

src/Bridge/Elasticsearch/DataProvider/Extension/SortExtension.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,11 @@ private function getOrder(string $resourceClass, string $property, string $direc
8989
$order = ['order' => strtolower($direction)];
9090

9191
if (null !== $nestedPath = $this->getNestedFieldPath($resourceClass, $property)) {
92-
$nestedPath = null === $this->nameConverter ? $nestedPath : $this->nameConverter->normalize($nestedPath);
92+
$nestedPath = null === $this->nameConverter ? $nestedPath : $this->nameConverter->normalize($nestedPath, $resourceClass);
9393
$order['nested'] = ['path' => $nestedPath];
9494
}
9595

96-
$property = null === $this->nameConverter ? $property : $this->nameConverter->normalize($property);
96+
$property = null === $this->nameConverter ? $property : $this->nameConverter->normalize($property, $resourceClass);
9797

9898
return [$property => $order];
9999
}

src/Bridge/Elasticsearch/DataProvider/Filter/AbstractSearchFilter.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@ public function apply(array $clauseBody, string $resourceClass, ?string $operati
7272
continue;
7373
}
7474

75-
$property = null === $this->nameConverter ? $property : $this->nameConverter->normalize($property);
75+
$property = null === $this->nameConverter ? $property : $this->nameConverter->normalize($property, $resourceClass, null, $context);
7676
$nestedPath = $this->getNestedFieldPath($resourceClass, $property);
77-
$nestedPath = null === $nestedPath || null === $this->nameConverter ? $nestedPath : $this->nameConverter->normalize($nestedPath);
77+
$nestedPath = null === $nestedPath || null === $this->nameConverter ? $nestedPath : $this->nameConverter->normalize($nestedPath, $resourceClass, null, $context);
7878

7979
$searches[] = $this->getQuery($property, $values, $nestedPath);
8080
}

src/Bridge/Elasticsearch/DataProvider/Filter/OrderFilter.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,11 @@ public function apply(array $clauseBody, string $resourceClass, ?string $operati
7070
$order = ['order' => $direction];
7171

7272
if (null !== $nestedPath = $this->getNestedFieldPath($resourceClass, $property)) {
73-
$nestedPath = null === $this->nameConverter ? $nestedPath : $this->nameConverter->normalize($nestedPath);
73+
$nestedPath = null === $this->nameConverter ? $nestedPath : $this->nameConverter->normalize($nestedPath, $resourceClass, null, $context);
7474
$order['nested'] = ['path' => $nestedPath];
7575
}
7676

77-
$property = null === $this->nameConverter ? $property : $this->nameConverter->normalize($property);
77+
$property = null === $this->nameConverter ? $property : $this->nameConverter->normalize($property, $resourceClass, null, $context);
7878
$orders[] = [$property => $order];
7979
}
8080

src/Bridge/Elasticsearch/Serializer/ItemNormalizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public function normalize($object, $format = null, array $context = [])
8484
private function populateIdentifier(array $data, string $class): array
8585
{
8686
$identifier = $this->identifierExtractor->getIdentifierFromResourceClass($class);
87-
$identifier = null === $this->nameConverter ? $identifier : $this->nameConverter->normalize($identifier);
87+
$identifier = null === $this->nameConverter ? $identifier : $this->nameConverter->normalize($identifier, $class, self::FORMAT);
8888

8989
if (!isset($data['_source'][$identifier])) {
9090
$data['_source'][$identifier] = $data['_id'];

src/Bridge/Elasticsearch/Serializer/NameConverter/InnerFieldsNameConverter.php

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

1414
namespace ApiPlatform\Core\Bridge\Elasticsearch\Serializer\NameConverter;
1515

16+
use Symfony\Component\Serializer\NameConverter\AdvancedNameConverterInterface;
1617
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
1718
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
1819

@@ -23,7 +24,7 @@
2324
*
2425
* @author Baptiste Meyer <[email protected]>
2526
*/
26-
final class InnerFieldsNameConverter implements NameConverterInterface
27+
final class InnerFieldsNameConverter implements AdvancedNameConverterInterface
2728
{
2829
private $decorated;
2930

@@ -35,25 +36,25 @@ public function __construct(?NameConverterInterface $decorated = null)
3536
/**
3637
* {@inheritdoc}
3738
*/
38-
public function normalize($propertyName)
39+
public function normalize($propertyName, string $class = null, string $format = null, $context = [])
3940
{
40-
return $this->convertInnerFields($propertyName, true);
41+
return $this->convertInnerFields($propertyName, true, $class, $format, $context);
4142
}
4243

4344
/**
4445
* {@inheritdoc}
4546
*/
46-
public function denormalize($propertyName)
47+
public function denormalize($propertyName, string $class = null, string $format = null, $context = [])
4748
{
48-
return $this->convertInnerFields($propertyName, false);
49+
return $this->convertInnerFields($propertyName, false, $class, $format, $context);
4950
}
5051

51-
private function convertInnerFields(string $propertyName, bool $normalization): string
52+
private function convertInnerFields(string $propertyName, bool $normalization, string $class = null, string $format = null, $context = []): string
5253
{
5354
$convertedProperties = [];
5455

5556
foreach (explode('.', $propertyName) as $decomposedProperty) {
56-
$convertedProperties[] = $this->decorated->{$normalization ? 'normalize' : 'denormalize'}($decomposedProperty);
57+
$convertedProperties[] = $this->decorated->{$normalization ? 'normalize' : 'denormalize'}($decomposedProperty, $class, $format, $context);
5758
}
5859

5960
return implode('.', $convertedProperties);

0 commit comments

Comments
 (0)