Skip to content

Commit 0fda38c

Browse files
committed
feat: rector
1 parent f0b4e38 commit 0fda38c

31 files changed

+392
-183
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,7 @@ jobs:
841841
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
842842
restore-keys: ${{ runner.os }}-composer-
843843
- name: Require Symfony components and Rector dependencies
844-
run: composer require symfony/intl symfony/uid rector/rector --dev --no-interaction --no-progress --ansi
844+
run: git apply rector.patch && composer require symfony/intl symfony/uid rector/rector --dev --no-interaction --no-progress --ansi
845845
- name: Install PHPUnit
846846
run: vendor/bin/simple-phpunit --version
847847
- name: Clear test app cache

features/doctrine/eager_loading.feature

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Feature: Eager Loading
1717
LEFT JOIN thirdLevel_a1.fourthLevel fourthLevel_a2
1818
LEFT JOIN o.relatedToDummyFriend relatedToDummyFriend_a3
1919
LEFT JOIN relatedToDummyFriend_a3.dummyFriend dummyFriend_a4
20-
WHERE o.id = :id_id
20+
WHERE o.id = :id_p1
2121
"""
2222

2323
Scenario: Eager loading for the search filter
@@ -74,7 +74,7 @@ Feature: Eager Loading
7474
FROM ApiPlatform\Tests\Fixtures\TestBundle\Entity\DummyTravel o
7575
LEFT JOIN o.car car_a1
7676
LEFT JOIN o.passenger passenger_a2
77-
WHERE o.id = :id_id
77+
WHERE o.id = :id_p1
7878
"""
7979

8080
Scenario: Eager loading for a relation with complex sub-query filter

features/jsonld/input_output.feature

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ Feature: JSON-LD DTO input and output
282282

