I've built an application in Laravel and eloquent returns dates in this format: 2015-04-17 00:00:00. I'm sending one particular query to JSON so I can make a graph with D3, and I think I would like the dates in ISO8601 ('1995-12-17T03:24:00') or some other format that plays nice with the javascript Date() constructor.
Is there a way to change the date format being output to JSON on the Laravel end? I'm not sure using a mutator is the best approach because it would affect the date in other parts of my application.
Or would it be better to leave the JSON output as is, and use some javascript string methods to manipulate the date format before passing it to the Date() constructor? Which approach is more efficient?
Here is my model:
class Issue extends Model {
protected $fillable = [
'client_id',
'do',
'issue_advocate',
'service_number',
'issue_location',
'issue_description',
'level_of_service',
'outcome',
'referral_id',
'file_stale_date',
'date_closed',
'issue_note',
'staff_hours'
];
protected $dates = [
'do',
'date_closed',
'file_stale_date'
];
public function setDoAttribute($value)
{
$this->attributes['do'] = Carbon::createFromFormat('F j, Y', $value)->toDateString();
}
}
Here is my query:
$issues = Issue::with('issuetypes')
->select(['do','level_of_service','outcome','id'])
->whereBetween('do',[$lastyear,$now])
->get()->toJson();
And the JSON I get back:
[{"do":"2014-12-23 00:00:00","level_of_service":1,"outcome":1,"id":18995,"issuetypes":[{"id":9,"issuetype":"Non Liberty","pivot":{"issue_id":18995,"issuetype_id":9}}]}]
I know it's an old question, but there is still no good answer to that.
Changing protected $dateFormat will affect database, instead method serializeDate() must be overriden
class MyModel extends Eloquent {
protected function serializeDate(\DateTimeInterface $date) {
return $date->getTimestamp();
}
}
Or myself I chose to create trait
trait UnixTimestampSerializable
{
protected function serializeDate(\DateTimeInterface $date)
{
return $date->getTimestamp();
}
}
and then add
class SomeClassWithDates extends Model {
use UnixTimestampSerializable;
...
}
Expanding on umbrel's answer a bit I've created a trait that turns the DateTimeInstance into a Carbon instance so that I can easily make use of it's common formats.
In my particular case I wanted to serialize all dates according to ISO-8601.
The trait is as follows...
use DateTimeInterface;
use Carbon\Carbon;
trait Iso8601Serialization
{
/**
* Prepare a date for array / JSON serialization.
*
* #param \DateTimeInterface $date
* #return string
*/
protected function serializeDate(DateTimeInterface $date)
{
return Carbon::instance($date)->toIso8601String();
}
}
and from here I can simply use it on the relevant models...
class ApiObject extends Model
{
use Iso8601Serialization;
}
Obviously you could name the trait more appropriately if you're using a different format but the point is that you can use any of Carbon's common formats simply by replacing toIso8601String() with the format you need.
I strongly suggest you use the Carbon class to handle all your dates and datetimes variables, it already comes with Laravel 5 so you can start using whenever you want.
Check it out on Carbon Repo to see what you can do with it.
As an example, you can format dates from your model like this
Carbon::parse($model->created_at)->format('d-m-Y')
As for a good approach, I would suggest to use the Repository Pattern along with Presenters and Transformers. By using it you can define how you want your json to be displayed/mounted and opt to skip the presenter whenever you want in order to still get you Eloquent model returned when you make your queries.
use this function in any Model
protected function serializeDate(DateTimeInterface $date){
return $date->format('Y-m-d h:i:s');
}
Result
You can easily change the format that used to convert date/time to string when your models are serialized as JSON by setting $dateFormat property of your model to the format you need, e.g.:
class MyModel extends Eloquent {
protected $dateFormat = 'Y-m-d';
}
You can find docs on different placeholders you can use in the format string here: http://php.net/manual/en/datetime.createfromformat.php
If you use usuals techniques as
protected $dateFormat = 'Y-m-d';
or
protected function serializeDate(DateTimeInterface $date) { ... }
or
protected $casts = [ "myDate" => "date:Y-m-d" ];
It'll only works when laravel will serialize itself objects. And you will anyway to put those code inside all models, for all properties.
So my solution, you have to (too) put this code in all models for all date properties by at last, it works in ALL cases :
public function getMyDateAttribute()
{
return substr($this->attributes['my_date'], 0, 10);
}
Related
I have a birth date field in users table
$table->date('dob');
User model has Casts
protected $casts = [
'dob' => 'date:d-m-Y'
];
In Blade,
{{$user->dob}}
I was expecting 26-11-2019
but found it shows 2019-11-26 00:00:00
Why I need to format the date again in blade when display?
What did I miss?
Or what I was expecting, is not the purpose of formatting?
protected $casts = [...] tells Laravel to treat the properties as Carbon instances, but you still need to format them:
{{ $user->dob->format('d-m-Y') }}
As far as I'm aware, there isn't a way to output a default format, unless you use an accessor:
In your User.php model:
public function getDobFormattedAttribute(){
return $this->dob->format('y-m-D');
}
Then in your view:
{{ $user->dob_formatted }}
Date casting uses only for arrays or JSON, as explained here:
https://laravel.com/docs/6.x/eloquent-mutators#date-casting
You can try to do it via mutator:
https://laravel.com/docs/6.x/eloquent-mutators#date-mutators
protected $dates = [
'dob', // it will be 'Y-m-d H:i:s'
];
protected $dateFormat = 'd-m-Y'; // but you can redefine it
that will only work when you use a ->toArray() or ->toJson() on the object or collection in question, from the doc here https://laravel.com/docs/6.x/eloquent-mutators#date-casting
A way around that of you weren't using any of the above function call is to create an accessor or getter method in the model.
use Carbon\Carbon; // import the Carbon lib.
protected $dates = ['dob'];
public function getDobAttribute($value)
{
return Carbon::createFromFormat('d-m-Y', $value);
}
I'm looking for a DateTime Mutator that change the format of dates, I'm working with Oracle DB and the admitted Format is (DD/MM/YYYY) and the input type "date" stores dates in (YYYY,MM,DD) format.
I found the $date function and a trait by Torzer, but I have to indicate the fields that I want to convert the format.
is there some trait or function that detect all date fields and convert them automatically in a format (DD/MM/YYYY)? this without indicate the field.
nowadays I use protected $date in my model:
protected $dates = [ 'fecha_nac', 'fecha_nac1', 'fecha_nac2', ];
By default laravel uses date formate 'Y-m-d H:i:s' if you want to use a different format you can customize it in your model in the following way.
protected $dateFormat = 'your date formate';
in your case it will be.
protected $dateFormat = 'd-m-Y';
You can override the getDates method on HasAttributes trait.
/**
* Get the attributes that should be converted to dates.
*
* #return array
*/
public function getDates()
{
$defaults = [static::CREATED_AT, static::UPDATED_AT];
return $this->usesTimestamps()
? array_unique(array_merge($this->dates, $defaults))
: $this->dates;
}
On your model:
public function getDates()
{
$dates = parent::getDates();
// add your dynamic logic here
return $dates;
}
I would really go for explicitly defining which fields should be converted as these dynamic operations can be expensive if you are working with the model quite a lot.
I have a list of date fields, and all of them have the same logic in their mutators. I would like to extract this functionality to a trait so that in the future all I would need is to create an array of date fields in the model and use the trait.
Something like this:
foreach( $dates as $date ) {
$dateCamelCase = $this->dashesToUpperCase($date);
$setDateFunctionName ='set'.$dateCamelCase.'Attribute';
$this->{$setDateFunctionName} = function() use($date) {
$this->attributes[$date] = date( 'Y-m-d', strtotime( $date ));
};
}
Before answering your specific question, let's first see how Eloquent mutators work.
How eloquent mutators work
All Eloquent Model-derived classes have their __set() and offsetSet() methods to call the setAttribute method which takes care of setting the attribute value and mutating it, if needed.
Before setting the value, it checks for:
Custom mutator methods
Date fields
JSON castables and fields
Tapping into the process
By understanding this, we can simply tap into the process and overload it with our own custom logic. Here's an implementation:
<?php
namespace App\Models\Concerns;
use Illuminate\Database\Eloquent\Concerns\HasAttributes;
trait MutatesDatesOrWhatever
{
public function setAttribute($key, $value)
{
// Custom mutation logic goes here before falling back to framework's
// implementation.
//
// In your case, you need to check for date fields and mutate them
// as you wish. I assume you have put your date field names in the
// `$dates` model property and so we can utilize Laravel's own
// `isDateAttribute()` method here.
//
if ($value && $this->isDateAttribute($key)) {
$value = date('Y-m-d', strtotime($value));
}
// Handover the rest to Laravel's own setAttribute(), so that other
// mutators will remain intact...
return parent::setAttribute($key, $value);
}
}
Needless to say that your models require to use this trait to enable the functionality.
You ain't gonna need it
If mutating dates is the only usecase you need to have "dynamically named mutators", that's not required at all. As you might have already noticed, Eloquent's date fields can be reformatted by Laravel itself:
class Whatever extends Model
{
protected $dates = [
'date_field_1',
'date_field_2',
// ...
];
protected $dateFormat = 'Y-m-d';
}
All fields listed there will be formatted as per $dateFormat. Let's not reinvent the wheel then.
I have this, and it works nicely to give me UK-formatted dates:
protected $dateFormat = 'd-m-Y';
protected $dates = ['purchased', 'warranty_expires', 'scrapped_on', 'location_date', 'user_date'];
However, I also have created and modified (not the standard Laravel ones, but my own) that are Timestamps, not dates. How can I automate the formatting of those two fields when they are retrieved, to something like 'd-m-Y H:i:s' ?
You can do this with Carbon library
public function getFormattedPurchasedAttribute($date)
{
return Carbon::createFromFormat('Y-m-d H:i:s', $date)->format('d-m-Y H:i:s');
}
You have to use carbon class. Write this line in the top where all namespace are used.
use Carbon;
I personally don't use $dateFormat attribute because it changes format for all dates fields. What I would do is creating custom accessors to get formatted fields for example:
public function getFormattedPurchasedAttribute($value) {
return $this->asDateTime($value)->format('d-m-Y H:i:s');
}
so you can now use $model-formatted_purchased to get purchased field formatted in way you want
I've created a few datetime fields in my database, and as is described in Laravel documentation, I can "customize which fields are automatically mutated". However there's no example showing how it can be done, nor is there any search result. What should I do to make certain fields auto mutate?
For example, I created a table called "people" in migration, one of the fields is defined as this:
class CreatePeopleTable extends Migration {
public function up(){
Schema::create("bookings",function($table){
...
$table->dateTime("birthday");
...
}
}
}
And I defined a model for "people" in models:
class People extends Eloquent{
//nothing here
}
If I refer to the birthday of a People instance, it'll be string, instead of DateTime
$one=People::find(1);
var_dump($one->birthday);
//String
The date mutator should be able to convert it directly to Carbon object, but the documentation doesn't say much about how it should be implemented.
In your People model just add this array:
protected $dates = array('birthday');
Laravel's Model.php internaly merges your fields with the default ones like this:
/**
* Get the attributes that should be converted to dates.
*
* #return array
*/
public function getDates()
{
$defaults = array(static::CREATED_AT, static::UPDATED_AT, static::DELETED_AT);
return array_merge($this->dates, $defaults);
}
According to this doc, you can use model member function getDates() to customize which fileds are automatically mutated, so the following example will return Carbon instance instead of String:
$one = People::find(1);
var_dump($one->created_at);//created_at is a field mutated by default
//Carbon, which is a subclass of Datetime
But it doesn't say clearly how to add your own fields. I found out that the getDates() method returns an array of strings:
$one = People::find(1);
echo $one->getDates();
//["created_at","modified_at"]
So what you can do is appending field names to the return value of this method:
class People extends Eloquent{
public function getDates(){
$res=parent::getDates();
array_push($res,"birthday");
return $res;
}
}
Now birthday field will be returned as a Carbon instance whenever you call it:
$one = People::find(1);
var_dump($one->birthday);
//Carbon
What do you mean by: automatically mutated?
If you mean mutated after being retrieved from DB use Accessors and Mutators (Laravel docs).
Add this to your model:
public function getDateAttribute( $date )
{
// modify $date as you want, example
// $date = new \Carbon\Carbon($date);
// $date->addDay()
// return (string)$date
}
As Sasa Tokic says, add protected $dates = array('birthday'); to your People model like so:
class People extends Eloquent{
protected $dates = array('birthday');
}
You can then use Carbon to do clever things to this value, like so:
$people->birthday->format('jS F Y')
PHP's date() function docs (http://uk3.php.net/manual/en/function.date.php) and Carbon's docs (https://github.com/briannesbitt/Carbon) will help here: