Carbon Date Being Saved as String in Database - laravel

I am trying to seed some data using Factories. One of the fields I'm trying to seed is a date field -- and I do so with the following code in my factory:
return [
...
'date' => Carbon::now()->subDays(rand(1, 365))->startOfDay()
];
The problem is, this is getting saved in the database as a string -- which means that I CANNOT do the following in my blade templates: {{ $transaction->date->format('M, d Y') }},
When I try that, I get the following error message: Call to a member function format() on string.
Just as a test, I tried in my blade template the same exact code, just switching out created_at for date - and it works as I want. I.e., this works: {{ $transaction->created_at->format('M, d Y') }}.
In case it matters, the created_at field is created using $table->timestamps() in my migration file whereas the date field is created as follows: $table->date('date');.
Any idea what I need to do in order to get this to work?
Thanks.

Laravel provides a method to "cast" certain Model attributes to specific datatypes, including strings, integers, dates, etc. Since Carbon is built in to Laravel, specifying the date type auto-converts Model attributes to a Carbon instance. All you need to do is provide that logic to your model:
class Transaction extends Model {
protected $casts = [
'date' => 'date'
];
...
}
Now, when you retrieve a Transaction model record, the date attribute will automatically be a Carbon instance:
$transaction = Transaction::first();
dd($transaction->date, get_class($transaction->date));
// ^ Carbon\Carbon #1646769789^ {#4514 ... }, `Carbon\Carbon`
Now, you can perform Carbon logic, simply by chaining:
{{ $transaction->date->format('Y-m-d') }}
// `2022-03-08`
More casting types are available, and you can specify multiple attribute casts by simply adding them as key/value pairs to the $casts array. Full documentation is here:
https://laravel.com/docs/9.x/eloquent-mutators#attribute-casting

Related

How can I get the data inside my notifications from within my Controller?

I'm trying to get the numbers of records from my notifications, where the candidate_user_id column from inside the data attribute is the same as the UserId (Authenticated User).
After I dd, I was able to get the data from all of the records in the table by using the pluck method (Line 1). I then tried to use the Where clause to get the items that I need
but this just didn't work, it was still returning all of the records in the table.
DashboardController.php:
public function index()
{
$notifications = Notification::all()->pluck('data');
$notifications->where('candidate_user_id', Auth::user()->id);
dd($notifications);
}
Here is a partial screenshot of the data that is being plucked.
How can I get the data from this, in a way like this ->where('candidate_user_id', Auth::user()->id);?
If data was a JSON field on the table you could try to use a where condition to search the JSON using the -> operator:
Notification::where('data->candidate_user_id', Auth::id())->pluck('data');
Assuming you only want this data field and not the rest of the fields, you can call pluck on the builder directly. There isn't much reason to hydrate Model instances with all the fields to then just pluck a single field from them if it is just a table field, so you can ask the database for just the field you want.
The data in the data field is a json string, so you can tell Laravel to automatically cast it as an array using the $casts property on each of the models that is notifiable.
For instance, if you have a User model which uses the trait (ie has use Notifiable), add this:
protected $casts = [
'data' => 'array',
];
If you want to access all notifications for the auth user.
$user = auth()->user();
dd($user->notifications->pluck('data'));
If you really want to do in your question way, here is how.
$notifications = Notification::all()->pluck('data');
$notifications = $notifications->where('candidate_user_id', Auth::user()->id)
->all();
This assumes you that you did not modify the default laravel notifications relationship and database migration setup. If you have modified some of the default ones, you need to provide how you modify it.

What type of class of datetime fields in laravel

When I fetch data from database using Laravel, I got some timestamp field like 'created_at', 'updated_at',....
What type of class for these fields, what type of class which return true of expression $created_at instanceof DatetimeClass
Thanks
Recent Laravel uses (Carbon library v2)
for that type of data (Illuminate\Support\Carbon).
As for ... instanceof DatetimeClass part of your question, I am not sure what really you refer to as DatetimeClass class, but I assume that you meant built-in DateTime class. If that's so, you can get DateTime from Carbon object by calling toDateTime(), i.e.:
$dt = $model->created_at->toDateTime();
Alternatively, edit your ... instanceof ... uses to test against Carbon.
Please also have a look at date mutators in Laravel. Laravel will automatically cast a timestamp(created_at, updated_at) or when you need to define custom date fields in the db you can define these in the model: https://laravel.com/docs/7.x/eloquent-mutators#date-mutators
If you do not want to automatically cast you can parse a string representation of a date using $carbonObject = Carbon::parse('2020-02-03');

Laravel Date Mutator Requires Parsing?

