I have the users timezone stored (there is timezone
column in the users
DB table) and I want to display all $dates
attributes on all models in the user’s timezone, if authenticated.
I'm trying to find an elegant way to do this... Ideally, when there is something like this in Blade views:
{{ $post->created_at }}
OR
{{ $post->created_at->format('h:i:s A') }}
... for authenticated users it would be automatically in their timezones.
How would you handle this?
I'm thinking about creating one trait (for example, app/Traits/UserTimezoneAware.php
) and place there accessors that will simply return Carbon::createFromFormat('Y-m-d H:i:s', $value)->timezone(auth()->user()->timezone)
if the current user is authenticated. For example:
<?php
namespace App\Traits;
use Carbon\Carbon;
trait UserTimezoneAware
{
/**
* Get the created_at in the user's timezone.
*
* @param $value
* @return mixed
*/
public function getCreatedAtAttribute($value)
{
if (auth()->check()) {
return Carbon::createFromFormat('Y-m-d H:i:s', $value)->timezone(auth()->user()->timezone);
}
return Carbon::createFromFormat('Y-m-d H:i:s', $value);
}
/**
* Get the updated_at in the user's timezone.
*
* @param $value
* @return mixed
*/
public function getUpdatedAtAttribute($value) { ... }
}
But I'm not sure if this is good or bad to do (to create these accessros for the Laravel's $dates
attributes)?
Also, models will have different attributes specified in $dates
array: for example, User
model can have:
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $dates = [
'created_at',
'updated_at',
'last_login_at'
];
and Post
model can have:
protected $dates = [
'created_at',
'updated_at',
'approved_at',
'deleted_at'
];
Is it possible to dynamically create accessors in trait, based on the attirbutes specified in the $dates
array of a model that uses that trait?
Or maybe there is a better way to handle this, without accessors?
One way (without accessors) is to use this trait:
<?php
namespace App\Traits;
use DateTimeInterface;
use Illuminate\Support\Carbon;
trait UserTimezoneAware
{
/**
* Return a timestamp as DateTime object.
*
* @param mixed $value
* @return \Illuminate\Support\Carbon
*/
protected function asDateTime($value)
{
$timezone = auth()->check() ? auth()->user()->timezone : config('app.timezone');
if ($value instanceof Carbon) {
return $value->timezone($timezone);
}
if ($value instanceof DateTimeInterface) {
return new Carbon(
$value->format('Y-m-d H:i:s.u'), $timezone
);
}
if (is_numeric($value)) {
return Carbon::createFromTimestamp($value)->timezone($timezone);
}
if ($this->isStandardDateFormat($value)) {
return Carbon::createFromFormat('Y-m-d', $value)->startOfDay()->timezone($timezone);
}
return Carbon::createFromFormat(
str_replace('.v', '.u', $this->getDateFormat()), $value
)->timezone($timezone);
}
}
When using this trait, we are overriding asDateTime($value)
defined in Concerns\HasAttributes
trait (which is used in Illuminate\Database\Eloquent\Model
).
This seems to work OK, I have not yet encountered any problems.
But I'm not sure if there are any risks or potential problems when doing this (when using this trait that overrides asDateTime
method).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With