Always modify returned values from laravel model - php

So I have a model, Post that has no methods defined within it.
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use DB;
class Post extends Model
{
}
From a controller, I make calls to that model like:
return view('pages.post', ['post' => Post::where('url_route', '=', $url_route)->first()]
This works fine, but I now want to format the date column that is returned from that request, every time that model is called. Is there a way to modify the returned array without defining a new method?
I am new to Laravel to thanks for the help. Just trying to figure out the most efficient way of doing things within the framework...

If your model has $timestamps set to true, the created_at and updated_at fields are natively a Carbon instance.
This means you can format the date in the view like this as a basic example:
$post->updated_at->format('Y-m-d H:i:s')
Carbon instances allow you to leverage its extensive api as you can see at http://carbon.nesbot.com
If you would like to do the same for another field other than created_at and updated_at, you can add an extra property in your model:
protected $dates = ['added_on']
The fields you specify in the array will be treated as Carbon instances.

Related

Using soft delete in laravel 5 issue

This is my model:
namespace App\Models\Admin;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Image extends Model
{
use SoftDeletes;
//table
protected $table = 'images';
}
If I add my trait I cannot find any record if I use my model like this:
$imageFile = ImageModel::where('id', 12)->first();
The $imageFile is always null, if I remove my trait is working . Why ???
From what you've said, it is working as intended. When you use the soft-delete trait in Laravel, the model is NOT deleted from the DB. When you pass the model to destroy(), the only thing that happens is that the deleted_at field becomes non-null.
If you look deeper into Laravel's code, when you call
ImageModel::where('id', 12)->first();
the softdelete trait is adding
where null
to the SQL for the deleted_at column. This means that, as you said, if you turn off the trait, $imageFile will not be null (it is working). Because the softdelete never deleted the model from the DB - it just added a non-null value to the DB for that model and is thus visible to a normal laravel query: when you don't use softdeletes, Laravel doesn't care about the deleted_at field so it sees the model. When you turn softdeletes on, it looks for null values only, and because your model was softdeleted (it has a non-null value), it returns $imageFile as null.
As bytewave said, to properly use the softdeletes to NOT return a null value to $imageFile, you would need to add in the softdeleted models to your query like so:
Image::withTrashed()->where('id', 12')->first();
I think you were looking for slightly different functionality (a roll-back), which is a little different from the softdelete trait's intention. The manual is pretty good: 5.4 soft-deletes, but looking deeper into the trait code might help as well.
Take a look at the ->restore() function as well - this might help you get closer to the intended roll-back you were looking for. But, you need the logic up-front first to know which were deleted.

Mutate all dates on getAttributeValue automatically

I'm trying to create a sort of global mutator for any date string that is retreived from the database.
Now this is probably the wrong way of going about it (and doesn't even work), but I created a class called App\Http\Mutators\ModelMutator which extends Eloquent\Model.
I then changed the Eloquent alias in app.php to point to this new class.
Here is the code for the new class:
<?php
namespace App\Http\Mutators;
use Illuminate\Database\Eloquent\Model;
class ModelMutator extends Model
{
function getAttributeValue($key)
{
$value = parent::getAttributeValue($key);
if (strtotime($value)) {
$value = tolocal($value);
}
return $value;
}
}
This isn't working at all. In fact it appears it isn't even getting called whenever something is retrieved from the database.
I just want to have any date string retrieved from the database to apply the tolocal function.
My biggest issue is I am using packages outside of the App\ namespace and do not want to have to write mutators and modify every single package extends Model class.
Eloquent already has this functionality. Use the $dates property:
protected $dates = [
'created_at',
'updated_at',
'deleted_at'
];
By default, Eloquent will convert the created_at and updated_at columns to instances of Carbon, which extends the PHP DateTime class to provide an assortment of helpful methods. You may customize which dates are automatically mutated, and even completely disable this mutation, by overriding the $dates property of your model
https://laravel.com/docs/5.4/eloquent-mutators#date-mutators

How to override result of hasMany relationship?

Imagine I have a couple of simple objects like this:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function posts()
{
return $this->hasMany("App\Post");
}
}
class Post extends Model
{
public function user()
{
return $this->belongsTo("App\User");
}
}
We'll say the \App\Post object has an database column called jsondata which contains JSON-encoded data. When I want to display the user's posts in a view with that column decoded, I need to do this in the controller:
$posts = Auth::user()->posts()->get();
foreach ($posts as $post) {
$post->jsondata = json_decode($post->jsondata);
}
return view("user.show", ["posts"=>$posts]);
Is there a way to avoid that foreach loop in my controller and do the JSON decoding at a lower level?
I'm sure I could do this in App\User::posts() but that doesn't help other places where I need to display the decoded data. I tried defining App\Post::get() to override the parent method, but it doesn't work because hasMany() doesn't seem to return an instance of the model at all.
It can be done in different places/ways, but I would suggest to use an append for this property in your model if you want this data is always decoded everywhere and every time you retrieve a Post model, or simply a mutator.
see https://laravel.com/docs/master/eloquent-mutators
In your model you can define:
protected $appends = [
'name_of_property'
];
// calculated / mutated field
public function getNameOfPropertyAttribute()
{
return jsondecode($this->jsondata);
}
You then can always access this property with:
$post->name_of_property
Note the conversion from CamelCase to snake_case and the conversion from getNameOfPropertyAttribute > name_of_property. By default you need to respect this convention to get it working automagically.
You can substitute the name_of_property and NameOfProperty with what you want accordingly.
Cheers
Alessandro's answer seemed like the best one; it pointed me to the Laravel documentation on accessors and mutators. But, near the bottom of the page is an even easier method: attribute casting.
In typical Laravel fashion, they don't actually list all the types you can cast to, but they do mention being able to cast a JSON-formatted database column to an array. A little poking through the source and it turns out you can do the same with an object. So my answer, added to the App\Post controller, is this:
/**
* The attributes that should be casted to native types.
*
* #var array
*/
protected $casts = ["jsondata"=>"object"];
This automatically does the decode and makes the raw data available. As a bonus, it automatically does a JSON encode when saving!

Laravel updating a hasMany relationship

In my system users can have many timesheets, timesheets can have many data.
I'm trying to update the data rows in the data_timesheet table. I'm using this:
$data = ['column_1' => 'value'];
$this->findTimesheetById($id)->data()->saveMany($data);
However, it's giving me the following error:
Argument 1 passed to Illuminate\Database\Eloquent\Relations\HasOneOrMany::save() must be an instance of Illuminate\Database\Eloquent\Model, array given
I know why it's giving me the error, because I need to send it a model as opposed to an array. How can I send in a model, when the values being passed are from a user form?
EDIT:
I can do this which works, but there must be a better way?
$data = $this->findTimesheetById($id)->data();
$data->delete();
$data->insert($data);
The way to do that is:
1) You create a file model and put it in the Models folder. That model can even almost empty, like this:
<?php namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Session;
use DB;
class Booking extends Model{
2) Then in the Controller you create an instance of that Model, like this
$theinstance = new Booking;
3) Then you use that object to have the values given, like this:
$theinstance->name = $name;
$theinstance->telephone = $telephone;
$theinstance->save();
That would be the way to save data.
Now, with regards to the update, at the Controller you can have something like this:
$photosslide = Property::returnPhotosSlide($id);
$propertydetails = Property::returnPropertyDetails($id);
$alsointown = Property::returnAlsoInTown($id);
return view('detailsproperty', compact('photosslide','propertydetails','alsointown'));
This sends the values from a Form into the Model
and once you are in the Model, just do an Update query.

Store dates using Eloquent/Laravel

What's a good way of storing a date in a Eloquent Model? I'm using PHP with Laravel framework.
class MyModel extends Eloquent {
// I want a date field here... should it be:
public $endTime = ?;
}
Should I use a integer timestamp which gets saved as an int? What's the behaviour if I use a Date object in a atributte and save that object?
If you are using migration then use $table->timestamp('endTime'), this will create a timestamp field by using endTime as it's name.
By default, Eloquent will convert the created_at, updated_at, and
deleted_at columns to instances of Carbon, which provides an
assortment of helpful methods, and extends the native PHP DateTime
class. You may customize which fields are automatically mutated, and
even completely disable this mutation, by overriding the getDates
method of the model.
Add this method in your model:
public function getDates()
{
return array('created_at', 'updated_at', 'deleted_at', 'endTime');
}
Laravel will take care of these fields and by default each of these fields will be an instance of Carbon object. Read about date mutators. In this case all methods of Carbon could be used on these fields.

Categories