Skip to content

Commit 6004d9e

Browse files
committed
Merge branch '2.2'
2 parents 1ea1664 + 13751eb commit 6004d9e

30 files changed

+630
-129
lines changed

.php_cs.dist

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ return PhpCsFixer\Config::create()
2323
'@PHPUnit60Migration:risky' => true,
2424
'@Symfony' => true,
2525
'@Symfony:risky' => true,
26+
'align_multiline_comment' => [
27+
'comment_type' => 'phpdocs_only',
28+
],
29+
'array_indentation' => true,
2630
'array_syntax' => [
2731
'syntax' => 'short',
2832
],
@@ -41,7 +45,6 @@ return PhpCsFixer\Config::create()
4145
'header' => $header,
4246
'location' => 'after_open',
4347
],
44-
'modernize_types_casting' => true,
4548
'native_function_invocation' => [
4649
'include' => [
4750
'@compiler_optimized',
@@ -62,10 +65,16 @@ return PhpCsFixer\Config::create()
6265
],
6366
'no_useless_else' => true,
6467
'no_useless_return' => true,
65-
'ordered_imports' => true,
68+
'ordered_imports' => [
69+
'importsOrder' => [
70+
'class',
71+
'function',
72+
'const',
73+
],
74+
'sortAlgorithm' => 'alpha',
75+
],
6676
'phpdoc_order' => true,
67-
'psr4' => true,
68-
'semicolon_after_instruction' => true,
77+
// 'simplified_null_return' => true,
6978
'strict_comparison' => true,
7079
'strict_param' => true,
7180
'ternary_to_null_coalescing' => true,

features/main/validation.feature

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
Feature: Using validations groups
2+
As a client software developer
3+
I need to be able to use validation groups
4+
5+
@createSchema
6+
@dropSchema
7+
Scenario: Create a resource
8+
When I add "Content-Type" header equal to "application/ld+json"
9+
And I send a "POST" request to "/dummy_validation" with body:
10+
"""
11+
{
12+
"code": "My Dummy"
13+
}
14+
"""
15+
Then the response status code should be 201
16+
And the response should be in JSON
17+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
18+
19+
@createSchema
20+
@dropSchema
21+
Scenario: Create a resource with validation
22+
When I add "Content-Type" header equal to "application/ld+json"
23+
And I send a "POST" request to "/dummy_validation/validation_groups" with body:
24+
"""
25+
{
26+
"code": "My Dummy"
27+
}
28+
"""
29+
Then the response status code should be 400
30+
And the response should be in JSON
31+
And the JSON should be equal to:
32+
"""
33+
{
34+
"@context": "\/contexts\/ConstraintViolationList",
35+
"@type": "ConstraintViolationList",
36+
"hydra:title": "An error occurred",
37+
"hydra:description": "name: This value should not be null.",
38+
"violations": [
39+
{
40+
"propertyPath": "name",
41+
"message": "This value should not be null."
42+
}
43+
]
44+
}
45+
"""
46+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
47+
48+
@createSchema
49+
@dropSchema
50+
Scenario: Create a resource with validation group sequence
51+
When I add "Content-Type" header equal to "application/ld+json"
52+
And I send a "POST" request to "/dummy_validation/validation_sequence" with body:
53+
"""
54+
{
55+
"code": "My Dummy"
56+
}
57+
"""
58+
Then the response status code should be 400
59+
And the response should be in JSON
60+
And the JSON should be equal to:
61+
"""
62+
{
63+
"@context": "\/contexts\/ConstraintViolationList",
64+
"@type": "ConstraintViolationList",
65+
"hydra:title": "An error occurred",
66+
"hydra:description": "title: This value should not be null.",
67+
"violations": [
68+
{
69+
"propertyPath": "title",
70+
"message": "This value should not be null."
71+
}
72+
]
73+
}
74+
"""
75+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ private function getQueryBuilderWithNewAliases(QueryBuilder $queryBuilder, Query
135135
/** @var Join $joinPart */
136136
$joinString = str_replace($aliases, $replacements, $joinPart->getJoin());
137137
$pos = strpos($joinString, '.');
138+
if (false === $pos) {
139+
continue;
140+
}
138141
$alias = substr($joinString, 0, $pos);
139142
$association = substr($joinString, $pos + 1);
140143
$condition = str_replace($aliases, $replacements, $joinPart->getCondition());

src/Bridge/Doctrine/Orm/Filter/SearchFilter.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB
216216

217217
if (!$this->hasValidValues($values, $this->getDoctrineFieldType($property, $resourceClass))) {
218218
$this->logger->notice('Invalid filter ignored', [
219-
'exception' => new InvalidArgumentException(sprintf('Values for field "%s" are not valid according to the doctrine type.', $field)),
219+
'exception' => new InvalidArgumentException(sprintf('Values for field "%s" are not valid according to the doctrine type.', $field)),
220220
]);
221221

222222
return;
@@ -261,7 +261,7 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB
261261

262262
if (!$this->hasValidValues($values, $this->getDoctrineFieldType($property, $resourceClass))) {
263263
$this->logger->notice('Invalid filter ignored', [
264-
'exception' => new InvalidArgumentException(sprintf('Values for field "%s" are not valid according to the doctrine type.', $field)),
264+
'exception' => new InvalidArgumentException(sprintf('Values for field "%s" are not valid according to the doctrine type.', $field)),
265265
]);
266266

267267
return;

src/Bridge/Symfony/Validator/Validator.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use ApiPlatform\Core\Bridge\Symfony\Validator\Exception\ValidationException;
1717
use ApiPlatform\Core\Validator\ValidatorInterface;
1818
use Psr\Container\ContainerInterface;
19+
use Symfony\Component\Validator\Constraints\GroupSequence;
1920
use Symfony\Component\Validator\Validator\ValidatorInterface as SymfonyValidatorInterface;
2021

2122
/**
@@ -52,7 +53,9 @@ public function validate($data, array $context = [])
5253
$validationGroups = $validationGroups($data);
5354
}
5455

55-
$validationGroups = (array) $validationGroups;
56+
if (!$validationGroups instanceof GroupSequence) {
57+
$validationGroups = (array) $validationGroups;
58+
}
5659
}
5760

5861
$violations = $this->validator->validate($data, null, $validationGroups);

src/Hal/Serializer/ItemNormalizer.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ final class ItemNormalizer extends AbstractItemNormalizer
3030
const FORMAT = 'jsonhal';
3131

3232
private $componentsCache = [];
33+
private $attributesMetadataCache = [];
3334

3435
/**
3536
* {@inheritdoc}
@@ -169,7 +170,10 @@ private function getComponents($object, string $format = null, array $context)
169170
private function populateRelation(array $data, $object, string $format = null, array $context, array $components, string $type): array
170171
{
171172
$class = \get_class($object);
172-
$attributesMetadata = $this->classMetadataFactory ? $this->classMetadataFactory->getMetadataFor($object)->getAttributesMetadata() : null;
173+
174+
$attributesMetadata = \array_key_exists($class, $this->attributesMetadataCache) ?
175+
$this->attributesMetadataCache[$class] :
176+
$this->attributesMetadataCache[$class] = $this->classMetadataFactory ? $this->classMetadataFactory->getMetadataFor($object)->getAttributesMetadata() : null;
173177

174178
$key = '_'.$type;
175179
foreach ($components[$type] as $relation) {

src/Metadata/Extractor/XmlExtractor.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ private function getProperties(\SimpleXMLElement $resource): array
122122
'subresource' => $property->subresource ? [
123123
'collection' => $this->phpize($property->subresource, 'collection', 'bool'),
124124
'resourceClass' => $this->phpize($property->subresource, 'resourceClass', 'string'),
125+
'maxDepth' => $this->phpize($property->subresource, 'maxDepth', 'integer'),
125126
] : null,
126127
];
127128
}
@@ -143,6 +144,8 @@ private function phpize(\SimpleXMLElement $array, string $key, string $type)
143144
switch ($type) {
144145
case 'string':
145146
return (string) $array[$key];
147+
case 'integer':
148+
return (int) $array[$key];
146149
case 'bool':
147150
return (bool) XmlUtils::phpize($array[$key]);
148151
}

src/Metadata/Property/Factory/AnnotationSubresourceMetadataFactory.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
namespace ApiPlatform\Core\Metadata\Property\Factory;
1515

1616
use ApiPlatform\Core\Annotation\ApiSubresource;
17+
use ApiPlatform\Core\Exception\InvalidResourceException;
1718
use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
1819
use ApiPlatform\Core\Metadata\Property\SubresourceMetadata;
1920
use ApiPlatform\Core\Util\Reflection;
@@ -52,7 +53,7 @@ public function create(string $resourceClass, string $property, array $options =
5253
$annotation = $this->reader->getPropertyAnnotation($reflectionClass->getProperty($property), ApiSubresource::class);
5354

5455
if (null !== $annotation) {
55-
return $this->updateMetadata($annotation, $propertyMetadata, $resourceClass);
56+
return $this->updateMetadata($annotation, $propertyMetadata, $resourceClass, $property);
5657
}
5758
}
5859

@@ -70,16 +71,19 @@ public function create(string $resourceClass, string $property, array $options =
7071
$annotation = $this->reader->getMethodAnnotation($reflectionMethod, ApiSubresource::class);
7172

7273
if (null !== $annotation) {
73-
return $this->updateMetadata($annotation, $propertyMetadata, $resourceClass);
74+
return $this->updateMetadata($annotation, $propertyMetadata, $resourceClass, $property);
7475
}
7576
}
7677

7778
return $propertyMetadata;
7879
}
7980

80-
private function updateMetadata(ApiSubresource $annotation, PropertyMetadata $propertyMetadata, string $originResourceClass): PropertyMetadata
81+
private function updateMetadata(ApiSubresource $annotation, PropertyMetadata $propertyMetadata, string $originResourceClass, string $propertyName): PropertyMetadata
8182
{
8283
$type = $propertyMetadata->getType();
84+
if (null === $type) {
85+
throw new InvalidResourceException(sprintf('Property "%s" on resource "%s" is declared as a subresource, but its type could not be determined.', $propertyName, $originResourceClass));
86+
}
8387
$isCollection = $type->isCollection();
8488
$resourceClass = $isCollection ? $type->getCollectionValueType()->getClassName() : $type->getClassName();
8589
$maxDepth = $annotation->maxDepth;

src/Metadata/Property/Factory/ExtractorPropertyMetadataFactory.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ private function createSubresourceMetadata($subresource, PropertyMetadata $prope
142142
}
143143

144144
$type = $propertyMetadata->getType();
145+
$maxDepth = $subresource['maxDepth'] ?? null;
145146

146147
if (null !== $type) {
147148
$isCollection = $type->isCollection();
@@ -153,6 +154,6 @@ private function createSubresourceMetadata($subresource, PropertyMetadata $prope
153154
return null;
154155
}
155156

156-
return new SubresourceMetadata($resourceClass, $isCollection);
157+
return new SubresourceMetadata($resourceClass, $isCollection, $maxDepth);
157158
}
158159
}

src/Operation/Factory/SubresourceOperationFactory.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,11 @@ private function computeSubresourceOperations(string $resourceClass, array &$tre
100100
continue;
101101
}
102102

103+
if ($rootResourceClass === $resourceClass) {
104+
// reset depth when we return to rootResourceClass
105+
$depth = 0;
106+
}
107+
103108
$rootResourceMetadata = $this->resourceMetadataFactory->create($rootResourceClass);
104109
$operationName = 'get';
105110
$operation = [

0 commit comments

Comments
 (0)