Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

New Bundle, New Service: KnpTimeBundle

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.

On our site, you can create your own vinyl mix. (Or you'll eventually be able to do this... right now, this button doesn't do anything). But another great feature of our site is the ability to browse other user's mixes.

Now that I'm looking at this, it might be useful if we could see when each mix was created.

If you don't remember where in our code this page was built, you can use a trick. Down on the web debug toolbar, hover over the 200 status code. Ah, ha! This shows us that the controller behind this page is VinylController::browse.

Cool! Go open up src/Controller/VinylController.php. Here is the browse action:

... lines 1 - 9
class VinylController extends AbstractController
{
... lines 12 - 29
#[Route('/browse/{slug}', name: 'app_browse')]
public function browse(string $slug = null): Response
{
$genre = $slug ? u(str_replace('-', ' ', $slug))->title(true) : null;
$mixes = $this->getMixes();
return $this->render('vinyl/browse.html.twig', [
'genre' => $genre,
'mixes' => $mixes,
]);
}
... lines 41 - 65
}

By the way, I did update the code a little bit since episode one... so make sure you've got a fresh copy if you're coding along with me.

This method calls $this->getMixes()... which is a private function I created down at the bottom:

... lines 1 - 9
class VinylController extends AbstractController
{
... lines 12 - 41
private function getMixes(): array
{
// temporary fake "mixes" data
return [
[
'title' => 'PB & Jams',
'trackCount' => 14,
'genre' => 'Rock',
'createdAt' => new \DateTime('2021-10-02'),
],
[
'title' => 'Put a Hex on your Ex',
'trackCount' => 8,
'genre' => 'Heavy Metal',
'createdAt' => new \DateTime('2022-04-28'),
],
[
'title' => 'Spice Grills - Summer Tunes',
'trackCount' => 10,
'genre' => 'Pop',
'createdAt' => new \DateTime('2019-06-20'),
],
];
}
}

This returns a big array of fake data that represents the mixes we're going to render on the page. Eventually, we'll get this from a dynamic source, like a database.

Printing Dates in Twig

Notice that each mix has a createdAt date field. We get these mixes up in browse()... and pass them as a mixes variable into vinyl/browse.html.twig. Let's jump into that template.

Down here, we use Twig's for loop to loop over mixes. Simple enough!

... lines 1 - 3
<div class="container">
... lines 5 - 25
<div>
<h2 class="mt-5">Mixes</h2>
<div class="row">
{% for mix in mixes %}
<div class="col col-md-4">
<div class="mixed-vinyl-container p-3 text-center">
<img src="https://via.placeholder.com/300" data-src="https://via.placeholder.com/300" alt="Square placeholder img">
<p class="mt-2"><strong>{{ mix.title }}</strong></p>
<span>{{ mix.trackCount }} Tracks</span>
|
<span>{{ mix.genre }}</span>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
... lines 43 - 44

Let's also now print the "created at" date. Add a |, another <span> and then say {{ mix.createdAt }}.

There's just one problem. If you look at createdAt... it's a DateTime object. And you can't just print DateTime objects... you'll get a big error reminding you... that you can't just print DateTime objects. Cruel world!

Fortunately, Twig has a handy date filter. We talked about filters briefly in the first episode: we using them by adding a | after some value and then the name of the filter. This particular filter also takes an argument, which is the format the date should be printed. To keep things simple, let's use Y-m-d, or "year-month-day".

... lines 1 - 3
<div class="container">
... lines 5 - 25
<div>
<h2 class="mt-5">Mixes</h2>
<div class="row">
{% for mix in mixes %}
<div class="col col-md-4">
<div class="mixed-vinyl-container p-3 text-center">
... lines 32 - 35
<span>{{ mix.genre }}</span>
|
<span>{{ mix.createdAt|date('Y-m-d') }}</span>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
... lines 45 - 46

Head over and refresh and... okay! We can now see when each was created, though the format isn't very attractive. We could do more work to spruce this up... but it would be way cooler if we could print this out in the "ago" format.

You've probably seen it before.... like for comments on a blog post... they say something like "posted three months ago" or "posted 10 minutes ago".

So... the question is: How can we convert a DateTime object into that nice "ago" format? Well, that sounds like work to me and, as I said earlier, work in Symfony is done by a service. So the real question is: Is there a service in Symfony that can convert DateTime objects to the "ago" format? The answer is... no. But there is a third party bundle that can give us that service.

Installing KnpTimeBundle

