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

First Page

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.

Code goes in src/ and app/

You may have noticed that most of the committed files were in app/ and src/. That's on purpose: these are the only two directories you need to worry about. src/ will hold all the PHP classes you create and app/ will hold everything else: mostly configuration and template files. Ignore all the other directories for now and just focus on src/ and app/.

Building the First Page

Remember the functional homepage? It's coming from this DefaultController.php file. Delete that! Do it! Now we have an absolutely empty project. Refresh the homepage!

No route found for "GET /"

Perfect! That's Symfony's way of saying "Yo! There's no page here."

Now back to the main event: building a real page.

Our top secret project is called AquaNote: a research database for Aquanauts. These cool underwater explorers log their discoveries of different sea creatures to this nautical site. Our first page will show details about a specific genus, for example, the octopus genus.

Creating a page in Symfony - or any modern framework - is two steps: a route and a controller. The route is a bit of configuration that says what the URL is. The controller is a function that builds that page.

Namespaces

So, step 1: create a route! Actually, we're going to start with step 2: you'll see why. Create a new class in AppBundle/Controller called GenusController. But wait! The namespace box is empty. That's ok, but PhpStorm can help us out a bit more. Hit escape and then right-click on src and select "mark directory as sources root".

Now re-create GenusController. This time it fills in the namespace for me:

<?php
namespace AppBundle\Controller;
... lines 4 - 7
class GenusController
{
... lines 10 - 16
}

Go Deeper!

If namespaces are new to you, welcome! Take a break and watch our PHP Namespaces Tutorial.

The most important thing is that the namespace must match the directory structure. If it doesn't, Symfony won't be able to find the class. By setting the sources root, PhpStorm is able to guess the namespace. And that saves us precious time.

Controller and Route

Inside, add a public function showAction():

... lines 1 - 7
class GenusController
{
... lines 10 - 12
public function showAction()
{
... line 15
}
}

Hey, this is the controller - the function that will (eventually) build the page - and its name isn't important. To create the route, we'll use annotations: a comment that is parsed as configuration. Start with /** and add @Route. Be sure to let PhpStorm autocomplete that from the FrameworkExtraBundle by hitting tab. This is important: it added a use statement at the top of the class that we need. Finish this by adding "/genus":

... lines 1 - 4
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
... lines 6 - 7
class GenusController
{
/**
* @Route("/genus")
*/
public function showAction()
... lines 14 - 16
}

Beautiful, that's the route and the URL for the page is /genus.

Returning a Response

As I already said: the controller is the function right below this, and its job is to build the page. The only rule for a controller is that it must return a Symfony Response object.

But hold on. Let's just all remember what our only job is as web developers: to understand the incoming request and send back a response, whether that's an HTML response, a JSON response of a PDF file. Symfony is modeled around this idea.

Keep things simple: return new Response. The Response class is the one from the HttpFoundation component. Hit tab to auto-complete it. This adds the use statement on top that we need. For the content, how about: 'Under the Sea!':

... lines 1 - 5
use Symfony\Component\HttpFoundation\Response;
class GenusController
{
/**
* @Route("/genus")
*/
public function showAction()
{
return new Response('Under the sea!');
}
}

That's it!

We've only created one file with one function, but we already have a route, a controller and a lot of sea floor that needs discovering!

If you refresh the homepage, well... that's not going to work. Navigate instead to the URL: /genus. Woh! There's your first page in Symfony, done in about 10 lines of code. Simple enough for you?

Next, let's create a dynamic URL.

Leave a comment!

104
Login or Register to join the conversation
Default user avatar
Default user avatar disqus_L54JZwZGNQ | posted 4 years ago

Using the latest PhpStorm 2016.3.2. Have enabled, installed and reinstalled Symfony and PHP Annotations, restarted, invalidated cache, etc. I still cannot get @route to autocomplete. When I go to Settings>Editor>Inspections I see "Symfony | Route | Symfony: Route settings deprecated" I have googled but nothing. Help! Also this message in PHPAnnotations: Doc block annotation class found, but its missing in use statement You should import the class as use statement

9 Reply

Hey Marlene!

Dang, sorry about that - not having auto-complete takes all the fun out of it! A few questions:

1) When you type @Route, are you using a capital R - it'll only auto-complete if you are!
2) If you ignore the fact that it doesn't auto-complete and manually add the @Route (and manually add the use statement at the top of the class - use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route) - does the page work? I'm trying to make sure something else isn't going wrong.

If you ARE using a capital R and the page works when you setup things correctly... but still don't get auto-complete, you may need to check with the PhpStorm guys - I've never seen this specifically not work!

Good luck!

