Chapters
Loading CSS & JS Assets
Scroll down to the script below, click on any sentence (including terminal blocks) to jump to that spot in the video!
We have an HTML layout, yay! Good for us!
But... it's super boring... and that's just no fun. Besides, I want to talk about assets!
If you download the code for this project, in the start/
directory, you'll find a tutorial/
directory. I've already copied this into my project. It holds some goodies that we need to help get things looking less ugly, and more interesting!
Copying web files
First, copy the 4 directories in web/
... to web/
: this includes some CSS, images, JS and vendor files for Bootstrap and FontAwesome:
web/css
web/images
web/js
web/vendor
These are boring, normal, traditional static files: we're not doing anything fancy with frontend assets in this screencast.
Go Deeper!
One way to get fancy is by using Gulp to process, minify and combine assets. See Gulp! Refreshment for Your Frontend Assets.
Ok, important thing: the web/
directory is the document root. In other words, anything in web/
can be accessed by the public. If I wanted to load up that favicon.ico
, I'd use my hostname /favicon.ico
- like http://localhost:8000/favicon.ico
. If a file is outside of web, then it's not publicly accessible.
Including Static Assets
Ok, more work: go into the app/
directory and copy the new base.html.twig
file. Paste that over the original and open it up:
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>{% block title %}AquaNote!{% endblock %}</title> | |
{% block stylesheets %} | |
<link rel="stylesheet" href="{{ asset('vendor/bootstrap/css/bootstrap.min.css') }}"> | |
<link rel="stylesheet" href="{{ asset('css/styles.css') }}"> | |
<link rel="stylesheet" href="{{ asset('vendor/fontawesome/css/font-awesome.min.css') }}"> | |
{% endblock %} | |
<link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}" /> | |
</head> | |
<body> | |
<div class="search-bar"> | |
<form method="GET" action="" class="js-sea-search sea-search"> | |
<input type="search" name="q" placeholder="Search Sea Creatures" autocomplete="off" class="search-input"> | |
</form> | |
</div> | |
<header class="header"> | |
<img class="logo-icon" src="{{ asset('images/aquanote-logo.png') }}"> | |
<h1 class="logo">AquaNote</h1> | |
<ul class="navi"> | |
<li class="search"><a href="#" class="js-header-search-toggle"><i class="fa fa-search"></i></a></li> | |
<li><a href="#">Login</a></li> | |
</ul> | |
</header> | |
{% block body %}{% endblock %} | |
<div class="footer"> | |
<p class="footer-text">Made with <span class="heart"><3</span> <a href="https://knpuniversity.com">KnpUniversity</a></p> | |
</div> | |
{% block javascripts %} | |
<script src="//code.jquery.com/jquery-2.1.4.min.js"></script> | |
<script src="{{ asset('js/main.js') }}"></script> | |
{% endblock %} | |
</body> | |
</html> |
Hey! We have some real-looking HTML! To bring this to life, we need to include some of those CSS and JS assets that we just put into web/
. And here's the key: Symfony doesn't care about your assets... at all. It's not personal, it keeps things simple. You include CSS and JS files the way you always have: with tried-and-true link
and script
tags. These paths are relative to the web/
directory, because that's the document root.
The stylesheets
and javascripts
blocks
Ok ok, in reality there are two little-itty-bitty Symfony things to show you about assets. First, notice that the link tags live inside a block called stylesheets
:
Show Lines
|
// ... lines 1 - 6 |
{% block stylesheets %} | |
<link rel="stylesheet" href="{{ asset('vendor/bootstrap/css/bootstrap.min.css') }}"> | |
<link rel="stylesheet" href="{{ asset('css/styles.css') }}"> | |
<link rel="stylesheet" href="{{ asset('vendor/fontawesome/css/font-awesome.min.css') }}"> | |
{% endblock %} | |
Show Lines
|
// ... lines 12 - 41 |
Really, technically, that does... nothing! Seriously: you don't have to do this, it will make no difference... for now.
But, in the future, doing this will give you the power to add page-specific CSS by adding more link
tags to the bottom of the stylesheets
block from inside a child template. I'll show you that later. Just know that it's a good practice to put CSS inside of a block, like stylesheets
.
Go Deeper!
How can you add page-specific CSS or JS files? See ReactJS talks to your API.
The same is true for script tags: I've got mine in a block called javascripts
:
Show Lines
|
// ... lines 1 - 34 |
{% block javascripts %} | |
<script src="//code.jquery.com/jquery-2.1.4.min.js"></script> | |
<script src="{{ asset('js/main.js') }}"></script> | |
{% endblock %} | |
Show Lines
|
// ... lines 39 - 41 |
The asset function
You're probably already looking at the second important Symfony thing about assets: the asset()
function. Whenever you refer to a static file, you'll wrap the path in {{ asset() }}
. This does... you guessed it! Nothing! Ok, that's not totally true. But it really doesn't do much, and you'd be just fine if you forgot it and hardcoded the path.
So what does asset()
do? Well, if you eventually deploy and use a CDN, it will save your butt. With just one tiny config change, Symfony can prefix every static URL with your CDN host. So /css/styles.css
becomes http://superfastcdn.com/css/styles.css
. That's pretty awesome, so be good and use asset()
in case you need it. You can also do some cool cache-busting stuff.
Other than the asset stuff, the base layout is just like before: it has a title
block, a body
block in the middle and some javascripts
. We just added the pretty markup.
Updating show.html.twig
Let's finish this! Copy show.html.twig
and overwrite our boring version:
{% extends 'base.html.twig' %} | |
{% block title %}Genus {{ name }}{% endblock %} | |
{% block body %} | |
<h2 class="genus-name">{{ name }}</h2> | |
<div class="sea-creature-container"> | |
<div class="genus-photo"></div> | |
<div class="genus-details"> | |
<dl class="genus-details-list"> | |
<dt>Subfamily:</dt> | |
<dd>Octopodinae</dd> | |
<dt>Known Species:</dt> | |
<dd>289</dd> | |
<dt>Fun Fact:</dt> | |
<dd>Octopuses can change the color of their body in just three-tenths of a second!</dd> | |
</dl> | |
</div> | |
</div> | |
<div class="notes-container"> | |
<h2 class="notes-header">Notes</h2> | |
<div><i class="fa fa-plus plus-btn"></i></div> | |
</div> | |
<section id="cd-timeline"> | |
{% for note in notes %} | |
<div class="cd-timeline-block"> | |
<div class="cd-timeline-img"> | |
<img src="{{ asset('images/leanna.jpeg') }}" class="img-circle" alt="Leanna!"> | |
</div> | |
<div class="cd-timeline-content"> | |
<h2><a href="#">AquaPelham</a></h2> | |
<p>{{ note }}</p> | |
<span class="cd-date">Dec. 10, 2015</span> | |
</div> | |
</div> | |
{% endfor %} | |
</section> | |
{% endblock %} |
And yep, it's also similar to before - I swear I'm not trying to sneak in any magic! It still extends base.html.twig
, prints out the genus name and loops over the notes. Oh, and hey! When I refer to the image - which is a static file - I'm using the asset()
function.
Ok, ready for this? Refresh the page. Boom! So much prettier.
These days, you can do some pretty crazy things with assets via frontend tools like Gulp or PHP tools like Assetic. But you might not need any of these. If you can, keep it simple.
79 Comments
for windows users, from the working directory> php app/console cache:clear
Hey Victor,
You're right, you have to run Symfony console through the `php` interpreter. Thanks for this note!
Cheers!
Shouldn't it be 'php bin/console cache:clear' ?
Hey Tahkuri
It depends on the symfony version you are using, if it's B.C, then the "console" file it's inside "app" folder, but for newer versions (I believe >= 2.8 ) it's inside "bin" folder
Cheers!
Ah, this is an edge-case issue! Because you're physically copying my layout file, Symfony doesn't know that it needs to re-compile Twig (the last modified time on my layout file is OLDER than your original, so it does not look like the file was updated). So, good tip!
Hello, thanks for the great tutorial, I received an error "Variable "notes" does not exist in genus\show.html.twig at line 25." which is : {% for note in notes %}. After changing the code to : " {% for note in 'notes' %} the page loaded but not as expected. The notes are missing.
I work in windows enviorenment, I cleared the caches as "php bin/console cache:clear" and even manualy deleted the files in cache folder...
when i type http://127.0.0.1:8000/app_dev.php/genus/halil/notes : it renders the page as:
{"notes":[{"id":1,"username":"AquaPelham","avatarUri":"\/images\/leanna.jpeg","note":"Octopus asked me a riddle, outsmarted me","date":"Dec. 10, 2015"},{"id":2,"username":"AquaWeaver","avatarUri":"\/images\/ryan.jpeg","note":"I counted 8 legs... as they wrapped around me","date":"Dec. 1, 2015"},{"id":3,"username":"AquaPelham","avatarUri":"\/images\/leanna.jpeg","note":"Inked!","date":"Aug. 20, 2015"}]}
Hey Halil,
Wrapping notes with a quotes is a mistake - in this case Twig thinks about 'notes' as a string, so the correct example is: {% for note in notes %}
. But if you get "Variable notes does not exist..." error in this case - you will need to check the render call in the controller, are you passing the $notes variable there as a second argument, like:
yourAction()
{
return $this->render('your-template.html.twig', [
'notes' => $notes,
// other variables...
]);
}
Please, ensure you're passing the notes variable into the template and let me know if it help, but I don't think it's due to the windows environment.
Cheers!
Hi there,
I have copied the Genus Controller from the downloaded documents which is:
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
class GenusController extends Controller
{
/**
* @Route("/genus/{genusName}")
*/
public function showAction($genusName)
{
return $this->render('genus/show.html.twig', array(
'name' => $genusName,
));
}
/**
* @Route("/genus/{genusName}/notes", name="genus_show_notes")
* @Method("GET")
*/
public function getNotesAction($genusName)
{
$notes = [
['id' => 1, 'username' => 'AquaPelham', 'avatarUri' => '/images/leanna.jpeg', 'note' => 'Octopus asked me a riddle, outsmarted me', 'date' => 'Dec. 10, 2015'],
['id' => 2, 'username' => 'AquaWeaver', 'avatarUri' => '/images/ryan.jpeg', 'note' => 'I counted 8 legs... as they wrapped around me', 'date' => 'Dec. 1, 2015'],
['id' => 3, 'username' => 'AquaPelham', 'avatarUri' => '/images/leanna.jpeg', 'note' => 'Inked!', 'date' => 'Aug. 20, 2015'],
];
$data = [
'notes' => $notes
];
return new JsonResponse($data);
}
}
However I should not be updating this yet :)
which should be:
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
class GenusController extends Controller
{
/**
* @Route("/genus/{genusName}")
*/
public function showAction($genusName)
{
$notes = [
'Octopus asked me a riddle, outsmarted me',
'I counted 8 legs... as they wrapped around me',
'Inked!'
];
return $this->render('genus/show.html.twig', array(
'name' => $genusName,
'notes' => $notes
));
}
}
it works fine now thanks :)
Great! I'm glad it work now. As always, easy mistakes are hard to reveal ;)
Cheers!
wooooow it's a really fantastic tutorial . i whatched alot of symfony tutorials but i sware i nevere found like this one
Hi! Thanks for the great tutorial. You should mention about clearing the cache after copying the files.
Hello, well sorry to necro this, but for some reason the assets wont load at all, there is no octopus picture, no style at all, its still ugly, I tried changing/deleting a few things then putting them back to no avail, I restarted phpstorm and wamp with no luck as well
the page works but its as if the css and the other assets are not there
any ideas?
I use symfony 3.4
Hey George H
If you open the dev tools of your browser, in the network and source tabs, can you tell what you see?
Cheers!
Hey man, sorry my bad. I forgot to tell you that you need to refresh the page after opening the dev tools, so the network tab fills up, and I meant "console tab" not "source" (My bad again. I should not be so confident of my memory =/)
no problem ill just do that in just a bit
Hmm, this is odd. I just downloaded the project and all the asset files are there. Did you override the stylesheets
twig block in the template? If that's not the case, I'm wondering if you are using some sort of add blocker that's blocking the assets in your browser. Try using a different one
Cheers!
I fixed it on my own, thanks anyway :)
Woops, I just got to this comment. The problem was related to what I said here? https://symfonycasts.com/sc...
so for my project copied the modified base.html.twig in the genus folder without touching the default one in app/ressources/views which is why the page stayed ugly since the default one had no css on it whatsoever, so followup question, does a template always have to be in app/ressources/views? or can I put it somewhere else? because when I try putting it somewhere else an error shows up because it always look for it in app/ressources/views, even if I give the child template the full path in the {{ extends }}
Hey George H
Sorry for the late reply. By default Symfony will look inside app/Resources/views
, then iniside src/AppBundle/Resources/views
(and inside any other bundle you may have) and lastly, it will look inside third party bundles. I think you can configure more paths to look up by modifying your Twig config. Here you can read how: https://symfony.com/doc/3.4/reference/configuration/twig.html#paths
BTW, the structure changed considerable in Symfony4+
Cheers!
OK so, I downloaded the whole project and it works fine, but I would like to know why the css and bootstrap does not render in mine, the only difference is that I created a bundle instead of using the default one, and use .yml instead of annotation as routing, could that be the problem?
Hey George H
I just went through the starting steps of this tutorial and "kind of" hit the same problem as you. The thing (in my case) was that I was browsing the default page from the src/AppBundle/Controller/DefaultController.php
. That controller renders the default template app/Resources/default/index.html.twig
and down in the bottom, it overrides the stylesheets
block without calling {{ parent() }}
(which makes sense because it's just a default template that you should get rid off). So, try rendering another template that extends from the base.html.twig
template without overriding its styles
I hope it helps. Cheers!
I did not touch the files, I just copy pasted them like in the tutorial, I de-activated the addblocker and nothing changed :(
Hey Daniel!
That's a loaded question - a lot of people have opinions on that. I personally have an `assets` directory at the root of my project (so not in web) and I use gulp to process and move everything into `web/`. The Symfony Demo actually uses app/Resources/assets, which makes a lot of sense, but it is a bit deeper in your directory structure: https://github.com/symfony/...
Cheers!
Fatal error: Unknown: Failed opening required 'D:\SymfonyProjects\myBlog\vendor\symfony\symfony\src\Symfony\Bundle\WebServerBundle/Resources/router.php' (include_path='.;C:\php\pear') in Unknown on line 0
Hey Hamdi,
Could you provide a bit more information for us to be able to help you? What are you doing when you see this error? Have you installed Composer dependencies with "composer install" command?
Cheers!
hi great works super tutorial !! where can i find your css, vendor, images, js and their content ?
Hey Chris!
Thanks man! If you're a subscriber, you'll see a "Download" link on the upper right of this page. If not, you can also find the start code up on our GitHub page: https://github.com/knpunive...
Cheers!
I can't find tutorial directory, please help me
Hey Luciano! Check out my comment over here about that :) https://knpuniversity.com/s...
After searching a lot, i found it, thanks.
Can i get normal link to css and js files ? Becouse there i can download only some tar.gz file and i can ext it... So i cant move forward now :/ :) Thanks
Yo @Marek!
Here's another link to the tutorial directory that holds the CSS and js files from the code download: https://github.com/knpunive.... You'll find the CSS and JS in the web subdirectory.
Cheers!
I have the same issue, i cleared cache... but all files in web folder are 404 i cheked evrything million times and still got "failed to load resource: the server responded with a status of 404 (Not Found)" I'm using 3.1.1, whats wrong with symfony or problem in me?
Oh no!
Let's see if we can fix this once and for all! If you can help me answer a few questions, I bet we can get to the bottom of it. I have a feeling that it's not your fault - just some small misconfiguration somewhere (and it could be our fault!)
1) What assets are you not seeing exactly? For example, if you go to /genus/octopus - do you see the Octopus image? Do the CSS files load?
2) Supposing the CSS files - like styles.css - are not loading, if you view the HTML source of your page and click the CSS link (which should open it in a new browser tab) - does that work? If not, can you paste the full URL from this new tab? Here's a screenshot of the "link" in the HTML source that I want you to click - this is in Chrome - other browsers may not make this a link (so use Chrome for this!) http://i.imgur.com/EPTp8ar.png
I'm sure we can work out the problem :).
Cheers!
I think the problem was in my vhost I finally found same issue on stack overflow
http://stackoverflow.com/qu...
but after that manipulation i got 404. And I don't understand where coul'd be problem now
P.S. Thanks for your respond and sorry for my English
Hey, Ilya!
If assets loads properly with built-in PHP web server, but don't load on a real web server (Nginx, Apache, etc.) - so most probably you have a problem with your web server configuration. From your stackoverflow link I realized you have a problem with Apache config, right?
Could you check your VirtualHost configuration with the Configuring a Web Server Symfony official docs? There's an example how to configure Apache web server properly for Symfony app.
Cheers!
Victor, Hi
Yes, I think problem in Apache. Maybe permissions...
I tried to change vhosts as written with "Require all granted" and with\without htaaccess
Main problem when DocumentRoot is "project_name/web/app_dev.php" css,js,images is not loaded to the browser
when DocumentRoot is "project_name/web" I'm getting "Not Found
The requested URL /genus/octupus was not found on this server."
I'm using Apache 2.4.20 and PHP 7.0.7 on Windows 7
I tested same vhosts on other project It WORKS!
I'm thinkin about routing 'couse I remembered that when I was typing
/**
* @Route()
*/
in 3.1 First Page Wildcards PHP Storm didn't suggest autocomplete so I copy\paste line
"use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;"
Maybe that's the problem?
Hey Ilya!
Hmm! Were those other projects Symfony projects? What happens if you try going to "http://yourhostname.local/app_dev.php"? If you don't have the proper rewrite rules, then you may be getting a 404 because Apache doesn't know what file to execute if none is in the URL. And if those other projects have an index.php file, then it might be working there because Apache knows to look for that file :).
You were definitely right to add the above "use" statement! But I don't think it's the problem. Your error - "The requested URL /genus/octopus was not found on this server" is coming from your web server - not Symfony. If the @Route isn't configured properly, you would get an error from Symfony. What I mean is, we're not *yet* even hitting the Symfony app - so we can definitely say the problem is not (yet) with our code :).
Cheers!
Hey
1. All progects in Apache24\htdocs
2. It gives an error No route found for "GET /" 404 Not Found - NotFoundHttpException 1 linked Exception: ResourceNotFoundException ».
Error from logs (ERROR - Uncaught PHP Exception Symfony\Component\HttpKernel\Exception\NotFoundHttpException: "No route found for "GET /"" at Apache24\htdocs\aqua_note\var\cache\dev\classes.php line 2357 )
Ah, this is a good sign! This error - No route found for "GET /" - is coming from Symfony, which means that it's hitting our app! And the error makes sense - at this point in the project, we haven't created a "homepage" yet.
So, try going to http://yourhostname.local/app_dev.php/genus/octopus
The key is having the app_dev.php in the URL. Without this, Apache will either (A) not know what to do or (B) will try to execute app.php, which boots symfony in the "prod" environment (we talk about that in the next course). The reason that you don't see me using app_dev.php in the URL during the screencasts is that the built-in web server is pre-configured to know to use app_dev.php.
Let me know if this helps!
Hey, Ryan
I tried http://aqua_note/app_dev.php/genus/octopus and IT WORKS!!! So сan you briefly explain what was the problem? I tried change in htaaccess from app.dev to app_dev.php and it did't help
I am very grateful for your help. I was suffering about 4 days with this issue.
Thanks to all of you!
Hi (one more time)
Problems with app_dev.php keep following me =(
On next lesson when I go to http://aqua_note/app_dev.php/genus/octupus/notes it gives
"No route found for "GET /genus/octupus/notes"
404 Not Found - NotFoundHttpException
1 linked Exception: ResourceNotFoundException »"
Logs (ERROR - Uncaught PHP Exception Symfony\Component\HttpKernel\Exception\NotFoundHttpException: "No route found for "GET /genus/octupus/notes"" at C:\Apache24\htdocs\aqua_note\var\cache\dev\classes.php line 2357 )
When I go http://aqua_note/app.php/genus/octupus/notes It gives
"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. "
When I go http://aqua_note/genus/octupus/notes It gives
"Not Found
The requested URL /genus/octupus/notes was not found on this server."
Sorry for my obsession
I'm sorry the problem was that I didn't install PHP Annotation plugin.
Now It works fine!!!
Many thanks to you!
"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
}
}
dont 4 get to write in console, from working directory ./bin/console cache:clear "... & Boom. Soooo much prettier ..."