Skip to content

Commit 9b665a5

Browse files
author
roadiz-ci
committed
feat(NodesSources): Respect user set TTL on nodes-sources during API requests
1 parent 39e7492 commit 9b665a5

File tree

6 files changed

+101
-21
lines changed

6 files changed

+101
-21
lines changed

src/Entity/Node.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,12 @@ class Node extends AbstractDateTimedPositioned implements LeafInterface, Attribu
128128

129129
#[ORM\Column(type: 'integer', nullable: false, options: ['default' => 0])]
130130
#[Assert\GreaterThanOrEqual(value: 0)]
131+
#[Assert\NotNull]
131132
#[SymfonySerializer\Ignore]
132133
#[Serializer\Exclude]
133134
#[Gedmo\Versioned]
134-
private int $ttl = 0;
135+
// @phpstan-ignore-next-line
136+
private ?int $ttl = 0;
135137

136138
#[ORM\Column(type: 'boolean', nullable: false, options: ['default' => false])]
137139
#[SymfonySerializer\Groups(['node'])]
@@ -421,15 +423,15 @@ public function setStatus(int|string $status): Node
421423
*/
422424
public function getTtl(): int
423425
{
424-
return $this->ttl;
426+
return $this->ttl ?? 0;
425427
}
426428

427429
/**
428-
* @param int $ttl
430+
* @param int|null $ttl
429431
*
430432
* @return Node
431433
*/
432-
public function setTtl(int $ttl): Node
434+
public function setTtl(?int $ttl): Node
433435
{
434436
$this->ttl = $ttl;
435437
return $this;

src/Entity/NodeType.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,11 @@ class NodeType extends AbstractEntity implements NodeTypeInterface
145145
Serializer\Groups(["node_type"]),
146146
SymfonySerializer\Groups(["node_type"]),
147147
Serializer\Type("int"),
148-
Assert\GreaterThanOrEqual(value: 0)
148+
Assert\GreaterThanOrEqual(value: 0),
149+
Assert\NotNull
149150
]
150-
private int $defaultTtl = 0;
151+
// @phpstan-ignore-next-line
152+
private ?int $defaultTtl = 0;
151153
/**
152154
* Define if this node-type title will be indexed during its parent indexation.
153155
*/
@@ -340,15 +342,15 @@ public function setColor(?string $color): NodeType
340342
*/
341343
public function getDefaultTtl(): int
342344
{
343-
return $this->defaultTtl;
345+
return $this->defaultTtl ?? 0;
344346
}
345347