6 Reply
Default user avatar
Default user avatar disqus_L54JZwZGNQ | weaverryan | posted 4 years ago

I had to go into Terminal and run composer update????????

65 Reply

Hey Marlene,

Not `composer update` but `composer install`. You could check the README file inside downloaded code - there's instructions how to bootstrap project. Let us know if `composer install` doesn't help you.

Cheers!

Reply
Default user avatar

Make sure to also install the PHP Annotations plugin to PHP Storm. I was struggling to make that @Route auto-complete work

4 Reply
Default user avatar
Default user avatar Levan Lomia | ZB | posted 4 years ago

omg man thank you. I was struggling abaout that too. just read your comment accidentally

2 Reply

Yea, thanks for pointing this out! There was at least one other user that also commented (on another chapter) - so I've added a note about this: https://github.com/knpunive.... It was an oversight - you *definitely* want this awesome plugin :).

Thanks!

Reply
Default user avatar
Default user avatar utkarsh srivastava | ZB | posted 4 years ago

Hello

I've added php annotations plugin and restarted phpstorm but it still doesnt prompt the autocomplete. Please help me with this.

Reply
Default user avatar

I had the same problem, I did two things, I believe the latter fixed it.
Uninstalled plugin / restart /reinstalled plugin / restart..
Still didn't work.
File -> Invalidate Caches / Restart
Wait for indices to finish building on restart ( takes a min )
MAGIC.

Reply

+1 It makes sense to clear the cache. Also ensure that the "Power Save Mode" is off: "File" -> uncheck "Power Save Mode".

Reply

Oh no! I have seen in rare cases that it doesn't work for *some* people. I would open a support request with PhpStorm to see if they can help. Make sure you're also using the latest version of PhpStorm - that might not make a difference, but it's worth a try.

Reply
Default user avatar

After deleting DefaultController.php, I got 500 Internal server error.

Here is the message": Oops! An Error Occurred

The server returned a "500 Internal Server Error".

Something is broken. Please let us know what you were doing when this error occurred. We will fix it as soon as possible. Sorry for any inconvenience caused."

I'm using widows 10, PHP version: 5.6.24, Symfony 3 . As PHP server I'm using XAMPP.

What could be the reason for this error?

2 Reply

Hey Mamun!

I think I know the problem :). Whatever the URL is in your browser - add an app_dev.php to it. For example, if you setup XAMPP to have a URL like http://symfony.l, then go to http://symfony.l/app_dev.php. In fact, while developing, you'll always have that app_dev.php in your URL - e.g. http://symfony.l/app_dev.php/products (instead of just /products).

Having the app_dev.php in the URL loads Symfony in the "dev" environment, where you see any errors and your cache is always rebuilt. The reason you don't see this in the video is that we're using the built-in PHP web server through Symfony, which does this (automatically & invisibly) for you. But, if you're using your own web server, that's great - you just need to add that app_dev.php to the URL yourself. When you do that, I think you won't see this error anymore. There's more info about this here: http://knpuniversity.com/sc...

Let me know if that helps!

3 Reply
Default user avatar

I looked at the more info section you referenced. But when I tried adding app_dev.php (i.e. https://sitename.org/app_de..., I get "You are not allowed to access this file. Check app_dev.php for more information."

Help?

Reply

Hey Sean,

Great, actually it means you're on the right way! Now go to the "web/app_dev.php" file and temporarily comment out the security check https://github.com/symfony/... . Then refresh the page and you'll have access to get this page. Doing so isn't secure, but it's OK for your local host. But probably you need to invent more clever solution for your case, for example try to "dump($_SERVER['REMOTE_ADDR'])" to see what remote IP you you have and allow access from that IP.

Cheers!

1 Reply
Default user avatar
Default user avatar nickmarinho | posted 4 years ago

I'm trying this with symfony 3.1.2 but this chapter does not work.....the router not work =(

1 Reply

Hey Luciano!

Everything should still work just fine in 3.1.2 - what problems are you having? Do you have a "Route Not Found" error? What URL are you going to?

Cheers!

1 Reply
Default user avatar

Sorry, the error was mine. I'm new in this symfony world. Learning well with this tutorial. Thanks a lot;

Reply
Thomas W. Avatar
Thomas W. Avatar Thomas W. | posted 2 years ago

What made "GenusController" a Controller? Symfony docs say that the controller is actually the method contained within the class... fine. But still, what designates that particular file as a Controller. Later, you'll talk about Services and getting services from a controller and outside of a controller. That's what got me thinking... so.... what defines that particular file as a controller.

Is it because it lives in a folder called \Controller?

This question relates to both Drupal 8 and Symfony

Reply

Hey Thomas W.!

Just for others - I replied to your question (which is a good one!) over on a different thread: https://symfonycasts.com/sc...

I hope it helps :).

