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

Generating URLs

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.

Most of these links don't go anywhere yet. Whatever! No problem! We're going to fill them in as we continue. Besides, most of our users will be in hypersleep for at least a few more decades.

But we can hook up some of these - like the "Space Bar" logo text - that should go to the homepage.

Open templates/base.html.twig and search for "The Space Bar":

<!doctype html>
<html lang="en">
... lines 3 - 15
<body>
<nav class="navbar navbar-expand-lg navbar-dark navbar-bg mb-5">
<a style="margin-left: 75px;" class="navbar-brand space-brand" href="#">The Space Bar</a>
... lines 19 - 47
</nav>
... lines 49 - 66
</body>
</html>

Ok - let's point this link to the homepage. And yep, we could just say href="/".

But... there's a better way. Instead, we're going to generate a URL to the route. Yep, we're going to ask Symfony to give us the URL to the route that's above our homepage action:

... lines 1 - 8
class ArticleController extends AbstractController
{
/**
* @Route("/")
*/
public function homepage()
{
... line 16
}
... lines 18 - 34
}

Why? Because if we ever decided to change this route's URL - like to /news - if we generate the URL instead of hardcoding it, all the links will automatically update. Magic!

The Famous debug:router

So how can we do this? First, find your terminal and run:

./bin/console debug:router

This is an awesome little tool that shows you a list of all of the routes in your app. You can see our two routes and a bunch of routes that help the profiler and web debug toolbar.

There's one thing about routes that we haven't really talked about yet: each route has an internal name. This is never shown to the user, it only exists so that we can refer to that route in our code. For annotation routes, by default, that name is created for us.

Generating URLs with path()

This means, to generate a URL to the homepage, copy the route name, go back to base.html.twig, add {{ path() }} and paste the route name:

<!doctype html>
<html lang="en">
... lines 3 - 15
<body>
<nav class="navbar navbar-expand-lg navbar-dark navbar-bg mb-5">
<a style="margin-left: 75px;" class="navbar-brand space-brand" href="{{ path('app_article_homepage') }}">The Space Bar</a>
... lines 19 - 47
</nav>
... lines 49 - 66
</body>
</html>

That's it!

Refresh! Click it! Yes! We're back on the homepage.

But... actually I don't like to rely on auto-created route names because they could change if we renamed certain parts of our code. Instead, as soon as I want to generate a URL to a route, I add a name option: name="app_homepage":

... lines 1 - 8
class ArticleController extends AbstractController
{
/**
* @Route("/", name="app_homepage")
*/
public function homepage()
{
... line 16
}
... lines 18 - 34
}

Run debug:router again:

./bin/console debug:router

The only thing that changed is the name of the route. Now go back to base.html.twig and use the new route name here:

<!doctype html>
<html lang="en">
... lines 3 - 15
<body>
<nav class="navbar navbar-expand-lg navbar-dark navbar-bg mb-5">
<a style="margin-left: 75px;" class="navbar-brand space-brand" href="{{ path('app_homepage') }}">The Space Bar</a>
... lines 19 - 47
</nav>
... lines 49 - 66
</body>
</html>

It still works exactly like before, but we're in complete control of the route name.

Making the Homepage Pretty

We now have a link to our homepage... but I don't know why you'd want to go here: it's super ugly! So let's render a template. In ArticleController, instead of returning a Response, return $this->render() with article/homepage.html.twig:

... lines 1 - 8
class ArticleController extends AbstractController
{
/**
* @Route("/", name="app_homepage")
*/
public function homepage()
{
return $this->render('article/homepage.html.twig');
}
... lines 18 - 34
}

For now, don't pass any variables to the template.

This template does not exist yet. But if you look again in the tutorial/ directory from the code download, I've created a homepage template for us. Sweet! Copy that and paste it into templates/article:

