The Controller
==============
Still here after the first two parts? You are already becoming a Symfony2
addict! Without further ado, discover what controllers can do for you.
Using Formats
-------------
Nowadays, a web application should be able to deliver more than just HTML
pages. From XML for RSS feeds or Web Services, to JSON for Ajax requests,
there are plenty of different formats to choose from. Supporting those formats
in Symfony2 is straightforward. Tweak the route by adding a default value of
``xml`` for the ``_format`` variable::
// src/Acme/DemoBundle/Controller/DemoController.php
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
// ...
/**
* @Route("/hello/{name}", defaults={"_format"="xml"}, name="_demo_hello")
* @Template()
*/
public function helloAction($name)
{
return array('name' => $name);
}
By using the request format (as defined by the special ``_format`` variable),
Symfony2 automatically selects the right template, here ``hello.xml.twig``:
.. code-block:: xml+php
{{ name }}
That's all there is to it. For standard formats, Symfony2 will also
automatically choose the best ``Content-Type`` header for the response. If
you want to support different formats for a single action, use the ``{_format}``
placeholder in the route path instead::
// src/Acme/DemoBundle/Controller/DemoController.php
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
// ...
/**
* @Route(
* "/hello/{name}.{_format}",
* defaults = { "_format" = "html" },
* requirements = { "_format" = "html|xml|json" },
* name = "_demo_hello"
* )
* @Template()
*/
public function helloAction($name)
{
return array('name' => $name);
}
The controller will now match URLs like ``/demo/hello/Fabien.xml`` or
``/demo/hello/Fabien.json``.
The ``requirements`` entry defines regular expressions that variables must
match. In this example, if you try to request the ``/demo/hello/Fabien.js``
resource, you will get a 404 HTTP error, as it does not match the ``_format``
requirement.
Redirecting and Forwarding
--------------------------
If you want to redirect the user to another page, use the ``redirect()``
method::
return $this->redirect($this->generateUrl('_demo_hello', array('name' => 'Lucas')));
The ``generateUrl()`` is the same method as the ``path()`` function used in the
templates. It takes the route name and an array of parameters as arguments and
returns the associated friendly URL.
You can also internally forward the action to another using the ``forward()``
method::
return $this->forward('AcmeDemoBundle:Hello:fancy', array(
'name' => $name,
'color' => 'green'
));
Displaying Error Pages
----------------------
Errors will inevitably happen during the execution of every web application.
In the case of ``404`` errors, Symfony includes a handy shortcut that you can
use in your controllers::
throw $this->createNotFoundException();
For ``500`` errors, just throw a regular PHP exception inside the controller and
Symfony will transform it into a proper ``500`` error page::
throw new \Exception('Something went wrong!');
Getting Information from the Request
------------------------------------
Symfony automatically injects the ``Request`` object when the controller has an
argument that's type hinted with ``Symfony\Component\HttpFoundation\Request``::
use Symfony\Component\HttpFoundation\Request;
public function indexAction(Request $request)
{
$request->isXmlHttpRequest(); // is it an Ajax request?
$request->getPreferredLanguage(array('en', 'fr'));
$request->query->get('page'); // get a $_GET parameter
$request->request->get('page'); // get a $_POST parameter
}
In a template, you can also access the ``Request`` object via the
``app.request`` variable:
.. code-block:: html+jinja
{{ app.request.query.get('page') }}
{{ app.request.parameter('page') }}
Persisting Data in the Session
------------------------------
Even if the HTTP protocol is stateless, Symfony2 provides a nice session object
that represents the client (be it a real person using a browser, a bot, or a
web service). Between two requests, Symfony2 stores the attributes in a cookie
by using native PHP sessions.
Storing and retrieving information from the session can be easily achieved
from any controller::
use Symfony\Component\HttpFoundation\Request;
public function indexAction(Request $request)
{
$session = $this->request->getSession();
// store an attribute for reuse during a later user request
$session->set('foo', 'bar');
// get the value of a session attribute
$foo = $session->get('foo');
// use a default value if the attribute doesn't exist
$foo = $session->get('foo', 'default_value');
}
You can also store "flash messages" that will auto-delete after the next request.
They are useful when you need to set a success message before redirecting the
user to another page (which will then show the message)::
// store a message for the very next request (in a controller)
$session->getFlashBag()->add('notice', 'Congratulations, your action succeeded!');
.. code-block:: html+jinja
{# display the flash message in the template #}
{{ app.session.flashbag.get('notice') }}
Caching Resources
-----------------
As soon as your website starts to generate more traffic, you will want to
avoid generating the same resource again and again. Symfony2 uses HTTP cache
headers to manage resources cache. For simple caching strategies, use the
convenient ``@Cache()`` annotation::
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
/**
* @Route("/hello/{name}", name="_demo_hello")
* @Template()
* @Cache(maxage="86400")
*/
public function helloAction($name)
{
return array('name' => $name);
}
In this example, the resource will be cached for a day (``86400`` seconds).
Resource caching is managed by Symfony2 itself. But because caching is managed
using standard HTTP cache headers, you can use Varnish or Squid without having
to modify a single line of code in your application.
Final Thoughts
--------------
That's all there is to it, and I'm not even sure you'll have spent the full
10 minutes. You were briefly introduced to bundles in the first part, and all the
features you've learned about so far are part of the core framework bundle.
But thanks to bundles, everything in Symfony2 can be extended or replaced.
That's the topic of the :doc:`next part of this tutorial`.