Cheers!

Reply
Thomas W. Avatar
Thomas W. Avatar Thomas W. | posted 2 years ago

As always, your explanations and examples are easy to follow. Question about "Controllers". I've "googled" this and can not find a definitive answer.
You showed how to create a "Controller". And Drupal Documentation does as well
What is not explicitly said is What makes a controller a controller?

It is just a class.

Is it by virtue of living inside of my_module\src\Controller ?

I assume the answer to this is the same for "Symfony"?

Reply

Hey Thomas W.!

Excellent question! It really is :).

> What is not explicitly said is What makes a controller a controller

A controller is an "invented" word... as I like to joke - a word invented to make sure everyone gets confused ;). A "controller" is a word that frameworks give to "a function that builds a page". In a "small" framework like Symfony, the system is very simple:

A) Users goes to /foo
B) Symfony finds the route that matches /foo
C) Symfony calls the function that the /foo route points to (the "controller")
D) That function builds the page (e.g. HTML, JSON)
E) Symfony sends that back to the user

From an object-oriented perspective, a controller is not a special function in any ways: it looks like any other function. But we *intend* for it to be used in a special way: we *intend* to point a route to it and for it to "build a page". We call that "type" of function a controller in "framework land".

2 more things:

1) To make things more confusing, you will often hear the word "controller" used to describe both the actual function/method AND also the class that the controller method lives inside. Yep, you have a "controller class" and a "controller method" - it's just one of those things where the word "controller" can be applied to 2 different ideas... which adds to the confusion.

2) There is/was a *tiny* framework called Silex. An *entire* application could live in a file and looked like this:


$app = new Silex\Application();

$app->get('/hello/{name}', function($name) use($app) {
return 'Hello '.$app->escape($name);
});

$app->run();

I'm just showing this as another example of a controller. The "controller" in this case is the anonymous function callback. It's a function that builds the page - in this case "Hello $name".

Let me know if this helps!

Cheers!

Reply

hi i just wanna ask instead of getting all the files i only see .gitignore in every folder

Reply

Yo Brain!

If you're starting a new project today, my guess is that you've started a Symfony 4 project. In that case, check out our tutorial on Symfony 4 specifically - https://symfonycasts.com/sc... - Symfony 4 indeed starts much smaller, with less files (like you're describing).

Cheers!

Reply
Default user avatar
Default user avatar samuel fuentes | posted 3 years ago

What is the code editor..?

Reply

Hey Samuel,

We use PhpStorm in our screencasts - it's the best. We also have a free course about it here, check it out: https://symfonycasts.com/sc...

Cheers!

Reply
Default user avatar
Default user avatar Aika Sat | posted 4 years ago

hello! I'm using xamp server and still getting an error 404 (not the beautiful symphony one) I've also tried adding app_dev.php

Reply

Hey Aika,

Probably you're not in dev mode. What URL do you have in the address bar? Keep in mind, you need to point document root of your Xamp server to the web/ directory, otherwise you'll need to specify it in the URL like: http://localhost/web/app_dev.php

Cheers!

Reply
Default user avatar
Default user avatar Support | posted 4 years ago

I use Netbeans and it adds this:
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Route;

Could you please tell me what is the difference between your Sensio path and the symfony one please? Thanks

Reply

Hey Support

Each belongs to a different components, The Sensio one, allows you to define and use Symfony routes via annotations, so it's like a bridge

Cheers!

1 Reply
Default user avatar

Thank you Diego for explaining this, I wish it was said in the video as I really scratched my head on this one.

1 Reply

You are very welcome Ben :)

Reply
Default user avatar
Default user avatar Emilien Ramos | posted 4 years ago

Please what's your IDE ?

Reply

PHPstorm! With the Darcula theme :). We have some other PHPStorm goodies over here: http://knpuniversity.com/sc...

Cheers!

2 Reply
Default user avatar
Default user avatar Emilien Ramos | weaverryan | posted 4 years ago

Thanks ! :)

Reply
Default user avatar
Default user avatar Hike Nalbandyan | posted 4 years ago

Hi .
I followed this and did exactly as it says in the video (symfony 3.4.2)

I managed to get it running at sym3.tst/ , but when i go to stm3.tst/genus/ I'm getting this:
"Oops! An Error Occurred
The server returned a "404 Not Found".
Something is broken. Please let us know what you were doing when this error occurred. We will fix it as soon as possible. Sorry for any inconvenience caused".

I thought that the route is not created' but when i do 'php bin/console debug:router' I get this:
genusController ANY ANY ANY /genus

Have no idea what's wrong.. HELP pls

Reply
Default user avatar

