timestamp column is not in ISO 8601 format - laravel

I want all the dates from the server to be sent in the ISO 8601 standard in Laravel. Laravel version is 9. The created_at and updated_at columns are sent in the wanted format but my custom column is not in that format.
Here is the code of the Seeder
"questions_updated_at" => Carbon::now()->format('Y-m-d H:i:s'),
'created_at' => Carbon::now()->format('Y-m-d H:i:s'),
"updated_at" => Carbon::now()->format('Y-m-d H:i:s')
Also tested with now()
In MySQL their values are same. Migration
$table->timestamp("questions_updated_at");
$table->timestamps();
In the Model. protected $dateFormat = \DateTime::ISO8601;
In Controller return response(MyClass::where('show_on_front', true)->get());
And in Postman getting it like this.
[{
"questions_updated_at": "2022-11-16 15:02:44",
"created_at": "2022-11-16T15:02:44.000000Z",
"updated_at": "2022-11-16T15:02:44.000000Z"
}]
In app.php the time zone is 'timezone' => 'UTC'
Help will be appreciated.

You have to use casts in order to change the type of the data fetched from the data,
in the model file
protected $casts = [
'questions_updated_at' => 'datetime',
];
the timestamp is stored as timestamp and it is fetched as string from database , casts is used to change it to the prefered DateTime format , the created_at and updated_at are already casted in HasAtttributes trait

Related

How to fix upsert problem when seeding? (laravel)

I have these code below, all seems working but when I try to run unit test it returns an error below.
Here is my seeder (this seeder is called many times in different test cases):
DB::table('sizes')->upsert([
[
'name' => 'jumbo',
'created_at' => date("Y-m-d H:i:s"),
'updated_at' => date("Y-m-d H:i:s"),
],
[
'name' => 'large',
'created_at' => date("Y-m-d H:i:s"),
'updated_at' => date("Y-m-d H:i:s"),
]
], ['id'], ['name']);
And the errors pops out:
Illuminate\Database\QueryException: SQLSTATE[23000]: Integrity constraint violation: 19 UNIQUE constraint failed: sizes.name (SQL: insert into "sizes" ("created_at", "name", "updated_at") values (2021-05-10 12:52:18, jumbo, 2021-05-10 12:52:18), (2021-05-10 12:52:18, large, 2021-05-10 12:52:18) on conflict ("id") do update set "name" = "excluded"."name")
Here is the migration:
Schema::create('sizes', function (Blueprint $table) {
$table->id();
$table->string('name')
->unique();
$table->timestamps();
});
Your migration will result in such table:
id INT AUTO_INCREMENT PRIMARY_KEY
name VARCHAR UNIQUE
created_at TIMESTAMP
updated_at TIMESTAMP
Your seeder when run first time will insert such records:
id
name
created_at
updated_at
1
jumbo
...
...
2
large
...
...
Now, based on laravel's documentation on upsert:
If you would like to perform multiple "upserts" in a single query, then you should use the upsert method instead.
The method's first argument consists of the values to insert or update, while the second argument lists the column(s) that uniquely identify records within the associated table.
The method's third and final argument is an array of the columns that should be updated if a matching record already exists in the database.
The upsert method will automatically set the created_at and updated_at timestamps if timestamps are enabled on the model:
The important point is:
The method's first argument consists of the values to insert or update,
while the second argument lists the column(s) that uniquely identify records within the associated table.
The method's third and final argument is an array of the columns that should be updated if a matching record already exists in the database
That means, your command:
DB::table('sizes')->upsert([
[
'name' => 'jumbo',
'created_at' => date("Y-m-d H:i:s"),
'updated_at' => date("Y-m-d H:i:s"),
],
[
'name' => 'large',
'created_at' => date("Y-m-d H:i:s"),
'updated_at' => date("Y-m-d H:i:s"),
]
], ['id'], ['name']);
Will do this:
check if any record have id of (blank) => no record will match (so upsert will become insert instead)
insert into database, value name=jumbo, and insert into database, value name=large,
this second step will fail since there's already record on database that have name=jumbo (and another record with name=large)
remember that you have name VARCHAR UNIQUE constraint, and this second step violates the UNIQUE constraint
Instead, you should change your seeder into this:
DB::table('sizes')->upsert([
[
'name' => 'jumbo',
'created_at' => date("Y-m-d H:i:s"),
'updated_at' => date("Y-m-d H:i:s"),
],
[
'name' => 'large',
'created_at' => date("Y-m-d H:i:s"),
'updated_at' => date("Y-m-d H:i:s"),
]
], ['name'], ['created_at','updated_at']);
The edited version will do this:
check if any record have name of "jumbo"
no record will match initially (so upsert will become insert first time),
and for subsequent run will match (so upsert will become update for subsequent runs)

Laravel cast for created_at date field not being applied