By default, Eloquent will convert the created_at and updated_at columns to instances of Carbon. When retrieving attributes that are listed in the $dates property, they will automatically be cast to Carbon instances, allowing you to use any of Carbon's methods on your attributes.
I have the following in dates property - i've not included the created_at and updated_at columns as these are converted by default as per above:
protected $dates = ['deleted_at'];
Then I have the following accessor on the model:
public function getCreatedAtAttribute($datetime)
{
return $datetime->timezone('Europe/London');
}
However the above throws the following error:
Call to a member function timezone() on string
If I change the method to the following it works:
public function getCreatedAtAttribute($datetime)
{
return Carbon::parse($datetime)->timezone('Europe/London');
}
The question is why do I need to parse it since it's suppose cast it to a carbon instance when it's retrieved according to docs https://laravel.com/docs/6.x/eloquent-mutators#date-mutators ?
That completely depends on what $datetime is and how you're passing it this this function. It's clearly a string, and not a Carbon instance, but you didn't include the definition for $datetime in you question, so I can only speculate.
That being said, I haven't see mutators that use an external variable, as they are generally designed to access properties of the class you're applying them to, via $this:
public function getCreatedAtAttribute(){
return $this->created_at->timezone('Europe/London');
}
The only caveat I could see with this is naming conflict when trying to use $model->created_at. It should handle it, but something like getCreatedAtTzAttribute(), accesses via $model->created_at_tz might be necessary if you come across issues.
If you check the source code (here), you'll see that accessors have priority over date casts.
If Eloquent finds an accessor for your date attribute (getCreatedAtAttribute), date casting will be ignored. So you'll need to cast it manually within your accessor.

Laravel custom attribute not being mutated to Carbon date

I'm having problems getting Laravel to cast a custom attribute as a Carbon date. Here's an example model:
class Organisation extends Model
{
protected $dates = [
'my_date'
];
public function getMyDateAttribute()
{
return "2018-01-01 00:00:00";
}
}
I'd expect my_date to be cast to a Carbon date however if I do dd($organisation->my_date) it just returns the date as a string.
I've seen people suggest to just return a Carbon instance from the custom attribute, and this partially works, the my_date attribute is availabe as a Carbon instance within the application, however if you then return the model as Json you end up with:
{
"name": "Big Business",
"my_date": {
"date": "2018-01-01 00:00:00.000000",
"timezone_type": 3,
"timezone": "Europe/London"
}
}
Instead of the desired:
{
"name": "Big Business",
"my_date": "2018-01-01 00:00:00"
}
Has anyone else come across this and if so have you found a solution?
Update
Upon further investigation I've tracked down the problem (but I don't have a solution yet). When you return an Eloquent model the __toString magic method which runs the toJson method and as you trace down the chain it serializes any Carbon dates in the $dates variable. It also completely skips over this serialization for mutated attributes, which is what I'm seeing.
I need to find a way to seralize mutated attributes that return a Carbon date when __toString is called.
edit your Organization model to this:
use Carbon\Carbon;
class Organisation extends Model
{
public function getMyDateAttribute($value)
{
//you can manipulate the date here:
$date = Carbon::parse($value,config('timezone'));
return $date;
}
}
You if your model represents a table, you can change the data type to timestamp and laravel will put that attribute into a carbon object.
Once the attribute is a carbon object, you can change the format in the blade view.
{{ $organisation->mydate->format('Y-m-d') }}
If either cannot change the data type, or you need to a default format different from a timestamp you can use the 'cast' eloquent model property.
class Organisation extends Model{
protected $cast = [
'mydate'=>'datetime:Y-m-d H:i:s'
];
}
Casting the attribute effectively works the same as an accessor that wraps the date value with a carbon object. This is much cleaner way to write it, however.
As far as timezone is concerned, you should look to change that in the config/app.php file.
Here is the documentation.... https://laravel.com/docs/5.8/eloquent-mutators#attribute-casting

Convert timestamp to date in Laravel?

How can I return from the database all rows with a timestamp converted to a date (d-m-Y H:i), using the all() method?
It's not created_at or updated_at column, it's custom attribute, call it birthday if you wish.
timestamp columns like created_at are being parsed first. the U is simply the timestamp format. you can return your own format. for other formats see date docs.
edit: as stated in my comment, getDateFormat() is for both ways (insert, selects). your best bet would be using format inside the model. example:
public function getMyBirthdayAttribute()
{
return $this->my_birthday->format('d.m.Y');
}
use $model->my_birthday to call the attribute.
// controller
$posts = Post::all();
// within sometemplate.blade.php
#foreach($posts as $post)
my formatted date: {{ $post->my_birthday }}
#endforeach
The better way to manage dates with Laravel IMHO is to use the getDates accessor that is build into Laravel.
All you need to do is to set a method on your Model as such.
public function getDates()
{
return [
'my_birthday',
'created_at',
'updated_at',
];
}
This will return a Carbon object. Carbon is insanely awesome for manipulating dates.
It is what Laravel does by default to created_at and updated_at.
You can then do things like:
$my_birthday->diffForHumans() // 2 Days ago
$my_birthday->format('d.m.Y') // 20.02.1979
There are tons of helpers here for you. Check out the Carbon docs: https://github.com/briannesbitt/Carbon
Definitely worth learning. It might not be easy to understand for a beginner. However, I would advise you take a look and get your head around it. It will change your life - Dates can be a pain!
In this case, you need to convert the date within the query, if mysql is being used as your database then you may use a raw query like this:
// Assumed my_birthday is like 1255033470
$raw = DB::raw("date_format(from_unixtime(my_birthday),'%b %d, %Y %l:%i %p') as dob");
$result = Model::get(array('id', 'username', $raw));
You may write it within single line:
$result = Model::get(array('id', 'username', DB::raw("...")));
Just place this on your code
$post it depend on what you create
$post ->created_at->format('d.m.Y')

Resources