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!

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
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.0.7
        "symfony/phpunit-bridge": "^3.0" // v3.1.3
    }
}