283283
@!mongodb
284284
Scenario: Use messenger with an input where the handler gives a synchronous result
285-
And I send a "POST" request to "/messenger_with_inputs" with body:
285+
When I send a "POST" request to "/messenger_with_inputs" with body:
286286
"""
287287
{
288288
"var": "test"

features/jsonld/non_resource.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Feature: JSON-LD non-resource handling
77
Given I add "Accept" header equal to "application/ld+json"
88
And I add "Content-Type" header equal to "application/ld+json"
99

10+
@createSchema
1011
Scenario: Get a resource containing a raw object
1112
When I send a "GET" request to "/contain_non_resources/1"
1213
Then the response status code should be 200

rector.patch

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
diff --git a/composer.json b/composer.json
2+
index d5beb2afc..fca961b12 100644
3+
--- a/composer.json
4+
+++ b/composer.json
5+
@@ -47,10 +47,10 @@
6+
"phpdocumentor/reflection-docblock": "^3.0 || ^4.0 || ^5.1",
7+
"phpdocumentor/type-resolver": "^0.3 || ^0.4 || ^1.4",
8+
"phpstan/extension-installer": "^1.0",
9+
- "phpstan/phpstan": "^0.12.65",
10+
- "phpstan/phpstan-doctrine": "^0.12.7",
11+
- "phpstan/phpstan-phpunit": "^0.12.4",
12+
- "phpstan/phpstan-symfony": "^0.12.4",
13+
+ "phpstan/phpstan": "*",
14+
+ "phpstan/phpstan-doctrine": "*",
15+
+ "phpstan/phpstan-phpunit": "*",
16+
+ "phpstan/phpstan-symfony": "*",
17+
"psr/log": "^1.0",
18+
"ramsey/uuid": "^3.7 || ^4.0",
19+
"ramsey/uuid-doctrine": "^1.4",
20+

src/Bridge/Doctrine/Orm/State/CollectionProvider.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
2121
use ApiPlatform\State\ProviderInterface;
2222
use Doctrine\ORM\EntityManagerInterface;
23+
use Doctrine\ORM\EntityRepository;
2324
use Doctrine\Persistence\ManagerRegistry;
2425

2526
/**
@@ -51,6 +52,7 @@ public function provide(string $resourceClass, array $identifiers = [], ?string
5152
/** @var EntityManagerInterface $manager */
5253
$manager = $this->managerRegistry->getManagerForClass($resourceClass);
5354

55+
/** @var EntityRepository */
5456
$repository = $manager->getRepository($resourceClass);
5557
if (!method_exists($repository, 'createQueryBuilder')) {
5658
throw new RuntimeException('The repository class must have a "createQueryBuilder" method.');
@@ -61,7 +63,6 @@ public function provide(string $resourceClass, array $identifiers = [], ?string
6163

6264
$this->handleLinks($queryBuilder, $identifiers, $queryNameGenerator, $context, $resourceClass, $operationName);
6365

64-
// dd($queryBuilder->getQuery());
6566
foreach ($this->collectionExtensions as $extension) {
6667
$extension->applyToCollection($queryBuilder, $queryNameGenerator, $resourceClass, $operationName, $context);
6768

src/Bridge/Doctrine/Orm/State/LinksHandlerTrait.php

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ private function handleLinks(QueryBuilder $queryBuilder, array $identifiers, Que
6464

6565
$previousAlias = $alias;
6666
$previousIdentifiers = end($links)->getIdentifiers();
67+
$previousJoinProperty = $doctrineClassMetadata->getIdentifier()[0];
6768
$expressions = [];
6869
$identifiers = array_reverse($identifiers);
6970

@@ -73,14 +74,10 @@ private function handleLinks(QueryBuilder $queryBuilder, array $identifiers, Que
7374
}
7475

7576
$identifierProperties = $link->getIdentifiers();
76-
$currentAlias = $queryNameGenerator->generateJoinAlias($alias);
77-
78-
if ($link->getFromClass() === $resourceClass) {
79-
$currentAlias = $alias;
80-
}
8177

8278
if (!$link->getFromProperty() && !$link->getToProperty()) {
8379
$doctrineClassMetadata = $manager->getClassMetadata($link->getFromClass());
80+
$currentAlias = $link->getFromClass() === $resourceClass ? $alias : $queryNameGenerator->generateJoinAlias($alias);
8481

8582
foreach ($identifierProperties as $identifierProperty) {
8683
$placeholder = $queryNameGenerator->generateParameterName($identifierProperty);
@@ -90,22 +87,24 @@ private function handleLinks(QueryBuilder $queryBuilder, array $identifiers, Que
9087

9188
$previousAlias = $currentAlias;
9289
$previousIdentifiers = $identifierProperties;
90+
$previousJoinProperty = $doctrineClassMetadata->getIdentifier()[0];
9391
continue;
9492
}
9593

9694
if (1 < \count($previousIdentifiers) || 1 < \count($identifierProperties)) {
97-
throw new RuntimeException('Composite identifiers on a relation can not be handled automatically, implement your own query.');
95+
throw new RuntimeException('Multiple identifiers on a relation can not be handled automatically, implement your own query.');
9896
}
9997

10098
$previousIdentifier = $previousIdentifiers[0];
10199
$identifierProperty = $identifierProperties[0];
100+
$joinProperty = $doctrineClassMetadata->getIdentifier()[0];
102101
$placeholder = $queryNameGenerator->generateParameterName($identifierProperty);
103102

104103
if ($link->getFromProperty() && !$link->getToProperty()) {
105104
$doctrineClassMetadata = $manager->getClassMetadata($link->getFromClass());
106105
$joinAlias = $queryNameGenerator->generateJoinAlias('m');
107-
$assocationMapping = $doctrineClassMetadata->getAssociationMappings()[$link->getFromProperty()];
108-
$relationType = $assocationMapping['type'];
106+
$associationMapping = $doctrineClassMetadata->getAssociationMapping($link->getFromProperty());
107+
$relationType = $associationMapping['type'];
109108

110109
if ($relationType & ClassMetadataInfo::TO_MANY) {
111110
$nextAlias = $queryNameGenerator->generateJoinAlias($alias);
@@ -116,21 +115,22 @@ private function handleLinks(QueryBuilder $queryBuilder, array $identifiers, Que
116115
}
117116

118117
// A single-valued association path expression to an inverse side is not supported in DQL queries.
119-
if ($relationType & ClassMetadataInfo::TO_ONE && !$assocationMapping['isOwningSide']) {
120-
$queryBuilder->innerJoin("$previousAlias.".$assocationMapping['mappedBy'], $joinAlias);
118+
if ($relationType & ClassMetadataInfo::TO_ONE && !($associationMapping['isOwningSide'] ?? true)) {
119+
$queryBuilder->innerJoin("$previousAlias.".$associationMapping['mappedBy'], $joinAlias);
121120
} else {
122121
$queryBuilder->join(
123122
$link->getFromClass(),
124123
$joinAlias,
125124
'with',
126-
"{$previousAlias}.{$previousIdentifier} = $joinAlias.{$link->getFromProperty()}"
125+
"{$previousAlias}.{$previousJoinProperty} = $joinAlias.{$link->getFromProperty()}"
127126
);
128127
}
129128

130129
$queryBuilder->andWhere("$joinAlias.$identifierProperty = :$placeholder");
131130
$queryBuilder->setParameter($placeholder, array_shift($identifiers), $doctrineClassMetadata->getTypeOfField($identifierProperty));
132131
$previousAlias = $joinAlias;
133132
$previousIdentifier = $identifierProperty;
133+
$previousJoinProperty = $joinProperty;
134134
continue;
135135
}
136136

@@ -140,6 +140,7 @@ private function handleLinks(QueryBuilder $queryBuilder, array $identifiers, Que
140140
$queryBuilder->setParameter($placeholder, array_shift($identifiers), $doctrineClassMetadata->getTypeOfField($identifierProperty));
141141
$previousAlias = $joinAlias;
142142
$previousIdentifier = $identifierProperty;
143+
$previousJoinProperty = $joinProperty;
143144
}
144145

145146
if ($expressions) {

src/Core/Bridge/Doctrine/Orm/ItemDataProvider.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public function getItem(string $resourceClass, $id, string $operationName = null
101101
$queryNameGenerator = new QueryNameGenerator();
102102
$doctrineClassMetadata = $manager->getClassMetadata($resourceClass);
103103

104-
$this->addWhereForIdentifiers($identifiers, $queryBuilder, $doctrineClassMetadata);
104+
$this->addWhereForIdentifiers($identifiers, $queryBuilder, $doctrineClassMetadata, $queryNameGenerator);
105105

106106
foreach ($this->itemExtensions as $extension) {
107107
$extension->applyToItem($queryBuilder, $queryNameGenerator, $resourceClass, $identifiers, $operationName, $context);
@@ -116,19 +116,20 @@ public function getItem(string $resourceClass, $id, string $operationName = null
116116

117117
/**
118118
* Add WHERE conditions to the query for one or more identifiers (simple or composite).
119+
*
120+
* @param mixed $queryNameGenerator
119121
*/
120-
private function addWhereForIdentifiers(array $identifiers, QueryBuilder $queryBuilder, ClassMetadata $classMetadata)
122+
private function addWhereForIdentifiers(array $identifiers, QueryBuilder $queryBuilder, ClassMetadata $classMetadata, $queryNameGenerator)
121123
{
122124
$alias = $queryBuilder->getRootAliases()[0];
123125
foreach ($identifiers as $identifier => $value) {
124-
$placeholder = ':id_'.$identifier;
126+
$placeholder = $queryNameGenerator->generateParameterName($identifier);
125127
$expression = $queryBuilder->expr()->eq(
126128
"{$alias}.{$identifier}",
127-
$placeholder
129+
':'.$placeholder
128130
);
129131

130132
$queryBuilder->andWhere($expression);
131-
132133
$queryBuilder->setParameter($placeholder, $value, $classMetadata->getTypeOfField($identifier));
133134
}
134135
}

src/Core/Bridge/Rector/Rules/AbstractLegacyApiResourceToApiResourceAttribute.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ abstract class AbstractLegacyApiResourceToApiResourceAttribute extends AbstractR
3838
'itemOperations' => [
3939
'get',
4040
'put',
41-
'patch',
4241
'delete',
4342
],
4443
'collectionOperations' => [

src/Core/Bridge/Rector/Service/SubresourceTransformer.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ final class SubresourceTransformer
3030

3131
public function __construct()
3232
{
33-
$this->ormMetadataFactory = new AnnotationDriver(new AnnotationReader());
34-
$this->odmMetadataFactory = new ODMAnnotationDriver(new AnnotationReader());
33+
$this->ormMetadataFactory = class_exists(AnnotationDriver::class) ? new AnnotationDriver(new AnnotationReader()) : null;
34+
$this->odmMetadataFactory = class_exists(ODMAnnotationDriver::class) ? new ODMAnnotationDriver(new AnnotationReader()) : null;
3535
}
3636

3737
public function toUriVariables(array $subresourceMetadata): array
@@ -81,12 +81,16 @@ private function getDoctrineMetadata(string $class): ClassMetadata
8181
$metadata->initializeReflection(new RuntimeReflectionService());
8282

8383
try {
84-
$this->ormMetadataFactory->loadMetadataForClass($class, $metadata);
84+
if ($this->ormMetadataFactory) {
85+
$this->ormMetadataFactory->loadMetadataForClass($class, $metadata);
86+
}
8587
} catch (MappingException $e) {
8688
}
8789

8890
try {
89-
$this->odmMetadataFactory->loadMetadataForClass($class, $metadata);
91+
if ($this->odmMetadataFactory) {
92+
$this->odmMetadataFactory->loadMetadataForClass($class, $metadata);
93+
}
9094
} catch (ODMMappingException $e) {
9195
}
9296

src/Core/Hydra/Serializer/CollectionFiltersNormalizer.php

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@
1313

1414
namespace ApiPlatform\Core\Hydra\Serializer;
1515

16+
use ApiPlatform\Api\ResourceClassResolverInterface;
1617
use ApiPlatform\Core\Api\FilterCollection;
1718
use ApiPlatform\Core\Api\FilterInterface;
1819
use ApiPlatform\Core\Api\FilterLocatorTrait;
19-
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
20+
use ApiPlatform\Core\Api\ResourceClassResolverInterface as LegacyResourceClassResolverInterface;
2021
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
2122
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
2223
use Psr\Container\ContainerInterface;
@@ -33,23 +34,21 @@
3334
final class CollectionFiltersNormalizer implements NormalizerInterface, NormalizerAwareInterface, CacheableSupportsMethodInterface
3435
{
3536
use FilterLocatorTrait;
36-
3737
private $collectionNormalizer;
3838
private $resourceMetadataFactory;
3939
private $resourceClassResolver;
4040

4141
/**
42-
* @param ContainerInterface|FilterCollection $filterLocator The new filter locator or the deprecated filter collection
43-
* @param mixed $resourceMetadataFactory
42+
* @param ContainerInterface|FilterCollection $filterLocator The new filter locator or the deprecated filter collection
43+
* @param mixed $resourceMetadataFactory
44+
* @param ResourceClassResolverInterface|LegacyResourceClassResolverInterface $resourceClassResolver
4445
*/
45-
public function __construct(NormalizerInterface $collectionNormalizer, $resourceMetadataFactory, ResourceClassResolverInterface $resourceClassResolver, $filterLocator)
46+
public function __construct(NormalizerInterface $collectionNormalizer, $resourceMetadataFactory, $resourceClassResolver, $filterLocator)
4647
{
4748
$this->setFilterLocator($filterLocator);
48-
4949
$this->collectionNormalizer = $collectionNormalizer;
5050
$this->resourceMetadataFactory = $resourceMetadataFactory;
5151
$this->resourceClassResolver = $resourceClassResolver;
52-
5352
if (!$resourceMetadataFactory instanceof ResourceMetadataCollectionFactoryInterface) {
5453
trigger_deprecation('api-platform/core', '2.7', sprintf('Use "%s" instead of "%s".', ResourceMetadataCollectionFactoryInterface::class, ResourceMetadataFactoryInterface::class));
5554
}
@@ -80,17 +79,13 @@ public function normalize($object, $format = null, array $context = []): array
8079
if (!\is_array($data)) {
8180
throw new UnexpectedValueException('Expected data to be an array');
8281
}
83-
8482
if (!isset($context['resource_class']) || isset($context['api_sub_level'])) {
8583
return $data;
8684
}
87-
8885
$resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class']);
8986
$resourceFilters = null;
90-
9187
if ($this->resourceMetadataFactory instanceof ResourceMetadataFactoryInterface) {
9288
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
93-
9489
$operationName = $context['collection_operation_name'] ?? null;
9590
if (null === $operationName) {
9691
$resourceFilters = $resourceMetadata->getAttribute('filters', []);
@@ -101,23 +96,19 @@ public function normalize($object, $format = null, array $context = []): array
10196
$operation = $context['operation'] ?? $this->resourceMetadataFactory->create($resourceClass)->getOperation($context['operation_name'] ?? null);
10297
$resourceFilters = $operation->getFilters();
10398
}
104-
10599
if (!$resourceFilters) {
106100
return $data;
107101
}
108-
109102
$requestParts = parse_url($context['request_uri'] ?? '');
110103
if (!\is_array($requestParts)) {
111104
return $data;
112105
}
113-
114106
$currentFilters = [];
115107
foreach ($resourceFilters as $filterId) {
116108
if ($filter = $this->getFilter($filterId)) {
117109
$currentFilters[] = $filter;
118110
}
119111
}
120-
121112
if ($currentFilters) {
122113
$data['hydra:search'] = $this->getSearch($resourceClass, $requestParts, $currentFilters);
123114
}
@@ -147,20 +138,10 @@ private function getSearch(string $resourceClass, array $parts, array $filters):
147138
foreach ($filters as $filter) {
148139
foreach ($filter->getDescription($resourceClass) as $variable => $data) {
149140
$variables[] = $variable;
150-
$mapping[] = [
151-
'@type' => 'IriTemplateMapping',
152-
'variable' => $variable,
153-
'property' => $data['property'],
154-
'required' => $data['required'],
155-
];
141+
$mapping[] = ['@type' => 'IriTemplateMapping', 'variable' => $variable, 'property' => $data['property'], 'required' => $data['required']];
156142
}
157143
}
158144

159-
return [
160-
'@type' => 'hydra:IriTemplate',
161-
'hydra:template' => sprintf('%s{?%s}', $parts['path'], implode(',', $variables)),
162-
'hydra:variableRepresentation' => 'BasicRepresentation',
163-
'hydra:mapping' => $mapping,
164-
];
145+
return ['@type' => 'hydra:IriTemplate', 'hydra:template' => sprintf('%s{?%s}', $parts['path'], implode(',', $variables)), 'hydra:variableRepresentation' => 'BasicRepresentation', 'hydra:mapping' => $mapping];
165146
}
166147
}

src/Core/Hydra/Serializer/DocumentationNormalizer.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use ApiPlatform\Metadata\ApiProperty;
3030
use ApiPlatform\Metadata\ApiResource;
3131
use ApiPlatform\Metadata\Operation;
32+
use ApiPlatform\Metadata\Post;
3233
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
3334
use ApiPlatform\Metadata\Resource\ResourceMetadataCollection;
3435
use Symfony\Component\PropertyInfo\Type;
@@ -342,7 +343,7 @@ private function getHydraOperations(string $resourceClass, $resourceMetadata, st
342343
$hydraOperations = [];
343344
foreach ($resourceMetadataCollection as $resourceMetadata) {
344345
foreach ($resourceMetadata->getOperations() as $operationName => $operation) {
345-
if (($operation->isCollection() ?? false) !== $collection) {
346+
if (($operation instanceof Post || ($operation->isCollection() ?? false)) !== $collection) {
346347
continue;
347348
}
348349

src/Core/Metadata/Resource/ApiResourceToLegacyResourceMetadataTrait.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ private function transformUriVariablesToIdentifiers(array $arrayOperation): arra
9696

9797
$arrayOperation['identifiers'] = [];
9898
foreach ($arrayOperation['uri_variables'] as $parameterName => $identifiedBy) {
99-
if (1 === \count($identifiedBy->getIdentifiers())) {
100-
$arrayOperation['identifiers'][$parameterName] = [$identifiedBy->getFromClass(), $identifiedBy->getIdentifiers()[0]];
99+
if (1 === \count($identifiedBy->getIdentifiers() ?? ['id'])) {
100+
$arrayOperation['identifiers'][$parameterName] = [$identifiedBy->getFromClass(), $identifiedBy->getIdentifiers()[0] ?? ['id']];
101101
continue;
102102
}
103103

0 commit comments

Comments
 (0)