Skip to content

Commit

Permalink
Merge pull request #6338 from soyuka/merge-322
Browse files Browse the repository at this point in the history
Merge 3.2
  • Loading branch information
soyuka committed Apr 29, 2024
2 parents 6d15e22 + 812158d commit dfa1b13
Show file tree
Hide file tree
Showing 17 changed files with 261 additions and 11 deletions.
19 changes: 18 additions & 1 deletion features/hal/collection_uri_template.feature
Expand Up @@ -36,16 +36,33 @@ Feature: Exposing a property being a collection of resources
"_links": {
"self": {
"href": "/property_collection_iri_only_relations/1"
},
"children": {
"href": "/property_collection_iri_only_relations/1/children"
}
},
"name": "asb"
"name": "asb1"
},
{
"_links": {
"self": {
"href": "/property_collection_iri_only_relations/2"
},
"children": {
"href": "/property_collection_iri_only_relations/2/children"
}
},
"name": "asb2"
}
],
"iterableIri": [
{
"_links": {
"self": {
"href": "/property_collection_iri_only_relations/9999"
},
"children": {
"href": "/property_collection_iri_only_relations/9999/children"
}
},
"name": "Michel"
Expand Down
4 changes: 4 additions & 0 deletions features/jsonapi/collection_uri_template.feature
Expand Up @@ -33,6 +33,10 @@ Feature: Exposing a property being a collection of resources
{
"type": "PropertyCollectionIriOnlyRelation",
"id": "/property_collection_iri_only_relations/1"
},
{
"type": "PropertyCollectionIriOnlyRelation",
"id": "/property_collection_iri_only_relations/2"
}
]
},
Expand Down
5 changes: 2 additions & 3 deletions src/Doctrine/EventListener/PurgeHttpCacheListener.php
Expand Up @@ -24,7 +24,6 @@
use ApiPlatform\Metadata\ResourceClassResolverInterface;
use ApiPlatform\Metadata\UrlGeneratorInterface;
use ApiPlatform\Metadata\Util\ClassInfoTrait;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
Expand Down Expand Up @@ -60,7 +59,7 @@ public function preUpdate(PreUpdateEventArgs $eventArgs): void
$changeSet = $eventArgs->getEntityChangeSet();
// @phpstan-ignore-next-line
$objectManager = method_exists($eventArgs, 'getObjectManager') ? $eventArgs->getObjectManager() : $eventArgs->getEntityManager();
$associationMappings = $objectManager->getClassMetadata(ClassUtils::getClass($eventArgs->getObject()))->getAssociationMappings();
$associationMappings = $objectManager->getClassMetadata(\get_class($eventArgs->getObject()))->getAssociationMappings();

