Controllers play a major role in web applications by being at the forefront of any application output. They are the endpoints, the code that executes behind each URL. In a more technical manner, we can say the controller is any callable (a function, method on an object, or a closure) that takes the HTTP request and returns an HTTP response. The response is not bound to a single format like HTML, it can be anything from XML, JSON, CSV, image, redirect, error, and so on.
Let's take a look at the previously created (partial) src/AppBundle/Controller/CustomerController.php file, more precisely its newAction method:
/**
* Creates a new Customer entity.
*
* @Route("/new", name="customer_new")
* @Method({"GET", "POST"})
*/
public function newAction(Request $request)
{
//...
return $this->render('customer/new.html.twig', array(
'customer' => $customer,
'form' => $form->createView(),
));
}If we ignore the actual data retrieval part (//…), there are three important things to note in this little example:
@Route: this is the Symfony's annotation way of specifying HTTP endpoint, the URL we will use to access this. The first "/new" parameter states the actual endpoint, the second name="customer_new" parameter sets the name for this route that we can then use as an alias in URL generation functions in templates and so on. It is worth noting, that this builds upon the @Route("/customer") annotation set on the actual CustomerController class where the method is defined, thus making for the full URL to be something like http://test.app/customer/new.@Method: This takes the name of one or more HTTP methods. This means that the newAction method will trigger only if the HTTP requests match the previously defined @Route and are of one or more HTTP method types defined in @Method.$this->render: This returns the Response object. The $this->render calls the render function of the Symfony\Bundle\FrameworkBundle\Controller\Controller class, which instantiates new Response(), sets its content, and returns the whole instance of that object.Now let's take a look at the editAction method within our controller, as partially shown in the following code block:
/**
* Displays a form to edit an existing Customer entity.
*
* @Route("/{id}/edit", name="customer_edit")
* @Method({"GET", "POST"})
*/
public function editAction(Request $request, Customer $customer)
{
//...
}Here we see a route that accepts a singe ID, marked as {id} within the first @Route annotation parameter. The body of the method (excluded here), does not contain any direct reference to fetching the id parameter. We can see that the editAction function accepts two parameters, one being Request, the other being Customer. But how does the method know to accept the Customer object? This is where Symfony's @ParamConverter annotation comes into play. It calls converters to convert the request parameters to objects.
The great thing about @ParamConverter annotation is that we can use it explicitly or implicitly. That is, if we do not add @ParamConverter annotation but add type hinting to the method parameter, Symfony is going to try and load the object for us. This is the exact case we have in our example above, as we did not explicitly type the @ParamConverter annotation.
Terminology wise, controllers are often exchanged for routing. However, they are not the same thing.