Creating pages with Symfony 4

After getting started and created your first project with Symfony 4 (visit my previous story about it by clicking here) the natural step is to create pages to be consumed by users.

With this ready to go Symfony Skeleton you can start develop in an MVC oriented way. If you are not familiar with it, MVC (TL; DR;) is a development architecture where you split the ownership in Models (the objects you manage inside your application), Views (the template layer to show the information to your consumer) and Controllers (receive and process the request to return a response).

Learning some essentials: Symfony Components

Symfony Components are a bunch of single and independent PHP packages that you can use in your very own PHP applications. In fact, many other PHP frameworks/projects are using Symfony components to handle some specific parts like Laravel, Drupal, etc.…
Which Symfony Components you should learn at the very beginning?

HTTP Foundation

The HttpFoundation component defines an object-oriented layer for the HTTP specification. (

This component allows you to use in your application the request made to your application as an object (Request class). With this object you can get the data sent from the client (the form data, the query parameters on the URL, the headers defined…) like $request->get('userId') .
Also, you can make usage of Response / JsonResponse class objects to define the content to send to your client.

HTTP Kernel

The HttpKernel component provides a structured process for converting a Request into a Response by making use of the EventDispatcher component. It's flexible enough to create a full-stack framework (Symfony), a micro-framework (Silex) or an advanced CMS system (Drupal).

As a “TL; DR;”, this component will guide your request through a workflow and returns something as soon as a Response class is created. This component will request a route match (match url with your possible application routes), parse data sent into request attributes and execute your controller logic. I really recommend you to read the documentation for this component (it has a very nice chart!).

Dependency Injection

The DependencyInjection component implements a PSR-11 compatible service container that allows you to standardize and centralize the way objects are constructed in your application.

More than the advantages of a Dependency Injection pattern, this component allows you to have a cleaner architecture and code. With the Autowire and auto configure features, using the v4.+ the developers can just require the type of object you need to use and this component will prepare an instance and inject at your object.

Creating a page

The long answer is “you need HTTPFoundation, HTTPKernel, Router Symfony components”. I really recommend you to visit Symfony documentation and read more about these components.
The best answer is “let’s code”.

The controller

For our first page (our homepage), we will return a simple message. Within src/Controller folder you create your first controller IndexController.php.

namespace App\Controller;
use Symfony\Component\HTTPFoundation\Response;class IndexController
// This is an action, the function that gives you the content for
//your page
public function index()
return new Response('Hello world from your controller');

The IndexController will respond to many requests. For now it contains only the index action (index function), responsible to return the message Hello world from your controller .
Now that you have an action ready to respond we still need to tell to Symfony that calling http://<site-url>/ will execute this index function. The right place to do this is the routing.

The router

path: /
controller: App\Controller\IndexController::index

First you say your route name. As child keys of this route you define the path (the url part user is browsing at your website) and the controller (the controller and function that will be called from that URL).

How to validate that the application is getting this new route? In the console run:

$ bin/console debug:router

You can use the Symfony Console component to debug all your routes. After running the debugger, you should see one route:

------- -------- -------- ------ ------
Name Method Scheme Host Path
------- -------- -------- ------ ------
index ANY ANY ANY /
------- -------- -------- ------ ------

This is good! You can browse now your application and see the content showing up in your browser.

The output for this first controller > action

If you get to here without problems, congrats! You are able to create your pages!

Creating pages like a Pro

Of course, there are other ways to do it. Not because is a best practice, but honestly depends on the your “personal taste” regarding code organisation. I prefer this way because… well, I’m lazy… So let’s code.

The Controller

$ bin/console make:controller

And the result, possibly will be an error. The console will warn us that we need first a new dependency, the Annotations package. This package will allow us, using the DocBlocks to describe our controller and also call other features. so…

$ composer require annotations

… and then run the console again to create the controller (I’ll do it for a PagesController)

$ bin/console make:controller
Choose a name for your controller class (e.g. FierceGnomeController):
> <here you will be prompted to name your controller>
created: src/Controller/<yourControllerName>.phpSuccess!Next: Open your new controller class and add some pages!

so, you just created a new controller and the router already knows about it:

bin/console debug:router
------- -------- -------- ------ --------
Name Method Scheme Host Path
------- -------- -------- ------ --------
pages ANY ANY ANY /pages
------- -------- -------- ------ --------

What about the controller code?

// path: src/Controller/PagesController.php
namespace App\Controller;use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class PagesController extends Controller
@Route("/pages", name="pages")

public function index()
return $this->json([
'message' => 'Welcome to your new controller!',
'path' => 'src/Controller/PagesController.php',

Comparing with our primary controller class, there’s two things we can notice very quickly.
First, out controller class is extending another Controller class. This controller class acts as a “base class” to give us more functionalities out of the box (such as call twig rendering, expose the Dependency Injection container to require all kinds of services, and much more).
The second change is the DocBlock on top of index action. Using that notation we are able to give a path and name to our route, the same we did with our route.yaml file. You don’t need to worry about which is more powerful because both have the same features, it’s all about the way you write it and where do you store them.

After you can change the index action and make it return your new Response object with your content.

Story recap

  • We saw how we can create static content pages (even with a lazy way)

In the next story I’ll cover up twig and how to create dynamic content pages.

Software Engineering Manager and Software Engineer | Server Side Trainer | Human stuff as a hobby.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store