Back

"Loading" model event? Need to adjust attributes of unknown names...


Hey Guys,

I have been doing some Timezone work on my app. I need the database to be 100% UTC based. But I need all interaction to be in the users local timezone. I considered just using Moment.js to mutate them after the page loaded, but that won't really work with Carbon's diffForHumans() function which I want to take advantage of.

Currently, I have a timezone column in my users table where the user can select the timezone they want their dates/times displayed in.

If the person is not a user, then MomentJS, it's Timezone companions, and a third Javascript plugin that returns the current timezone in a PHP friends Date/Time Timezone string quickly figures out the timezone and sets a cookie with that string.

If the App doesn't see a preferred timezone set, it uses the timezone stored in the browser as the default timezone. (For good measure, it just falls back to the apps default timezone if no cookie or user setting is defined).

I have some Middleware that then looks for either the User's default timezone preference, and if not found look for the cookie (before defaulting to the app's timezone). If it finds either of those first two timezones, it updates

\Config::set('app.timezone', $timezoneString);

I have read pros and cons about actually modifying the App's timezone config setting. But my trait is meant to insure that all database dates are stored as UTC.

The Trait can be found here http://laravel.io/bin/KkXxX

The trait uses Setters and Mutators to adjust the created_at, updated_at, and if needed deleted_at time stamps to be UTC when going in, and users timezone when being pulled out. (It returns the Carbon instance as well).

Using the Saving event, I am also able to check to see if there are any dates in the dates array. If there are, I update them all to UTC also. As a result, all of my dates are being written as UTC. (assuming that I populate my $dates array correctly in each model).

To use this trait, I just "use" it, and then register the "Saving" event to one of the trait functions in the Model's constructor.

It works like a champ for getting and setting of the default dates. It also works like a champ for setting of other dates.

##The Problem If I were to add a "foo_at" column to my table, and then add it to the $dates array, then while it will be mutated to UTC upon saving, I have no way of modifying it when accessing it.

I know I could make a getFooAtAttribute() function, but then I would have to hard code the code over and over again for each of the dates fields. I am hoping to be able to just pull in this trait and add the event to the class constructor and as long as the $dates array is populated it just works.

If I knew more about how the get[attrName]Attribute function worked, I might be able to tap into it so that it would be dynamic.

The other option was to see if there is an event fired once the model is populated from the database. Similar to saving, updating, deleting, I was looking for a loading (or in this case loaded) event. From what I can see restoring and restored is in regards to undeleting a record that was soft deleted.

If anybody has any thoughts of how I might be able to accomplish this it would be greatly appreciated.

Note: I am not looking for a debate on the merits of timezones or updating app config on the fly...I am just looking for a solution for my trait.

Thank You :)

beanmoss replied 4 years ago

Hi, take a look at this one, http://laravel.io/bin/mGJEm . I used a closure bind, a php 5.4.x feature http://php.net/manual/en/closure.bind.php . You can integrate it to your trait etc.. Also, it needs improvement since converting your model to array using toArray() wont get the mutated value. Let me know if this works for you.

Xum replied 4 years ago

@eckenroed, maybe you could go a bit deeper and do automatic date conversions by overriding fromDateTime and asDateTime functions of default Eloquent Model class. I haven't tried it personally, just looks like a good point to tackle this issue.

eckenroed replied 4 years ago

2 great ideas. I hadn't thought about toAray(). I'm determined to make this work. Once I do I'll publish the final result (including middleware and JavaScript) so that others don't have to re-invent the wheel.

eckenroed replied 4 years ago

Well, I made some progress on this..but I'm not sure if I like the cost.

I still have to work on the toArray() which I think I can accomplish...however, in order to accomplish it I would have to do what I did to get to this point...which is to override core functions.

Essentially I copied the fromDateTime() and asDateTime() function from Illuminate\Database\Eloquent\Model and put them in my trait to allow them to override the existing method.

All I did was add a few lines to set the timezone for outgoing dates and convert it back to UTC for incoming dates. So this covers all dates that are marked both in the $dates array and known dates.

Is that bad practice? To override a core method like that? I feel like it might be, but it solves my problem so (pardon the phrase) eloquently.

I found the toArray() function and I can do the same to it so that it provides dates in the current users time when casting to an array. So I may do that just so I can use it and cross my fingers that there are not future changes to the core versions of the methods...?

Here's the updated trait: http://laravel.io/bin/LkNad

eckenroed replied 4 years ago Solution

##Final Working version of Trait

So I got the trait working on multiple models to always output the time in the current timezone and to store it in UTC back in the database. I had to copy and paste the toArray() function from Illuminate\Database\Eloquent\Model and throw it in the trait. This is so that when it calls the asDateTime and fromDateTime method it calls the ones in the trait and not the ones in the class.

Again, I have to override the original functions, and again it assumes you always want UTC in your database but it works. If you wanted to store in another timezone you could add an Environmental or Config option and then call that instead of "UTC" in the trait.

I plan on putting together an entire package of stuff for people that want to implement something like this (since I see it all over google with really no easily workable solution).

My solution requires you to include a few javascript files...include a middleware file...and include a trait on the models you want it to work on and that's it.

Final Trait: http://laravel.io/bin/32MY4

eckenroed replied 4 years ago

In case anybody wants to see the full final package I am using I put it on GitHub. It's my first public code to share...so be gentle if you find problems.

https://github.com/eckenroed/LaravelTimezone

Steve-45 replied 3 years ago

If you want another backup software then try Ahsay Software. Ahsay Backup Software is equipped with various speed boosting technologies, including multi-threading, In-File Delta block level incremental backup, server side delta merging, etc. that can greatly enhance the backup and restore speed. You can try this software too.


Sign in to participate in this thread!



We'd like to thank these amazing companies for supporting us