11# WebfactoryHttpCacheBundle
22
3- WebfactoryHttpCacheBundle is a Symfony bundle that features a more
3+ ` WebfactoryHttpCacheBundle ` is a Symfony bundle that features a more
44powerful [ HTTP cache validation via the last modified header] than the
5- ``` @ Cache``` annotation in the excellent [ SensioFrameworkExtraBundle ] .
5+ ` #[ Cache] ` attribute contained in the [ symfony/http-kernel package ] .
66
77[ HTTP cache validation via the last modified header ] : https://symfony.com/doc/current/http_cache/validation.html#validation-with-the-last-modified-header
8- [ SensioFrameworkExtraBundle ] : https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/cache .html
8+ [ symfony/http-kernel package ] : https://symfony.com/doc/current/http_cache .html#http-cache-expiration-intro
99
10- While the SensioFrameworkExtraBundle's ``` @Cache ``` annotation restricts
11- you to the request parameters, the ``` @ReplaceWithNotModifiedResponse ```
12- annotation lets you write small LastModifiedDeterminators for each one
13- of the underlying ressources of the requested page, They can be reused
14- and combined freely and can even be defined as services.
10+ The ` #[ReplaceWithNotModifiedResponse] ` attribute lets you write small
11+ ` LastModifiedDeterminators ` for each one of the underlying resources
12+ of the requested page. They can be reused and combined freely and can
13+ even be defined as services.
1514
16- Lets take the example from the SensioFrameworkExtraBundle docs (stripped
17- off the ETag part, which is not supported by the
18- WebfactoryHttpCacheBundle):
15+ Consider this controller code:
1916
2017``` php
21- use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
22-
23- /**
24- * @Cache(lastModified="post.getUpdatedAt()")
25- */
26- public function indexAction(Post $post)
27- {
28- // your code
29- // won't be called in case of a 304
30- }
31- ```
32-
33- This falls short if the rendered template e.g. contains information
34- about the x latest posts. That can be done with the
35- ``` @ReplaceWithNotModifiedResponse ``` annotation:
18+ <?php
3619
37- ``` php
38- use Webfactory\HttpCacheBundle\NotModified\Annotation \ReplaceWithNotModifiedResponse;
20+ // ...
21+ use Webfactory\HttpCacheBundle\NotModified\Attribute \ReplaceWithNotModifiedResponse;
3922
40- /**
41- * @ReplaceWithNotModifiedResponse({"@app_caching_post", "@app_caching_latest_posts"})
42- */
43- public function indexAction(Post $post)
44- {
45- // your code
46- // won't be called in case of a 304
23+ class MyController {
24+ // Routing etc. configuration skipped for brevity
25+
26+ #[ReplaceWithNotModifiedResponse(["@app_caching_post", "@app_caching_latest_posts"])]
27+ public function indexAction(Post $post): Response
28+ {
29+ // your code
30+ // won't be called in case of a 304
31+ }
4732}
4833```
4934
5035When Symfony's routing has chosen this controller action, all of the
51- LastModifiedDeterminators are called to return their respective last
36+ ` LastModifiedDeterminator ` s are called to return their respective last
5237modified date.
5338
5439In this case, both LastModifiedDeterminators are configured as services:
55- ``` @app_caching_post ``` and ``` @app_caching_latest_posts `` ` . The first
56- one returns the update date of the requests $post, the second one may
40+ ` @app_caching_post ` and ` @app_caching_latest_posts ` . The first
41+ one returns the update date of the requests ` $post ` , the second one may
5742use the PostRepository injected from the DI container to return the last
5843update date of the x latest posts.
5944
60- Then, ReplaceWithNotModifiedResponse combines all of the
61- LastModifiedDeterminators dates to determine to last modified date of
45+ ` #[ ReplaceWithNotModifiedResponse] ` combines all of the
46+ ` LastModifiedDeterminators ` dates to determine to last modified date of
6247the overall page. Finally, if the request contains an appropriate
63- ``` if-not-modified-since `` ` header, the execution of the controller
64- action will be skipped and an empty response with a 304 Not Modified
65- status code will be sent. If your LastModifiedDeterminators are fast,
48+ ` if-not-modified-since ` header, the execution of the controller
49+ action will be skipped and an empty response with a " 304 Not Modified"
50+ status code will be sent. If your ` LastModifiedDeterminators ` are fast,
6651this can improve your performance greatly.
6752
68- What we like about the LastModifiedDeterminators is that they encourage
53+ What we like about the ` LastModifiedDeterminators ` is that they encourage
6954to separate the concerns nicely and encapsulate the tasks into small
7055units that are easy to understand, reusable and unit test.
7156
72- * Note:* ` @ ReplaceWithNotModifiedResponse` does not alter or add
57+ * Note:* ` #[ ReplaceWithNotModifiedResponse] ` does not alter or add
7358` Cache-Control ` header settings. So, by default your response will
7459remain ` private ` and end up in browser caches only. If you want it to be
7560kept in surrogate caches (like Varnish or the Symfony Http Cache), you
76- can add ` @ Cache(smaxage="0") ` . This will make the response ` public ` , but
61+ can add ` #[ Cache(smaxage: 0)] ` . This will make the response ` public ` , but
7762also requires a revalidation on every request as the response is
7863* always* considered stale. [ Learn more about Symonfy's HTTP caching] .
7964
8065[ Learn more about Symonfy's HTTP caching ] : http://symfony.com/doc/current/book/http_cache.html
8166
82-
83-
84- ## Installation
85-
86- Install via [ composer] ( https://getcomposer.org/ ) :
87-
88- composer require webfactory/http-cache-bundle
89-
90- Register the bundle in your application:
91-
92- ``` php
93- <?php
94- // app/AppKernel.php
95-
96- public function registerBundles()
97- {
98- $bundles = array(
99- // ...
100- new Webfactory\HttpCacheBundle\WebfactoryHttpCacheBundle(),
101- // ...
102- );
103- // ...
104- }
105- ```
106-
107-
108-
10967## Usage
11068
11169Choose a controller action you want to possibly replace with a 304 Not Modified response. Write one LastModifiedDeterminator for each
112- of the different underlying resources, implementing the ``` Webfactory\HttpCacheBundle\NotModified\LastModifiedDeterminator `` ` interface.
70+ of the different underlying resources, implementing the ` Webfactory\HttpCacheBundle\NotModified\LastModifiedDeterminator ` interface.
11371
11472``` php
11573<?php
@@ -125,28 +83,25 @@ use Webfactory\HttpCacheBundle\NotModified\LastModifiedDeterminator;
12583 */
12684final class PostsLastModifiedDeterminator implements LastModifiedDeterminator
12785{
128- /** @var EntityRepository */
129- private $postRepository;
130-
131- public function __construct(PostRepository $postRepository)
132- {
133- $this->postRepository = $postRepository;
134- }
86+ public function __construct(
87+ private readonly BlogPostRepository $blogPostRepository,
88+ ) {
13589
136- public function getLastModified(Request $request)
90+ public function getLastModified(Request $request): ?\DateTime
13791 {
138- $post = $this->postRepository->findLatest();
139- return $post->getPublishingDate();
92+ $post = $this->blogPostRepository->findLatest();
93+
94+ return $post?->getPublishingDate();
14095 }
14196}
14297```
14398
144- You can use the ``` $request `` ` in the getLastModified e.g. to get route parameters, which is necessary e.g. if you have
99+ You can use the ` $request ` in the getLastModified e.g. to get route parameters, which is necessary e.g. if you have
145100some filters coded in the requested URL.
146101
147102If your LastModifiedDeterminator has dependencies you'd like to be injected, configure it as a service.
148103
149- Then, simply add the ``` ReplaceWithNotModifiedResponse ``` annotation to the chosen controller method and parameterise it
104+ Then, add the ` #[ ReplaceWithNotModifiedResponse] ` attribute to the chosen controller method and parameterize it
150105with your LastModifiedDeterminators:
151106
152107``` php
@@ -155,13 +110,11 @@ with your LastModifiedDeterminators:
155110namespace src\Controller;
156111
157112use Symfony\Component\HttpFoundation\Response;
158- use Webfactory\HttpCacheBundle\NotModified\Annotation \ReplaceWithNotModifiedResponse;
113+ use Webfactory\HttpCacheBundle\NotModified\Attribute \ReplaceWithNotModifiedResponse;
159114
160115final class MyController
161116{
162- /**
163- * @ReplaceWithNotModifiedResponse({...})
164- */
117+ #[ReplaceWithNotModifiedResponse([...])]
165118 public function indexAction()
166119 {
167120 // ...
@@ -172,36 +125,36 @@ final class MyController
172125
173126The most simple form of adding a LastModifiedDeterminator is passing its fully qualfified class name:
174127
175- @ ReplaceWithNotModifiedResponse({" \App\Caching\MySimpleLastModifiedDeterminator"})
128+ #[ ReplaceWithNotModifiedResponse([ \App\Caching\MySimpleLastModifiedDeterminator::class])]
176129
177130If your LastModifiedDeterminator needs simple constructor arguments, you can pass them in array form:
178131
179- @ ReplaceWithNotModifiedResponse({ {" \App\Caching\MyLastModifiedDeterminator" = { "key1" = 1, "key2" = { "*"} } } })
132+ #[ ReplaceWithNotModifiedResponse([ \App\Caching\MyLastModifiedDeterminator::class => [ "key1" => 1, "key2" => [ "*"]]])]
180133
181134This would pass the array [ 'key1' => 1, 'key2' => [ '* ']] as an argument to MyLastModifiedDeterminator's constructor.
182135
183136If your LastModifiedDeterminator has more sophisticated dependencies, you can define the LastModifiedDeterminator as a service, e.g.:
184137
185- ``` yaml
138+ `yaml
186139// services.yml
187140services:
188141 app_caching_latest_posts:
189142 class: App\Caching\PostsLastModifiedDeterminator
190143 arguments:
191144 - @repository_post
192- ```
145+ `
193146
194147and note the service name to the Annotation:
195148
196- @ ReplaceWithNotModifiedResponse({" app_caching_latest_posts"})
149+ #[ ReplaceWithNotModifiedResponse(["@ app_caching_latest_posts"])]
197150
198151To combine multiple LastModifiedDeterminators, simply add all of them to the annotation:
199152
200- @ ReplaceWithNotModifiedResponse({
153+ #[ ReplaceWithNotModifiedResponse([
201154 "@app_caching_latest_posts",
202- " \App\Caching\MySimpleLastModifiedDeterminator" ,
203- {" \App\Caching\MyLastModifiedDeterminator" = { "key1" = 1, "key2" = { "*"}}}
204- })
155+ \App\Caching\MySimpleLastModifiedDeterminator::class ,
156+ [ \App\Caching\MyLastModifiedDeterminator::class => [ "key1" = 1, "key2" => [ "*"]]
157+ ])]
205158
206159The latest last modified date determines the last modified date of the response.
207160
@@ -212,4 +165,4 @@ This bundle was started at webfactory GmbH, Bonn.
212165- < https://www.webfactory.de >
213166- < https://twitter.com/webfactory >
214167
215- Copyright 2018-2019 webfactory GmbH, Bonn. Code released under [ the MIT license] ( LICENSE ) .
168+ Copyright 2018-2024 webfactory GmbH, Bonn. Code released under [ the MIT license] ( LICENSE ) .
0 commit comments