Below you will find pages that utilize the taxonomy term “Controller”
Testing your controllers when you have a decoupled core
A lot can happen in 9 years. Back then I was still advocating that you should unit-test your controllers and that setter injection is very helpful when replacing controller dependencies with test doubles. I’ve changed my mind: constructor injection is the right way for any service object, including controllers. And controllers shouldn’t be unit tested, because:
- Those unit tests tend to be a one-to-one copy of the controller code itself. There is no healthy distance between the test and the implementation.
- Controllers need some form of integrated testing, because by zooming in on the class-level, you don’t know if the controller will behave well when the application is actually used. Is the routing configuration correct? Can the framework resolve all of the controller’s arguments? Will dependencies be injected properly? And so on.
The alternative I mentioned in 2012 is to write functional tests for your controller. But this is not preferable in the end. These tests are slow and fragile, because you end up invoking much more code than just the domain logic.
Symfony2: Framework independent controllers part 3: Loose ends
Thanks! Let me explain myself
First of all: thanks everybody for reading the previous parts of this series. Many people posted some interesting comments. I realized quickly that I had to explain myself a bit: why am I writing all of this? Why would you ever want to decouple controllers from the (Symfony2) framework? You probably don’t need to bother anyway, because
The chances of you needing to move controllers to other frameworks is next to none. — Rafael Dohms
Symfony2: Framework independent controllers part 2: Don't use annotations
In the previous part of this series we decreased coupling of a Symfony controller to the Symfony2 framework by removing its dependency on the standard Controller
class from the FrameworkBundle.
Now we take a look at annotations. They were initially introduced for rapid development (no need to create/modify some configuration file, just solve the issues inline!):
namespace Matthias\ClientBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
/**
* @Route("/client")
*/
class ClientController
{
/**
* @Route('/{id}')
* @Method("GET")
* @ParamConverter(name="client")
* @Template
*/
public function detailsAction(Client $client)
{
return array(
'client' => $client
);
}
}
When you use these annotations, the details
action will be executed when the URL matches /client/{id}
. A param converter will be used to fetch a Client
entity from the database based on the id
parameter which is extracted from the URL by the router. The return value of the action is an array. These are used as template variables for rendering the Resources/views/Client/Details.html.twig
template.
Symfony2: How to create framework independent controllers?
Part I: Don’t use the standard controller
The general belief is that controllers are the most tightly coupled classes in every application. Most of the time based on the request data, they fetch and/or store persistent data from/in some place, then turn the data into HTML, which serves as the response to the client who initially made the request.
So controllers are “all over the place”, they glue parts of the application together which normally lie very far from each other. This would make them highly coupled: they depend on many different things, like the Doctrine entity manager, the Twig templating engine, the base controller from the FrameworkBundle, etc.
Prevent controller execution with annotations and return a custom response
Symfony2 provides multiple ways of blocking, providing or modifying the response. You can:
-
Intercept each request by listening to the
kernel.request
event and set the response directly on the event (which will effectively skip execution of a controller) -
Modify the controller or its arguments by listening to the
kernel.controller
event, then callingsetController
on the event object and modifying the attributes of theRequest
object. -
Change the response rendered by a controller, by listening to the
kernel.response
event.
Combining GridFS files with ORM entities
In my previous post I wrote about uploading files to GridFS. Therefor I created a MongoDB Document with a $file
property annotated with @MongoDB\File
. Because I am using ORM entities more often then ODM documents, I was looking for a seamless way to access a Document from an Entity.
Because it isn’t possible to define a direct relationship between an Entity and a Document I thought it would be a solid solution to create a custom field type. By defining a custom field type I can control the way the reference to the Document will be stored and at the same time I will be able to restore the reference when retrieving the field. The steps needed to create a custom field type for ORM entities are very similar to the post of Matthias on how to create custom field types for ODM documents.
Uploading files to MongoDB GridFS
Almost at the same time, I silently celebrate the first birthday of my blog. My first article appeared a little over a year ago. It is great to see how Symfony2 has become more and more popular during these twelve months. Your comments and visits encourage me to keep posting articles. So, thank you all! And thanks, Dennis, for contributing.
GridFS is a specification for storing large files in MongoDB. In this post I will explain how you can easily upload a file to GridFS and then retrieve it from the database to serve it to the browser.
Let Silex Wrap Your Legacy PHP Application (and add Twig for templating)
Ever since I am using the Symfony Framework (be it version 1 or 2), I tend to describe every other project I’ve done (including those that were built on top of some third party “framework” like Joomla or WordPress) as a “legacy project”. Though this has sometimes felt like treason, I still keep doing it: the quality of applications written using Symfony is usually so much higher in terms of maintainability, security and code cleanliness, that even a project done last year using “only PHP” looks like a mess and seems to be no good software at all. So I feel the strong urge to rebuild everything I have in portfolio (as do many other developers), but “this time, I will do it the right way”.
Symfony2: Testing Your Controllers
Apparently not everyone agrees on how to unit test their Symfony2 controllers. Some treat controller code as the application’s “glue”: a controller does the real job of transforming a request to a response. Thus it should be tested by making a request and check the received response for the right contents. Others treat controller code just like any other code - which means that every path the interpreter may take, should be tested.
Symfony2: running PHPUnit from within a controller
When you don’t have access to the command-line of your webserver, it may be nice to still run all your unit tests; so you need a way to execute the phpunit
command from within a controller. This way, you can call your test suite by browsing to a URL of your site. To do things right, we start with a “test” controller /web/app_test.php
containing these lines of code:
if (!in_array(@$_SERVER['REMOTE_ADDR'], array(
'127.0.0.1',
'::1',
))) {
header('HTTP/1.0 403 Forbidden');
exit('You are not allowed to access this file. Check '.basename(__FILE__).' for more information.');
}
require_once __DIR__.'/../app/bootstrap.php.cache';
require_once __DIR__.'/../app/AppKernel.php';
use Symfony\Component\HttpFoundation\Request;
$kernel = new AppKernel('test', true);
$kernel->loadClassCache();
$kernel->handle(Request::createFromGlobals())->send();
In fact, just copy everything from /web/app_dev.php
but replace the string “dev” by “test”.