DEV Community

Cover image for Cleaning up dates within Laravel Blade
André Luiz Lunelli
André Luiz Lunelli

Posted on

Cleaning up dates within Laravel Blade

TL;DR

So basically, you can use a Custom Echo Handler to apply the same datetime format when printing Carbon instances within blade files.

Instead of have something like this:

<tr>
    <td>{{ $user->created_at->format('m/d/Y H:i') }}</td>
    <td>{{ $user->updated_at->format('m/d/Y H:i') }}</td>
    <td>{{ $user->deleted_at->format('m/d/Y H:i') }}</td>
    <td>{{ $user->email_verified_at->format('m/d/Y H:i') }}</td>
</tr>
Enter fullscreen mode Exit fullscreen mode

You can have this:

<tr>
    <td>{{ $user->created_at }}</td>
    <td>{{ $user->updated_at }}</td>
    <td>{{ $user->deleted_at }}</td>
    <td>{{ $user->email_verified_at }}</td>
</tr>
Enter fullscreen mode Exit fullscreen mode

By doing this:

// AppServiceProvider.php

use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Blade;

public function boot(): void
{
    Blade::stringable(function (Carbon $carbon) {
        return $carbon->format('m/d/Y H:i');
    });
}
Enter fullscreen mode Exit fullscreen mode

And that's it.

Read the Full Thing

I went to review some pull requests, and there was one that had many carbon dates repeating the output format to display in the right US format.

There isn't a real problem doing it that way; it's very straightforward. But it's boring. Can it be simpler? Let's give it a try.

First, I tried to create a class that would inherit from the Carbon instance and override the __toString method. You can do it in the DateFactory by informing which class you want to use as concrete.

\Illuminate\Support\DateFactory::useClass(MyCarbonDate::class);
Enter fullscreen mode Exit fullscreen mode

I didn't like the idea of ending with a new class just to prevent me from typing the format of the datetimes. Seems like overengineering, so I decided to move on.

Also, sniffing a little bit, I found that you can set a "toString" strategy using the Date facade:

\Illuminate\Support\Facades\Date::setToStringFormat('m/d/Y H:i')
Enter fullscreen mode Exit fullscreen mode

But who can say that it won't break anything else in the future, like, I don't know, some inner Laravel hydration thing.

I need a simpler solution and don't want to reinvent the wheel. So I remember about Laravel Eloquent Casting and I saw the Date Casting. That is what I want!

Eventually the code didn't work, and I went back to the docs to figure it why, and that was the reason:

This format will be used when the model is serialized to an array or JSON

In summary, this is going to work through API Resources.

Ok, maybe I'm overthinking this. Forget about it, let's move on. I started to do another daily task. But my brain still didn't give up; I was intrigued. How it's possible that anyone wanted to do this in their blade files?

Ok, let's take only 5 minutes to look again at the docs, maybe I miss something. And I did :D

At the blade section I could find the Custom Echo Handlers

This closure should type-hint the type of object that it is responsible for rendering.

But wait, it isn't working. Well, clearing the cache sometimes solves things. So I did:

php artisan view:clear
Enter fullscreen mode Exit fullscreen mode

And it works!

It's nice to only type the variable that you wanna print, without always having to type the format every time, and with a chance to misspell the format. Additionally, if in the future I support another locale, I could even provide different formats based on this, something like:

Blade::stringable(function (Carbon $carbon) {
    if (app()->getLocale() !== 'en') {
        return $carbon->locale(app()->getLocale())->format('Y-m-d H:i');
    }

    return $carbon->format('m/d/Y H:i');
});
Enter fullscreen mode Exit fullscreen mode

That's all, folks.

Top comments (3)

Collapse
 
xwero profile image
david duymelinck

I love your journey to come to the function. You did miss a few tricks.

For the casting to work you could pass the model to the view as an array instead of passing it as an instance.

If you want to pass it as an instance you can use an accessor. It isn't the best method because the format of the date can be different in other parts of the application that need the dates.

Instead of feeding the data directly from the model to the view, you could use a data transformer class which prepares the data from the controller for the view.
The biggest benefit of this method is that it is testable with a unit test, instead of an integration test.

Collapse
 
andreluizlunelli profile image
André Luiz Lunelli

Nice tip passing the array to the view!

Maybe with accessors and fields mixed, I'll end up with different cases like, slug_case and camelCase.

To me, it is very handy to pass an instance because of how things work in Livewire, I frequently use it.

Thanks for sharing!

:D

Collapse
 
xwero profile image
david duymelinck

There is no requirement for object instances in Livewire, arrays work too.