Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

"5 Minutes Ago" Strings

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.

Let's make this date dynamic! The field on Question that we're going to use is $askedAt, which - remember - might be null. If a Question hasn't been published yet, then it won't have an askedAt.

Let's plan for this. In the template, add {% if question.askedAt %} with an {% else %} and {% endif %}

... lines 1 - 5
<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="{{ asset('images/tisha.png') }}" width="100" height="100" alt="Tisha avatar">
<div class="mt-3">
<small>
{% if question.askedAt %}
... lines 18 - 19
{% else %}
... line 21
{% endif %}
</small>
... lines 24 - 29
</div>
</div>
... lines 32 - 39
</div>
</div>
</div>
</div>
</div>
... lines 45 - 72
</div>
... lines 74 - 75

If the question is not published, say (unpublished).

... lines 1 - 5
<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="{{ asset('images/tisha.png') }}" width="100" height="100" alt="Tisha avatar">
<div class="mt-3">
<small>
{% if question.askedAt %}
... lines 18 - 19
{% else %}
(unpublished)
{% endif %}
</small>
... lines 24 - 29
</div>
</div>
... lines 32 - 39
</div>
</div>
</div>
</div>
</div>
... lines 45 - 72
</div>
... lines 74 - 75

In a real app, we would probably not allow users to see unpublished questions... we could do that in our controller by checking for this field and saying throw $this->createNotFoundException() if it's null. But... maybe a user will be able to preview their own unpublished questions. If they did, we'll show unpublished.

The Twig date Filter

The easiest way to try to print the date would be to say {{ question.askedAt }}.

... lines 1 - 5
<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="{{ asset('images/tisha.png') }}" width="100" height="100" alt="Tisha avatar">
<div class="mt-3">
<small>
{% if question.askedAt %}
... line 18
{{ question.askedAt }}
{% else %}
(unpublished)
{% endif %}
</small>
... lines 24 - 29
</div>
</div>
... lines 32 - 39
</div>
</div>
</div>
</div>
</div>
... lines 45 - 72
</div>
... lines 74 - 75

But... you might be shouting: "Hey Ryan! That's not going to work!".

And... you're right:

Object of class DateTime could not be converted to string

We know that when we have a datetime type in Doctrine, it's stored in PHP as a DateTime object. That's nice because DateTime objects are easy to work with... but we can't simply print them.

To fix this, pass the DateTime object through a |date() filter. This takes a format argument - something like Y-m-d H:i:s.

... lines 1 - 5
<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="{{ asset('images/tisha.png') }}" width="100" height="100" alt="Tisha avatar">
<div class="mt-3">
<small>
{% if question.askedAt %}
... line 18
{{ question.askedAt|date('Y-m-d H:i:s') }}
{% else %}
(unpublished)
{% endif %}
</small>
... lines 24 - 29
</div>
</div>
... lines 32 - 39
</div>
</div>
</div>
</div>
</div>
... lines 45 - 72
</div>
... lines 74 - 75

When we try the page now... it's technically correct... but yikes! This... well... how can I put this politely: it looks like a backend developer designed this.

KnpTimeBundle

Whenever I render dates, I like to make them relative. Instead of printing an exact date, I prefer something like "10 minutes ago". It also avoids timezone problems... because 10 minutes ago makes sense to everyone! But this exact date would really need a timezone to make sense.

So let's do this. Start by adding the word "Asked" back before the date. Cool.

... lines 1 - 5
<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="{{ asset('images/tisha.png') }}" width="100" height="100" alt="Tisha avatar">
<div class="mt-3">
<small>
{% if question.askedAt %}
Asked <br>
{{ question.askedAt|date('Y-m-d H:i:s') }}
{% else %}
(unpublished)
{% endif %}
</small>
... lines 24 - 29
</div>
</div>
... lines 32 - 39
</div>
</div>
</div>
</div>
</div>
... lines 45 - 72
</div>
... lines 74 - 75

To convert the DateTime into a friendly string, we can install a nice bundle. At your terminal, run:

composer require "knplabs/knp-time-bundle:1.19.0"

