Laravel always return date column with carbon - php

Is there a way to always make the dates from a column return using carbon?
Let say I have a Challenges model/table with date_end column.
When I do this call in my ChallengesController it returns all the challenges in json for my app:
public function index()
{
return response()->json([
'status'=>'success',
'challenges' => Auth::user()->challenges,
]);
}
But the date is still MySQL format. I know I can do :
public function index()
{
$challenges = Auth::user()->challenges;
foreach ( $challenges as $challenge){
$challenge['humandate'] = $challenge->date_end->diffForHumans();
}
return response()->json([
'status'=>'success',
'challenges' => $challenges,
]);
}
then get the date via challenge.humandate But is there a cleaner way?

In your model you can declare which columns should be converted to Carbon instances by adding them to the protected $dates array:
protected $dates = ['created_at', 'updated_at', 'date_end'];
Of course the first two are only necessary if you're using the ->timestamps() method in your migrations.

As #Castis points out, you can use attribute mutators and accessors to modify the data is it goes to the model and as it comes from the model. In your case define getCreatedAtAttribute method, for example, and return from it Carbon and the created_at field parsed into it.

Related

Laravel ->with(") is there a way to use it with attributes?

I've been using ->("with) for relationships when querying models like so...
return Model::
where('set_id', $request->set)
->without('set')
->with('user:id,first_name,last_name,profile_photo')
->get();
Which works great for a relationship:
public function user(){
return $this->belongsTo(User::class, 'user_id');
}
But what about doing the same with an attribute?
public function getPermissionsAttribute(){
$permissions = $this->permissions;
$new = [];
foreach($permissions as $p){
$new[$p->permission] = true;
}
return json_encode($new);
}
Is there a way to do this in laravel? As at the moment I'm getting the following error...
App\Models\User::permissions must return a relationship instance. (View: C:\Users\Nick\PhpstormProjects\laravel-vue\resources\views\index.blade.php)
I did have it in:
public $appends = [
'full_name',
'profile_photo_thumb',
'permissions'
......... etc ............
];
But, the list was getting longer and I was sending out data to the front end that largely wasn't used, so was causing longer loading
You can append accessor at Run Time to help you decide what data you want serialized in the response by calling append on your Eloquent Collection:
return $collection->append('attribute');
Or for multiple, you can pass an array:
return $collection->append(['attribute1', 'attribute2']);
Laravel 8.x Docs - Eloquent - Serialization - Append Values to JSON - Appending at Run Time append
Try to get it by select function like this
return Model::where('set_id', $request->set)->with(['user' => function ($query) {
$query->select('id','first_name','last_name','profile_photo');
}])

Laravel replicate including relationships but maintain original timestamps

I am trying to replicate a record and its relationships in Laravel 8. But I want to keep the original records timestamps. I am able to do this on the main record, but the timestamps on all the relationships get set to the datetime that the record is replicated. I also have some observers set up and I dont want them firing when the replication takes place.
$order = App\Models\Order::with('comments', 'tracking_details')->find(4096);
$clone = $order->replicate()->fill([
'tracking_number' => null,
'created_at' => $order->created_at,
'updated_at' => $order->updated_at,
]);
$clone->saveQuietly();
foreach ($order->comments as $comment) {
App\Models\Comments::withoutEvents(function() use ($clone, $comment) {
$clone->comments()->create($comment->toArray());
});
}
foreach ($order->tracking_details as $details) {
App\Models\TrackingDetails::withoutEvents(function() use ($clone, $details) {
$clone->tracking_details()->create($details->toArray());
});
}
Any help would be greatly appreciated.
For relationships, you can use query builder instead.
// main record
$order = Order::find(4096);
$clone = $order->replicate()->fill([
'tracking_number' => null,
'created_at' => $order->created_at,
'updated_at' => $order->updated_at
]);
$clone->saveQuietly();
// comments relationship
$comments = Comments::toBase()->where('order_id', $order->id)->orderBy('id')->get()->map(function ($item) use ($clone) {
unset($item->id);
$item->order_id = $clone->id;
return (array) $item;
});
Comments::insert($comments->toArray());
// tracking_details relationship
$trackingDetails = TrackingDetails::toBase()->where('order_id', $order->id)->orderBy('id')->get()->map(function ($item) use ($clone) {
unset($item->id);
$item->order_id = $clone->id;
return (array) $item;
});
TrackingDetails::insert($trackingDetails->toArray());
In this case you don't work with eloquent, which means that no events will be fired and you will keep the original records timestamps.
Not sure if this will work, but have you tried disabling timestamps in your model?
public $timestamps = FALSE;
If you want your models to be able to save your timestamps via create() method, you should add created_at and updated_at properties to $fillable on your models. Reference: Making properties mass assignable
The created_at and updated_at columns are default timestamps which Laravel provides out of the box, create a cloned_at column default to null in the database and in your database model, add it to a fillable property, then you can simple insert data in the database where created_at and updated_at values will be parsed as a comma separated value, when retrieving the data, you can split and modify the string from CSV and parse it as a Carbon format, it's a dirty but a possible solution.

Laravel model - uncast specific column

I have a laravel table with a column I've defined like this in the migration:
$table->json('json');
And in the model, I cast it to an array:
protected $casts = [
'json' => 'array'
];
This works perfectly the majority of the time I need it, but there's one api call I'm making where I actually want my collection of that Model to give me the raw string rather than casting it to the array.
So, assuming my model is called Model, my api call looks like this:
$systemModels = Model::whereNull('user_id')->get();
$userModels = Model::where('user_id', $user->id)->get();
return response()->json([
'user_models' => $userModels->toArray(),
'system_models' => $systemModels->toArray()
]);
This is where I'd like the 'json' column of my Model to be rendered as a string rather than cast to an array. Is there a reliable way to do that?
Inside your model you can define a custom attribute which is added when the model is serialized:
class YourModel extends Model
{
protected $appends = ['json_raw'];
public function getJsonRawAttribute()
{
return $this->attributes['json'];
// or return json_encode($this->attributes['json']);
}
}
And then when doing the toArray() you can do $userModels->makeHidden('json')->toArray(); to remove the casted field you do not want.

return unix-timestamp format for datetime formatted field in laravel

I have a Message model like this :
class Message extends Model
{
protected $primaryKey = 'message_id';
protected $dates = ['created_at', 'updated_at', 'receiver_deleted_at', 'sender_deleted_at', 'sender_read_at', 'receiver_read_at'];
}
As you can see there are some date fields that hold dates as DATETIME(YYYY-MM-DD HH:MM:SS) format.
But I want when get an instance of model all date fields convert to unix-timestamp format and then return.
I know that can use accessor like this for every field :
public function getCreatedAtAttribute()
{
dd(strtotime($this->attributes['created_at']));
return strtotime($this->attributes['created_at']);
}
But in this case I must define a function like above for each field.
But I want an easier way that force model to return date fields as unix-timestamp format.
protected $casts = [
'created_at' => 'timestamp',
'updated_at' => 'timestamp'
];
In model just add these cast to get unix timestamp
You can override the :
protected function getDateFormat()
{
return 'U';
}
OR
Use timestamp property for each column:
$model->created_at->timestamp
Reference: https://laravel.com/docs/4.2/eloquent#timestamps

Why Accessorized fields are required when fetch a unAccessorized fields in laravel

Suppose I have a Homework Eloquent model like this :
class Homework extends Model
{
protected $primaryKey = 'homework_id';
protected $dates = ['deadline'];
protected $appends = ['deadline_picker'];
protected $fillable = ['lesson_id', 'teacher', 'title', 'desc', 'deadline', 'address', 'size', 'type'];
public function getDeadlineAttribute ($value)
{
return date('d/m/Y H:i', strtotime($value));
}
public function getDeadlinePickerAttribute ()
{
return date('l d F Y-H:i', strtotime($this->attributes['deadline']));
}
}
As you see I define a new Accessor (deadline_picker) for deadline field.
Now I want to select only title of this model like this :
public function Homeworks ()
{
$homeworks =
Homework::where('deadline', '>=', Carbon::now())->get(['title']);
return $homeworks;
//return view('pages/homeworks', compact('homeworks'));
}
But after running above code, bellow error is shown:
ErrorException in Homework.php line 30:
Undefined index: deadline
Seems that in this case, getting deadline field in ->get(['title']); is required (means ->get(['title','deadline']);).
Why it happens?
This is because you've only selected the title attribute out of the database, but your accessor is expecting deadline to always exist too. The reason for this is because when you're returning $homeworks out of your controller, Laravel is converting the models into json (or similar) using the ->toArray() or ->toJson() methods on your model, which is calling your accessor.
You have 2 options, you can either always pull deadline out of the database reguardless of whether you actually want it or not, or you can add an is_null() check to your accessor, something similar to below
public function getDeadlinePickerAttribute()
{
return is_null($this->deadline) ? null : date('l d F Y-H:i', strtotime($this->deadline));
}
Theres one more improvement you could make, as you've supplied the deadline attribute on the $dates property of your model, Eloquent will automatically convert this into a Carbon object allowing you to easily manipulate the date without using strtotime() or any of those nasty older date handling functions.
public function getDeadlinePickerAttribute()
{
return $this->deadline instanceof Carbon ? $this->deadline->format('l d F Y-H:i') : null;
}
More on Carbon http://carbon.nesbot.com/ and date mutators https://laravel.com/docs/5.3/eloquent-mutators#date-mutators

Categories