Skip to content

Commit

Permalink
feat: rector
Browse files Browse the repository at this point in the history
  • Loading branch information
soyuka committed Nov 19, 2021
1 parent f0b4e38 commit 0fda38c
Show file tree
Hide file tree
Showing 31 changed files with 392 additions and 183 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -841,7 +841,7 @@ jobs:
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
restore-keys: ${{ runner.os }}-composer-
- name: Require Symfony components and Rector dependencies
run: composer require symfony/intl symfony/uid rector/rector --dev --no-interaction --no-progress --ansi
run: git apply rector.patch && composer require symfony/intl symfony/uid rector/rector --dev --no-interaction --no-progress --ansi
- name: Install PHPUnit
run: vendor/bin/simple-phpunit --version
- name: Clear test app cache
Expand Down
4 changes: 2 additions & 2 deletions features/doctrine/eager_loading.feature
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Feature: Eager Loading
LEFT JOIN thirdLevel_a1.fourthLevel fourthLevel_a2
LEFT JOIN o.relatedToDummyFriend relatedToDummyFriend_a3
LEFT JOIN relatedToDummyFriend_a3.dummyFriend dummyFriend_a4
WHERE o.id = :id_id
WHERE o.id = :id_p1
"""

Scenario: Eager loading for the search filter
Expand Down Expand Up @@ -74,7 +74,7 @@ Feature: Eager Loading
FROM ApiPlatform\Tests\Fixtures\TestBundle\Entity\DummyTravel o
LEFT JOIN o.car car_a1
LEFT JOIN o.passenger passenger_a2
WHERE o.id = :id_id
WHERE o.id = :id_p1
"""

Scenario: Eager loading for a relation with complex sub-query filter
Expand Down
2 changes: 1 addition & 1 deletion features/jsonld/input_output.feature
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ Feature: JSON-LD DTO input and output

