143 lines
4.5 KiB
ReStructuredText
143 lines
4.5 KiB
ReStructuredText
|
Service Controllers
|
||
|
===================
|
||
|
|
||
|
As your Silex application grows, you may wish to begin organizing your
|
||
|
controllers in a more formal fashion. Silex can use controller classes out of
|
||
|
the box, but with a bit of work, your controllers can be created as services,
|
||
|
giving you the full power of dependency injection and lazy loading.
|
||
|
|
||
|
.. ::todo Link above to controller classes cookbook
|
||
|
|
||
|
Why would I want to do this?
|
||
|
----------------------------
|
||
|
|
||
|
- Dependency Injection over Service Location
|
||
|
|
||
|
Using this method, you can inject the actual dependencies required by your
|
||
|
controller and gain total inversion of control, while still maintaining the
|
||
|
lazy loading of your controllers and its dependencies. Because your
|
||
|
dependencies are clearly defined, they are easily mocked, allowing you to test
|
||
|
your controllers in isolation.
|
||
|
|
||
|
- Framework Independence
|
||
|
|
||
|
Using this method, your controllers start to become more independent of the
|
||
|
framework you are using. Carefully crafted, your controllers will become
|
||
|
reusable with multiple frameworks. By keeping careful control of your
|
||
|
dependencies, your controllers could easily become compatible with Silex,
|
||
|
Symfony (full stack) and Drupal, to name just a few.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
|
||
|
There are currently no parameters for the ``ServiceControllerServiceProvider``.
|
||
|
|
||
|
Services
|
||
|
--------
|
||
|
|
||
|
There are no extra services provided, the ``ServiceControllerServiceProvider``
|
||
|
simply extends the existing **resolver** service.
|
||
|
|
||
|
Registering
|
||
|
-----------
|
||
|
|
||
|
.. code-block:: php
|
||
|
|
||
|
$app->register(new Silex\Provider\ServiceControllerServiceProvider());
|
||
|
|
||
|
Usage
|
||
|
-----
|
||
|
|
||
|
In this slightly contrived example of a blog API, we're going to change the
|
||
|
``/posts.json`` route to use a controller, that is defined as a service.
|
||
|
|
||
|
.. code-block:: php
|
||
|
|
||
|
use Silex\Application;
|
||
|
use Demo\Repository\PostRepository;
|
||
|
|
||
|
$app = new Application();
|
||
|
|
||
|
$app['posts.repository'] = function() {
|
||
|
return new PostRepository;
|
||
|
};
|
||
|
|
||
|
$app->get('/posts.json', function() use ($app) {
|
||
|
return $app->json($app['posts.repository']->findAll());
|
||
|
});
|
||
|
|
||
|
Rewriting your controller as a service is pretty simple, create a Plain Ol' PHP
|
||
|
Object with your ``PostRepository`` as a dependency, along with an
|
||
|
``indexJsonAction`` method to handle the request. Although not shown in the
|
||
|
example below, you can use type hinting and parameter naming to get the
|
||
|
parameters you need, just like with standard Silex routes.
|
||
|
|
||
|
If you are a TDD/BDD fan (and you should be), you may notice that this
|
||
|
controller has well defined responsibilities and dependencies, and is easily
|
||
|
tested/specced. You may also notice that the only external dependency is on
|
||
|
``Symfony\Component\HttpFoundation\JsonResponse``, meaning this controller could
|
||
|
easily be used in a Symfony (full stack) application, or potentially with other
|
||
|
applications or frameworks that know how to handle a `Symfony/HttpFoundation
|
||
|
<http://symfony.com/doc/master/components/http_foundation/introduction.html>`_
|
||
|
``Response`` object.
|
||
|
|
||
|
.. code-block:: php
|
||
|
|
||
|
namespace Demo\Controller;
|
||
|
|
||
|
use Demo\Repository\PostRepository;
|
||
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||
|
|
||
|
class PostController
|
||
|
{
|
||
|
protected $repo;
|
||
|
|
||
|
public function __construct(PostRepository $repo)
|
||
|
{
|
||
|
$this->repo = $repo;
|
||
|
}
|
||
|
|
||
|
public function indexJsonAction()
|
||
|
{
|
||
|
return new JsonResponse($this->repo->findAll());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
And lastly, define your controller as a service in the application, along with
|
||
|
your route. The syntax in the route definition is the name of the service,
|
||
|
followed by a single colon (:), followed by the method name.
|
||
|
|
||
|
.. code-block:: php
|
||
|
|
||
|
$app['posts.controller'] = function() use ($app) {
|
||
|
return new PostController($app['posts.repository']);
|
||
|
};
|
||
|
|
||
|
$app->get('/posts.json', "posts.controller:indexJsonAction");
|
||
|
|
||
|
In addition to using classes for service controllers, you can define any
|
||
|
callable as a service in the application to be used for a route.
|
||
|
|
||
|
.. code-block:: php
|
||
|
|
||
|
namespace Demo\Controller;
|
||
|
|
||
|
use Demo\Repository\PostRepository;
|
||
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||
|
|
||
|
function postIndexJson(PostRepository $repo) {
|
||
|
return function() use ($repo) {
|
||
|
return new JsonResponse($repo->findAll());
|
||
|
};
|
||
|
}
|
||
|
|
||
|
And when defining your route, the code would look like the following:
|
||
|
|
||
|
.. code-block:: php
|
||
|
|
||
|
$app['posts.controller'] = function($app) {
|
||
|
return Demo\Controller\postIndexJson($app['posts.repository']);
|
||
|
};
|
||
|
|
||
|
$app->get('/posts.json', 'posts.controller');
|