Skip to content

Commit e84373d

Browse files
fix (cache): prevent avoidable purge
1 parent 9eafd71 commit e84373d

File tree

3 files changed

+102
-7
lines changed

3 files changed

+102
-7
lines changed

src/Bridge/Doctrine/EventListener/PurgeHttpCacheListener.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ public function onFlush(OnFlushEventArgs $eventArgs): void
9999
*/
100100
public function postFlush(): void
101101
{
102+
if (empty($this->tags)) {
103+
return;
104+
}
105+
102106
$this->purger->purge($this->tags);
103107
$this->tags = [];
104108
}
@@ -107,16 +111,16 @@ private function gatherResourceAndItemTags($entity, bool $purgeItem): void
107111
{
108112
try {
109113
$resourceClass = $this->resourceClassResolver->getResourceClass($entity);
114+
$iri = $this->iriConverter->getIriFromResourceClass($resourceClass);
115+
$this->tags[$iri] = $iri;
116+
if ($purgeItem) {
117+
$iri = $this->iriConverter->getIriFromItem($entity);
118+
$this->tags[$iri] = $iri;
119+
}
110120
} catch (InvalidArgumentException $e) {
111121
return;
112122
}
113123

114-
$iri = $this->iriConverter->getIriFromResourceClass($resourceClass);
115-
$this->tags[$iri] = $iri;
116-
if ($purgeItem) {
117-
$iri = $this->iriConverter->getIriFromItem($entity);
118-
$this->tags[$iri] = $iri;
119-
}
120124
}
121125

122126
private function gatherRelationTags(EntityManagerInterface $em, $entity): void

tests/Bridge/Doctrine/EventListener/PurgeHttpCacheListenerTest.php

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
use ApiPlatform\Core\Api\IriConverterInterface;
1717
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
1818
use ApiPlatform\Core\Bridge\Doctrine\EventListener\PurgeHttpCacheListener;
19+
use ApiPlatform\Core\Exception\InvalidArgumentException;
1920
use ApiPlatform\Core\HttpCache\PurgerInterface;
2021
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Dummy;
22+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyNoGetOperation;
2123
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\RelatedDummy;
2224
use Doctrine\ORM\EntityManagerInterface;
2325
use Doctrine\ORM\Event\OnFlushEventArgs;
@@ -47,27 +49,34 @@ public function testOnFlush()
4749
$toDelete2 = new Dummy();
4850
$toDelete2->setId(4);
4951

52+
$toDeleteNoPurge = new DummyNoGetOperation();
53+
$toDeleteNoPurge->setId(5);
54+
5055
$purgerProphecy = $this->prophesize(PurgerInterface::class);
5156
$purgerProphecy->purge(['/dummies' => '/dummies', '/dummies/1' => '/dummies/1', '/dummies/2' => '/dummies/2', '/dummies/3' => '/dummies/3', '/dummies/4' => '/dummies/4'])->shouldBeCalled();
5257

5358
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
5459
$iriConverterProphecy->getIriFromResourceClass(Dummy::class)->willReturn('/dummies')->shouldBeCalled();
60+
$iriConverterProphecy->getIriFromResourceClass(DummyNoGetOperation::class)->willThrow(new InvalidArgumentException())->shouldBeCalled();
5561
$iriConverterProphecy->getIriFromItem($toUpdate1)->willReturn('/dummies/1')->shouldBeCalled();
5662
$iriConverterProphecy->getIriFromItem($toUpdate2)->willReturn('/dummies/2')->shouldBeCalled();
5763
$iriConverterProphecy->getIriFromItem($toDelete1)->willReturn('/dummies/3')->shouldBeCalled();
5864
$iriConverterProphecy->getIriFromItem($toDelete2)->willReturn('/dummies/4')->shouldBeCalled();
65+
$iriConverterProphecy->getIriFromItem($toDeleteNoPurge)->shouldNotBeCalled();
5966

6067
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
6168
$resourceClassResolverProphecy->getResourceClass(Argument::type(Dummy::class))->willReturn(Dummy::class)->shouldBeCalled();
69+
$resourceClassResolverProphecy->getResourceClass(Argument::type(DummyNoGetOperation::class))->willReturn(DummyNoGetOperation::class)->shouldBeCalled();
6270

6371
$uowProphecy = $this->prophesize(UnitOfWork::class);
6472
$uowProphecy->getScheduledEntityInsertions()->willReturn([$toInsert1, $toInsert2])->shouldBeCalled();
6573
$uowProphecy->getScheduledEntityUpdates()->willReturn([$toUpdate1, $toUpdate2])->shouldBeCalled();
66-
$uowProphecy->getScheduledEntityDeletions()->willReturn([$toDelete1, $toDelete2])->shouldBeCalled();
74+
$uowProphecy->getScheduledEntityDeletions()->willReturn([$toDelete1, $toDelete2, $toDeleteNoPurge])->shouldBeCalled();
6775

6876
$emProphecy = $this->prophesize(EntityManagerInterface::class);
6977
$emProphecy->getUnitOfWork()->willReturn($uowProphecy->reveal())->shouldBeCalled();
7078
$emProphecy->getClassMetadata(Dummy::class)->willReturn(new ClassMetadata(Dummy::class))->shouldBeCalled();
79+
$emProphecy->getClassMetadata(DummyNoGetOperation::class)->willReturn(new ClassMetadata(DummyNoGetOperation::class))->shouldBeCalled();
7180
$eventArgs = new OnFlushEventArgs($emProphecy->reveal());
7281

7382
$listener = new PurgeHttpCacheListener($purgerProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal());
@@ -111,4 +120,32 @@ public function testPreUpdate()
111120
$listener->preUpdate($eventArgs);
112121
$listener->postFlush();
113122
}
123+
124+
public function testNothingToPurge()
125+
{
126+
$dummyNoGetOperation = new DummyNoGetOperation();
127+
$dummyNoGetOperation->setId(1);
128+
129+
$purgerProphecy = $this->prophesize(PurgerInterface::class);
130+
$purgerProphecy->purge([])->shouldNotBeCalled();
131+
132+
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
133+
$iriConverterProphecy->getIriFromResourceClass(DummyNoGetOperation::class)->willThrow(new InvalidArgumentException())->shouldBeCalled();
134+
$iriConverterProphecy->getIriFromItem($dummyNoGetOperation)->shouldNotBeCalled();
135+
136+
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
137+
$resourceClassResolverProphecy->getResourceClass(Argument::type(DummyNoGetOperation::class))->willReturn(DummyNoGetOperation::class)->shouldBeCalled();
138+
139+
$emProphecy = $this->prophesize(EntityManagerInterface::class);
140+
141+
$classMetadata = new ClassMetadata(DummyNoGetOperation::class);
142+
$emProphecy->getClassMetadata(DummyNoGetOperation::class)->willReturn($classMetadata)->shouldBeCalled();
143+
144+
$changeSet = ['lorem' => 'ipsum'];
145+
$eventArgs = new PreUpdateEventArgs($dummyNoGetOperation, $emProphecy->reveal(), $changeSet);
146+
147+
$listener = new PurgeHttpCacheListener($purgerProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal());
148+
$listener->preUpdate($eventArgs);
149+
$listener->postFlush();
150+
}
114151
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity;
15+
16+
use ApiPlatform\Core\Annotation\ApiResource;
17+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Dto\InputDto;
18+
use Doctrine\ORM\Mapping as ORM;
19+
20+
/**
21+
* DummyNoGetOperation
22+
*
23+
* @author Grégoire Hébert [email protected]
24+
*
25+
* @ORM\Entity
26+
*
27+
* @ApiResource(
28+
* collectionOperations={"post"},
29+
* itemOperations={"put"}
30+
* )
31+
*/
32+
class DummyNoGetOperation
33+
{
34+
/**
35+
* @var int The id
36+
*
37+
* @ORM\Column(type="integer")
38+
* @ORM\Id
39+
* @ORM\GeneratedValue(strategy="AUTO")
40+
*/
41+
private $id;
42+
43+
/**
44+
* @var string
45+
*
46+
* @ORM\Column
47+
*/
48+
public $lorem;
49+
50+
public function setId($id)
51+
{
52+
$this->id = $id;
53+
}
54+
}

0 commit comments

Comments
 (0)