Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This tutorial has a new version, check it out!

Routes, Controllers, Pages, oh my!

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

Let's create our first page! Actually, this is the main job of a framework: to give you a route and controller system. A route is configuration that defines the URL for a page and a controller is a function that we write that actually builds the content for that page.

And right now... our app is really small! Instead of weighing down your project with every possible feature you could ever need - after all, we're not in zero-gravity yet - a Symfony app is basically just a small route-controller system. Later, we'll install more features when we need them, like a warp drive! Those always come in handy. Adding more features is actually going to be pretty awesome. More on that later.

First Route & Controller

Open your app's main routing file: config/routes.yaml:

#index:
# path: /
# controller: App\Controller\DefaultController::index

Hey! We already have an example! Uncomment that. Ignore the index key for now: that's the internal name of the route, but it's not important yet.

This says that when someone goes to the homepage - / - Symfony should execute an index() method in a DefaultController class. Change this to ArticleController and the method to homepage:

index:
path: /
controller: App\Controller\ArticleController::homepage

And... yea! That's a route! Hi route! It defines the URL and tells Symfony what controller function to execute.

The controller class doesn't exist yet, so let's create it! Right-click on the Controller directory and go to "New" or press Cmd+N on a Mac. Choose "PHP Class". And, yes! Remember that Composer setup we did in Preferences? Thanks to that, PhpStorm correctly guesses the namespace! The force is strong with this one... The namespace for every class in src/ should be App plus whatever sub-directory it's in.

Name this ArticleController:

<?php
namespace App\Controller;
... lines 4 - 6
class ArticleController
{
... lines 9 - 12
}

And inside, add public function homepage():

... lines 1 - 2
namespace App\Controller;
... lines 4 - 6
class ArticleController
{
public function homepage()
{
... line 11
}
}

This function is the controller... and it's our place to build the page. To be more confusing, it's also called an "action", or "ghob" to its Klingon friends.

Anyways, we can do whatever we want here: make database queries, API calls, take soil samples looking for organic materials or render a template. There's just one rule: a controller must return a Symfony Response object.

So let's say: return new Response(): we want the one from HttpFoundation. Give it a calm message: OMG! My first page already! WOOO!:

... lines 1 - 2
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
class ArticleController
{
public function homepage()
{
return new Response('OMG! My first page already! WOOO!');
}
}

Ahem. Oh, and check this out: when I let PhpStorm auto-complete the Response class it added this use statement to the top of the file automatically:

... lines 1 - 4
use Symfony\Component\HttpFoundation\Response;
... lines 6 - 14

You'll see me do that a lot. Good job Storm!

Let's try the page! Find your browser. Oh, this "Welcome" page only shows if you don't have any routes configured. Refresh! Yes! This is our page. Our first of many.

Annotation Routes

That was pretty easy, but it can be easier! Instead of creating our routes in YAML, let's use a cool feature called annotations. This is an extra feature, so we need to install it. Find your open terminal and run:

composer require annotations

Interesting... this annotations package actually installed sensio/framework-extra-bundle. We're going to talk about how that works very soon.

Now, about these annotation routes. Comment-out the YAML route:

#index:
# path: /
# controller: App\Controller\ArticleController::homepage

Then, in ArticleController, above the controller method, add /**, hit enter, clear this out, and say @Route(). You can use either class - but make sure PhpStorm adds the use statement on top. Then add "/":

... lines 1 - 4
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
... lines 6 - 7
class ArticleController
{
/**
* @Route("/")
*/
public function homepage()
{
... line 15
}
}

Tip

When you auto-complete the @Route annotation, be sure to choose the one from Symfony\Component\Routing - the one we chose is now deprecated. Both work the same.

That's it! The route is defined right above the controller, which is why I love annotation routes: everything is in one place. But don't trust me, find your browser and refresh. It's a traaaap! I mean, it works!

Tip

What exactly are annotations? They're PHP comments that are read as configuration.

Fancy Wildcard Routes

So what else can we do with routes? Create another public function called show(). I want this page to eventually display a full article. Give it a route: @Route("/news/why-asteroids-taste-like-bacon"):

... lines 1 - 4
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
... lines 6 - 7
class ArticleController
{
... lines 10 - 17
/**
* @Route("/news/why-asteroids-taste-like-bacon")
*/
public function show()
{
... line 23
}
}

