@@ -155,9 +155,12 @@ kernel::
155155 $kernel->loadClassCache();
156156 // wrap the default AppKernel with the AppCache one
157157 $kernel = new AppCache($kernel);
158+
158159 $request = Request::createFromGlobals();
160+
159161 $response = $kernel->handle($request);
160162 $response->send();
163+
161164 $kernel->terminate($request, $response);
162165
163166The caching kernel will immediately act as a reverse proxy - caching responses
@@ -576,16 +579,22 @@ each ``ETag`` must be unique across all representations of the same resource.
576579
577580To see a simple implementation, generate the ETag as the md5 of the content::
578581
582+ // src/AppBundle/Controller/DefaultController.php
583+ namespace AppBundle\Controller;
584+
579585 use Symfony\Component\HttpFoundation\Request;
580586
581- public function indexAction(Request $request)
587+ class DefaultController extends Controller
582588 {
583- $response = $this->render('MyBundle:Main:index.html.twig');
584- $response->setETag(md5($response->getContent()));
585- $response->setPublic(); // make sure the response is public/cacheable
586- $response->isNotModified($request);
589+ public function homepageAction(Request $request)
590+ {
591+ $response = $this->render('homepage.html.twig');
592+ $response->setETag(md5($response->getContent()));
593+ $response->setPublic(); // make sure the response is public/cacheable
594+ $response->isNotModified($request);
587595
588- return $response;
596+ return $response;
597+ }
589598 }
590599
591600The :method: `Symfony\\ Component\\ HttpFoundation\\ Response::isNotModified `
@@ -632,28 +641,36 @@ For instance, you can use the latest update date for all the objects needed to
632641compute the resource representation as the value for the ``Last-Modified ``
633642header value::
634643
644+ // src/AppBundle/Controller/ArticleController.php
645+ namespace AppBundle\Controller;
646+
647+ // ...
635648 use Symfony\Component\HttpFoundation\Request;
649+ use AppBundle\Entity\Article;
636650
637- public function showAction($articleSlug, Request $request)
651+ class ArticleController extends Controller
638652 {
639- // ...
653+ public function showAction(Article $article, Request $request)
654+ {
655+ $author = $article->getAuthor();
640656
641- $articleDate = new \DateTime($article->getUpdatedAt());
642- $authorDate = new \DateTime($author->getUpdatedAt());
657+ $articleDate = new \DateTime($article->getUpdatedAt());
658+ $authorDate = new \DateTime($author->getUpdatedAt());
643659
644- $date = $authorDate > $articleDate ? $authorDate : $articleDate;
660+ $date = $authorDate > $articleDate ? $authorDate : $articleDate;
645661
646- $response->setLastModified($date);
647- // Set response as public. Otherwise it will be private by default.
648- $response->setPublic();
662+ $response->setLastModified($date);
663+ // Set response as public. Otherwise it will be private by default.
664+ $response->setPublic();
649665
650- if ($response->isNotModified($request)) {
651- return $response;
652- }
666+ if ($response->isNotModified($request)) {
667+ return $response;
668+ }
653669
654- // ... do more work to populate the response with the full content
670+ // ... do more work to populate the response with the full content
655671
656- return $response;
672+ return $response;
673+ }
657674 }
658675
659676The :method: `Symfony\\ Component\\ HttpFoundation\\ Response::isNotModified `
@@ -682,40 +699,46 @@ Put another way, the less you do in your application to return a 304 response,
682699the better. The ``Response::isNotModified() `` method does exactly that by
683700exposing a simple and efficient pattern::
684701
702+ // src/AppBundle/Controller/ArticleController.php
703+ namespace AppBundle\Controller;
704+
705+ // ...
685706 use Symfony\Component\HttpFoundation\Response;
686707 use Symfony\Component\HttpFoundation\Request;
687708
688- public function showAction($articleSlug, Request $request)
709+ class ArticleController extends Controller
689710 {
690- // Get the minimum information to compute
691- // the ETag or the Last-Modified value
692- // (based on the Request, data is retrieved from
693- // a database or a key-value store for instance)
694- $article = ...;
695-
696- // create a Response with an ETag and/or a Last-Modified header
697- $response = new Response();
698- $response->setETag($article->computeETag());
699- $response->setLastModified($article->getPublishedAt());
700-
701- // Set response as public. Otherwise it will be private by default.
702- $response->setPublic();
703-
704- // Check that the Response is not modified for the given Request
705- if ($response->isNotModified($request)) {
706- // return the 304 Response immediately
707- return $response;
708- }
711+ public function showAction($articleSlug, Request $request)
712+ {
713+ // Get the minimum information to compute
714+ // the ETag or the Last-Modified value
715+ // (based on the Request, data is retrieved from
716+ // a database or a key-value store for instance)
717+ $article = ...;
718+
719+ // create a Response with an ETag and/or a Last-Modified header
720+ $response = new Response();
721+ $response->setETag($article->computeETag());
722+ $response->setLastModified($article->getPublishedAt());
723+
724+ // Set response as public. Otherwise it will be private by default.
725+ $response->setPublic();
726+
727+ // Check that the Response is not modified for the given Request
728+ if ($response->isNotModified($request)) {
729+ // return the 304 Response immediately
730+ return $response;
731+ }
709732
710- // do more work here - like retrieving more data
711- $comments = ...;
733+ // do more work here - like retrieving more data
734+ $comments = ...;
712735
713- // or render a template with the $response you've already started
714- return $this->render(
715- 'MyBundle:MyController: article.html.twig' ,
716- array('article' => $article, 'comments' => $comments),
717- $response
718- );
736+ // or render a template with the $response you've already started
737+ return $this->render('Article/show.html.twig', array (
738+ ' article' => $article ,
739+ 'comments' => $comments
740+ ), $response);
741+ }
719742 }
720743
721744When the ``Response `` is not modified, the ``isNotModified() `` automatically sets
@@ -865,10 +888,10 @@ Here is how you can configure the Symfony reverse proxy to support the
865888
866889 // app/AppCache.php
867890
868- // ...
869891 use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache;
870892 use Symfony\Component\HttpFoundation\Request;
871893 use Symfony\Component\HttpFoundation\Response;
894+ // ...
872895
873896 class AppCache extends HttpCache
874897 {
@@ -930,7 +953,7 @@ have one limitation: they can only cache whole pages. If you can't cache
930953whole pages or if parts of a page has "more" dynamic parts, you are out of
931954luck. Fortunately, Symfony provides a solution for these cases, based on a
932955technology called `ESI `_, or Edge Side Includes. Akamai wrote this specification
933- almost 10 years ago, and it allows specific parts of a page to have a different
956+ almost 10 years ago and it allows specific parts of a page to have a different
934957caching strategy than the main page.
935958
936959The ESI specification describes tags you can embed in your pages to communicate
@@ -996,6 +1019,7 @@ First, to use ESI, be sure to enable it in your application configuration:
9961019 http://symfony.com/schema/dic/services/services-1.0.xsd
9971020 http://symfony.com/schema/dic/symfony
9981021 http://symfony.com/schema/dic/symfony/symfony-1.0.xsd" >
1022+ ">
9991023
10001024 <framework : config >
10011025 <!-- ... -->
@@ -1017,13 +1041,19 @@ independent of the rest of the page.
10171041
10181042.. code-block :: php
10191043
1020- public function indexAction()
1044+ // src/AppBundle/Controller/DefaultController.php
1045+
1046+ // ...
1047+ class DefaultController extends Controller
10211048 {
1022- $response = $this->render('MyBundle:MyController:index.html.twig');
1023- // set the shared max age - which also marks the response as public
1024- $response->setSharedMaxAge(600);
1049+ public function aboutAction()
1050+ {
1051+ $response = $this->render('about.html.twig');
1052+ // set the shared max age - which also marks the response as public
1053+ $response->setSharedMaxAge(600);
10251054
1026- return $response;
1055+ return $response;
1056+ }
10271057 }
10281058
10291059 In this example, the full-page cache has a lifetime of ten minutes.
@@ -1038,21 +1068,36 @@ matter), Symfony uses the standard ``render`` helper to configure ESI tags:
10381068
10391069 .. code-block :: jinja
10401070
1071+ {# app/Resources/views/about.html.twig #}
1072+
10411073 {# you can use a controller reference #}
1042- {{ render_esi(controller('...:news ', { 'maxPerPage': 5 })) }}
1074+ {{ render_esi(controller('AppBundle:News:latest ', { 'maxPerPage': 5 })) }}
10431075
10441076 {# ... or a URL #}
10451077 {{ render_esi(url('latest_news', { 'maxPerPage': 5 })) }}
10461078
10471079 .. code-block :: html+php
10481080
1081+ <!-- app/Resources/views/about.html.php -->
1082+
1083+ // you can use a controller reference
1084+ use Symfony\C omponent\H ttpKernel\C ontroller\C ontrollerReference;
10491085 <?php echo $view['actions']->render(
1050- new \S ymfony\C omponent\H ttpKernel\C ontroller\C ontrollerReference('...:news', array('maxPerPage' => 5)),
1051- array('strategy' => 'esi'))
1052- ?>
1086+ new ControllerReference(
1087+ 'AppBundle:News: latest',
1088+ array('maxPerPage' => 5)
1089+ ),
1090+ array('strategy' => 'esi')
1091+ ) ?>
10531092
1093+ // ... or a URL
1094+ use Symfony\C omponent\R outing\G enerator\U rlGeneratorInterface;
10541095 <?php echo $view['actions']->render(
1055- $view['router']->generate('latest_news', array('maxPerPage' => 5), true),
1096+ $view['router']->generate(
1097+ 'latest_news',
1098+ array('maxPerPage' => 5),
1099+ UrlGeneratorInterface::ABSOLUTE_URL
1100+ ),
10561101 array('strategy' => 'esi'),
10571102 ) ?>
10581103
@@ -1072,7 +1117,7 @@ if there is no gateway cache installed.
10721117When using the default ``render `` function (or setting the renderer to
10731118``inline ``), Symfony merges the included page content into the main one
10741119before sending the response to the client. But if you use the ``esi `` renderer
1075- (i.e. call ``render_esi ``), *and * if Symfony detects that it's talking to a
1120+ (i.e. call ``render_esi ``) *and * if Symfony detects that it's talking to a
10761121gateway cache that supports ESI, it generates an ESI include tag. But if there
10771122is no gateway cache or if it does not support ESI, Symfony will just merge
10781123the included page content within the main one as it would have done if you had
@@ -1089,11 +1134,19 @@ of the master page.
10891134
10901135.. code-block :: php
10911136
1092- public function newsAction($maxPerPage)
1137+ // src/AppBundle/Controller/NewsController.php
1138+ namespace AppBundle\Controller;
1139+
1140+ // ...
1141+ class NewsController extends Controller
10931142 {
1094- // ...
1143+ public function latestAction($maxPerPage)
1144+ {
1145+ // ...
1146+ $response->setSharedMaxAge(60);
10951147
1096- $response->setSharedMaxAge(60);
1148+ return $response;
1149+ }
10971150 }
10981151
10991152 With ESI, the full page cache will be valid for 600 seconds, but the news
0 commit comments