-
-
Notifications
You must be signed in to change notification settings - Fork 5.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update book to comply with best practices, round 3 #4779
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
c49f75d
Made validation chapter best-practices-compatible
wouterj a272f99
Made testing chapter best-practices-compatible and lots of other fixes
wouterj 4840768
Made propel chapter best-practices-compatible and lots of other fixes
wouterj c6ff013
Made http cache chapter best-practices-compatible and lots of other f…
wouterj 3ab53a6
Fixed some of the comments
wouterj File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -155,9 +155,12 @@ kernel:: | |
$kernel->loadClassCache(); | ||
// wrap the default AppKernel with the AppCache one | ||
$kernel = new AppCache($kernel); | ||
|
||
$request = Request::createFromGlobals(); | ||
|
||
$response = $kernel->handle($request); | ||
$response->send(); | ||
|
||
$kernel->terminate($request, $response); | ||
|
||
The 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. | |
|
||
To see a simple implementation, generate the ETag as the md5 of the content:: | ||
|
||
// src/AppBundle/Controller/DefaultController.php | ||
namespace AppBundle\Controller; | ||
|
||
use Symfony\Component\HttpFoundation\Request; | ||
|
||
public function indexAction(Request $request) | ||
class DefaultController extends Controller | ||
{ | ||
$response = $this->render('MyBundle:Main:index.html.twig'); | ||
$response->setETag(md5($response->getContent())); | ||
$response->setPublic(); // make sure the response is public/cacheable | ||
$response->isNotModified($request); | ||
public function homepageAction(Request $request) | ||
{ | ||
$response = $this->render('static/homepage.html.twig'); | ||
$response->setETag(md5($response->getContent())); | ||
$response->setPublic(); // make sure the response is public/cacheable | ||
$response->isNotModified($request); | ||
|
||
return $response; | ||
return $response; | ||
} | ||
} | ||
|
||
The :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 | |
compute the resource representation as the value for the ``Last-Modified`` | ||
header value:: | ||
|
||
// src/AppBundle/Controller/ArticleController.php | ||
namespace AppBundle\Controller; | ||
|
||
// ... | ||
use Symfony\Component\HttpFoundation\Request; | ||
use AppBundle\Entity\Article; | ||
|
||
public function showAction($articleSlug, Request $request) | ||
class ArticleController extends Controller | ||
{ | ||
// ... | ||
public function showAction(Article $article, Request $request) | ||
{ | ||
$author = $article->getAuthor(); | ||
|
||
$articleDate = new \DateTime($article->getUpdatedAt()); | ||
$authorDate = new \DateTime($author->getUpdatedAt()); | ||
$articleDate = new \DateTime($article->getUpdatedAt()); | ||
$authorDate = new \DateTime($author->getUpdatedAt()); | ||
|
||
$date = $authorDate > $articleDate ? $authorDate : $articleDate; | ||
$date = $authorDate > $articleDate ? $authorDate : $articleDate; | ||
|
||
$response->setLastModified($date); | ||
// Set response as public. Otherwise it will be private by default. | ||
$response->setPublic(); | ||
$response->setLastModified($date); | ||
// Set response as public. Otherwise it will be private by default. | ||
$response->setPublic(); | ||
|
||
if ($response->isNotModified($request)) { | ||
return $response; | ||
} | ||
if ($response->isNotModified($request)) { | ||
return $response; | ||
} | ||
|
||
// ... do more work to populate the response with the full content | ||
// ... do more work to populate the response with the full content | ||
|
||
return $response; | ||
return $response; | ||
} | ||
} | ||
|
||
The :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, | |
the better. The ``Response::isNotModified()`` method does exactly that by | ||
exposing a simple and efficient pattern:: | ||
|
||
// src/AppBundle/Controller/ArticleController.php | ||
namespace AppBundle\Controller; | ||
|
||
// ... | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Symfony\Component\HttpFoundation\Request; | ||
|
||
public function showAction($articleSlug, Request $request) | ||
class ArticleController extends Controller | ||
{ | ||
// Get the minimum information to compute | ||
// the ETag or the Last-Modified value | ||
// (based on the Request, data is retrieved from | ||
// a database or a key-value store for instance) | ||
$article = ...; | ||
|
||
// create a Response with an ETag and/or a Last-Modified header | ||
$response = new Response(); | ||
$response->setETag($article->computeETag()); | ||
$response->setLastModified($article->getPublishedAt()); | ||
|
||
// Set response as public. Otherwise it will be private by default. | ||
$response->setPublic(); | ||
|
||
// Check that the Response is not modified for the given Request | ||
if ($response->isNotModified($request)) { | ||
// return the 304 Response immediately | ||
return $response; | ||
} | ||
public function showAction($articleSlug, Request $request) | ||
{ | ||
// Get the minimum information to compute | ||
// the ETag or the Last-Modified value | ||
// (based on the Request, data is retrieved from | ||
// a database or a key-value store for instance) | ||
$article = ...; | ||
|
||
// create a Response with an ETag and/or a Last-Modified header | ||
$response = new Response(); | ||
$response->setETag($article->computeETag()); | ||
$response->setLastModified($article->getPublishedAt()); | ||
|
||
// Set response as public. Otherwise it will be private by default. | ||
$response->setPublic(); | ||
|
||
// Check that the Response is not modified for the given Request | ||
if ($response->isNotModified($request)) { | ||
// return the 304 Response immediately | ||
return $response; | ||
} | ||
|
||
// do more work here - like retrieving more data | ||
$comments = ...; | ||
// do more work here - like retrieving more data | ||
$comments = ...; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i would prefer valid php here. |
||
|
||
// or render a template with the $response you've already started | ||
return $this->render( | ||
'MyBundle:MyController:article.html.twig', | ||
array('article' => $article, 'comments' => $comments), | ||
$response | ||
); | ||
// or render a template with the $response you've already started | ||
return $this->render('Article/show.html.twig', array( | ||
'article' => $article, | ||
'comments' => $comments | ||
), $response); | ||
} | ||
} | ||
|
||
When 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 | |
|
||
// app/AppCache.php | ||
|
||
// ... | ||
use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\Response; | ||
// ... | ||
|
||
class AppCache extends HttpCache | ||
{ | ||
|
@@ -930,7 +953,7 @@ have one limitation: they can only cache whole pages. If you can't cache | |
whole pages or if parts of a page has "more" dynamic parts, you are out of | ||
luck. Fortunately, Symfony provides a solution for these cases, based on a | ||
technology called `ESI`_, or Edge Side Includes. Akamai wrote this specification | ||
almost 10 years ago, and it allows specific parts of a page to have a different | ||
almost 10 years ago and it allows specific parts of a page to have a different | ||
caching strategy than the main page. | ||
|
||
The ESI specification describes tags you can embed in your pages to communicate | ||
|
@@ -1017,13 +1040,19 @@ independent of the rest of the page. | |
|
||
.. code-block:: php | ||
|
||
public function indexAction() | ||
// src/AppBundle/Controller/DefaultController.php | ||
|
||
// ... | ||
class DefaultController extends Controller | ||
{ | ||
$response = $this->render('MyBundle:MyController:index.html.twig'); | ||
// set the shared max age - which also marks the response as public | ||
$response->setSharedMaxAge(600); | ||
public function aboutAction() | ||
{ | ||
$response = $this->render('static/about.html.twig'); | ||
// set the shared max age - which also marks the response as public | ||
$response->setSharedMaxAge(600); | ||
|
||
return $response; | ||
return $response; | ||
} | ||
} | ||
|
||
In this example, the full-page cache has a lifetime of ten minutes. | ||
|
@@ -1038,21 +1067,36 @@ matter), Symfony uses the standard ``render`` helper to configure ESI tags: | |
|
||
.. code-block:: jinja | ||
|
||
{# app/Resources/views/static/about.html.twig #} | ||
|
||
{# you can use a controller reference #} | ||
{{ render_esi(controller('...:news', { 'maxPerPage': 5 })) }} | ||
{{ render_esi(controller('AppBundle:News:latest', { 'maxPerPage': 5 })) }} | ||
|
||
{# ... or a URL #} | ||
{{ render_esi(url('latest_news', { 'maxPerPage': 5 })) }} | ||
|
||
.. code-block:: html+php | ||
|
||
<!-- app/Resources/views/static/about.html.php --> | ||
|
||
// you can use a controller reference | ||
use Symfony\Component\HttpKernel\Controller\ControllerReference; | ||
<?php echo $view['actions']->render( | ||
new \Symfony\Component\HttpKernel\Controller\ControllerReference('...:news', array('maxPerPage' => 5)), | ||
array('strategy' => 'esi')) | ||
?> | ||
new ControllerReference( | ||
'AppBundle:News:latest', | ||
array('maxPerPage' => 5) | ||
), | ||
array('strategy' => 'esi') | ||
) ?> | ||
|
||
// ... or a URL | ||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | ||
<?php echo $view['actions']->render( | ||
$view['router']->generate('latest_news', array('maxPerPage' => 5), true), | ||
$view['router']->generate( | ||
'latest_news', | ||
array('maxPerPage' => 5), | ||
UrlGeneratorInterface::ABSOLUTE_URL | ||
), | ||
array('strategy' => 'esi'), | ||
) ?> | ||
|
||
|
@@ -1072,7 +1116,7 @@ if there is no gateway cache installed. | |
When using the default ``render`` function (or setting the renderer to | ||
``inline``), Symfony merges the included page content into the main one | ||
before sending the response to the client. But if you use the ``esi`` renderer | ||
(i.e. call ``render_esi``), *and* if Symfony detects that it's talking to a | ||
(i.e. call ``render_esi``) *and* if Symfony detects that it's talking to a | ||
gateway cache that supports ESI, it generates an ESI include tag. But if there | ||
is no gateway cache or if it does not support ESI, Symfony will just merge | ||
the included page content within the main one as it would have done if you had | ||
|
@@ -1089,11 +1133,19 @@ of the master page. | |
|
||
.. code-block:: php | ||
|
||
public function newsAction($maxPerPage) | ||
// src/AppBundle/Controller/NewsController.php | ||
namespace AppBundle\Controller; | ||
|
||
// ... | ||
class NewsController extends Controller | ||
{ | ||
// ... | ||
public function latestAction($maxPerPage) | ||
{ | ||
// ... | ||
$response->setSharedMaxAge(60); | ||
|
||
$response->setSharedMaxAge(60); | ||
return $response; | ||
} | ||
} | ||
|
||
With ESI, the full page cache will be valid for 600 seconds, but the news | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i would prefer valid php here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is our standard