Eventually, this is how we want our URLs to look. This is called a "slug", it's a URL version of the title. As usual, return a new Response('Future page to show one space article!'):

... lines 1 - 4
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Response;
class ArticleController
{
... lines 10 - 17
/**
* @Route("/news/why-asteroids-taste-like-bacon")
*/
public function show()
{
return new Response('Future page to show one space article!');
}
}

Perfect! Copy that URL and try it in your browser. It works... but this sucks! I don't want to build a route and controller for every single article that lives in the database. Nope, we need a route that can match /news/ anything. How? Use {slug}:

... lines 1 - 7
class ArticleController
{
... lines 10 - 17
/**
* @Route("/news/{slug}")
*/
public function show($slug)
{
... lines 23 - 26
}
}

This route now matches /news/ anything: that {slug} is a wildcard. Oh, and the name slug could be anything. But whatever you choose now becomes available as an argument to your "ghob", I mean your action.

So let's refactor our success message to say:

Future page to show the article

And then that slug:

... lines 1 - 7
class ArticleController
{
... lines 10 - 17
/**
* @Route("/news/{slug}")
*/
public function show($slug)
{
return new Response(sprintf(
'Future page to show the article: "%s"',
$slug
));
}
}

Try it! Refresh the same URL. Yes! It matches the route and the slug prints! Change it to something else: /why-asteroids-taste-like-tacos. So delicious! Go back to bacon... because... ya know... everyone knows that's what asteroids really taste like.

And... yes! We're 3 chapters in and you now know the first half of Symfony: the route & controller system. Sure, you can do fancier things with routes, like match regular expressions, HTTP methods or host names - but that will all be pretty easy for you now.

It's time to move on to something really important: it's time to learn about Symfony Flex and the recipe system. Yum!

Leave a comment!

93
Login or Register to join the conversation
Nick S. Avatar
Nick S. Avatar Nick S. | posted 3 years ago | edited

I'm at "omg my first page already" and I'm following along/started the project by myself, and I have this error loading index.php:

Parse error: syntax error, unexpected '?' in C:\www\eds-www\tuts\the_spacebar\public\index.php on line 15

Here's line 15-17 of index.php:

`if ($trustedProxies = $_SERVER['TRUSTED_PROXIES'] ?? $_ENV['TRUSTED_PROXIES'] ?? false) {

Request::setTrustedProxies(explode(',', $trustedProxies), Request::HEADER_X_FORWARDED_ALL ^ Request::HEADER_X_FORWARDED_HOST);

}`

What have I fouled up?

1 Reply

Hey Nick S.

At first sight I think you are not running on PHP 7. The operator ?? was introduce in PHP 7. If that's not the case, check the first line of your file, probably you have something like this "<"??php (had to wrap it with double quotes so Disqus can render it)

Cheers!

Reply
Nick S. Avatar
Nick S. Avatar Nick S. | MolloKhan | posted 3 years ago | edited

Thanks for the reply MolloKhan!

Unfortunately (fortunately?) I am, I had to update it from 5.6 to 7.1.3 to run Composer. Also, I think your "cheers" being in code format means there's something there that I'm not seeing?

Also thank you for being here to help, and being so responsive to everyone. Looking forward to being part of this community. :)

Reply

> Also, I think your "cheers" being in code format means there's something there that I'm not seeing?

Yeah you are right! Disqus messed it up - I just fixed it

If you still have this problem. Could you show me your entire index.php file please?

> Also thank you for being here to help, and being so responsive to everyone. Looking forward to being part of this community. :)

You are welcom man :)

Reply
Nick S. Avatar

I can't get it to paste into code, so I'm linking a screenshot: https://imgur.com/a/07J6TBh

Does that help?

Thanks again!

Reply

Hmm, your file looks correct. It makes think that you are not running on PHP7 in your web server (You may have installed PHP7 only to the CLI). Check the phpinfo of your webserver to double check what I'm saying. If that's the case, then you now know what to do :)

Cheers!

Reply
Imane Avatar

Hi everyone! If you have Symfony version 4.3 to use the annotations you need to insert this in your BlogController:
use Symfony\Component\Routing\Annotation\Route;

They changed the directory, that's why so many of you are receiving error messages.

Tip: To easily configure a new controller you can use the terminal by writing
php bin/console make:controller

