New Bundle, New Service: KnpTimeBundle
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 | |
'/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:
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!
Just want to say: THANK YOU for your great Symfony and Vue tutorials. I've learned such a lot. Much more in one single tutorial than in the last five years of learning-by-doing and self-studying. Your pronouniation is clear and distinct. And your humour is uplifting. Also, all the stuff works, no problems to follow. Downloadable code start/finish rounds it up. Keep on making such tutorials please.