foreach ($changeSet as $key => $value) {
if (!isset($associationMappings[$key])) {
Expand Down Expand Up @@ -127,7 +126,7 @@ private function gatherResourceAndItemTags(object $entity, bool $purgeItem): voi

private function gatherRelationTags(EntityManagerInterface $em, object $entity): void
{
$associationMappings = $em->getClassMetadata(ClassUtils::getClass($entity))->getAssociationMappings();
$associationMappings = $em->getClassMetadata($entity::class)->getAssociationMappings();
/** @var array|AssociationMapping $associationMapping according to the version of doctrine orm */
foreach ($associationMappings as $property => $associationMapping) {
if ($associationMapping instanceof AssociationMapping && ($associationMapping->targetEntity ?? null) && !$this->resourceClassResolver->isResourceClass($associationMapping->targetEntity)) {
Expand Down
2 changes: 1 addition & 1 deletion src/GraphQl/State/Provider/ResolverProvider.php
Expand Up @@ -69,7 +69,7 @@ private function getResourceClass(?object $item, ?string $resourceClass, string
return $itemClass;
}

if ($resourceClass !== $itemClass) {
if ($resourceClass !== $itemClass && !$item instanceof $resourceClass) {
throw new \UnexpectedValueException(sprintf($errorMessage, (new \ReflectionClass($resourceClass))->getShortName(), (new \ReflectionClass($itemClass))->getShortName()));
}

Expand Down
17 changes: 17 additions & 0 deletions src/GraphQl/Tests/State/Provider/ResolverProviderTest.php
Expand Up @@ -15,6 +15,9 @@

use ApiPlatform\GraphQl\Resolver\QueryItemResolverInterface;
use ApiPlatform\GraphQl\State\Provider\ResolverProvider;
use ApiPlatform\GraphQl\Tests\Fixtures\ApiResource\ChildFoo;
use ApiPlatform\GraphQl\Tests\Fixtures\ApiResource\ParentFoo;
use ApiPlatform\Metadata\GraphQl\Query;
use ApiPlatform\Metadata\GraphQl\QueryCollection;
use ApiPlatform\State\ProviderInterface;
use PHPUnit\Framework\TestCase;
Expand All @@ -35,4 +38,18 @@ public function testProvide(): void
$provider = new ResolverProvider($decorated, $resolverLocator);
$this->assertEquals($res, $provider->provide($operation, [], $context));
}

public function testProvideInheritedClass(): void
{
$res = new ChildFoo();
$operation = new Query(class: ParentFoo::class, resolver: 'foo');
$context = [];
$decorated = $this->createMock(ProviderInterface::class);
$resolverMock = $this->createMock(QueryItemResolverInterface::class);
$resolverMock->expects($this->once())->method('__invoke')->willReturn($res);
$resolverLocator = $this->createMock(ContainerInterface::class);
$resolverLocator->expects($this->once())->method('get')->with('foo')->willReturn($resolverMock);
$provider = new ResolverProvider($decorated, $resolverLocator);
$this->assertEquals($res, $provider->provide($operation, [], $context));
}
}
3 changes: 2 additions & 1 deletion src/Hal/Serializer/ItemNormalizer.php
Expand Up @@ -186,6 +186,7 @@ private function getComponents(object $object, ?string $format, array $context):

$relation['iri'] = $this->iriConverter->getIriFromResource($object, UrlGeneratorInterface::ABS_PATH, $operation, $childContext);
$relation['operation'] = $operation;
$cacheKey = null;
}

if ($propertyMetadata->isReadableLink()) {
Expand All @@ -202,7 +203,7 @@ private function getComponents(object $object, ?string $format, array $context):
}
}

if (false !== $context['cache_key']) {
if ($cacheKey && false !== $context['cache_key']) {
$this->componentsCache[$cacheKey] = $components;
}

Expand Down
1 change: 1 addition & 0 deletions src/Metadata/Extractor/XmlResourceExtractor.php
Expand Up @@ -408,6 +408,7 @@ private function buildOperations(\SimpleXMLElement $resource, array $root): ?arr
'queryParameterValidate' => $this->phpize($operation, 'queryParameterValidate', 'bool'),
'priority' => $this->phpize($operation, 'priority', 'integer'),
'name' => $this->phpize($operation, 'name', 'string'),
'routeName' => $this->phpize($operation, 'routeName', 'string'),
]);
}

Expand Down
1 change: 1 addition & 0 deletions src/Metadata/Extractor/schema/resources.xsd
Expand Up @@ -48,6 +48,7 @@
<xsd:attribute type="xsd:positiveInteger" name="priority"/>
<xsd:attribute type="xsd:string" name="name"/>
<xsd:attribute type="xsd:string" name="itemUriTemplate"/>
<xsd:attribute type="xsd:string" name="routeName"/>
</xsd:complexType>

<xsd:complexType name="graphQlOperations">
Expand Down
2 changes: 2 additions & 0 deletions src/Metadata/Tests/Extractor/XmlExtractorTest.php
Expand Up @@ -278,6 +278,7 @@ public function testValidXML(): void
'links' => null,
'headers' => ['hello' => 'world'],
'parameters' => null,
'routeName' => 'custom_route_name',
],
[
'name' => null,
Expand Down Expand Up @@ -389,6 +390,7 @@ public function testValidXML(): void
extraProperties: ['foo' => 'bar']
),
],
'routeName' => null,
],
],
'graphQlOperations' => null,
Expand Down
2 changes: 1 addition & 1 deletion src/Metadata/Tests/Extractor/xml/valid.xml
Expand Up @@ -99,7 +99,7 @@
</exceptionToStatus>