Well I managed to access that route by adding the app_dev.php to my url.
Why did this fix it for me? will it be like this in prod? that is weird

Reply

Hey Hike,

See my answer above: https://knpuniversity.com/s... - I bet you just have a trailing slash problem... or prod cache problem ;)

Cheers!

Reply
Default user avatar
Default user avatar Hike Nalbandyan | victor | posted 4 years ago

thanks for the reply.
actually, stm3.tst/genus/ => stm3.tst/app.php/genus (NOT working)
stm3.tst/app_dev/genus (Works just fine)..
still, have no idea why. I'll be glad if you could explain it

Reply

Hey Hike,

Symfony cache is not rebuilt in prod mode for performance and security reasons, that means you need manually clear it with:
bin/console cache:clear --env=prod

But in dev mode, when you perform some changes, cache is automatically rebuilt for convenience

Try to clear the prod cache with the command I mentioned and try to reach that URL again: http://stm3.tst/app.php/genus

Also, do you use PHP build in web server (bin/console server:run) or do you configure a real web server like Nginx or Apache? :)

Cheers!

1 Reply
Default user avatar
Default user avatar Hike Nalbandyan | victor | posted 4 years ago

Yeah, clearing the cache did the job for me tnx a lot.
Im using Nginx+Apache

Reply

Great! Then that was the answer, you need always to clear the cache for any change in configuration, routes, translations, tempaltes, etc. The exception is only for PHP code, but that's not always the rule. So always clear the cache if you want to check things in prod, especially if you have some weird errors like this, which works for dev mode.

Also, if you use a real web server like Nginx or Apache - it may depend on your Nginx / Apache configuration, but looks like this's not related to the problem in your case.

Cheers!

1 Reply
Default user avatar
Default user avatar Hike Nalbandyan | victor | posted 4 years ago

Tnx Victor.

I do have this other small problem.

the {{ dump() }} function works great in dev but in prod I get "500 Internal Server Error", and yes, I cleared the cache :D. I wonder why...

I don't have to fix this but I'm trying to figure out symfony, so I'd appreciate the help.

Reply

Hey Hike,

Glad you got it working. Keep in mind you always can look over logs in production to see what's wrong with the next command:
tail -f var/logs/prod.log

Cheers!

1 Reply
Default user avatar

actually nvm. I found the reason.
THANKS A LOT you're very helpful

Reply

Hey Hike,

Good debugging with "bin/console debug:router"! Actually, looks like you had found an answer but didn't notice it :) As it's said:
genusController ANY ANY ANY /genus but I see you're trying to reach http://stm3.tst/genus/ URI, i.e. you add an extra slash "/" in the end, try to request "http://stm3.tst/genus" instead. Remember, that "/genus/" and "/genus" are 2 different URLs for Symfony Router ;)

P.S. Here's a nice trick how to avoid errors with trailing slash: http://symfony.com/doc/curr...

P.P.S. Also don't forget to clear the prod cache when you add or edit routes.

Cheers!

Reply
Default user avatar
Default user avatar Josh Hardman | posted 4 years ago

What is actually calling showAction()?

Reply

Hi Josh!

It's actually the internal a of Symfony that call showAction. Here's what happens:

1) the user loads the page - Symfony boots up

2) Synfony reads the routing info (the @Route annotation) to determine which route matches the current URL

3) Symfony calls the method associated with the matched route (called the controller) so that we can build the page.

Does that help? It's a good question - understanding this flow is a crucial step!

Cheers!

Reply
Default user avatar

Hey weaverryan,

Thanks for your quick response! Your explanation does help. In my experience the controller has referred to the entire class, which has various methods that handles business logic, along with a display function that passes variables on to the template. In Symfony, is the controller simply the single method associated with the matched route which returns a response object? I'm sure this will all make better sense as I keep working through the videos!

Reply

Hi Josh!

Yea, unfortunately we use "controller" interchangeably to refer to *both* the class, and the individual methods in the class (sometimes the individual methods are sometimes called "actions"). When I say "controller", I am typically referring to the single method that returns the Response object. When I'm talking about the class, I'll usually say "controller class". I can totally see how that can cross some wires :)

Cheers!

Reply
Cat in space

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

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.1.*", // v3.1.4
        "doctrine/orm": "^2.5", // v2.7.2
        "doctrine/doctrine-bundle": "^1.6", // 1.6.4
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
        "symfony/swiftmailer-bundle": "^2.3", // v2.3.11
        "symfony/monolog-bundle": "^2.8", // 2.11.1
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "composer/package-versions-deprecated": "^1.11" // 1.11.99
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.0.7
        "symfony/phpunit-bridge": "^3.0" // v3.1.3
    }
}