@!mongodb
Scenario: Use messenger with an input where the handler gives a synchronous result
And I send a "POST" request to "/messenger_with_inputs" with body:
When I send a "POST" request to "/messenger_with_inputs" with body:
"""
{
"var": "test"
Expand Down
1 change: 1 addition & 0 deletions features/jsonld/non_resource.feature
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Feature: JSON-LD non-resource handling
Given I add "Accept" header equal to "application/ld+json"
And I add "Content-Type" header equal to "application/ld+json"

@createSchema
Scenario: Get a resource containing a raw object
When I send a "GET" request to "/contain_non_resources/1"
Then the response status code should be 200
Expand Down
20 changes: 20 additions & 0 deletions rector.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
diff --git a/composer.json b/composer.json
index d5beb2afc..fca961b12 100644
--- a/composer.json
+++ b/composer.json
@@ -47,10 +47,10 @@
"phpdocumentor/reflection-docblock": "^3.0 || ^4.0 || ^5.1",
"phpdocumentor/type-resolver": "^0.3 || ^0.4 || ^1.4",
"phpstan/extension-installer": "^1.0",
- "phpstan/phpstan": "^0.12.65",
- "phpstan/phpstan-doctrine": "^0.12.7",
- "phpstan/phpstan-phpunit": "^0.12.4",
- "phpstan/phpstan-symfony": "^0.12.4",
+ "phpstan/phpstan": "*",
+ "phpstan/phpstan-doctrine": "*",
+ "phpstan/phpstan-phpunit": "*",
+ "phpstan/phpstan-symfony": "*",
"psr/log": "^1.0",
"ramsey/uuid": "^3.7 || ^4.0",
"ramsey/uuid-doctrine": "^1.4",

3 changes: 2 additions & 1 deletion src/Bridge/Doctrine/Orm/State/CollectionProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
use ApiPlatform\State\ProviderInterface;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ManagerRegistry;

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

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

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

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

Expand Down
23 changes: 12 additions & 11 deletions src/Bridge/Doctrine/Orm/State/LinksHandlerTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ private function handleLinks(QueryBuilder $queryBuilder, array $identifiers, Que

$previousAlias = $alias;
$previousIdentifiers = end($links)->getIdentifiers();
$previousJoinProperty = $doctrineClassMetadata->getIdentifier()[0];
$expressions = [];
$identifiers = array_reverse($identifiers);

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

$identifierProperties = $link->getIdentifiers();
$currentAlias = $queryNameGenerator->generateJoinAlias($alias);

if ($link->getFromClass() === $resourceClass) {
$currentAlias = $alias;
}

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

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

$previousAlias = $currentAlias;
$previousIdentifiers = $identifierProperties;
$previousJoinProperty = $doctrineClassMetadata->getIdentifier()[0];
continue;
}

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

$previousIdentifier = $previousIdentifiers[0];
$identifierProperty = $identifierProperties[0];
$joinProperty = $doctrineClassMetadata->getIdentifier()[0];
$placeholder = $queryNameGenerator->generateParameterName($identifierProperty);

if ($link->getFromProperty() && !$link->getToProperty()) {
$doctrineClassMetadata = $manager->getClassMetadata($link->getFromClass());
$joinAlias = $queryNameGenerator->generateJoinAlias('m');
$assocationMapping = $doctrineClassMetadata->getAssociationMappings()[$link->getFromProperty()];
$relationType = $assocationMapping['type'];
$associationMapping = $doctrineClassMetadata->getAssociationMapping($link->getFromProperty());
$relationType = $associationMapping['type'];

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

// A single-valued association path expression to an inverse side is not supported in DQL queries.
if ($relationType & ClassMetadataInfo::TO_ONE && !$assocationMapping['isOwningSide']) {
$queryBuilder->innerJoin("$previousAlias.".$assocationMapping['mappedBy'], $joinAlias);
if ($relationType & ClassMetadataInfo::TO_ONE && !($associationMapping['isOwningSide'] ?? true)) {
$queryBuilder->innerJoin("$previousAlias.".$associationMapping['mappedBy'], $joinAlias);
} else {
$queryBuilder->join(
$link->getFromClass(),
$joinAlias,
'with',
"{$previousAlias}.{$previousIdentifier} = $joinAlias.{$link->getFromProperty()}"
"{$previousAlias}.{$previousJoinProperty} = $joinAlias.{$link->getFromProperty()}"
);
}

$queryBuilder->andWhere("$joinAlias.$identifierProperty = :$placeholder");
$queryBuilder->setParameter($placeholder, array_shift($identifiers), $doctrineClassMetadata->getTypeOfField($identifierProperty));
$previousAlias = $joinAlias;
$previousIdentifier = $identifierProperty;
$previousJoinProperty = $joinProperty;
continue;
}

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

if ($expressions) {
Expand Down
11 changes: 6 additions & 5 deletions src/Core/Bridge/Doctrine/Orm/ItemDataProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public function getItem(string $resourceClass, $id, string $operationName = null
$queryNameGenerator = new QueryNameGenerator();
$doctrineClassMetadata = $manager->getClassMetadata($resourceClass);

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

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

/**
* Add WHERE conditions to the query for one or more identifiers (simple or composite).
*
* @param mixed $queryNameGenerator
*/
private function addWhereForIdentifiers(array $identifiers, QueryBuilder $queryBuilder, ClassMetadata $classMetadata)
private function addWhereForIdentifiers(array $identifiers, QueryBuilder $queryBuilder, ClassMetadata $classMetadata, $queryNameGenerator)
{
$alias = $queryBuilder->getRootAliases()[0];
foreach ($identifiers as $identifier => $value) {
$placeholder = ':id_'.$identifier;
$placeholder = $queryNameGenerator->generateParameterName($identifier);
$expression = $queryBuilder->expr()->eq(
"{$alias}.{$identifier}",
$placeholder
':'.$placeholder
);

$queryBuilder->andWhere($expression);

$queryBuilder->setParameter($placeholder, $value, $classMetadata->getTypeOfField($identifier));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ abstract class AbstractLegacyApiResourceToApiResourceAttribute extends AbstractR
'itemOperations' => [
'get',
'put',
'patch',
'delete',
],
'collectionOperations' => [
Expand Down
12 changes: 8 additions & 4 deletions src/Core/Bridge/Rector/Service/SubresourceTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ final class SubresourceTransformer

public function __construct()
{
$this->ormMetadataFactory = new AnnotationDriver(new AnnotationReader());
$this->odmMetadataFactory = new ODMAnnotationDriver(new AnnotationReader());
$this->ormMetadataFactory = class_exists(AnnotationDriver::class) ? new AnnotationDriver(new AnnotationReader()) : null;
$this->odmMetadataFactory = class_exists(ODMAnnotationDriver::class) ? new ODMAnnotationDriver(new AnnotationReader()) : null;
}

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

try {
$this->ormMetadataFactory->loadMetadataForClass($class, $metadata);
if ($this->ormMetadataFactory) {
$this->ormMetadataFactory->loadMetadataForClass($class, $metadata);
}
} catch (MappingException $e) {
}

try {
$this->odmMetadataFactory->loadMetadataForClass($class, $metadata);
if ($this->odmMetadataFactory) {
$this->odmMetadataFactory->loadMetadataForClass($class, $metadata);
}
} catch (ODMMappingException $e) {
}

Expand Down
35 changes: 8 additions & 27 deletions src/Core/Hydra/Serializer/CollectionFiltersNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@

namespace ApiPlatform\Core\Hydra\Serializer;

use ApiPlatform\Api\ResourceClassResolverInterface;
use ApiPlatform\Core\Api\FilterCollection;
use ApiPlatform\Core\Api\FilterInterface;
use ApiPlatform\Core\Api\FilterLocatorTrait;
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
use ApiPlatform\Core\Api\ResourceClassResolverInterface as LegacyResourceClassResolverInterface;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
use Psr\Container\ContainerInterface;
Expand All @@ -33,23 +34,21 @@
final class CollectionFiltersNormalizer implements NormalizerInterface, NormalizerAwareInterface, CacheableSupportsMethodInterface
{
use FilterLocatorTrait;

private $collectionNormalizer;
private $resourceMetadataFactory;
private $resourceClassResolver;

/**
* @param ContainerInterface|FilterCollection $filterLocator The new filter locator or the deprecated filter collection
* @param mixed $resourceMetadataFactory
* @param ContainerInterface|FilterCollection $filterLocator The new filter locator or the deprecated filter collection
* @param mixed $resourceMetadataFactory
* @param ResourceClassResolverInterface|LegacyResourceClassResolverInterface $resourceClassResolver
*/
public function __construct(NormalizerInterface $collectionNormalizer, $resourceMetadataFactory, ResourceClassResolverInterface $resourceClassResolver, $filterLocator)
public function __construct(NormalizerInterface $collectionNormalizer, $resourceMetadataFactory, $resourceClassResolver, $filterLocator)
{
$this->setFilterLocator($filterLocator);

$this->collectionNormalizer = $collectionNormalizer;
$this->resourceMetadataFactory = $resourceMetadataFactory;
$this->resourceClassResolver = $resourceClassResolver;

if (!$resourceMetadataFactory instanceof ResourceMetadataCollectionFactoryInterface) {
trigger_deprecation('api-platform/core', '2.7', sprintf('Use "%s" instead of "%s".', ResourceMetadataCollectionFactoryInterface::class, ResourceMetadataFactoryInterface::class));
}
Expand Down Expand Up @@ -80,17 +79,13 @@ public function normalize($object, $format = null, array $context = []): array
if (!\is_array($data)) {
throw new UnexpectedValueException('Expected data to be an array');
}

if (!isset($context['resource_class']) || isset($context['api_sub_level'])) {
return $data;
}

$resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class']);
$resourceFilters = null;

if ($this->resourceMetadataFactory instanceof ResourceMetadataFactoryInterface) {
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);

$operationName = $context['collection_operation_name'] ?? null;
if (null === $operationName) {
$resourceFilters = $resourceMetadata->getAttribute('filters', []);
Expand All @@ -101,23 +96,19 @@ public function normalize($object, $format = null, array $context = []): array
$operation = $context['operation'] ?? $this->resourceMetadataFactory->create($resourceClass)->getOperation($context['operation_name'] ?? null);
$resourceFilters = $operation->getFilters();
}

if (!$resourceFilters) {
return $data;
}

$requestParts = parse_url($context['request_uri'] ?? '');
if (!\is_array($requestParts)) {
return $data;
}

$currentFilters = [];
foreach ($resourceFilters as $filterId) {
if ($filter = $this->getFilter($filterId)) {
$currentFilters[] = $filter;
}
}

if ($currentFilters) {
$data['hydra:search'] = $this->getSearch($resourceClass, $requestParts, $currentFilters);
}
Expand Down Expand Up @@ -147,20 +138,10 @@ private function getSearch(string $resourceClass, array $parts, array $filters):
foreach ($filters as $filter) {
foreach ($filter->getDescription($resourceClass) as $variable => $data) {
$variables[] = $variable;
$mapping[] = [
'@type' => 'IriTemplateMapping',
'variable' => $variable,
'property' => $data['property'],
'required' => $data['required'],
];
$mapping[] = ['@type' => 'IriTemplateMapping', 'variable' => $variable, 'property' => $data['property'], 'required' => $data['required']];
}
}

return [
'@type' => 'hydra:IriTemplate',
'hydra:template' => sprintf('%s{?%s}', $parts['path'], implode(',', $variables)),
'hydra:variableRepresentation' => 'BasicRepresentation',
'hydra:mapping' => $mapping,
];
return ['@type' => 'hydra:IriTemplate', 'hydra:template' => sprintf('%s{?%s}', $parts['path'], implode(',', $variables)), 'hydra:variableRepresentation' => 'BasicRepresentation', 'hydra:mapping' => $mapping];
}
}
3 changes: 2 additions & 1 deletion src/Core/Hydra/Serializer/DocumentationNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
use ApiPlatform\Metadata\Resource\ResourceMetadataCollection;
use Symfony\Component\PropertyInfo\Type;
Expand Down Expand Up @@ -342,7 +343,7 @@ private function getHydraOperations(string $resourceClass, $resourceMetadata, st
$hydraOperations = [];
foreach ($resourceMetadataCollection as $resourceMetadata) {
foreach ($resourceMetadata->getOperations() as $operationName => $operation) {
if (($operation->isCollection() ?? false) !== $collection) {
if (($operation instanceof Post || ($operation->isCollection() ?? false)) !== $collection) {
continue;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ private function transformUriVariablesToIdentifiers(array $arrayOperation): arra

$arrayOperation['identifiers'] = [];
foreach ($arrayOperation['uri_variables'] as $parameterName => $identifiedBy) {
if (1 === \count($identifiedBy->getIdentifiers())) {
$arrayOperation['identifiers'][$parameterName] = [$identifiedBy->getFromClass(), $identifiedBy->getIdentifiers()[0]];
if (1 === \count($identifiedBy->getIdentifiers() ?? ['id'])) {
$arrayOperation['identifiers'][$parameterName] = [$identifiedBy->getFromClass(), $identifiedBy->getIdentifiers()[0] ?? ['id']];
continue;
}

Expand Down

0 comments on commit 0fda38c

Please sign in to comment.