@@ -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
@@ -574,16 +577,22 @@ each ``ETag`` must be unique across all representations of the same resource.
574577
575578To see a simple implementation, generate the ETag as the md5 of the content::
576579
580+ // src/AppBundle/Controller/DefaultController.php
581+ namespace AppBundle\Controller;
582+
577583 use Symfony\Component\HttpFoundation\Request;
578584
579- public function indexAction(Request $request)
585+ class DefaultController extends Controller
580586 {
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);
585593
586- return $response;
594+ return $response;
595+ }
587596 }
588597
589598The :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
630639compute the resource representation as the value for the ``Last-Modified ``
631640header value::
632641
642+ // src/AppBundle/Controller/ArticleController.php
643+ namespace AppBundle\Controller;
644+
645+ // ...
633646 use Symfony\Component\HttpFoundation\Request;
647+ use AppBundle\Entity\Article;
634648
635- public function showAction($articleSlug, Request $request)
649+ class ArticleController extends Controller
636650 {
637- // ...
651+ public function showAction(Article $article, Request $request)
652+ {
653+ $author = $article->getAuthor();
638654
639- $articleDate = new \DateTime($article->getUpdatedAt());
640- $authorDate = new \DateTime($author->getUpdatedAt());
655+ $articleDate = new \DateTime($article->getUpdatedAt());
656+ $authorDate = new \DateTime($author->getUpdatedAt());
641657
642- $date = $authorDate > $articleDate ? $authorDate : $articleDate;
658+ $date = $authorDate > $articleDate ? $authorDate : $articleDate;
643659
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();
647663
648- if ($response->isNotModified($request)) {
649- return $response;
650- }
664+ if ($response->isNotModified($request)) {
665+ return $response;
666+ }
651667
652- // ... do more work to populate the response with the full content
668+ // ... do more work to populate the response with the full content
653669
654- return $response;
670+ return $response;
671+ }
655672 }
656673
657674The :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,
680697the better. The ``Response::isNotModified() `` method does exactly that by
681698exposing a simple and efficient pattern::
682699
700+ // src/AppBundle/Controller/ArticleController.php
701+ namespace AppBundle\Controller;
702+
703+ // ...
683704 use Symfony\Component\HttpFoundation\Response;
684705 use Symfony\Component\HttpFoundation\Request;
685706
686- public function showAction($articleSlug, Request $request)
707+ class ArticleController extends Controller
687708 {
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());
707721
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() ;
710724
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+ }
717740 }
718741
719742When 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
863886
864887 // app/AppCache.php
865888
866- // ...
867889 use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache;
868890 use Symfony\Component\HttpFoundation\Request;
869891 use Symfony\Component\HttpFoundation\Response;
892+ // ...
870893
871894 class AppCache extends HttpCache
872895 {
@@ -925,7 +948,7 @@ have one limitation: they can only cache whole pages. If you can't cache
925948whole pages or if parts of a page has "more" dynamic parts, you are out of
926949luck. Fortunately, Symfony provides a solution for these cases, based on a
927950technology 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
929952caching strategy than the main page.
930953
931954The 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:
9871010 <container xmlns =" http://symfony.com/schema/dic/symfony"
9881011 xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
9891012 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+ " >
9921019
9931020 <framework : config >
9941021 <!-- ... -->
@@ -1010,13 +1037,19 @@ independent of the rest of the page.
10101037
10111038.. code-block :: php
10121039
1013- public function indexAction()
1040+ // src/AppBundle/Controller/DefaultController.php
1041+
1042+ // ...
1043+ class DefaultController extends Controller
10141044 {
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);
10181050
1019- return $response;
1051+ return $response;
1052+ }
10201053 }
10211054
10221055 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:
10311064
10321065 .. code-block :: jinja
10331066
1067+ {# app/Resources/views/about.html.twig #}
1068+
10341069 {# you can use a controller reference #}
1035- {{ render_esi(controller('...:news ', { 'maxPerPage': 5 })) }}
1070+ {{ render_esi(controller('AppBundle:News:latest ', { 'maxPerPage': 5 })) }}
10361071
10371072 {# ... or a URL #}
10381073 {{ render_esi(url('latest_news', { 'maxPerPage': 5 })) }}
10391074
10401075 .. code-block :: html+php
10411076
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;
10421081 <?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+ ) ?>
10461088
1089+ // ... or a URL
1090+ use Symfony\C omponent\R outing\G enerator\U rlGeneratorInterface;
10471091 <?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+ ),
10491097 array('strategy' => 'esi'),
10501098 ) ?>
10511099
@@ -1065,7 +1113,7 @@ if there is no gateway cache installed.
10651113When using the default ``render `` function (or setting the renderer to
10661114``inline ``), Symfony merges the included page content into the main one
10671115before 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
10691117gateway cache that supports ESI, it generates an ESI include tag. But if there
10701118is no gateway cache or if it does not support ESI, Symfony will just merge
10711119the included page content within the main one as it would have done if you had
@@ -1082,11 +1130,19 @@ of the master page.
10821130
10831131.. code-block :: php
10841132
1085- public function newsAction($maxPerPage)
1133+ // src/AppBundle/Controller/NewsController.php
1134+ namespace AppBundle\Controller;
1135+
1136+ // ...
1137+ class NewsController extends Controller
10861138 {
1087- // ...
1139+ public function latestAction($maxPerPage)
1140+ {
1141+ // ...
1142+ $response->setSharedMaxAge(60);
10881143
1089- $response->setSharedMaxAge(60);
1144+ return $response;
1145+ }
10901146 }
10911147
10921148 With ESI, the full page cache will be valid for 600 seconds, but the news
0 commit comments