A basic guide to PHP Symfony – Routes, templates and controllers

Series index

  1. Setup
  2. Routes, templates and controllers (You are here)
  3. Working with the database (Not yet finished)
  4. Security (Not yet finished)

Symfony handles routing for us, that is one of its super powers. Routing means, that when we open /hello/World in our browser, it will match the request to the function generating our response.

Symfony is based arround the concept of routes, a route is the path that comes after our domain. In the case of https://example.com/hello/World the /hello/World is our route. the functions symfony will match that request to, are organized in multiple controllers. It is possible, and therefore highly recommended, to use multiple well named controllers. Example given: an online Shop would have commended, to use multiple well named controllers. Example given: in an online Shop we may have a ProductController, a PrivacyController (may the GDPR be blessed), a PurchaseController and so on. If we someone looks at our code it immediatly is clear what this controller is for.

Step 1 – Creating our first route

At the beginning we generate all of our dynamic page contents inside of a Controller. We will use bin/console to create our first controller. After running php bin/console make:controller a prompt will ask us for the controller name. For now we will use DefaultController.(Click to expand) Output of the command

Our first controller with routes has just been created, congratulations!

In our editor we can see two new files. Those being src/Controller/DefaultController.php and templates/default/index.html.twig. The name of our controller, default, is included in both of those paths.

We notice more similarities when opening our two files, preferably side by side. In DefaultController.php the following is called:

return $this->render('default/index.html.twig', [
	'controller_name' => 'DefaultController',
]);

Here we can see two things:

  • default/index.html.twig is the path to the new files insides of our templates folder. This is the path to our twig template.
  • As a second argument we pass an array, containing controller_name with the value DefaultController. We use those parameters inside of our template. More on that later.

Before we go ahead it’s vital to understand the created code.

Before the index function, inside of our new controller, are so called Annotations.

/**
 * @Route("/default", name="default")
 */
public function index()
{
    return $this->render('default/index.html.twig', [
        'controller_name' => 'DefaultController',
    ]);
}

We can see in this comment-block, that our route is /default

When we open localhost:8000/default, given your development server is running at this port, we should see a output containing Hello DefaultController! ✅. This is brought to us by the return $this->render expression inside of our index function. This generates the HTML content show the user, based on the passed template. fault/index.html.twig is that in our case. Parameters are also passed to our template. They are located inside of the square brackets at the end.

At the moment we only pass the argument controller_name with the value 'DefaultController'. In the next step we will learn how to use those arguments passed.

Step 3 – What the template?

Symfony uses Twig as a template engine. Such a template engine is handy because:

  • we can reuse parts of it
  • it doesn’t clutter our PHP code
  • it’s easier to split the work for the backend / frontend

For now we look at the templates/default/index.html.twig . In here we notice a couple of things:

  • {% extends 'base.html.twig' %}: This indicates that all of our content is based on the base.html.twig. In here we would include our CSS and JS. Navabars would preferably be defined here to.
  • {% block body %} [...]{% endblock %}: In our base template we define several blocks. They act like placeholders. If a block is previously defined in another template, it will be overwritten. By utilizing that we may define default values, for example in the title.
  • <h1>Hello {{ controller_name }}! ✅</h1>: This is a basic h1 tag. The {{ controller_name }} is the most interesting part in here. In the previous section we passed an argument with the same name to our template.

(Click to expand) full index.html.twig

This is a simple task to consolidate our knowledge:

  • Pass the argument owner_name to the template. Print it out as a h2 tag under the h1. Feel free to add a short description.
  • Given the information that there is a stylesheets block in the base template, go ahead and make the background of the body lightgreen. Note: you have to write the style tags out too. The stylesheets block is not surrounded with them by default.

(Click to expand) Solution

We now know how templates work and how we pass arguments to them. For a more in depth documentation of the functions twig offers please refer to the official Twig documentation.

Step 4 – Creating our first custom routes

Creating our first custom route is the first step to take full advantage of Symfonys Routing feature. An infamous hello world page will be enough for now:

  • Add the route /default/hello
  • Create a hello.html.twig template

We also want to pass who to greet and display it in the template.

We need a route, for now we stick to our DefaultController with that. The route is defined by using the aforementioned annotations. As mentioned, our route is /default/hello. The resulting annotation look like so:

/**
 * @Route("/default/hello", name="default_hello")
 */

We put our hello function right bellow that. It handles the rendering, generating html code, from our template. The who parameter, stating who to greet, is also passed to our template. For now this will have the value World, that’s because we want to greet the world.

public function hello()
{
    return $this->render('default/hello.html.twig', [
        'who' => 'World',
    ]);
}

Our template isn’t complex either. We expand the base.html.twig, set the title and print it out in a h1 block. Because we as programmers are lazy by nature we only want to write the title once. Twigs set function is made for that. We set the title variable to Hello + who we want to greet + an exclamation mark(!). Using the tilde (~) we connect (concatenate) our strings.

{% set title = 'Hello ' ~ who ~ '!' %}

This variable can be used everywhere, being in our title block or inside of our body:

{% block title %}{{ title }}{% endblock %}

{% block body %}
    <h1>{{ title }}</h1>
{% endblock %}

(Click to expand) Full code

We now have learned how simple it is to create a custom route by hand. In the next step we learn why the concept of routes is so powerful and how to pass arguments with our routes.

Step 5 – Passing arguments with our routes

We have all seen it, for example in an online shop: the name or ID of a product is part of the url. In some cases those parameters may be passed using so called GET parameters (e.g. ?item=674). In other cases you may see something like this: /item/674. This is exactly what we are creating.

The content of the following steps is:

  • outsourcing our hello world into a HelloController
  • rename the route /default/hello to /hello/World where the World part is modifiably by the user and is passed to the template

We create our new controller by using the command php bin/console make:controller HelloController. In Step 1 this was already covered. The /default/hello route is no longer needed and can therefore be removed. We also move our hello.html.twig from templates/default/hello.html.twig to templates/hello/index.html.twig. The hello/index.html.twig can be overwritten / deleted beforehand.

A placeholder must be defined to access the URL parameters. Curly brackets indicate the placeholder. The name of it is stated inside of them. Our route therefore is /hello/{who}. As a result of that, a request to /hello/World has a who value of World. We pass this parameter just like we would pass any other parameter to a PHP function. Inside of the function it is available just like any other variable and can simply be passed to our template.

The resulting code now looks like this:

/**
 * @Route("/hello/{who}", name="hello")
 */
public function index($who)
{
    return $this->render('hello/index.html.twig', [
        'who' => $who,
    ]);
}

Please note: the name of the placeholder must match the name of the function parameter!

When we open localhost:8000/hello/World. We should be greeted by a friendly Hello World.

That’s just what we had before. But what if we want to greet Hetzner as a thank you for this community space? Well, just replace the World with Hetzner and we’re greeted by a Hello Hetzner!.

This is cool isn’t it? But what happens when we open /hello/? Now we are greeted by a No route found for "GET /hello/": HTTP 404 Not Found. This is neither our template nor even anything nice at all. But what can we do about it? We can define default values! Since the route parameters are passed as function parametes, we can define this directly at the function.

We just change the function from public function index($who) to public function index($who = "World")! This is the exact same way we set default values for a function parameter in php!

If you want to do more reading on route parameters, for example how to verify them by using regular expressions, the Symfony Documentation on Routing is great.

Step 6 – Linking and redirecting

You may have noticed that we set a name for each and every one of our routes. But we haven’t used them yet, so it’s reasonable to assume that it’s unneeded overhead. First of all, it’s not needed! We do not have to write the name="default" part of the annotation. But why are we doing it anyways? Because you can do powerful things with it!

There are two main features we will take a look at for now. Those beeing redirecting and linking.

Redirecting

Given we want to redirect our users from / to /hello/Guest in order to greet our new visitors.

In normal PHP we would do it like this:

header('Location: /hello/Guest');

But this sucks, Symfony can do this way easier! Inside of the function that generates our content we utilize the following function:

return $this->redirectToRoute('hello', [
    'who' => 'Guest'
]);

The name of our route is the first parameter passed to this function. hello in our case. The parameters we want to pass with our route come in second place. Guest will be the value of our who parameter.

Now go ahead and:

  • create the / route inside of our DefaultController
  • redirect from / to /hello/Guest

(Click to expand) Full code

Linking

It is super easy to generate links inside of our template. Those we can use or an a href but also everywhere else.

Two twig functions will be employed by us in the following steps:

  • path
  • url

The first one we mostly use for linking on the website because it only generates the relative URL. So it spits out the /hello/Guest. When the user is already on our website this is perfectly fine. But when we want to offer users a share link or anythink like this, it isn’t feesable to only have the relative path. The url function is used when you want to include the domain in the output. Passing a simple argument we can get the result of the other function too, it’s way clearer when we stick to path for on-site linking and url for anything that comes from of-site.

We do the following:

  • link from /default/ to /hello/You
  • show the user a sharable url on /hello/{who}

By adding one line to our templates/default/index.html.twig we can accomplish the first thing. Since this is a HTML Hyperlink, we are using the a tag.

<li><a href="{{ path('hello', {'who': 'You'}) }}">Hello You!</a></li>

We pass two parameters to our path function, the same as we did when redirecting. First is the route_name and in second position the route parameters.

By adding the following lines to our hello page, we show our users a sharable link:

<br>
<p>Share this:</p>
<a href="{{ url('hello', {'who': who}) }}">{{ url('hello', {'who': who}) }}</a>

Same syntax as the path function, but the result should be like this:

<br>
<p>Share this:</p>
<a href="http://localhost:8000/hello/You">http://localhost:8000/hello/You</a>

Conclusion

We have created multiple routes, and passed values with then. We also know how we can use route names. This is essential to go ahead with the next parts.

Reprint: https://community.hetzner.com/tutorials/a-basic-guide-to-php-symfony/routes-templates-and-controllers


Posted

in

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *