Chapters
First Page
Scroll down to the script below, click on any sentence (including terminal blocks) to jump to that spot in the video!
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; | |
Show Lines
|
// ... lines 4 - 7 |
class GenusController | |
{ | |
Show Lines
|
// ... 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()
:
Show Lines
|
// ... lines 1 - 7 |
class GenusController | |
{ | |
Show Lines
|
// ... lines 10 - 12 |
public function showAction() | |
{ | |
Show Lines
|
// ... 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"
:
Show Lines
|
// ... lines 1 - 4 |
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | |
Show Lines
|
// ... lines 6 - 7 |
class GenusController | |
{ | |
/** | |
* @Route("/genus") | |
*/ | |
public function showAction() | |
Show Lines
|
// ... 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!'
:
Show Lines
|
// ... 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.
104 Comments
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!
I had to go into Terminal and run composer update????????
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!
Make sure to also install the PHP Annotations plugin to PHP Storm. I was struggling to make that @Route auto-complete work
omg man thank you. I was struggling abaout that too. just read your comment accidentally
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!
Hello
I've added php annotations plugin and restarted phpstorm but it still doesnt prompt the autocomplete. Please help me with this.
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.
+1 It makes sense to clear the cache. Also ensure that the "Power Save Mode" is off: "File" -> uncheck "Power Save Mode".
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.
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?
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!
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?
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!
I'm trying this with symfony 3.1.2 but this chapter does not work.....the router not work =(
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!
Sorry, the error was mine. I'm new in this symfony world. Learning well with this tutorial. Thanks a lot;
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
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!
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"?
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!
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!
What is the code editor..?
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!
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
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!
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
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!
Thank you Diego for explaining this, I wish it was said in the video as I really scratched my head on this one.
You are very welcome Ben :)
Please what's your IDE ?
PHPstorm! With the Darcula theme :). We have some other PHPStorm goodies over here: http://knpuniversity.com/sc...
Cheers!
Thanks ! :)
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
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
Hey Hike,
See my answer above: https://knpuniversity.com/s... - I bet you just have a trailing slash problem... or prod cache problem ;)
Cheers!
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
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!
Yeah, clearing the cache did the job for me tnx a lot.
Im using Nginx+Apache
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!
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.
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!
actually nvm. I found the reason.
THANKS A LOT you're very helpful
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!
What is actually calling showAction()?
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!
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!
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!
"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
}
}
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