8000 Update book to comply with best practices, round 3 by wouterj · Pull Request #4779 · symfony/symfony-docs · GitHub
[go: up one dir, main page]

Skip to content

Update book to comply with best practices, round 3 #4779

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

Merged
merged 5 commits into from
Mar 13, 2015
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Made http cache chapter best-practices-compatible and lots of other f…
…ixes
  • Loading branch information
wouterj committed Feb 16, 2015
commit c6ff013ca4726fea73b77db30df96a87ab950e84
179 changes: 116 additions & 63 deletions book/http_cache.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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('homepage.html.twig');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be something like default/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`
Expand Down Expand Up @@ -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`
Expand Down Expand Up @@ -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 = ...;
Copy link
Contributor

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is our standard


// 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 = ...;
Copy link
Contributor

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.


// 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
Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -996,6 +1019,7 @@ First, to use ESI, be sure to enable it in your application configuration:
http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/symfony
http://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks invalid (or you need to remove it from the end of the previous line)


<framework:config>
<!-- ... -->
Expand All @@ -1017,13 +1041,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('about.html.twig');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here - missing the directory

// 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.
Expand All @@ -1038,21 +1068,36 @@ matter), Symfony uses the standard ``render`` helper to configure ESI tags:

.. code-block:: jinja

{# app/Resources/views/about.html.twig #}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh I see, it's because you weren't using a sub-directory. I think we should - I think the root dir should be for base layout kind of stuff.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a really strong opinion about it. However, I don't like using default as sub-directory. I also don't really like DefaultController. Both are not default, they are as not default as any other template and controller. What do you think about using statics and StaticController

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 (but only static not statics)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 also - and like @xabbuh said, StaticController and static

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DefaultController is basically due to a lack of imagination. And so, I think we can try a little harder in many areas and give things more meaningful names.


{# 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/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'),
) ?>

Expand All @@ -1072,7 +1117,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
Expand All @@ -1089,11 +1134,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
Expand Down
0