4 min read

Rebuilding Wardrobe: Week 4

Rebuilding Wardrobe: Week 4

In this weeks Rebuilding Wardrobe series I started working on the list of posts. That sounds easy right? Grab all the posts from the database, loop them, and print them in table rows. But in our crazy world of web development it’s never that easy. One of the fields that should be included is the published date and that means I have to account for timezones. Yuck!

Since Wardrobe is going to be a downloadable app I can’t guarantee the end users server will have the proper timezone, and ideally dates should be stored in UTC. With them stored as UTC it prevents having to worry about offsets, daylight savings, and everything else.

There are a few ways of handling the conversion to local time and of course Carbon, the date time parser included in Laravel, has support for it. However, then I have to allow users select their timezone, and if they are in a part of the world that observes daylight savings they will need to edit the setting twice a year.

The second solution is to use JavaScript and that has the advantage of using the browsers timezone. However, it’s not all roses, I may eventually want to show graphs on the admin dashboard and to get a list of all posts posted in a time frame would need to be handled on the backend. I’m pretty confident I can work around this by fetching the graphs via ajax and passing in the browsers timezone. I haven’t experimented with this yet so I could be harder than I am thinking. None the less I think this is the best solution for Wardrobe.

Lets get started by first creating the Blade views and then work on the JavaScript.

Create the table

I’m a big fan of having minimal view files and break out as much as possible. To show you an example here is my controller and views:

public function index()

{

   $posts = $this->posts->allPaged();

   return view('wardrobe::admin.post.grid', compact('posts'));

}

Then my admin.post.grid view includes:

@extends('wardrobe::admin.layout')



@section('content')

    <table class="grid grid__posts">

        <tr>

            <th>Title</th>

            <th>Status</th>

            <th>Published</th>

            <th></th>

        </tr>

        @foreach ($posts as $post)

            @include('wardrobe::admin.post.item')

        @endforeach

    </table>

@stop

As you can see I’m just including the post.item which will have each row. One thing to note is the @include accepts an array as a second parameter. This is great for creating shared included views but I found that in this situation it causes the dates to loose the Carbon object, as they are cast to a string. So I can’t display them as I need with that.

My post.item looks like this:

<tr>

    <td class="grid__posts--title">{{ $post->title }}</td>

    <td>{{ $post->active == 1 ? "Active" : "Draft" }}</td>

    <td>

        <time datetime="{{ $post->publish_date->toISO8601String() }}">

            {{ $post->publish_date->toFormattedDateString() }}

        </time>

    </td>

    <td></td>

</tr>

I used the html time tag and I plan to use it throughout the app for any dates and times that need conversion.

With this displaying now it’s time to setup the JavaScript to handle the actual conversion.

JavaScript Local Timezone

So far I haven’t needed to use any JavaScript and before I can, all the infrastructure needs to be setup. I’m going to skip covering that part as I only did enough to really get going with Gulp. If you are interested in this then you can see the gulpfile.js.

To handle timezones Moment.js is great as it includes everything you typically need. I pulled it in along with jQuery via Bower then in the Gulp task just concat these two with my own main.js file.

With this all setup, to put the dates into the proper timezone all I need is the following:

$(function() {

    // Time conversion

    $("time").each(function(){

        var $el = $(this);

        var date = $el.attr("datetime");

        $el.text(moment(date).format("lll"));

    });

});

If you are not aware all this does is find all the “time” elements, loop them, convert to moment() and finally displays the parsed version. If you remember from the view I set the datetime attribute to “$post->publish_date->toISO8601String()” which sets a timezone like this: “2014-09-17T01:28:09+0000”. With this format it tells moment it’s in UTC so it knows to convert.

If this app was going to be bigger I would also add a data-* attribute for the format so this could be used for full dates or times. For example something like this:

<time datetime="{{ $post->publish_date->toISO8601String() }}" data-format="time">
<time datetime="{{ $post->publish_date->toISO8601String() }}" data-format="datetime">

Then:

// Time conversion
$("time").each(function(){
    var $el = $(this);
    var format = ($(this).data("type") == 'time') ? 'LT' : 'lll';

    var date = $el.attr("datetime");

    $el.text(moment(date).format(format));

});

Wrap Up

You can see all the code for this in the week 4 branch. Unfortunately, I pushed up a lot in a single commit so it might be a little hard to follow.

If you enjoyed this post be sure and check out the rest of this series:

As always if you have any questions or advice post a comment. I’m happy to listen and glad to respond.