Hope it helped!

1 Reply
Default user avatar

Hi guys , I am a new beginner on this course. And i already face some issues.

someone can help me with this please.

After adding

/**
* @Route("/")
*/

I have this path but !!!!!!!
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

the last word Route is crossing. And it says that Class Route is depreciated

thank you!

1 Reply

Hey Yao,

I see you noticed this comment: https://symfonycasts.com/sc... - great, and I suppose your problem is solved. So basically, "Symfony\Component\Routing\Annotation\Route" class is a new official way to add routes annotations instead of deprecated "Sensio\Bundle\FrameworkExtraBundle\Configuration\Route" one. But instead of providing a BC break, Symfony warn us first saying it will be removed in future releases so you have time to update your code.

Cheers!

Reply
Default user avatar

Hi Victor,

Nice to read you. Yeah i finally fixed my issue by replacing "Sensio\Bundle\FrameworkExtraBundle\Configuration\Route" with "Symfony\Component\Routing\Annotation\Route" class which is really cool.

Thanks for your assistance.

Reply

Yay, glad you had got it working before even we replied you. And yeah, it was an easy fix ;)

Cheers!

Reply
Mike P. Avatar
Mike P. Avatar Mike P. | posted 5 years ago

Want to share my experience, SF 4.1.2 Deprecation Message:
User Deprecated: The "Sensio\Bundle\FrameworkExtraBundle\Configuration\Route" annotation is deprecated since version 5.2. Use "Symfony\Component\Routing\Annotation\Route" instead.

The solution is pretty obvious, choose the other Route Class :)

1 Reply

Hey Mike,

Thanks for sharing it with others! And yeah, this one is an easy win :)

Cheers!

1 Reply
Default user avatar

How long do you use Symfony for developing apps ?

Reply

Hey Yao,

Hm, I personally started to get familiar with Symfony since 2.1, but mostly started developing apps since 2.3 IIRC :)

Cheers!

Reply
Default user avatar

Is it normal that the debug pack just has 5 symfony plugins instead of 6 ? bc in the tutorial he said it comes with 6.

Reply

Hey yao!

Yep, that's totally ok :). When I recorded this tutorial, that package downloaded 6 things. But a few months ago, one of the packages was removed (nothing too important) and so now you only get 5.

Cheers!

Reply
Default user avatar

So that means you are senior right now... Great job

Reply

Haha, probably so... thanks! ;)

Cheers!

Reply
Default user avatar

Thanks Mike it works.

Reply
Default user avatar
Default user avatar Kutay Deril | posted 3 years ago

In 3:00 Chapter 3 I only have client and env. Why it is different?

Reply

Hey Kutay Deril

What you mean by only client and env? Did you download the course code or did you started the project by yourself?

Cheers!

Reply
Avraham M. Avatar
Avraham M. Avatar Avraham M. | posted 3 years ago

Hello!
I have controller and 2 child classes,
pretty similar to Template design pattern.

public class MyController
{
/**
*
*@Route("/add-item", name="addItem")
*/
public function addItem(){
$this->common();
$this->specific();
}

public function common() { ...}
}

} //MyController

public class Child1 extends MyController
{
public function specific( ) {...};
} // Child1

public class Child2
{
public function specific() {...}
} // Child2

I want to build links from twig so child class Child1::addItem be called.
Is there some special annotation for that?
How can it be achieved?

Thanks!

Reply

Hey Avraham M.

I don't think that it's a good idea to use hierarchies like that on controllers. If you want to re-use some methods among controllers, then, you can create a base class to hold all that logic, or maybe use a treat, besides that, what a controller should only have are route actions

Cheers!

Reply
Avraham M. Avatar
Avraham M. Avatar Avraham M. | MolloKhan | posted 3 years ago | edited

Hey MolloKhan
I try to avoid code repetition.
The simple option I have is to create 2 ( or more) actions in Controller,
so actions make common stuff and some specific stuff for each.
I thought about template design pattern..
Can you please be more detailed about the base class and the structure to organize it?

Thanks!

p.s.
I thought about custom actions inside MyController,
how is it?

public class MyController
{

/**
*@Route(/specific1, "specific1")
**/
public function specific1()
{
$this->addItem(array $specificParams);
}

public function addItem(array $specific1Params, string $specific1Function)
{
common();
$this->{$specific1Function}(specific1Params);
}

} // MyController