Go to https://github.com/KnpLabs/KnpTimeBundle. If you look at this bundle's documentation, you'll see that it gives us a service that can do that conversion. So... let's get it installed!

Scroll to the composer require line, copy that, spin over to our terminal, and paste.

composer require knplabs/knp-time-bundle

Cool! This grabbed knplabs/knp-time-bundle... as well as symfony/translation: Symfony's translation component, which is a dependency of KnpTimeBundle. Near the bottom, it also configured two recipes. Let's see what those did. Run:

git status

Awesome! Any time you install a third party package, Composer will always modify your composer.json and composer.lock files. This also updated the config/bundles.php file:

<?php
return [
... lines 4 - 11
Knp\Bundle\TimeBundle\KnpTimeBundle::class => ['all' => true],
];

That's because we just installed a bundle - KnpTimeBundle - and its recipe handled that automatically. It also looks like the translation recipe added a config file and a translations/ directory. The translator is needed to use KnpTimeBundle... but we won't need to work with it directly.

So... what did installing this new bundle give us? Services of course! Let's find and use those next!

Leave a comment!

7
Login or Register to join the conversation
Gitanjali-N Avatar
Gitanjali-N Avatar Gitanjali-N | posted 2 months ago

Hi there,

I completed the previous course and it went well! On downloading a fresh copy of the code for this course and setting it up as per the readme file, the website is up and everthing works, except that the audios do not play and the turbo function is also not working. Any hints as to why this could be ?
Thanks!

Reply

Hey @Gitanjali-N

Can you tell me if you get any console errors in your browser? Did you run yarn watch? I'm guessing this problem could be related to your browser, if you try with a different one, does it work?
Also, try hard-refreshing the page

1 Reply
Gitanjali-N Avatar
Gitanjali-N Avatar Gitanjali-N | MolloKhan | posted 2 months ago | edited

Hi @MolloKhan , thank you so much for your response. Indeed, it worked in another browser-profile window!

Reply

Git Versioning

I'm keeping a git version of this course as I code along. One challenge with downloading a fresh copy of the code is that it creates conflicts in my version control. Do you have any thoughts on to address this? If the changes are code specific, I don't think it's too big of a deal as I can just update the specific code changes where they exist. Are there any package changes between the first tutorial and this one-or even subsequent ones?

Thanks

Reply

Hey robertocannella,

I'm not sure I understand your problem completely. Do you mean, you have a course code from a different (past) tutorial, and wanted to follow the current course base on the finish code of the previous tutorial? If so, that's not something we recommend. We always recommend users to download a fresh course code for each tutorial to code along with us - this way you will have the exact code as the course author has. The reason behind of this is that we always d some upgrades and minor changes between our courses, so "finish course 1 code" != "start course 2 code", always.

But if you want, of course, you can try to follow the current course base on finish course code from the past tutorial, but be aware of possible changes and conflicts that we may done between courses that you would need to resolve manually. It's up to you :)

Cheers!

Reply

can we use Carbon library in a Symfony project ?

Reply

Hi,

Of course you can! I don't see any problem with it. It's a cool library!

Cheers!

1 Reply
Cat in space

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

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=8.1",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "knplabs/knp-time-bundle": "^1.18", // v1.19.0
        "symfony/asset": "6.1.*", // v6.1.0-RC1
        "symfony/console": "6.1.*", // v6.1.0-RC1
        "symfony/dotenv": "6.1.*", // v6.1.0-RC1
        "symfony/flex": "^2", // v2.1.8
        "symfony/framework-bundle": "6.1.*", // v6.1.0-RC1
        "symfony/http-client": "6.1.*", // v6.1.0-RC1
        "symfony/monolog-bundle": "^3.0", // v3.8.0
        "symfony/runtime": "6.1.*", // v6.1.0-RC1
        "symfony/twig-bundle": "6.1.*", // v6.1.0-RC1
        "symfony/ux-turbo": "^2.0", // v2.1.1
        "symfony/webpack-encore-bundle": "^1.13", // v1.14.1
        "symfony/yaml": "6.1.*", // v6.1.0-RC1
        "twig/extra-bundle": "^2.12|^3.0", // v3.4.0
        "twig/twig": "^2.12|^3.0" // v3.4.0
    },
    "require-dev": {
        "symfony/debug-bundle": "6.1.*", // v6.1.0-RC1
        "symfony/maker-bundle": "^1.41", // v1.42.0
        "symfony/stopwatch": "6.1.*", // v6.1.0-RC1
        "symfony/web-profiler-bundle": "6.1.*" // v6.1.0-RC1
    }
}
userVoice