@@ -155,9 +155,12 @@ kernel::
155
155
$kernel->loadClassCache();
156
156
// wrap the default AppKernel with the AppCache one
157
157
$kernel = new AppCache($kernel);
158
+
158
159
$request = Request::createFromGlobals();
160
+
159
161
$response = $kernel->handle($request);
160
162
$response->send();
163
+
161
164
$kernel->terminate($request, $response);
162
165
163
166
The caching kernel will immediately act as a reverse proxy - caching responses
@@ -574,16 +577,22 @@ each ``ETag`` must be unique across all representations of the same resource.
574
577
575
578
To see a simple implementation, generate the ETag as the md5 of the content::
576
579
580
+ // src/AppBundle/Controller/DefaultController.php
581
+ namespace AppBundle\Controller;
582
+
577
583
use Symfony\Component\HttpFoundation\Request;
578
584
579
- public function indexAction(Request $request)
585
+ class DefaultController extends Controller
580
586
{
581
- $response = $this->render('MyBundle:Main:index.html.twig');
582
- $response->setETag(md5($response->getContent()));
583
- $response->setPublic(); // make sure the response is public/cacheable
584
- $response->isNotModified($request);
587
+ public function homepageAction(Request $request)
588
+ {
589
+ $response = $this->render('homepage.html.twig');
590
+ $response->setETag(md5($response->getContent()));
591
+ $response->setPublic(); // make sure the response is public/cacheable
592
+ $response->isNotModified($request);
585
593
586
- return $response;
594
+ return $response;
595
+ }
587
596
}
588
597
589
598
The :method: `Symfony\\ Component\\ HttpFoundation\\ Response::isNotModified `
@@ -630,28 +639,36 @@ For instance, you can use the latest update date for all the objects needed to
630
639
compute the resource representation as the value for the ``Last-Modified ``
631
640
header value::
632
641
642
+ // src/AppBundle/Controller/ArticleController.php
643
+ namespace AppBundle\Controller;
644
+
645
+ // ...
633
646
use Symfony\Component\HttpFoundation\Request;
647
+ use AppBundle\Entity\Article;
634
648
635
- public function showAction($articleSlug, Request $request)
649
+ class ArticleController extends Controller
636
650
{
637
- // ...
651
+ public function showAction(Article $article, Request $request)
652
+ {
653
+ $author = $article->getAuthor();
638
654
639
- $articleDate = new \DateTime($article->getUpdatedAt());
640
- $authorDate = new \DateTime($author->getUpdatedAt());
655
+ $articleDate = new \DateTime($article->getUpdatedAt());
656
+ $authorDate = new \DateTime($author->getUpdatedAt());
641
657
642
- $date = $authorDate > $articleDate ? $authorDate : $articleDate;
658
+ $date = $authorDate > $articleDate ? $authorDate : $articleDate;
643
659
644
- $response->setLastModified($date);
645
- // Set response as public. Otherwise it will be private by default.
646
- $response->setPublic();
660
+ $response->setLastModified($date);
661
+ // Set response as public. Otherwise it will be private by default.
662
+ $response->setPublic();
647
663
648
- if ($response->isNotModified($request)) {
649
- return $response;
650
- }
664
+ if ($response->isNotModified($request)) {
665
+ return $response;
666
+ }
651
667
652
- // ... do more work to populate the response with the full content
668
+ // ... do more work to populate the response with the full content
653
669
654
- return $response;
670
+ return $response;
671
+ }
655
672
}
656
673
657
674
The :method: `Symfony\\ Component\\ HttpFoundation\\ Response::isNotModified `
@@ -680,40 +697,46 @@ Put another way, the less you do in your application to return a 304 response,
680
697
the better. The ``Response::isNotModified() `` method does exactly that by
681
698
exposing a simple and efficient pattern::
682
699
700
+ // src/AppBundle/Controller/ArticleController.php
701
+ namespace AppBundle\Controller;
702
+
703
+ // ...
683
704
use Symfony\Component\HttpFoundation\Response;
684
705
use Symfony\Component\HttpFoundation\Request;
685
706
686
- public function showAction($articleSlug, Request $request)
707
+ class ArticleController extends Controller
687
708
{
688
- // Get the minimum information to compute
689
- // the ETag or the Last-Modified value
690
- // (based on the Request, data is retrieved from
691
- // a database or a key-value store for instance)
692
- $article = ...;
693
-
694
- // create a Response with an ETag and/or a Last-Modified header
695
- $response = new Response();
696
- $response->setETag($article->computeETag());
697
- $response->setLastModified($article->getPublishedAt());
698
-
699
- // Set response as public. Otherwise it will be private by default.
700
- $response->setPublic();
701
-
702
- // Check that the Response is not modified for the given Request
703
- if ($response->isNotModified($request)) {
704
- // return the 304 Response immediately
705
- return $response;
706
- }
709
+ public function showAction($articleSlug, Request $request)
710
+ {
711
+ // Get the minimum information to compute
712
+ // the ETag or the Last-Modified value
713
+ // (based on the Request, data is retrieved from
714
+ // a database or a key-value store for instance)
715
+ $article = ...;
716
+
717
+ // create a Response with an ETag and/or a Last-Modified header
718
+ $response = new Response();
719
+ $response->setETag($article->computeETag());
720
+ $response->setLastModified($article->getPublishedAt());
707
721
708
- // do more work here - like retrieving more data
709
- $comments = ... ;
722
+ // Set response as public. Otherwise it will be private by default.
723
+ $response->setPublic() ;
710
724
711
- // or render a template with the $response you've already started
712
- return $this->render(
713
- 'MyBundle:MyController:article.html.twig',
714
- array('article' => $article, 'comments' => $comments),
715
- $response
716
- );
725
+ // Check that the Response is not modified for the given Request
726
+ if ($response->isNotModified($request)) {
727
+ // return the 304 Response immediately
728
+ return $response;
729
+ }
730
+
731
+ // do more work here - like retrieving more data
732
+ $comments = ...;
733
+
734
+ // or render a template with the $response you've already started
735
+ return $this->render('Article/show.html.twig', array(
736
+ 'article' => $article,
737
+ 'comments' => $comments
738
+ ), $response);
739
+ }
717
740
}
718
741
719
742
When the ``Response `` is not modified, the ``isNotModified() `` automatically sets
@@ -863,10 +886,10 @@ Here is how you can configure the Symfony reverse proxy to support the
863
886
864
887
// app/AppCache.php
865
888
866
- // ...
867
889
use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache;
868
890
use Symfony\Component\HttpFoundation\Request;
869
891
use Symfony\Component\HttpFoundation\Response;
892
+ // ...
870
893
871
894
class AppCache extends HttpCache
872
895
{
@@ -925,7 +948,7 @@ have one limitation: they can only cache whole pages. If you can't cache
925
948
whole pages or if parts of a page has "more" dynamic parts, you are out of
926
949
luck. Fortunately, Symfony provides a solution for these cases, based on a
927
950
technology called `ESI `_, or Edge Side Includes. Akamai wrote this specification
928
- almost 10 years ago, and it allows specific parts of a page to have a different
951
+ almost 10 years ago and it allows specific parts of a page to have a different
929
952
caching strategy than the main page.
930
953
931
954
The ESI specification describes tags you can embed in your pages to communicate
@@ -987,8 +1010,12 @@ First, to use ESI, be sure to enable it in your application configuration:
987
1010
<container xmlns =" http://symfony.com/schema/dic/symfony"
988
1011
xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
989
1012
xmlns : framework =" http://symfony.com/schema/dic/symfony"
990
- xsi : schemaLocation =" http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
991
- http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd" >
1013
+ xsi : schemaLocation ="
1014
+ http://symfony.com/schema/dic/services
1015
+ http://symfony.com/schema/dic/services/services-1.0.xsd
1016
+ http://symfony.com/schema/dic/symfony
1017
+ http://symfony.com/schema/dic/symfony/symfony-1.0.xsd
1018
+ " >
992
1019
993
1020
<framework : config >
994
1021
<!-- ... -->
@@ -1010,13 +1037,19 @@ independent of the rest of the page.
1010
1037
1011
1038
.. code-block :: php
1012
1039
1013
- public function indexAction()
1040
+ // src/AppBundle/Controller/DefaultController.php
1041
+
1042
+ // ...
1043
+ class DefaultController extends Controller
1014
1044
{
1015
- $response = $this->render('MyBundle:MyController:index.html.twig');
1016
- // set the shared max age - which also marks the response as public
1017
- $response->setSharedMaxAge(600);
1045
+ public function aboutAction()
1046
+ {
1047
+ $response = $this->render('about.html.twig');
1048
+ // set the shared max age - which also marks the response as public
1049
+ $response->setSharedMaxAge(600);
1018
1050
1019
- return $response;
1051
+ return $response;
1052
+ }
1020
1053
}
1021
1054
1022
1055
In this example, the full-page cache has a lifetime of ten minutes.
@@ -1031,21 +1064,36 @@ matter), Symfony uses the standard ``render`` helper to configure ESI tags:
1031
1064
1032
1065
.. code-block :: jinja
1033
1066
1067
+ {# app/Resources/views/about.html.twig #}
1068
+
1034
1069
{# you can use a controller reference #}
1035
- {{ render_esi(controller('...:news ', { 'maxPerPage': 5 })) }}
1070
+ {{ render_esi(controller('AppBundle:News:latest ', { 'maxPerPage': 5 })) }}
1036
1071
1037
1072
{# ... or a URL #}
1038
1073
{{ render_esi(url('latest_news', { 'maxPerPage': 5 })) }}
1039
1074
1040
1075
.. code-block :: html+php
1041
1076
1077
+ <!-- app/Resources/views/about.html.php -->
1078
+
1079
+ // you can use a controller reference
1080
+ use Symfony\C omponent\H ttpKernel\C ontroller\C ontrollerReference;
1042
1081
<?php echo $view['actions']->render(
1043
- new \S ymfony\C omponent\H ttpKernel\C ontroller\C ontrollerReference('...:news', array('maxPerPage' => 5)),
1044
- array('strategy' => 'esi'))
1045
- ?>
1082
+ new ControllerReference(
1083
+ 'AppBundle:News: latest',
1084
+ array('maxPerPage' => 5)
1085
+ ),
1086
+ array('strategy' => 'esi')
1087
+ ) ?>
1046
1088
1089
+ // ... or a URL
1090
+ use Symfony\C omponent\R outing\G enerator\U rlGeneratorInterface;
1047
1091
<?php echo $view['actions']->render(
1048
- $view['router']->generate('latest_news', array('maxPerPage' => 5), true),
1092
+ $view['router']->generate(
1093
+ 'latest_news',
1094
+ array('maxPerPage' => 5),
1095
+ UrlGeneratorInterface::ABSOLUTE_URL
1096
+ ),
1049
1097
array('strategy' => 'esi'),
1050
1098
) ?>
1051
1099
@@ -1065,7 +1113,7 @@ if there is no gateway cache installed.
1065
1113
When using the default ``render `` function (or setting the renderer to
1066
1114
``inline ``), Symfony merges the included page content into the main one
1067
1115
before sending the response to the client. But if you use the ``esi `` renderer
1068
- (i.e. call ``render_esi ``), *and * if Symfony detects that it's talking to a
1116
+ (i.e. call ``render_esi ``) *and * if Symfony detects that it's talking to a
1069
1117
gateway cache that supports ESI, it generates an ESI include tag. But if there
1070
1118
is no gateway cache or if it does not support ESI, Symfony will just merge
1071
1119
the included page content within the main one as it would have done if you had
@@ -1082,11 +1130,19 @@ of the master page.
1082
1130
1083
1131
.. code-block :: php
1084
1132
1085
- public function newsAction($maxPerPage)
1133
+ // src/AppBundle/Controller/NewsController.php
1134
+ namespace AppBundle\Controller;
1135
+
1136
+ // ...
1137
+ class NewsController extends Controller
1086
1138
{
1087
- // ...
1139
+ public function latestAction($maxPerPage)
1140
+ {
1141
+ // ...
1142
+ $response->setSharedMaxAge(60);
1088
1143
1089
- $response->setSharedMaxAge(60);
1144
+ return $response;
1145
+ }
1090
1146
}
1091
1147
1092
1148
With ESI, the full page cache will be valid for 600 seconds, but the news
0 commit comments