Reply

Ohh, I see. Then, what you can do is to create a base abstract class that all your controllers with inherit and add all of your common logic/methods on it.

Tip: A controller should not *own* too much logic (and even less if it's business logic), so if you find yourself adding a lot of code to a controller that's a good hint that you may need a service class.

Cheers!

Reply
Thomas W. Avatar
Thomas W. Avatar Thomas W. | posted 4 years ago | edited

Not sure where my last comment went - or how to get the "support" that is advertised in the subscription promotional...

In this video, things seems to work as advertised.. until we get to the second method

`
/**

  • @Route("/")
    */
    public function homepage()
    {
    return new Response("OMG my first page");
    

    }

/**

  • @Route("/news/item")
    */
    public function show()
    {
    return new Response("Content for the second page");
    

    }
    `

I'm using MAMP to serve up these pages.. I've got a virtual host pointing at http://spacebar
So

http://spacebar/
<b>Works</b>
http://spacebar/news/item
<b>Does not work (e.g., throws a 404 page not found</b>

<b>The full controller page looks like this</b>

`
<?php

namespace App\Controller;

use Symfony\Com``ponent\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class ArticleController
{

/**

  • @Route("/")
    */
    public function homepage()
    {
    return new Response("OMG my first page");
    

    }

/**

  • @Route("/news/item")
    */
    public function show()
    {
    return new Response("Content for the second page");
    

    }

}
`

Reply

Hey Thomas W.

I'm sorry for the late reply. For some reason our software lost track of messages for a couple of days but it's back on business again!

As Vladimir said in the other comment. It seems like a problem with your web server. Probably this guide may help you out configuring it right https://symfony.com/doc/cur...
But, I recommend using Symfony's built-in webserver, it's just easier and faster to use when working locally

Cheers!

Reply
Thomas W. Avatar
Thomas W. Avatar Thomas W. | posted 4 years ago | edited

First annotated thing worked...
Second... getting page not found...
(I shorted the URL to /news for brevity)

` /**

  • @Route("/news")
    */
    public function show() {
    return new Response('future page');
    

    }

`

Reply

Hey Thomas W.

Looks like you webserver doesn't pass you request to public/index.php. First page works because server pass / to index.php by default. If you are using apache behind the MAMP, than you should try to install symfony/apache-pack and it should fix it.

Cheers!

Reply
Default user avatar
Default user avatar Saul Solé | posted 4 years ago

If anybody is having a 404 error from apache here's what i did to solve it:
composer require symfony/apache-pack.
manually create your .htaccess file and fill it with this:
https://github.com/symfony/...

And it should work just fine.

Reply

Hey Saul Solé

Thanks for sharing it :)
If you want to know more about configuring Apache for a Symfony app you can read this guide: https://symfony.com/doc/current/setup/web_server_configuration.html
You may have to add the DirectoryIndex index.php rule if your homepage never ends loading

Cheers!

Reply
Sourodipta G. Avatar
Sourodipta G. Avatar Sourodipta G. | posted 4 years ago

How to validate url in the route. Can you please help me in that regards And can provide me the tutorial link on

Sure, you can do fancier things with routes, like match regular expressions, HTTP methods or host names - but that will all be pretty easy for you now.

Thanks

Reply

Hey Sourodipta,

I'd recommend you to take a look at docs about this: https://symfony.com/doc/cur... . In particular:
> like match regular expressions
https://symfony.com/doc/cur...

> HTTP methods
https://symfony.com/doc/cur...

> host names
https://symfony.com/doc/cur...

We do cover some of it in further screencasts, but I'd not recommend you to jump to them directly and skip chapters to avoid losing context. Just go further in this track: https://symfonycasts.com/tr... . Otherwise, you can use our search for find related articles.

I hope this helps!

Cheers!

Reply
Abelardo Avatar
Abelardo Avatar Abelardo | posted 4 years ago

Hi there,
What's the font which is used in screenshots?

Best regards.

Reply

Hey Abelardo L. ,

What screenshots do you mean?

Cheers!

Reply
Abelardo Avatar

Those which are integrated into your explanations.

Reply

Hi Abelardo L. the little notes that roll up in our videos use the Open Sans font

2 Reply
Abelardo Avatar

Thanks, but I referred to the font used to code your examples in PHPStorm.

Reply

Hey Abelardo L.!

It's Menlo - size 19 px :).

Cheers!

1 Reply
Abelardo Avatar

Do you know the Firacode font?

1 Reply

Hmm, I don't! I just checked it out. It looks crazy! Really beautiful! I imagine it takes some getting used to!

Reply
Default user avatar
Default user avatar Teodora Moldovan | posted 4 years ago

I am getting "An exception has been thrown during the rendering of a template ("Unable to generate a URL for the named route "workspace_show" as such route does not exist.").". When I run php bin/console debug:router, I can find the route name there. Where is than the problem coming from?

Reply

Hey Teodora Moldovan

That's odd, it makes me think that you may have restricted that route to work with only specific request methods (such as POST). Let me see the output of your php bin/console debug:router
BTW, try clearing the cache, I mean, just in case :)

