Skip to content

Commit c363f66

Browse files
richardmiller-zzweaverryan
authored andcommitted
Adding to the controllers as services cookbook
1 parent 7b4625b commit c363f66

File tree

1 file changed

+232
-34
lines changed

1 file changed

+232
-34
lines changed

cookbook/controller/service.rst

+232-34
Original file line numberDiff line numberDiff line change
@@ -9,57 +9,255 @@ extends the base
99
:class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller` class. While
1010
this works fine, controllers can also be specified as services.
1111

12+
.. note::
13+
14+
Specifying a controller as a service takes a little bit more work. The
15+
primary advantage is that the entire controller or any services passed to
16+
the controller can be modified via the service container configuration.
17+
This is especially useful when developing an open-source bundle or any
18+
bundle that will be used in many different projects. So, even if you don't
19+
specify your controllers as services, you'll likely see this done in some
20+
open-source Symfony2 bundles.
21+
22+
Defining the Controller as a Service
23+
------------------------------------
24+
25+
A controller can be defined as a service in the same way as any other class.
26+
For example, if you have the following simple controller::
27+
28+
// src/Acme/HelloBundle/Controller/HelloController.php
29+
namespace Acme\HelloBundle\Controller;
30+
31+
use Symfony\Component\HttpFoundation\Response;
32+
33+
class HelloController
34+
{
35+
public function indexAction($name)
36+
{
37+
return new Response('<html><body>Hello '.$name.'!</body></html>');
38+
}
39+
}
40+
41+
Then you can define it as a service as follows:
42+
43+
.. configuration-block::
44+
45+
.. code-block:: yaml
46+
47+
# src/Acme/HelloBundle/Resources/config/services.yml
48+
parameters:
49+
# ...
50+
acme.controller.hello.class: Acme\HelloBundle\Controller\HelloController
51+
52+
services:
53+
acme.hello.controller:
54+
class: "%acme.controller.hello.class%"
55+
56+
.. code-block:: xml
57+
58+
<!-- src/Acme/HelloBundle/Resources/config/services.xml -->
59+
<parameters>
60+
<!-- ... -->
61+
<parameter key="acme.controller.hello.class">Acme\HelloBundle\Controller\HelloController</parameter>
62+
</parameters>
63+
64+
<services>
65+
<service id="acme.hello.controller" class="%acme.controller.hello.class%" />
66+
</services>
67+
68+
.. code-block:: php
69+
70+
// src/Acme/HelloBundle/Resources/config/services.php
71+
use Symfony\Component\DependencyInjection\Definition;
72+
73+
// ...
74+
$container->setParameter(
75+
'acme.controller.hello.class',
76+
'Acme\HelloBundle\Controller\HelloController'
77+
);
78+
79+
$container->setDefinition('acme.hello.controller', new Definition(
80+
'%acme.controller.hello.class%'
81+
));
82+
83+
Referring to the service
84+
------------------------
85+
1286
To refer to a controller that's defined as a service, use the single colon (:)
13-
notation. For example, suppose you've defined a service called
14-
``my_controller`` and you want to forward to a method called ``indexAction()``
15-
inside the service::
87+
notation. For example, to forward to the ``indexAction()`` method of the service
88+
defined above with the id ``acme.hello.controller``::
89+
90+
$this->forward('acme.hello.controller:indexAction');
91+
92+
.. note::
1693

17-
$this->forward('my_controller:indexAction', array('foo' => $bar));
94+
You cannot drop the ``Action`` part of the method name when using this
95+
syntax.
1896

19-
You need to use the same notation when defining the route ``_controller``
20-
value:
97+
You can also route to the service by using the same notation when defining
98+
the route ``_controller`` value:
2199

22-
.. code-block:: yaml
100+
.. configuration-block::
23101

24-
my_controller:
25-
path: /
26-
defaults: { _controller: my_controller:indexAction }
102+
.. code-block:: yaml
27103
28-
To use a controller in this way, it must be defined in the service container
29-
configuration. For more information, see the :doc:`Service Container
30-
</book/service_container>` chapter.
104+
# app/config/routing.yml
105+
hello:
106+
pattern: /hello
107+
defaults: { _controller: acme.hello.controller:indexAction }
108+
109+
.. code-block:: xml
110+
111+
<!-- app/config/routing.xml -->
112+
<route id="hello" pattern="/hello">
113+
<default key="_controller">acme.hello.controller:indexAction</default>
114+
</route>
115+
116+
.. code-block:: php
117+
118+
// app/config/routing.php
119+
$collection->add('hello', new Route('/hello', array(
120+
'_controller' => 'acme.hello.controller:indexAction',
121+
)));
122+
123+
Using Annotation Routing
124+
~~~~~~~~~~~~~~~~~~~~~~~~
125+
126+
When using annotations to configure routing using a controller defined as a
127+
service, you need to specify the service id as follows::
128+
129+
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
130+
131+
/**
132+
* @Route("/hello", service="acme.hello.controller")
133+
*/
134+
class HelloController extends Controller
135+
{
136+
// ...
137+
}
138+
139+
This is documented in the :doc:`/bundles/SensioFrameworkExtraBundle/annotations/routing`
140+
chapter.
141+
142+
Alternatives to Base Controller Methods
143+
---------------------------------------
31144

32145
When using a controller defined as a service, it will most likely not extend
33146
the base ``Controller`` class. Instead of relying on its shortcut methods,
34147
you'll interact directly with the services that you need. Fortunately, this is
35148
usually pretty easy and the base ``Controller`` class itself is a great source
36149
on how to perform many common tasks.
37150

38-
.. note::
151+
For example, if you want to use templates instead of creating the ``Response``
152+
object directly then if you were extending from the base controller you could
153+
use::
39154

40-
Specifying a controller as a service takes a little bit more work. The
41-
primary advantage is that the entire controller or any services passed to
42-
the controller can be modified via the service container configuration.
43-
This is especially useful when developing an open-source bundle or any
44-
bundle that will be used in many different projects. So, even if you don't
45-
specify your controllers as services, you'll likely see this done in some
46-
open-source Symfony2 bundles.
155+
// src/Acme/HelloBundle/Controller/HelloController.php
156+
namespace Acme\HelloBundle\Controller;
47157

48-
Using Annotation Routing
49-
------------------------
158+
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
159+
use Symfony\Component\HttpFoundation\Response;
50160

51-
When using annotations to setup routing when using a controller defined as a
52-
service, you need to specify your service as follows::
161+
class HelloController extends Controller
162+
{
163+
public function indexAction($name)
164+
{
165+
return $this->render(
166+
'AcmeHelloBundle:Hello:index.html.twig',
167+
array('name' => $name)
168+
);
169+
}
170+
}
53171

54-
/**
55-
* @Route("/blog", service="my_bundle.annot_controller")
56-
* @Cache(expires="tomorrow")
57-
*/
58-
class AnnotController extends Controller
172+
This method actually uses the ``templating`` service::
173+
174+
public function render($view, array $parameters = array(), Response $response = null)
59175
{
176+
return $this->container->get('templating')->renderResponse($view, $parameters, $response);
60177
}
61178

62-
In this example, ``my_bundle.annot_controller`` should be the id of the
63-
``AnnotController`` instance defined in the service container. This is
64-
documented in the :doc:`/bundles/SensioFrameworkExtraBundle/annotations/routing`
65-
chapter.
179+
So in our controller as a service we can instead inject the ``templating``
180+
service and use it directly::
181+
182+
// src/Acme/HelloBundle/Controller/HelloController.php
183+
namespace Acme\HelloBundle\Controller;
184+
185+
use Symfony\Component\HttpFoundation\Response;
186+
187+
class HelloController
188+
{
189+
private $templating;
190+
191+
public function __construct($templating)
192+
{
193+
$this->templating = $templating;
194+
}
195+
196+
public function indexAction($name)
197+
{
198+
return $this->templating->renderResponse(
199+
'AcmeHelloBundle:Hello:index.html.twig',
200+
array('name' => $name)
201+
);
202+
}
203+
}
204+
205+
The service definition also needs modifying to specify the constructor
206+
argument:
207+
208+
.. configuration-block::
209+
210+
.. code-block:: yaml
211+
212+
# src/Acme/HelloBundle/Resources/config/services.yml
213+
parameters:
214+
# ...
215+
acme.controller.hello.class: Acme\HelloBundle\Controller\HelloController
216+
217+
services:
218+
acme.hello.controller:
219+
class: "%acme.controller.hello.class%"
220+
arguments: ["@templating"]
221+
222+
.. code-block:: xml
223+
224+
<!-- src/Acme/HelloBundle/Resources/config/services.xml -->
225+
<parameters>
226+
<!-- ... -->
227+
<parameter key="acme.controller.hello.class">Acme\HelloBundle\Controller\HelloController</parameter>
228+
</parameters>
229+
230+
<services>
231+
<service id="acme.hello.controller" class="%acme.controller.hello.class%">
232+
<argument type="service" id="templating"/>
233+
</service>
234+
</services>
235+
236+
.. code-block:: php
237+
238+
// src/Acme/HelloBundle/Resources/config/services.php
239+
use Symfony\Component\DependencyInjection\Definition;
240+
use Symfony\Component\DependencyInjection\Reference;
241+
242+
// ...
243+
$container->setParameter(
244+
'acme.controller.hello.class',
245+
'Acme\HelloBundle\Controller\HelloController'
246+
);
247+
248+
$container->setDefinition('acme.hello.controller', new Definition(
249+
'%acme.controller.hello.class%',
250+
array(new Reference('templating'))
251+
));
252+
253+
Rather than fetching the ``templating`` service from the container just the
254+
service required is being directly injected into the controller.
255+
256+
.. note::
257+
258+
This does not mean that you cannot extend these controllers from a base
259+
controller. The move away from the standard base controller is because
260+
its helper method rely on having the container available which is not
261+
the case for controllers defined as services. However, it is worth considering
262+
extracting common code into a service to be injected in rather than a parent
263+
class.

0 commit comments

Comments
 (0)