{% extends 'base.html.twig' %}
{% block body %}
<div class="container">
<div class="row">
<!-- Article List -->
<div class="col-sm-12 col-md-8">
<!-- H1 Article -->
<a class="main-article-link" href="#">
<div class="main-article mb-5 pb-3">
<img src="{{ asset('images/meteor-shower.jpg') }}" alt="meteor shower">
<h1 class="text-center mt-2">Ursid Meteor Shower: <br>Healthier than a regular shower?</h1>
</div>
</a>
<!-- Supporting Articles -->
<div class="article-container my-1">
<a href="#">
<img class="article-img" src="{{ asset('images/asteroid.jpeg') }}">
<div class="article-title d-inline-block pl-3 align-middle">
<span>Why do Asteroids Taste Like Bacon?</span>
<br>
<span class="align-left article-details"><img class="article-author-img rounded-circle" src="{{ asset('images/alien-profile.png') }}"> Mike Ferengi </span>
<span class="pl-5 article-details float-right"> 3 hours ago</span>
</div>
</a>
</div>
<div class="article-container my-1">
<a href="#">
<img class="article-img" src="{{ asset('images/mercury.jpeg') }}">
<div class="article-title d-inline-block pl-3 align-middle">
<span>Life on Planet Mercury: <br> Tan, Relaxing and Fabulous</span>
<br>
<span class="align-left article-details"><img class="article-author-img rounded-circle" src="{{ asset('images/astronaut-profile.png') }}"> Amy Oort </span>
<span class="pl-5 article-details float-right"> 6 days ago</span>
</div>
</a>
</div>
<div class="article-container my-1">
<a href="#">
<img class="article-img" src="{{ asset('images/lightspeed.png') }}">
<div class="article-title d-inline-block pl-3 align-middle">
<span>Light Speed Travel: <br> Fountain of Youth or Fallacy</span>
<br>
<span class="align-left article-details"><img class="article-author-img rounded-circle" src="{{ asset('images/astronaut-profile.png') }}"> Amy Oort </span>
<span class="pl-5 article-details float-right"> 2 weeks ago</span>
</div>
</a>
</div>
</div>
<!-- Right bar ad space -->
<div class="col-sm-12 col-md-4 text-center">
<div class="ad-space mx-auto mt-1 pb-2 pt-2">
<img class="advertisement-img" src="{{ asset('images/space-ice.png') }}">
<p><span class="advertisement-text">New:</span> Space Ice Cream!</p>
<button class="btn btn-info">Buy Now!</button>
</div>
<div class="quote-space pb-2 pt-2 px-5">
<h3 class="text-center pb-3">Trending Quotes</h3>
<p><i class="fa fa-comment"></i> "Our two greatest problems are gravity and paperwork. We can lick gravity, but sometimes the paperwork is overwhelming." <br><a href="https://en.wikipedia.org/wiki/Wernher_von_Braun">Wernher von Braun, Rocket Engineer</a></p>
<p class="pt-4"><i class="fa fa-comment"></i> "Let's face it, space is a risky business. I always considered every launch a barely controlled explosion." <br><a href="https://en.wikipedia.org/wiki/Aaron_Cohen_(Deputy_NASA_administrator)">Aaron Cohen, NASA Administrator</a></p>
<p class="pt-4"><i class="fa fa-comment"></i> "If offered a seat on a rocket ship, don't ask what seat. Just get on."<br><a href="https://en.wikipedia.org/wiki/Christa_McAuliffe">Christa McAuliffe, Challenger Astronaut</a>
</div>
</div>
</div>
</div>
{% endblock %}

It's nothing special: just a bunch of hardcoded information and fascinating space articles. It does make for a pretty cool-looking homepage. And yea, we'll make this all dynamic once we have a database.

Generating a URL with a {wildcard}

One of the hardcoded articles is the one we've been playing with: Why Asteroids Taste like Bacon! The link doesn't go anywhere yet, so let's fix that by generating a URL to our article show page!

Step 1: now that we want to link to this route, give it a name: article_show:

... lines 1 - 8
class ArticleController extends AbstractController
{
... lines 11 - 18
/**
* @Route("/news/{slug}", name="article_show")
*/
public function show($slug)
{
... lines 24 - 33
}
}

Step 2: inside homepage.html.twig, find the article... and... for the href, use {{ path('article_show') }}:

... lines 1 - 2
{% block body %}
<div class="container">
<div class="row">
<!-- Article List -->
<div class="col-sm-12 col-md-8">
... lines 10 - 18
<!-- Supporting Articles -->
<div class="article-container my-1">
<a href="{{ path('article_show') }}">
... lines 23 - 29
</a>
</div>
... lines 32 - 56
</div>
... lines 58 - 77
</div>
</div>
{% endblock %}

