Series index
- Setup
- Routes, templates and controllers (You are here)
- Working with the database (Not yet finished)
- 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 ourtemplates
folder. This is the path to our twig template.- As a second argument we pass an array, containing
controller_name
with the valueDefaultController
. 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 thebase.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 ah2
tag under theh1
. 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 bodylightgreen
. 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 theWorld
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.
Leave a Reply