Skip to content

Commit 0569e10

Browse files
authored
Merge pull request #2697 from GregoireHebert/fix-cache-purge
fix (cache): prevent avoidable purge
2 parents 5ef88f8 + a228079 commit 0569e10

File tree

3 files changed

+101
-8
lines changed

3 files changed

+101
-8
lines changed

src/Bridge/Doctrine/EventListener/PurgeHttpCacheListener.php

Lines changed: 10 additions & 7 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,15 @@ 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
}
113-
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-
}
120123
}
121124

122125
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: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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 Doctrine\ORM\Mapping as ORM;
18+
19+
/**
20+
* DummyNoGetOperation.
21+
*
22+
* @author Grégoire Hébert [email protected]
23+
*
24+
* @ORM\Entity
25+
*
26+
* @ApiResource(
27+
* collectionOperations={"post"},
28+
* itemOperations={"put"}
29+
* )
30+
*/
31+
class DummyNoGetOperation
32+
{
33+
/**
34+
* @var int The id
35+
*
36+
* @ORM\Column(type="integer")
37+
* @ORM\Id
38+
* @ORM\GeneratedValue(strategy="AUTO")
39+
*/
40+
private $id;
41+
42+
/**
43+
* @var string
44+
*
45+
* @ORM\Column
46+
*/
47+
public $lorem;
48+
49+
public function setId($id)
50+
{
51+
$this->id = $id;
52+
}
53+
}

0 commit comments

Comments
 (0)