Cheers!

Reply
Default user avatar
Default user avatar Teodora Moldovan | MolloKhan | posted 4 years ago

Thank you! I tried clearing the cache before and it didn’t work. However, it worked after I closed and reopened PhpStorm.

Reply

Really? PHPStorm? that's even weirder! haha, but I'm glad to hear that your problem is fixed now

Cheers!

Reply
Default user avatar
Default user avatar Mau España | posted 4 years ago

Hi!! I have a question, when I want the autocomplete for my code php don't appear, but in my first test appear, I have the package php-autocomplete, but in this test don't work, what can I do?
PD1: Sorry for my english.
PD2: I'm using Atom instead phpstorm

Reply

Hey Mau España!

If you're serious about getting auto-completion, use PHPStorm. It's that simple. You can get some really nice things from Atom (I use it as my editor for everything *except* developing), but PHPStorm is many levels better. You will get PHP autocompletion and Symfony-specific auto-completion. You can out our phpstorm tutorial to learn more about this: https://symfonycasts.com/sc...

Cheers!

Reply
Default user avatar
Default user avatar Dmitrii Utrobin | posted 4 years ago | edited

Hello! When I use Route, i get error 404 Not Found.

ArticleController.php
`
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class ArticleController
{

/**
 * @Route("/")
 */
public function homepage()
{
    return new Response("My first page done! v2");
}

/**
 * @Route("/news/why")
 */
public function show(){
    return new Response("Future page to show one spse");
}

}
`

Homepage is working. But "/news/why" page not working. I get error 404.
Help to fix it, please.

Reply

Hey Dmitrii,

First of all, try to clear the cache! Especially, if you're running your website in prod mode. Then, here's the trick to make sure if your route exist or no - debug it in console with "php bin/console debug:router" and search for "/news/why" in the output. Maybe your route has some prefix you forgot about :)

If nothing helped, please, double sure you use the correct namespace: "Symfony\Component\Routing\Annotation\Route". But it may depend on your Symfony version. For older version you probably need to use this one: "Sensio\Bundle\FrameworkExtraBundle\Configuration\Route" - at least you can try to use this one, but not for the latest version.

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.1.3",
        "ext-iconv": "*",
        "sensio/framework-extra-bundle": "^5.1", // v5.1.3
        "symfony/asset": "^4.0", // v4.0.3
        "symfony/console": "^4.0", // v4.0.14
        "symfony/flex": "^1.0", // v1.17.6
        "symfony/framework-bundle": "^4.0", // v4.0.14
        "symfony/lts": "^4@dev", // dev-master
        "symfony/twig-bundle": "^4.0", // v4.0.3
        "symfony/web-server-bundle": "^4.0", // v4.0.3
        "symfony/yaml": "^4.0" // v4.0.14
    },
    "require-dev": {
        "easycorp/easy-log-handler": "^1.0.2", // v1.0.4
        "sensiolabs/security-checker": "^5.0", // v5.0.3
        "symfony/debug-bundle": "^3.3|^4.0", // v4.0.3
        "symfony/dotenv": "^4.0", // v4.0.14
        "symfony/monolog-bundle": "^3.0", // v3.1.2
        "symfony/phpunit-bridge": "^3.3|^4.0", // v4.0.3
        "symfony/profiler-pack": "^1.0", // v1.0.3
        "symfony/var-dumper": "^3.3|^4.0" // v4.0.3
    }
}
userVoice