<operations>
<operation class="ApiPlatform\Metadata\GetCollection" name="custom_operation_name"/>
<operation class="ApiPlatform\Metadata\GetCollection" name="custom_operation_name" routeName="custom_route_name"/>
<operation class="ApiPlatform\Metadata\Get" uriTemplate="/users/{userId}/comments/{id}{._format}">
<uriVariables>
<uriVariable parameterName="userId" fromClass="ApiPlatform\Metadata\Tests\Fixtures\ApiResource\User" fromProperty="author"/>
Expand Down
2 changes: 2 additions & 0 deletions tests/.ignored-deprecations
Expand Up @@ -10,3 +10,5 @@

# Fixed when ApiPlatform\Api\FilterLocatorTrait will we deleted
%ApiPlatform\\Api\\FilterInterface is deprecated in favor of ApiPlatform\\Metadata\\FilterInterface%

%The "Symfony\\Bundle\\MakerBundle\\Maker\\MakeAuthenticator" class is deprecated, use any of the Security\\Make\* commands instead%
2 changes: 2 additions & 0 deletions tests/.ignored-deprecations-legacy-events
Expand Up @@ -23,3 +23,5 @@

# Fixed when ApiPlatform\Api\FilterLocatorTrait will we deleted
%ApiPlatform\\Api\\FilterInterface is deprecated in favor of ApiPlatform\\Metadata\\FilterInterface%

%The "Symfony\\Bundle\\MakerBundle\\Maker\\MakeAuthenticator" class is deprecated, use any of the Security\\Make\* commands instead%
13 changes: 9 additions & 4 deletions tests/Behat/DoctrineContext.php
Expand Up @@ -2019,18 +2019,23 @@ public function thereAreIriOnlyDummies(int $nb): void
*/
public function thereAreResourcesWithPropertyUriTemplates(): void
{
$propertyCollectionIriOnlyRelation = $this->isOrm() ? new PropertyCollectionIriOnlyRelation() : new PropertyCollectionIriOnlyRelationDocument();
$propertyCollectionIriOnlyRelation->name = 'asb';
$propertyCollectionIriOnlyRelation1 = $this->isOrm() ? new PropertyCollectionIriOnlyRelation() : new PropertyCollectionIriOnlyRelationDocument();
$propertyCollectionIriOnlyRelation1->name = 'asb1';

$propertyCollectionIriOnlyRelation2 = $this->isOrm() ? new PropertyCollectionIriOnlyRelation() : new PropertyCollectionIriOnlyRelationDocument();
$propertyCollectionIriOnlyRelation2->name = 'asb2';

$propertyToOneRelation = $this->isOrm() ? new PropertyUriTemplateOneToOneRelation() : new PropertyUriTemplateOneToOneRelationDocument();
$propertyToOneRelation->name = 'xarguš';

$propertyCollectionIriOnly = $this->isOrm() ? new PropertyCollectionIriOnly() : new PropertyCollectionIriOnlyDocument();
$propertyCollectionIriOnly->addPropertyCollectionIriOnlyRelation($propertyCollectionIriOnlyRelation);
$propertyCollectionIriOnly->addPropertyCollectionIriOnlyRelation($propertyCollectionIriOnlyRelation1);
$propertyCollectionIriOnly->addPropertyCollectionIriOnlyRelation($propertyCollectionIriOnlyRelation2);
$propertyCollectionIriOnly->setToOneRelation($propertyToOneRelation);

$this->manager->persist($propertyCollectionIriOnly);
$this->manager->persist($propertyCollectionIriOnlyRelation);
$this->manager->persist($propertyCollectionIriOnlyRelation1);
$this->manager->persist($propertyCollectionIriOnlyRelation2);
$this->manager->persist($propertyToOneRelation);
$this->manager->flush();
}
Expand Down
Expand Up @@ -13,9 +13,12 @@

namespace ApiPlatform\Tests\Fixtures\TestBundle\Document;

use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Link;
use ApiPlatform\Metadata\Post;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
use Symfony\Component\Serializer\Annotation\Groups;

Expand All @@ -42,6 +45,16 @@ class PropertyCollectionIriOnlyRelation
#[ODM\ReferenceOne(targetDocument: PropertyCollectionIriOnly::class)]
private ?PropertyCollectionIriOnly $propertyCollectionIriOnly = null;