That should work... right? Refresh! No! It's a huge, horrible, error!

Some mandatory parameters are missing - {slug} - to generate a URL for article_show.

That totally makes sense! This route has a wildcard... so we can't just generate a URL to it. Nope, we need to also tell Symfony what value it should use for the {slug} part.

How? Add a second argument to path(): {}. That's the syntax for an associative array when you're inside Twig - it's similar to JavaScript. Give this a slug key set to why-asteroids-taste-like-bacon:

... lines 1 - 2
{% block body %}
<div class="container">
<div class="row">
<!-- Article List -->
<div class="col-sm-12 col-md-8">
... lines 10 - 18
<!-- Supporting Articles -->
<div class="article-container my-1">
<a href="{{ path('article_show', {slug: 'why-asteroids-taste-like-bacon'}) }}">
... lines 23 - 29
</a>
</div>
... lines 32 - 56
</div>
... lines 58 - 77
</div>
</div>
{% endblock %}

Try it - refresh! Error gone! And check this out: the link goes to our show page.

Next, let's add some JavaScript and an API endpoint to bring this little heart icon to life!

Leave a comment!

6
Login or Register to join the conversation
Default user avatar
Default user avatar Yacine Rerbal | posted 5 years ago

can't wait for database part, really nice job on those videos! big thanks

1 Reply

Hey Yacine!

Thanks for your kind feedback! Top secret info for you (do not tell anyone!): we're going to start working on the 2nd episode of Symfony 4 series, so it'll be releasing soon. Unfortunately no interaction with DB yet, but we're close to it ;)

Cheers!

3 Reply
John alcher Avatar
John alcher Avatar John alcher | posted 4 years ago

Was really liking Twig up until that part where the syntax for an associative array is the same as with JSON. I hope they stuck with traditional PHP syntax on that one. The Python-like for loop is awesome though, so I guess I can't complain :D

Reply

Hey agin John alcher!

Ha! That was weird for me at first too, to be honest - especially because { is already used to *start* Twig :). But, at least it's consistent with JSON/JavaScript... and not something totally invented ;).

Glad you like the loop - Twig is just full of goodies - more and more of them when you start digging.

Cheers!

1 Reply
Mike P. Avatar
Mike P. Avatar Mike P. | posted 5 years ago

I got an error:
Looks like you try to load a template outside configured directories (../../templates/base.html.twig) in article/homepage.html.twig at line 1.

My fix:
homepage.html.twig (downloaded from here):
Old: 1st line: {% extends '../../templates/base.html.twig' %}
New 1st line: {% extends 'base.html.twig' %}

Thanks for this great series!

Reply

Hey Mike P.

I believe your paths got modified when you moved the templates from the "tutorial" directory. Oh, and thanks for the compliment :)

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.1.3",
        "ext-iconv": "*",
        "sensio/framework-extra-bundle": "^5.1", // v5.1.3
        "symfony/asset": "^4.0", // v4.0.3
        "symfony/console": "^4.0", // v4.0.14
        "symfony/flex": "^1.0", // v1.17.6
        "symfony/framework-bundle": "^4.0", // v4.0.14
        "symfony/lts": "^4@dev", // dev-master
        "symfony/twig-bundle": "^4.0", // v4.0.3
        "symfony/web-server-bundle": "^4.0", // v4.0.3
        "symfony/yaml": "^4.0" // v4.0.14
    },
    "require-dev": {
        "easycorp/easy-log-handler": "^1.0.2", // v1.0.4
        "sensiolabs/security-checker": "^5.0", // v5.0.3
        "symfony/debug-bundle": "^3.3|^4.0", // v4.0.3
        "symfony/dotenv": "^4.0", // v4.0.14
        "symfony/monolog-bundle": "^3.0", // v3.1.2
        "symfony/phpunit-bridge": "^3.3|^4.0", // v4.0.3
        "symfony/profiler-pack": "^1.0", // v1.0.3
        "symfony/var-dumper": "^3.3|^4.0" // v4.0.3
    }
}
userVoice