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

Loading CSS & JS Assets

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.

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:

... 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 %}
... 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:

... 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 %}
... 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.

Leave a comment!

79
Login or Register to join the conversation
Default user avatar

dont 4 get to write in console, from working directory ./bin/console cache:clear "... & Boom. Soooo much prettier ..."

9 Reply
Default user avatar
Default user avatar Victor Boutte' | Tadas | posted 4 years ago

for windows users, from the working directory> php app/console cache:clear

1 Reply

Hey Victor,

You're right, you have to run Symfony console through the `php` interpreter. Thanks for this note!

Cheers!

Reply
Default user avatar

Shouldn't it be 'php bin/console cache:clear' ?

Reply

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!

Reply

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!

Reply
Default user avatar
Default user avatar Halil Yaman | posted 4 years ago

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.

3 Reply
Default user avatar

I work in windows enviorenment, I cleared the caches as "php bin/console cache:clear" and even manualy deleted the files in cache folder...

3 Reply
Default user avatar

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"}]}

1 Reply

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!

1 Reply
Default user avatar
Default user avatar Halil Yaman | victor | posted 4 years ago

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 :)

56 Reply

Great! I'm glad it work now. As always, easy mistakes are hard to reveal ;)

Cheers!

Reply
Default user avatar
Default user avatar Samar Samy | posted 4 years ago

wooooow it's a really fantastic tutorial . i whatched alot of symfony tutorials but i sware i nevere found like this one

3 Reply
Default user avatar

Hi! Thanks for the great tutorial. You should mention about clearing the cache after copying the files.

1 Reply
Default user avatar
Default user avatar George H | posted 2 years ago

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

Reply

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!

Reply

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 =/)

Reply
Default user avatar

no problem ill just do that in just a bit

Reply

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!

Reply
Default user avatar

I fixed it on my own, thanks anyway :)

Reply

Woops, I just got to this comment. The problem was related to what I said here? https://symfonycasts.com/sc...

Reply
Default user avatar

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 }}

Reply

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...

BTW, the structure changed considerable in Symfony4+

Cheers!

Reply
Default user avatar

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?

Reply

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!

Reply
Default user avatar

I did not touch the files, I just copy pasted them like in the tutorial, I de-activated the addblocker and nothing changed :(

Reply

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!

Reply
Default user avatar
Default user avatar Hamdi Ali | posted 4 years ago

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

Reply

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!

Reply
Default user avatar
Default user avatar Chris Christian | posted 4 years ago

hi great works super tutorial !! where can i find your css, vendor, images, js and their content ?

Reply

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!

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

I can't find tutorial directory, please help me

Reply

Hey Luciano! Check out my comment over here about that :) https://knpuniversity.com/s...

Reply
Default user avatar

After searching a lot, i found it, thanks.

Reply
Default user avatar

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

Reply

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!

Reply
Default user avatar

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?

Reply

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!

Reply
Default user avatar

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

Reply

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!

Reply
Default user avatar

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

Reply
Default user avatar

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?

Reply

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!

Reply
Default user avatar

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 )

Reply

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!

Reply
Default user avatar

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!

Reply
Default user avatar

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

Reply
Default user avatar

I'm sorry the problem was that I didn't install PHP Annotation plugin.
Now It works fine!!!
Many thanks to you!

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
    }
}