#[ODM\ReferenceMany(targetDocument: PropertyCollectionIriOnlyRelationSecondLevel::class)]
#[ApiProperty(uriTemplate: '/property_collection_iri_only_relations/{parentId}/children')]
#[Groups('read')]
private Collection $children;

public function __construct()
{
$this->children = new ArrayCollection();
}

public function getId(): ?int
{
return $this->id ?? 9999;
Expand All @@ -56,4 +69,34 @@ public function setPropertyCollectionIriOnly(?PropertyCollectionIriOnly $propert
{
$this->propertyCollectionIriOnly = $propertyCollectionIriOnly;
}

/**
* @return Collection<int, PropertyCollectionIriOnlyRelationSecondLevel>
*/
public function getChildren(): Collection
{
return $this->children;
}

public function addChild(PropertyCollectionIriOnlyRelationSecondLevel $child): self
{
if (!$this->children->contains($child)) {
$this->children->add($child);
$child->setParent($this);
}

return $this;
}

public function removeChild(PropertyCollectionIriOnlyRelationSecondLevel $child): self
{
if ($this->children->removeElement($child)) {
// set the owning side to null (unless already changed)
if ($child->getParent() === $this) {
$child->setParent(null);
}
}

return $this;
}
}
@@ -0,0 +1,54 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Tests\Fixtures\TestBundle\Document;

use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Link;
use ApiPlatform\Metadata\Post;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;

#[
Post,
GetCollection(uriTemplate: '/property-collection-relation-second-levels'),
GetCollection(
uriTemplate: '/property_collection_iri_only_relations/{parentId}/children',
uriVariables: [
'parentId' => new Link(toProperty: 'parent', fromClass: PropertyCollectionIriOnlyRelation::class),
]
)
]
#[ODM\Document]
class PropertyCollectionIriOnlyRelationSecondLevel
{
#[ODM\Id(strategy: 'INCREMENT', type: 'int')]
private ?int $id = null;

#[ODM\ReferenceOne(targetDocument: PropertyCollectionIriOnlyRelation::class)]
private ?PropertyCollectionIriOnlyRelation $parent = null;

public function getId(): ?int
{
return $this->id ?? 9999;
}

public function getParent(): ?PropertyCollectionIriOnlyRelation
{
return $this->parent;
}

public function setParent(?PropertyCollectionIriOnlyRelation $parent): void
{
$this->parent = $parent;
}
}
Expand Up @@ -13,9 +13,12 @@

namespace ApiPlatform\Tests\Fixtures\TestBundle\Entity;

use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Link;
use ApiPlatform\Metadata\Post;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints\NotBlank;
Expand Down Expand Up @@ -49,6 +52,16 @@ class PropertyCollectionIriOnlyRelation
#[ORM\ManyToOne(inversedBy: 'propertyCollectionIriOnlyRelation')]
private ?PropertyCollectionIriOnly $propertyCollectionIriOnly = null;

#[ORM\OneToMany(mappedBy: 'parent', targetEntity: PropertyCollectionIriOnlyRelationSecondLevel::class)]
#[ApiProperty(uriTemplate: '/property_collection_iri_only_relations/{parentId}/children')]
#[Groups('read')]
private Collection $children;

public function __construct()
{
$this->children = new ArrayCollection();
}

public function getId(): ?int
{
return $this->id ?? 9999;
Expand All @@ -63,4 +76,34 @@ public function setPropertyCollectionIriOnly(?PropertyCollectionIriOnly $propert
{
$this->propertyCollectionIriOnly = $propertyCollectionIriOnly;
}

/**
* @return Collection<int, PropertyCollectionIriOnlyRelationSecondLevel>
*/
public function getChildren(): Collection
{
return $this->children;
}

public function addChild(PropertyCollectionIriOnlyRelationSecondLevel $child): self
{
if (!$this->children->contains($child)) {
$this->children->add($child);
$child->setParent($this);
}

return $this;
}

public function removeChild(PropertyCollectionIriOnlyRelationSecondLevel $child): self
{
if ($this->children->removeElement($child)) {
// set the owning side to null (unless already changed)
if ($child->getParent() === $this) {
$child->setParent(null);
}
}

return $this;
}
}

0 comments on commit dfa1b13

Please sign in to comment.