6

In order to set the response without the cache in the controller you can do this:

$response = new Response();
$result = $this->renderView(
        'AcmeDemoBundle:Default:index.html.twig',
         array('products' => $products, 'form' => $form->createView()));
$response->headers->addCacheControlDirective('no-cache', true);
$response->headers->addCacheControlDirective('max-age', 0);
$response->headers->addCacheControlDirective('must-revalidate', true);
$response->headers->addCacheControlDirective('no-store', true);
$response->setContent($result);

return $response;

But using annotations, to ensure that each method has the same result, how can you do?

I tried so but continues to save the cache and if I use the browser's Back button keeps the cache:

/**
 * @Cache(maxage="0", vary="no-cache, must-revalidate, no-store", smaxage="0", expires="now", public="false")
 */
class DefaultController extends Controller
{
/**
 * Homepage: show products
 * 
 * @Route("/", name="homepage")
 * @Template
 */
public function indexAction()
{
    $sessionCart = $this->get('demo');
    $filters = $sessionCart->getFilters($this->getDoctrine()->getEntityManager());
    $products = $this->getDoctrine()->getRepository('AcmeDemoBundle:Product')->search($filters);
    $form = $this->createForm(new FilterType, $filters);

    return array('products' => $products, 'form' => $form->createView());
}

If imposed as the documentation says:

@Cache(vary=["no-cache", "must-revalidate", "no-store"]...

gives me a syntax error which does not expect "[", so I tried as above.

Lughino
  • 4,060
  • 11
  • 31
  • 61
  • 1
    Normaly hashmaps are defined with curly braces in annotations. Try it: `@Cache(vary={"..."})` – Emii Khaos Jul 18 '13 at 20:22
  • I tried, is the same as using: ```vary="no-cache, must-revalidate, no-store"``` In response-header I always find ```vary no-cache,must-revalidate,no-store```, but continues to take the cache – Lughino Jul 18 '13 at 20:43
  • mom, you get the headers in the response? – Emii Khaos Jul 18 '13 at 20:57
  • I am facing this issue too. You can see the vary headers in response, but it does not actually enforce the caching on the Vary headers logic (except for the first header in the array). The only way to get the ```@Cache``` annotation to work with vary is by doing ```@Cache(vary = "Accept-Encoding, X-Foo, X-Foo2")``` – epicwhale Oct 03 '14 at 07:11

2 Answers2

14

You are mixing two things. In your first snippet you are setting cache control headers, but with the annotation you want to set the Vary header. But Vary is complete different than the Cache-Control header, in which no-cache, must-revalidate, no-store should stand. Vary means on which thinks of the request (i.e. Cookies) the Response can vary. See this answer for understanding: https://stackoverflow.com/a/1975677/2084176

In your case (no-cache) you can rely on the defaults, which symfony sets, if no cache headers are present:

Symfony2 automatically sets a sensible and conservative Cache-Control header when none is set by the developer by following these rules:

  • If no cache header is defined (Cache-Control, Expires, ETag or Last-Modified), Cache-Control is set to no-cache, meaning that the response will not be cached;

EDIT: if you need to set the cache header for every controller action, you can work with the kernel.response event. Create a listener which reacts on this event and modify the response with appropriate cache control.

namespace Acme\DemoBundle\EventListener;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;

class AcmeCacheListener
{
    public function onKernelResponse(FilterResponseEvent $event)
    {
        $response = $event->getResponse();

        $response->headers->addCacheControlDirective('no-cache', true);
        $response->headers->addCacheControlDirective('max-age', 0);
        $response->headers->addCacheControlDirective('must-revalidate', true);
        $response->headers->addCacheControlDirective('no-store', true);
    }
}

and in your services.yml

services:
    kernel.listener.your_listener_name:
        class: Acme\DemoBundle\EventListener\AcmeCacheListener
        tags:
            - { name: kernel.event_listener, event: kernel.response, method: onKernelResponse }
Community
  • 1
  • 1
Emii Khaos
  • 9,983
  • 3
  • 34
  • 57
  • Thanks for the clarification, but if I do not set anything when I press the back button browser keeps me with the cache, but if I set ```$response->headers->addCacheControlDirective('no-cache', true); $response->headers->addCacheControlDirective('max-age', 0); $response->headers->addCacheControlDirective('must-revalidate', true); $response->headers->addCacheControlDirective('no-store', true);``` the browser does not keep the cache and every time I press the back button reloads the page. – Lughino Jul 18 '13 at 21:29
  • You MUST NOT set any cache headers to take this default into action. No Vary header, nothing. But the Back button is a browser specific behavior and you can not bet on it. `no-store` should work in most cases, but hmmm. Here is another good answer: http://stackoverflow.com/a/866866/2084176 – Emii Khaos Jul 18 '13 at 21:35
  • I have tried both on chrome and on firefox and I both had the same result. If I set as you said ```no-store``` no longer takes the cache. The question then is: is the possibility to set ```no-store``` on the annotation or at least once in the controller? – Lughino Jul 18 '13 at 21:45
  • Do you need this for every action? – Emii Khaos Jul 18 '13 at 21:56
  • Yes, I have a shopping cart and the data are placed in the session, every time I press the back button on your browser lose the products of the cart until you reload the page! – Lughino Jul 18 '13 at 22:09
  • whats the "your_listener_name" ? – Jordan Georgiadis Jul 12 '18 at 07:18
  • A simple placeholder for this example. Name it however you want.. For Symfony 3 and for you can omit this completely and use the fully qualified class name as service name – Emii Khaos Jul 23 '18 at 08:27
0

I guess that the main problem it's that Cache annotation doesn't have an option for no-cache, no-store and must-revalidate. I can't explain why. As an addition to Patrick's answer, if you have more than one controller and you want to apply the headers to only one, you can check the controller class inside the listener like:

class AcmeCacheListener
{
    public function onKernelResponse(FilterResponseEvent $event)
    {
        $controller = $event->getRequest()->attributes->get('_controller');
        $requiredController = "Acme\Controller\DefaultController";
        if( substr($controller, 0,strlen($requiredController)) == $requiredController) {

            $response = $event->getResponse();
            $response->headers->addCacheControlDirective('no-cache', true);
            $response->headers->addCacheControlDirective('max-age', 0);
            $response->headers->addCacheControlDirective('must-revalidate', true);
            $response->headers->addCacheControlDirective('no-store', true);
         }
    }
}
glerendegui
  • 1,457
  • 13
  • 15