I have a created_at column in my user model that I would like to format differently when accessing it in my blade template, so it is more readable. It seems like the best way to go about this is to use a cast.
I created a cast like so:
protected $casts = [
'email_verified_at' => 'datetime',
'created_at' => 'm-d-y',
];
... in my controller, I am getting a User and then doing:
dd($member->toArray());
Now, when getting a user record and doing $user->toArray() the created_at column is still in the original uncasted format, it seems the cast isn't being used at all, any idea why this is?
You can define a format for date columns using following snippet(in model):
protected $dateFormat = 'm-d-Y';
In case of updating individual timestamp fields use the following.
protected $casts = [
'created_at' => 'date:m-d-Y',
// 'updated_at' => 'datetime:Y-m-d H:00',
];

How change updated_at from timestamp type to datetime?

Now field updated_at is as datetime(3), so it invokes an error:
errors: "Trailing data"
Because Laravel expects timestamp type instead datetime. How to say Laravel that it is datetime field?
Log file is:
[2019-06-02 14:41:07] local.ERROR: Trailing data {"userId":8,"exception":"[object] (InvalidArgumentException(code: 0): Trailing data atCarbon\\Traits\\Creator.php:537)
[stacktrace]
esbot\\carbon\\src\\Carbon\\Traits\\Creator.php(559): Carbon\\Carbon::rawCreateFromFormat('Y-m-d H:i:s', '2019-06-07 00:0...', NULL)
I tried this:
const UPDATED_AT = "AT_lastupdateuser";
protected $casts = [
'AT_lastupdateuser' => 'datetime',
];
You can cast an attribute to your model by adding a specific key/value to the $cast array of your model. See here for more details: https://laravel.com/docs/5.8/eloquent-mutators#attribute-casting
In your case you should add the following array to your model:
protected $casts = [
'updated_at' => 'datetime',
];

Laravel Query: get models where child's date ranges is not in array of date ranges

I have Event model that hasMany childs Time models.
Time is a datetime range that has start_time and end_time fields.
How to get all Events where none of childs Times crossing with an array of date ranges?
Example:
$events = Event::notInDateRange([
[ 'start_date' => '2000.01.01 00:00:00', 'end_date' => '2000.01.01 12:00:00' ],
[ 'start_date' => '2000.01.02 12:00:00', 'end_date' => '2000.01.02 16:00:00' ],
[ 'start_date' => '2000.01.03 10:00:00', 'end_date' => '2000.01.03 12:30:00' ],
])->get();
// In this case I don't want to get Event where one of Time is
// [ 'start_date' => '2000.01.03 12:00:00' => 'end_date' => '2000.01.03 14:00:00' ]
You could create a new query scope to create a notInDateRange scope in your Event model and use the whereNotBetween where clause inside the new scope.
Inside your Event model class, define a new function called scopeNotInDateRange that accepts a start and end date and define it as follows:
public function scopeNotInDateRange($query, $start_date, $end_date)
$start = new Carbon($start_date);
$end = new Carbon($end_date);
return $query->whereNotBetween('start_date', [$start, $end])
->whereNotBetween('end_date', [$start, $end]);
Also remember to add use Carbon\Carbon; to the top of your Event class. We will use Carbon to convert strings to dates.
You could then use your new query scope like App\Event::notInDateRange($start, $end). You can also chain scopes, so in your example you could use:
$events = Event::notInDateRange('2000-01-01 00:00:00', '2000-01-01 12:00:00')
->notInDateRange('2000-01-02 12:00:00', '2000-01-02 16:00:00')
->notInDateRange('2000-01-03 10:00:00', '2000-01-03 12:30:00')
->get();
Note that I also changed the .'s you used in your dates to -'s so that Carbon would be able to convert the strings to dates.
If you haven't yet, also ensure that your start_date and end_date columns are converted to Carbon dates using Date Mutators when you use access their values in Laravel. To do this, add the following snippet to your Event model class:
/**
* The attributes that should be mutated to dates.
*
* #var array
*/
protected $dates = [
'start_date', 'end_date'
];

Laravel 5.2 - How to update "updated_at" field with UTC timestamp?

I'm using Laravel 5.2 (on PHP version 7) and below is the update query I'm running:
$data=DB::table('posts')
->where("id", $post_id)
->update(
array(
'title' => $request['title'],
'body' => $request['body'],
'slug' => str_slug($request['title']),
'updated_at' => DB::raw('CURRENT_TIMESTAMP')
));
The "updated_at" field updated with the current time. But I want to update it with the UTC timestamp. How can I achieve this?
If you use the model->save() method it should handle created and updated dates for you. In general you should not need to do raw DB updates in Laravel.
If you wish to have a different timezone for this then look at the timezone setting in app.php. The default however is 'UTC' so you should not have to worry.

Resources