gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
We're doing really well, but yikes! Our site is ugly. Time to fix that.
If you download the course code from this page, after you unzip it, you'll find a start/
directory with a tutorial/
directory inside: the same tutorial/
directory you see here. We're going to copy a few files from it over the next few minutes.
The first is base.html.twig
. I'll open it up, copy its contents, close it, and then open our templates/base.html.twig
. Paste the new stuff here.
<html> | |
<head> | |
<meta charset="UTF-8"> | |
<title>{% block title %}Welcome!{% endblock %}</title> | |
{% block stylesheets %} | |
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous"> | |
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Spartan&display=swap"> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.1/css/all.min.css" integrity="sha256-mmgLkCYLUQbXn0B1SRqzHar6dCnv9oZFPEC1g1cwlkk=" crossorigin="anonymous" /> | |
<link rel="stylesheet" href="/css/app.css"> | |
{% endblock %} | |
</head> | |
<body> | |
<nav class="navbar navbar-light bg-light" style="height: 100px;"> | |
<a class="navbar-brand" href="#"> | |
<i style="color: #444; font-size: 2rem;" class="pb-1 fad fa-cauldron"></i> | |
<p class="pl-2 d-inline font-weight-bold" style="color: #444;">Cauldron Overflow</p> | |
</a> | |
<button class="btn btn-dark">Sign up</button> | |
</nav> | |
{% block body %}{% endblock %} | |
<footer class="mt-5 p-3 text-center"> | |
Made with <i style="color: red;" class="fa fa-heart"></i> by the guys and gals at <a style="color: #444; text-decoration: underline;" href="https://symfonycasts.com">SymfonyCasts</a> | |
</footer> | |
{% block javascripts %}{% endblock %} | |
</body> | |
</html> |
This was not a huge change: this added some CSS files - including Bootstrap - and some basic HTML markup. But we have the same blocks as before: {% block body %}
in the middle, {% block javascripts %}
, {% block title %}
, etc.
Notice that the link tags are inside a block called stylesheets
. But that's not important yet. I'll explain why it's done that way a bit later.
<html> | |
<head> | |
... lines 4 - 5 | |
{% block stylesheets %} | |
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous"> | |
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Spartan&display=swap"> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.1/css/all.min.css" integrity="sha256-mmgLkCYLUQbXn0B1SRqzHar6dCnv9oZFPEC1g1cwlkk=" crossorigin="anonymous" /> | |
<link rel="stylesheet" href="/css/app.css"> | |
{% endblock %} | |
</head> | |
... lines 13 - 27 | |
</html> |
One of the link tags is pointing to /css/app.css
. That's another file that lives in this tutorial/
directory. In fact, select the images/
directory and app.css
and copy both. Now, select the public/
folder and paste. Add another css/
directory and move app.css
inside.
Remember: the public/
directory is our document root. So if you need a file to be accessible by a user's browser, it needs to live here. The path /css/app.css
will load this public/css/app.css
file.
Let's see what this looks like! Spin over to your browser and refresh. Much better. The middle still looks terrible... but that's because we haven't added any markup to the template for this page.
So let me ask a question... and answer it: what features does Symfony offer when it comes to CSS and JavaScript? The answer is... none... or a lot!
Symfony has two different levels of integration with CSS and JavaScript. Right now, we're using the basic level. Really, right now, Symfony isn't doing anything for us: we created a CSS file, then added a very traditional link tag to it in HTML. Symfony is doing nothing: it's all up to you.
The other, bigger level of integration is to use something called Webpack Encore: a fantastic library that handles minification, Sass support, React or Vue.js support and many other things. I'll give you a crash course into Webpack Encore at the end of this tutorial.
But right now, we're going to keep it simple: you create CSS or JavaScript files, put them in the public/
directory, and then create link
or script
tags that point to them.
Well, actually, even with this, "basic" integration, there is one small Symfony feature you should use.
Before I show you, go into your PhpStorm preference... and search again for "Symfony" to find the Symfony plugin. See this web directory option? Change that to public/
- this was called web/
in older versions of Symfony. This will give us better auto-completion soon. Hit "Ok".
Here's the deal: whenever you reference a static file on your site - like a CSS file, JavaScript file or image, instead of just putting /css/app.css
, you should use a Twig function called asset()
. So, {{ asset() }}
and then the same path as before, but without the opening /
: css/app.css
.
... line 1 | |
<html> | |
<head> | |
... lines 4 - 5 | |
{% block stylesheets %} | |
... lines 7 - 9 | |
<link rel="stylesheet" href="{{ asset('css/app.css') }}"> | |
{% endblock %} | |
</head> | |
... lines 13 - 27 | |
</html> |
What does this super-cool-looking asset()
function do? Almost... nothing. In fact, this will output the exact same path as before: /css/app.css
.
So why are we bothering to use a function that does nothing? Well, it does do two things... which you may or may not care about. First, if you decide to deploy your app to a subdirectory of a domain - like ILikeMagic.com/cauldron_overflow
, the asset()
function will automatically prefix all the paths with /cauldron_overflow
. Super great... if you care.
The second thing it does is more useful: if you decide to deploy your assets to a CDN, by adding one line to one config file, suddenly, Symfony will prefix every path with the URL to your CDN.
So... it's really not that important, but if you use asset()
everywhere, you'll be happy later when you need it.
But... if we move over and refresh... surprise! It explodes!
Did you forget to run
composer require symfony/asset
? Unknown functionasset
.
How cool is that? Remember, Symfony starts small: you install things when you need them. In this case, we're trying to use a feature that's not installed... so Symfony gives us the exact command we need to run. Copy it, move over and go:
composer require symfony/asset
When this finishes... move back over and... it works. If you look at the HTML source and search for app.css
... yep! It's printing the same path as before.
Let's make the middle of our page look a bit nicer. Back in the tutorial/
directory, open show.html.twig
, copy its contents, close it, then open up our version: templates/question/show.html.twig
. Paste the new code.
{% extends 'base.html.twig' %} | |
{% block title %}Question: {{ question }}{% endblock %} | |
{% block body %} | |
<div class="container"> | |
<div class="row"> | |
<div class="col-12"> | |
<h2 class="my-4">Question</h2> | |
<div style="box-shadow: 2px 3px 9px 4px rgba(0,0,0,0.04);"> | |
<div class="q-container-show p-4"> | |
<div class="row"> | |
<div class="col-2 text-center"> | |
<img src="/images/tisha.png" width="100" height="100"> | |
</div> | |
<div class="col"> | |
<h1 class="q-title-show">{{ question }}</h1> | |
<div class="q-display p-3"> | |
<i class="fa fa-quote-left mr-3"></i> | |
<p class="d-inline">I've been turned into a cat, any thoughts on how to turn back? While I'm adorable, I don't really care for cat food.</p> | |
<p class="pt-4"><strong>--Tisha</strong></p> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="d-flex justify-content-between my-4"> | |
<h2 class="">Answers <span style="font-size:1.2rem;">({{ answers|length }})</span></h2> | |
<button class="btn btn-sm btn-secondary">Submit an Answer</button> | |
</div> | |
<ul class="list-unstyled"> | |
{% for answer in answers %} | |
<li class="mb-4"> | |
<div class="d-flex justify-content-center"> | |
<div class="mr-2 pt-2"> | |
<img src="/images/tisha.png" width="50" height="50"> | |
</div> | |
<div class="mr-3 pt-2"> | |
{{ answer }} | |
<p>-- Mallory</p> | |
</div> | |
<div class="vote-arrows flex-fill pt-2" style="min-width: 90px;"> | |
<a class="vote-up" href="#"><i class="far fa-arrow-alt-circle-up"></i></a> | |
<a class="vote-down" href="#"><i class="far fa-arrow-alt-circle-down"></i></a> | |
<span>+ 6</span> | |
</div> | |
</div> | |
</li> | |
{% endfor %} | |
</ul> | |
</div> | |
{% endblock %} |
Once again, there's nothing important happening here: we're still overriding the same title
and body
blocks. We're still using the same question
variable and we're still looping over the answers
down here. There's just a lot of extra markup... which... ya know... makes things pretty.
When we refresh... see! Pretty! Back in the template, notice that this page has a few img
tags... but these are not using the asset()
function. Let's fix that. I'll use a shortcut! I can just type "tisha", hit tab and... boom! It takes care of the rest. Search for img
... and replace this one too with "tisha". Wondering who tisha is? Oh, just one of the several cats we keep on staff here at SymfonyCasts. This one manages Vladimir.
... lines 1 - 4 | |
{% block body %} | |
<div class="container"> | |
<div class="row"> | |
<div class="col-12"> | |
... line 9 | |
<div style="box-shadow: 2px 3px 9px 4px rgba(0,0,0,0.04);"> | |
<div class="q-container-show p-4"> | |
<div class="row"> | |
<div class="col-2 text-center"> | |
<img src="{{ asset('images/tisha.png') }}" width="100" height="100"> | |
</div> | |
... lines 16 - 23 | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
... lines 29 - 36 | |
<ul class="list-unstyled"> | |
{% for answer in answers %} | |
<li class="mb-4"> | |
<div class="d-flex justify-content-center"> | |
<div class="mr-2 pt-2"> | |
<img src="{{ asset('images/tisha.png') }}" width="50" height="50"> | |
</div> | |
... lines 44 - 52 | |
</div> | |
</li> | |
{% endfor %} | |
</ul> | |
</div> | |
{% endblock %} |
By the way, in a real app, instead of these images being static files in our project, that might be files that users upload. Don't worry: we have an entire tutorial on handling file uploads.
Make sure this works and... it does.
The last page that we haven't styled is the homepage... which right now... prints some text. Open its controller: src/Controller/QuestionController.php
. Yep! It's just return new Response()
and text. We can do better. Replace this with return $this->render()
. Let's call the template question/homepage.html.twig
. And... right now... I don't think we need to pass any variables into the template... so I'll leave the second argument off.
... lines 1 - 8 | |
class QuestionController extends AbstractController | |
{ | |
... lines 11 - 13 | |
public function homepage() | |
{ | |
return $this->render('question/homepage.html.twig'); | |
} | |
... lines 18 - 34 | |
} |
Inside templates/question/
, create the new file: homepage.html.twig
.
Most templates start the exact same way. Yay consistency! On top, {% extends 'base.html.twig' %}
, {% block body %}
and {% endblock %}
. In between, add some markup so we can see if this is working.
{% extends 'base.html.twig' %} | |
{% block body %} | |
<h1>Voilà</h1> | |
{% endblock %} |
Ok... refresh the page and... excellent! Except for the "this looks totally awful" part.
Let's steal some code from the tutorial/
directory one last time. Open homepage.html.twig
. This is just a bunch of hardcoded markup to make things look nicer. Copy it, close that file... and then paste it over our homepage.html.twig
code.
{% extends 'base.html.twig' %} | |
{% block body %} | |
<div class="jumbotron-img jumbotron jumbotron-fluid"> | |
<div class="container"> | |
<h1 class="display-4">Your Questions Answered</h1> | |
<p class="lead">And even answers for those questions you didn't think to ask!</p> | |
</div> | |
</div> | |
<div class="container"> | |
<div class="row mb-3"> | |
<div class="col"> | |
<button class="btn btn-question">Ask a Question</button> | |
</div> | |
</div> | |
<div class="row"> | |
<div class="col-12"> | |
<div style="box-shadow: 2px 3px 9px 4px rgba(0,0,0,0.04);"> | |
<div class="q-container p-4"> | |
<div class="row"> | |
<div class="col-2 text-center"> | |
<img src="{{ asset('images/tisha.png') }}" width="100" height="100"> | |
<div class="d-block mt-3 vote-arrows"> | |
<a class="vote-up" href="#"><i class="far fa-arrow-alt-circle-up"></i></a> | |
<a class="vote-down" href="#"><i class="far fa-arrow-alt-circle-down"></i></a> | |
</div> | |
</div> | |
<div class="col"> | |
<a class="q-title" href="#"><h2>Reversing a Spell</h2></a> | |
<div class="q-display p-3"> | |
<i class="fa fa-quote-left mr-3"></i> | |
<p class="d-inline">I've been turned into a cat, any thoughts on how to turn back? While I'm adorable, I don't really care for cat food.</p> | |
<p class="pt-4"><strong>--Tisha</strong></p> | |
</div> | |
</div> | |
</div> | |
</div> | |
<a class="answer-link" href="#" style="color: #fff;"> | |
<p class="q-display-response text-center p-3"> | |
<i class="fa fa-magic magic-wand"></i> 6 answers | |
</p> | |
</a> | |
</div> | |
</div> | |
<div class="col-12 mt-3"> | |
<div class="q-container p-4"> | |
<div class="row"> | |
<div class="col-2 text-center"> | |
<img src="{{ asset('images/magic-photo.png') }}" width="100" height="100"> | |
<div class="d-block mt-3 vote-arrows"> | |
<a class="vote-up" href="#"><i class="far fa-arrow-alt-circle-up"></i></a> | |
<a class="vote-down" href="#"><i class="far fa-arrow-alt-circle-down"></i></a> | |
</div> | |
</div> | |
<div class="col"> | |
<a class="q-title" href="#"><h2>Pausing a Spell</h2></a> | |
<div class="q-display p-3"> | |
<i class="fa fa-quote-left mr-3"></i> | |
<p class="d-inline">I mastered the floating card, but now how do I get it back to the ground?</p> | |
<p class="pt-4"><strong>--Jerry</strong></p> | |
</div> | |
</div> | |
</div> | |
</div> | |
<a class="answer-link" href="#" style="color: #fff;"> | |
<p class="q-display-response text-center p-3"> | |
<i class="fa fa-magic magic-wand"></i> 15 answers | |
</p> | |
</a> | |
</div> | |
</div> | |
</div> | |
{% endblock %} | |
And now... it looks much better.
So that's the basic CSS and JavaScript integration inside of Symfony: you manage it yourself. Sure, you should use this asset()
function, but it's not doing anything too impressive.
If you want more, you're in luck! In the last chapter, we'll take our assets up to the next level. You're going to love it.
Next: our site now has some links on it! And they all go nowhere! Let's learn how to generate URLs to routes.
I don't know if I've missed something, but I had to add in the public directory a build folder including manifest.json with a path to the css files for the asset to work.
Hey Matěj V.!
Hmmm, that's interesting! That should not be required. The manifest.json file that you're referring to comes if you use Webpack Encore in Symfony, which is a great library, but one that we're not using at this moment in the tutorial (we do add it later on). When you use Encore, it creates a public/build/manifest.json file (among other files) that contains a map of "source filename" => "final filename" for all the things it builds.
But out of the box, Symfony doesn't look for this file. It only starts looking for it once you have Encore installed. More accurately, when you install Encore, its recipe adds a configuration file that activates this: https://github.com/symfony/recipes/blob/master/symfony/webpack-encore-bundle/1.9/config/packages/assets.yaml
The tl;dr is this: I think that somehow you have this json_manifest_path
config already in your app... which normally comes when you install Encore. If you DO have this, but don't have Encore installed, then delete it. If you have this because you DID install Encore, then you can use Encore to build your assets, including this manifest.json file :).
Cheers!
Thank you for answer and mainly for this great course, It's one of the clearest guides on Symfony or anything programming wise that I've seen so far, while along being really positive! :)
And yeah I think I've copied the libraries that are used in this tutorial in the versions tab right away on start of the course, so it just got confused that the file wasn't present.
Hi, I have set up everything as the tutorial says. But somehow when I paste in the new 'show.html.twig' code and refresh, the page loads and loads until it crashes and says 'unable to fetch the response from the backend: unexpected EOF'.
In Terminal it says that it matched the route, but than it gives the ERROR from above and it says: maximum execution time of 30 seconds exceeded in /vendor/twig/twig/src/Extension/ProfilerExtension.php on line 33.
What to do?
EDIT: I found the problem already. I accidentally pasted the show.html.twig in the base.html.twig file.... dumb! :P
Hey Paulus D.
No worries, it happens. As a tip, when weird things happens, sometimes clearing the cache or restarting the web server helps.
Cheers!
Hi, i tried to to put in public my css called style.css but idk it doesn't work. If i rename the file app.css it works but i cannot rename all file that i need. How can i solve this? thanks
Hey Alessandro,
OK, as far as I understand you moved style.css file to public/ folder, right? Then, in the template you should be able to link it as:
<link rel="stylesheet" href="/style.css">
But if you moved that css file to a subfolder e.g. public/css/ - then you would need to reflect that subdirectory in the path:
<link rel="stylesheet" href="/css/style.css">
Also, make sure you haven't started the project from a subfolder, otherwise it still may fail loading. The asset() Twig function that we will show in this video would help with this:
<link rel="stylesheet" href="{{ asset('style.css') }}">
But make sure to write the correct path there that's related to the public/ folder.
I hope this helps!
Cheers!
I havent the autocompletion for the "asset" term. Can someone help me. Ho do i get it because i already installed the twig plugin for phpstorm
Hey Duncan W.
A few Twig functions just don't work but in this case it should. Have you the Symfony Support plugin installed and enabled too?
Hi there,
I have a question about the asset
-function - cite: <blockquote>So why are we bothering to use a function that does nothing? Well, it
does do two things... which you may or may not care about. First, if you decide to deploy your app to a subdirectory of a domain - like ILikeMagic.com/cauldron_overflow, the asset() function will automatically prefix all the paths with /cauldron_overflow. Super great... if you care</blockquote>".
My hosting service wants me to deploy to a subdirectory of document root. Apache finds index.php
because of a redirect statement in .htaccess
. How do I tell the asset
-function to prefix the paths with 'public'?
Hey Georg,
Do you use the Symfony's official .htaccess? You can get it by installing "symfony/apache-pack", it has a recipe that will bring this .htaccess into your project: https://github.com/symfony/recipes-contrib/blob/master/symfony/apache-pack/1.0/public/.htaccess
I suppose this .htaccess should help. If not, probably take a look at the redirect statement you have and try to create a similar for assets. Unfortunately, I can't help with it.
But in case you want to prefix all your assets with "public/" - you can do it via:
framework:
assets:
base_path: 'public'
You can make it for a specific env, e.g. do it only for prod - then add it in "config/packages/prod/asset.yaml" only. Or you can base it on env var, then you can set it to empty string locally and to "public/" on production.
Cheers!
Hey Victor,
Yes I use .htaccess from "symfony/apache-pack". I'll try to adjust it for '/css, /images' etc. It's good to know that there is a quite elegant fallback by editing the config file(s).
Thanks a lot!
Hey Georg,
In case you didn't add any custom redirects to your .htaccess - I'd suppose it just should work out of the box. If you did - then yeah, you have to adjust it for your assets as well. But yeah, as you said, you have a fallback option thanks to assets() Twig function ;)
Good luck with adjusting .htaccess! :)
Cheers!
It MAY have snuck in there after a few chapters were already recorded 😀- you have a good eye for detail! It's definitely in the code download, though ;).
Hi! Can you please tell how did you get that à in voilà? Was it some shortcut in PHPStorm or it's just from Mac?
Hey Alexey,
Yes, this is the recent upgrade of Mac OS I suppose. If you press and hold the keyboard letter button for a second or two, e.g. "a" - it will show additional chars for that letter and you can choose from the list. I suppose it might be available on other OS, but I'm not sure how to do it, better google for it if you really need this feature.
Cheers!
I have a problem with this asset autocomplete option. I use PHPStorm and have Twig Plugin enabled. But if I type asset into the "" I only have some angular functions but not from Twig. I must write {{}} and if i type asset into this brackets i've got a autocomplete option. Do you know maybe what is the problem? I don't have any of this autocomplete functions like you shown (asset, asseticjs or asseticcss). Thanks for help.
Hey Łukasz,
Do you have Symfony Plugin installed and enabled in your PhpStorm? I suppose that autocompletion for "asset()" Twig function works thanks to that plugin. You need to install it first, then go to its configuration and enable it. Also, check that paths are correct. For example, the path to the web directory should be "public/". Then, after you enabled it and made necessary config tweaks inside - restart your PhpStorm just in case and try again.
I hope this helps!
Cheers!
Ok I know what is wrong. If you want to use that shortcuts you must install Live Templates from this link: https://github.com/knpunive.... If you do everything like it's said there, you should have this autocompletion asset thing. So you have it installed on your version of PHPStorm but didn't said about it :P
Hey Łukasz,
Oh, now I see you were talking about live templates, it's not actually an autocompletion :) Anyway, glad you figured out how to get it working yourself, good job!
Cheers!
I did everything like you said (install and enable Symfony plugin, configure it to public directory and restart PHPStorm), but nothing happens. This autocomplete only works after I write {{}} and inside it type asset. But if I wrote asset only inside "" it does nothing.
Do you have another autocomplete plugin (like Tabnine ?)
After remove it, I was able to autocomplete {{ asset("image/tisha.png" }} with just "tisha" and then hit tab.
Hey Tesdy14,
Thanks for this tip! Yeah, other plugins really may cause incompatibility problems sometimes.
Cheers!
Hey Łukasz,
Ah, yes, sure, it will only work in curly braces {{}} - this is how PhpStorm knows when you actually call that asset() function. Otherwise, it won't autocomplete because, well, the syntax is incorrect.
Cheers!
Hey @Dean
The CSS file comes with the course code inside the tutorial
directory. You just need to download it from this page, at the right top corner it's the button :)
Cheers!
// composer.json
{
"require": {
"php": "^7.3.0 || ^8.0.0",
"ext-ctype": "*",
"ext-iconv": "*",
"easycorp/easy-log-handler": "^1.0.7", // v1.0.9
"sensio/framework-extra-bundle": "^6.0", // v6.2.1
"symfony/asset": "5.0.*", // v5.0.11
"symfony/console": "5.0.*", // v5.0.11
"symfony/debug-bundle": "5.0.*", // v5.0.11
"symfony/dotenv": "5.0.*", // v5.0.11
"symfony/flex": "^1.3.1", // v1.17.5
"symfony/framework-bundle": "5.0.*", // v5.0.11
"symfony/monolog-bundle": "^3.0", // v3.5.0
"symfony/profiler-pack": "*", // v1.0.5
"symfony/routing": "5.1.*", // v5.1.11
"symfony/twig-pack": "^1.0", // v1.0.1
"symfony/var-dumper": "5.0.*", // v5.0.11
"symfony/webpack-encore-bundle": "^1.7", // v1.8.0
"symfony/yaml": "5.0.*" // v5.0.11
},
"require-dev": {
"symfony/profiler-pack": "^1.0" // v1.0.5
}
}
Hi I'm not seeing any info logs in the profiler although I can see the 'voting up' and 'voting down' logs in the log file in the var directory. As far as I can see monolog.yaml has config for debug level in dev. Any ideas? (Symfony 5.4)