346348
/**
347-
* @param int $defaultTtl
349+
* @param int|null $defaultTtl
348350
*
349351
* @return NodeType
350352
*/
351-
public function setDefaultTtl(int $defaultTtl): NodeType
353+
public function setDefaultTtl(?int $defaultTtl): NodeType
352354
{
353355
$this->defaultTtl = $defaultTtl;
354356

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace RZ\Roadiz\CoreBundle\EventSubscriber;
6+
7+
use ApiPlatform\Util\RequestAttributesExtractor;
8+
use RZ\Roadiz\CoreBundle\Entity\NodesSources;
9+
use RZ\Roadiz\CoreBundle\Preview\PreviewResolverInterface;
10+
use Symfony\Bundle\SecurityBundle\Security;
11+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
12+
use Symfony\Component\HttpKernel\Event\ResponseEvent;
13+
use Symfony\Component\HttpKernel\KernelEvents;
14+
15+
final class NodesSourcesAddHeadersSubscriber implements EventSubscriberInterface
16+
{
17+
public function __construct(
18+
private readonly PreviewResolverInterface $previewResolver,
19+
private readonly Security $security
20+
) {
21+
}
22+
23+
public static function getSubscribedEvents(): array
24+
{
25+
return [
26+
KernelEvents::RESPONSE => ['onKernelResponse', 0]
27+
];
28+
}
29+
30+
public function onKernelResponse(ResponseEvent $event): void
31+
{
32+
$request = $event->getRequest();
33+
$response = $event->getResponse();
34+
35+
if (!$request->isMethodCacheable()) {
36+
return;
37+
}
38+
if (!$response->getContent() || !$response->isSuccessful()) {
39+
return;
40+
}
41+
42+
$attributes = RequestAttributesExtractor::extractAttributes($request);
43+
if (\count($attributes) < 1) {
44+
return;
45+
}
46+
47+
if ($this->previewResolver->isPreview()) {
48+
return;
49+
}
50+
51+
if ($this->security->isGranted('IS_AUTHENTICATED')) {
52+
return;
53+
}
54+
55+
$resourceCacheHeaders = $attributes['cache_headers'] ?? [];
56+
$data = $request->attributes->get('data');
57+
58+
// if the public-property is defined and not yet set; apply it to the response
59+
$public = $resourceCacheHeaders['public'] ?? null;
60+
if (null !== $public && !$response->headers->hasCacheControlDirective('public')) {
61+
$public ? $response->setPublic() : $response->setPrivate();
62+
}
63+
64+
if (!$data instanceof NodesSources) {
65+
return;
66+
}
67+
68+
if ($data->getNode()->getTtl() <= 0) {
69+
return;
70+
}
71+
72+
if (null !== ($maxAge = $resourceCacheHeaders['max_age'] ?? $data->getNode()->getTtl()) && !$response->headers->hasCacheControlDirective('max-age')) {
73+
$response->setMaxAge($maxAge * 60);
74+
}
75+
// Cache-Control "s-maxage" is only relevant is resource is not marked as "private"
76+
if (false !== $public && null !== ($sharedMaxAge = $resourceCacheHeaders['shared_max_age'] ?? $data->getNode()->getTtl()) && !$response->headers->hasCacheControlDirective('s-maxage')) {
77+
$response->setSharedMaxAge($sharedMaxAge * 60);
78+
}
79+
}
80+
}

src/Preview/EventSubscriber/PreviewBarSubscriber.php

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,10 @@
1010
use Symfony\Component\HttpKernel\Event\ResponseEvent;
1111
use Symfony\Component\HttpKernel\KernelEvents;
1212

13-
class PreviewBarSubscriber implements EventSubscriberInterface
13+
final class PreviewBarSubscriber implements EventSubscriberInterface
1414
{
15-
protected PreviewResolverInterface $previewResolver;
16-
17-
/**
18-
* @param PreviewResolverInterface $previewResolver
19-
*/
20-
public function __construct(PreviewResolverInterface $previewResolver)
15+
public function __construct(private readonly PreviewResolverInterface $previewResolver)
2116
{
22-
$this->previewResolver = $previewResolver;
2317
}
2418

2519
/**

src/Preview/EventSubscriber/PreviewModeSubscriber.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ public static function getSubscribedEvents(): array
3131
return [
3232
KernelEvents::REQUEST => ['onKernelRequest', 2047],
3333
KernelEvents::CONTROLLER => ['onControllerMatched', 10],
34-
KernelEvents::RESPONSE => 'onResponse',
34+
// Must Triggered after API platform AddHeadersListener
35+
KernelEvents::RESPONSE => ['onResponse', -255],
3536
];
3637
}
3738

@@ -88,10 +89,11 @@ public function onResponse(ResponseEvent $event): void
8889
{
8990
if ($this->supports()) {
9091
$response = $event->getResponse();
91-
$response->expire();
92+
$response->setMaxAge(0);
93+
$response->setSharedMaxAge(0);
9294
$response->headers->addCacheControlDirective('no-store');
9395
$response->headers->add(['X-Roadiz-Preview' => true]);
94-
$event->setResponse($response);
96+
$response->setPrivate();
9597
}
9698
}
9799
}

src/Preview/RequestPreviewRevolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public function isPreview(): bool
3333
if (null === $request) {
3434
return false;
3535
}
36-
return $request->attributes->get('preview', false);
36+
return $request->attributes->getBoolean('preview');
3737
}
3838

3939
public function getRequiredRole(): string

0 commit comments

Comments
 (0)