KnpTimeBundle: Install the Bundle, Get its Service
On our site, customers have a convenient "Ship Repair Queue" that lists all of the ships being repaired and their status. For this tutorial, we've added a new $arrivedAt field to our Starship class with some getters and setters. We want to print this field on the homepage.
| // ... lines 1 - 4 | |
| class Starship | |
| { | |
| public function __construct( | |
| // ... lines 8 - 12 | |
| private \DateTimeImmutable $arrivedAt, | |
| ) { | |
| } | |
| // ... lines 16 - 55 | |
| public function getArrivedAt(): \DateTimeImmutable | |
| { | |
| return $this->arrivedAt; | |
| } | |
| } |
If you forgot which controller is responsible for the homepage, you can always hover over the page info in the web debug toolbar and... boom! It says "MainController::homepage". Let's open that up - MainController.php - and find the homepage() action. Down here, we can see that it renders a template: main/homepage.html.twig.
Printing Dates in Twig Templates
Open that, find the "Ship Repair Queue" and, down here after {{ ship.name }}, add a new <div> with Arrived at: {{ ship.arrivedAt }}.
| // ... lines 1 - 4 | |
| {% block body %} | |
| // ... lines 6 - 14 | |
| {% for ship in ships %} | |
| // ... lines 16 - 18 | |
| <div class="ml-5"> | |
| // ... lines 20 - 29 | |
| <div> | |
| Arrived at: {{ ship.arrivedAt }} | |
| </div> | |
| </div> | |
| // ... lines 34 - 46 | |
| {% endfor %} | |
| // ... lines 48 - 55 | |
| {% endblock %} |
Now, if we head back to the browser and refresh the homepage... ah... an error.
An exception has been thrown during the rendering of a template ("Object of class
DateTimeImmutablecould not be converted to string").
That makes sense. PHP can't just print DateTime objects because it doesn't know which format we want. How do we fix that? Easy! We can use a Twig filter. Back over here, after arrivedAt, say |date. If we refresh again... awesome! Here's the date and time in a specific format.
| // ... lines 1 - 18 | |
| <div class="ml-5"> | |
| // ... lines 20 - 29 | |
| <div> | |
| Arrived at: {{ ship.arrivedAt|date }} | |
| </div> | |
| </div> | |
| // ... lines 34 - 57 |
We can pass an optional DateTime format as the first argument to this |date filter, but if we skip it, the application's default format will be used. Which one, exactly? Good question! Let's check out the config. At your terminal, run:
bin/console config:dump twig
Here, you can see the date format configuration for your application. I actually cheated a bit when running this command. The full command name is config:dump-reference. With Symfony commands, you can shorten the name as much as you want as long as it's not ambiguous with another command's name. If multiple commands match, the console will ask which one you want to run.
Installing a New Bundle
All right, back to the browser. We've printed our date, but it would be so much cooler if we could say something like "2 hours ago" instead of this long date. Unfortunately, we don't have a service in our app that can do that for us yet. And I certainly don't want to write it myself. I've got more fun things to do like playing board games. But, hm... Is there a bundle with a service that can do this? Yep! It's called "KnpTimeBundle". Let's find it on GitHub. Here it is! Scroll down to the "Installation" section and copy this command. At your terminal, paste that command and run it:
composer require knplabs/knp-time-bundle
This installs the bundle, the required dependencies, and it also executes some recipes. If we run:
git status
Check it out! Every time we install a new bundle, it changes our composer.json, composer.lock, symfony.lock, and bundles.php files. Let's open that.
| // ... lines 1 - 2 | |
| return [ | |
| // ... lines 4 - 13 | |
| Knp\Bundle\TimeBundle\KnpTimeBundle::class => ['all' => true], | |
| ]; |
Down here, we can see that KnpTimeBundle was added to this array. That's where Symfony activates this bundle in our application. Remember, bundles give us services, and this one's no exception. But... what services did it give us? We could read the docs to learn more about this, but I'm going to be lazy and run:
bin/console debug:container time
I'll select datetime_formatter, which is option 10, for more information. Cool!
To see if we can autowire it, let's run another command:
bin/console debug:autowiring time
And... we can! If we want to use the ago format for our date object, this is the typehint we need to use to inject this service in our PHP classes. But, since we only want this in our Twig template, there's a better solution.
Use a Twig filter
This bundle also comes with a Twig integration that provides some nice Twig filters and functions. We can see that if we run:
bin/console debug:twig
and search for ago. Here it is! If this date looks familiar, that's because it is. That's the one we used before. Let's try the ago filter this time.
| // ... lines 1 - 18 | |
| <div class="ml-5"> | |
| // ... lines 20 - 29 | |
| <div> | |
| Arrived at: {{ ship.arrivedAt|ago }} | |
| </div> | |
| </div> | |
| // ... lines 34 - 57 |
Back over here, replace date with ago... save, and open the browser. Refresh the homepage and... there it is! We now have this nice "ago" format. So, bundles give us services, services are tools, and tools are fun.
Next, let's add even more services by installing new Symfony components.
14 Comments
Hi. You forget to comment that you have to add a sixth parameter in Repository with the DateTimeImmutable. Something like this:
Hey Javier!
Thanks for the head ups and sorry for the long reply! I just double-checked the course project code you can download from this page, and I do see in both start/ and finish/ directories "new \DateTimeImmutable('...')" lines as 6th arg. It might be so that you downloaded the course code earlier that the final current version, i.e. something was tweaked in the code, so your downloaded version is a little bit incomplete. But no problem, just add those 6th args there, you can re-download the course code again and steal that part from it.
Cheers!
Hi)
I think Javier, like me, just followed the course from the first part without downloading the project.
Maybe you should add some kind of hint to the video so that people don't go to the comments section)
Hey Kostiantyn,
It's still not clear to me, could you add more context please? Are you talking about a missing code block? Could you link to a specific time in this video where do that but do not show it? Or what?
Cheers!
Ммм, та в цілому проблеми немає)
Просто якщо слідувати крок за кроком першою частиною і вручну робити все те, що робить викладач (а не використовувати готовий код проекту), то виходить, що наприкінці першої частини в
StarshipRepository.phpвідсутня дата (там деnew Starship(...)), а в другій частині дата вже додана в цьому файлі, але в відео про це не згадано (лише сказано, що було додано нове поле$arrivedAtв модельStarship:)Тому якщо слідувати курсом вручну, треба ще додати ще
new \DateTimeImmutable('-1 day')і т.д. у цей файл)Ага, тепер зрозумів, дякую :)
Тобто ви почали проходити цей курс на коді проєкту попереднього, тобто продовжуючи https://symfonycasts.com/screencast/symfony . Так, це відома проблема. Ми рекомендуємо скачувати новий код проєкту для кожного нового курсу, навіть якщо він є продовженням. Це насамперед тому що перед кожним новим курсом ми робимо деякі зміни, а саме оновлюємо залежності до останніх версій та трохи змінюємо основний код проєкта так щоб зробити його максимально гладким для проходження з точки зору нового курса а не продовження старого. Якби ми не змінювали код для кожного нового курсу - користувачам доводилось би робити набагато більше змін в коді в нових курсах що заплутувало б користувачів.
Тобто якщо простими словами то ми змінюємо код попередніх проєктів саму тому щоб мати кращі приклади тих тем які ми хочемо показати в курсі і при цьому вимагати менше сторонніх дій від користувачів.
Тож наша офіційна рекомендація для того щоб уникати таких моментів - скачувати новий курс проєкту кожного разу для нового курсу. Або ж мати на увазі що якщо ви продовжуєте попередній курс - в новому коді проєкту точно будуть зроблені деякі зміни. Сподіваюсь це допоможе краще розуміти нашу стратегію :)
Cheers!
Hi Kevin, I thought you might struggle a bit on your first tutorial... But... It's like you've been doing it for years already. I'm going to miss Ryan as the first one who made me truly understand Symfony and all it's magic but I think you're an amazing addition to the team. I'll be more than happy to tune in everytime you publish a new lecture/course!
Thank you so much for the kind words @Julian-K!
While we still miss Rayn, congratulations on doing a great job, and a warm welcome to our new tutor! Thank you all for your efforts in maintaining the same level of professionalism and talent that Rayn provided.
Thank you @Lubna! ❤️
Hi guys! Thank you for the tutorials! I had the impression that single page apps were exercises in scrolling. Boy was I wrong! Love the smaller footprint of Symphony! Great job, guys, you are brilliant to get where you are. I know, I have been trying.
I am going to go do the Laracasts, too, when I am done with everything I can get here. I need a job!
Love you guys, thank you!
Hey David,
Thank you for your feedback about SymfonyCasts courses! And stay tuned for more cool content upcoming, the next Symfony course will be about Symfony Doctrine Relations: https://symfonycasts.com/screencast/symfony7-doctrine-relations
Cheers!
While I also miss Ryan (sooo much) I have to confess I also kinda like your style Kevin ;)
I encountered some glitches that (hopefully) are my faults...
startdirectory so i grabbed the one fromfinishto follow the video.Apart from this: well done so far ;)
Hey @elkuku ,
Whops, my bad, thanks!
I added the missing
php bin/console tailwind:buildcommand to the README file - indeed it's needed to run the project for the first time, or you will hit. an exception saying running that command :)Also, you're right, the code from the start/ directory was missing some changes, I fixed that too. But good workaround to steal that code from the finish/ folder.. or usually it's should be possible to grab it from the code blocks in scripts below the video.
Cheers!
"Houston: no signs of life"
Start the conversation!