You could find this bundle if you googled for "Symfony ago". As we know, the main thing that a bundle gives us is more services. In this case, the bundle gives us one main service that provides a Twig filter called ago.

It's pretty awesome. Back in the template, add |ago.

... lines 1 - 5
<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="{{ asset('images/tisha.png') }}" width="100" height="100" alt="Tisha avatar">
<div class="mt-3">
<small>
{% if question.askedAt %}
Asked <br>
{{ question.askedAt|ago }}
{% else %}
(unpublished)
{% endif %}
</small>
... lines 24 - 29
</div>
</div>
... lines 32 - 39
</div>
</div>
</div>
</div>
</div>
... lines 45 - 72
</div>
... lines 74 - 75

We're done! When we refresh now... woohoo!

Asked 1 month ago

Next: let's make the homepage dynamic by querying for all of the questions in the database and rendering them. Along the way, we're going to learn a secret about the repository object.

Leave a comment!

5
Login or Register to join the conversation
Kevin Avatar

Hi, I get this error when running composer require knplabs/knp-time-bundle

https://i.imgur.com/PW9cVEi...

Reply

Hey Ccc123,

Looks like you're using a newer version of dependencies in your project. Did you download the course code and started from the start/ directory? Or you started with a fresh new Symfony application? If you started with our course code, try to install the same version of the bundle like we use in our videos, for this run:

composer require "knplabs/knp-time-bundle:v1.16.0"

Let me know if it does not help. Otherwise, I would like to know more about your Symfony version so I could help further.

Cheers!

1 Reply
Kevin Avatar

Hi Victor,

Yeah I downloaded the course code before I started. I just tried installing the version that was in the final/ directory's composer.json and it worked! Thank you.

Reply
Ruslan Avatar

Hi.
How to add auto fill in twig templates?
Thank you.

Reply

Hey Ruslan,

What do you mean about "auto fill"? Something like PhpStorm live templates? If so - here's related screencast about it: https://symfonycasts.com/sc... . Otherwise, you're most probably talking about Symfony Plugin that auto completes a lot of useful things in PhpStorm. You can find more info on how to install and enable this plugin here: https://symfonycasts.com/sc...

I hope this helps!

Cheers!

Reply
Cat in space

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

This tutorial also works great for Symfony 6!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.4.1",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "doctrine/doctrine-bundle": "^2.1", // 2.1.1
        "doctrine/doctrine-migrations-bundle": "^3.0", // 3.0.2
        "doctrine/orm": "^2.7", // 2.8.2
        "knplabs/knp-markdown-bundle": "^1.8", // 1.9.0
        "knplabs/knp-time-bundle": "^1.11", // v1.16.0
        "sensio/framework-extra-bundle": "^6.0", // v6.2.1
        "sentry/sentry-symfony": "^4.0", // 4.0.3
        "stof/doctrine-extensions-bundle": "^1.4", // v1.5.0
        "symfony/asset": "5.1.*", // v5.1.2
        "symfony/console": "5.1.*", // v5.1.2
        "symfony/dotenv": "5.1.*", // v5.1.2
        "symfony/flex": "^1.3.1", // v1.17.5
        "symfony/framework-bundle": "5.1.*", // v5.1.2
        "symfony/monolog-bundle": "^3.0", // v3.5.0
        "symfony/stopwatch": "5.1.*", // v5.1.2
        "symfony/twig-bundle": "5.1.*", // v5.1.2
        "symfony/webpack-encore-bundle": "^1.7", // v1.8.0
        "symfony/yaml": "5.1.*", // v5.1.2
        "twig/extra-bundle": "^2.12|^3.0", // v3.0.4
        "twig/twig": "^2.12|^3.0" // v3.0.4
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.3", // 3.4.0
        "symfony/debug-bundle": "5.1.*", // v5.1.2
        "symfony/maker-bundle": "^1.15", // v1.23.0
        "symfony/var-dumper": "5.1.*", // v5.1.2
        "symfony/web-profiler-bundle": "5.1.*", // v5.1.2
        "zenstruck/foundry": "^1.1" // v1.5.0
    }
}