Laravel model - uncast specific column - php

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.

Related

laravel model mutators not accessing the defined field

I am trying to define a model mutator
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Custom_fields extends Model
{
protected $table = 'custom_fields';
public function setContentAttribute($value){
return unserialize($value);
}
}
and on test controller, tried
Custom_fields::all()[0]->content;
Unfortunately, it is not working; the value is still not an array (serialize)
I want to access the field content and unserialize the value of the field so I can get an array automatically on retrieve, but it's not working.
You're trying to "get an array automatically on retrieve" with a method called setContentAttribute. You are confusing accessors and mutators. As the method name implies, the mutator is called when you set the content attribute.
To create an accessor that is called when you get the content attribute:
public function getContentAttribute($value)
{
return unserialize($value);
}
Likely if this data is to be stored in a serialized form you want your mutator to look like this:
public function setContentAttribute($value)
{
$this->attributes["content"] = serialize($value);
}
But really, you should not be using these serialization functions at all. Laravel can natively convert objects to JSON for storage in a database. Simply define the property in your model's $casts property and data will be converted automatically when getting or setting the property.
protected $casts = [
"content" => "array",
];

After create - why does it not return relationship?

When creating an entry using create() - Why does it return a relationship of pilot_id table instead of just showing the value of pilot_id?
For example, in the repository class:
public function createFlight()
$flight = App\Flight::create([
'name' => 'Flight 10'
'pilot_id' => 4
]);
return $flight;
}
In the controller:
public function show()
{
$data = $this->flight->createFlight();
return $data
}
It would return json on the screen but it is not showing the relationship table (pilot) of pilot_id.
Try adding the following to your Flight model, like so. By default you need to tell Laravel to include any relationships.
protected $with = ['pilot'];
That will make it so everytime it includes the relationship. If this is not desirable, then you will want to load the relationships when you return the flight, like so.
return $flight->load(['pilot']);
It shows pilot_id is 4 because that's what its value is. Did you create a relationship on the Flight so that Laravel knows how to retrieve the model for Pilot? It should look something like this:
public function pilot()
{
return $this->hasOne('App\Pilot');
}
When you return a model directly from the controller, it invokes the toJson() method to convert the object to a string. If you want to append the contents of a related model you can do so by adding the relationship to the $with variable on the Flight model.
protected $with = ['pilot']

Eloquent $appends attributes returning null

I am loading some data into an Eloquent model via an appended attribute and the returned model's attribute is always null. I have protected $appends = array('survey_status); defined in my model (named Survey), and have the accessor defined as such:
public function getSurveyStatusAttribute($value){
return $value;
}
I have tried setting the attribute both as a property and in bracket notation($this->survey_status = ... & $this->attributes['survey_status'] = ..) and also tried using the setAppends() method prior to returning the model, all to no avail.
This was reported on the Laravel forums back at the end of Sept. 2013 and reported as fixed that Oct. (see: https://github.com/laravel/framework/issues/2336 and http://laravel.io/forum/02-26-2014-model-attribute-accessor-not-working-with-object-get-helper-function)
I am on the most current version of Laravel 4 (v4.2.17) which was released in Feb of this year. And from what I read in the docs and elsewhere it seems as though I'm doing everything correctly. Can any see something I'm not doing or confirm this is still an issue?
UPDATE
So I think I figured out 75% of my issue. I didn't realize you could pass an array to $model->load() to make complex queries using where/orWhere/etc. So this basic example works:
$survey = Survey::find(168);
$survey->load(array('surveyStatus' => function ($q){
$q->where('organization_id', '=', 7485);
}));
return Response::json($survey);
In the response my SurveyStatus model is supplied. My issue now is I am trying to iterate of a collection of Survey models to add a SurveyStatus relation just like the working one above but the attribute isn't there on the response. This is what I'm using to iterate the collection:
$org->surveySets->each(function ($ss) use ($id){
$fye = $ss->fiscal_year_end;
$ss->surveys->each(function ($survey) use ($id, $fye){
$sid = $survey->id;
$survey->load(array('surveyStatus' => function ($q) use($id, $fye){
$q->where('organization_id', '=', $id)->where('fiscal_year_end', '=', $fye);
}));
$survey->content_groups->each(function ($cg) use ($id, $sid, $fye){
$cg->content_rows->each(function ($cr) use ($id, $sid, $fye){
$cr->questions->each(function ($q) use ($id, $sid, $fye){
// do the same thing as surveyStatus but load surveyData relation into question
});
});
});
});
});
Is there some reason the loading doesn't 'stick' when iterating over a collection?
Correct me if I'm wrong, but appends doesn't get passed a $value because it's not mapped to a table column. I always thought of it as a computed property of sorts.
Given we have fillable columns 'first' and 'last' we might create an appends called 'fullname'.
protected $appends = [
'fullname'
];
public function getFullnameAttribute()
{
return $this->attributes['fullname'] = $this->first . ' ' . $this->last;
}
Essentially what I think your confusing is that appends is extra data that your appending to your model attributes. You are expected to return a value from the accessor. Returning $value will be null because $value doesn't exist, which is why your manually appending it. Try returning 'foo' in your accessor then you'll see what I mean.
Hello if you want to append some extra data related to another model you could do something like this.
protected $appends = [
'catName'
];
// relation
public function category()
{
return $this->hasOne(PostCat::class, 'id', 'id_cat');
}
//getters
public function getCatNameAttribute()
{
return $this->category()->getEager()->first()->name;
}
If your related model hold many db row considere this
protected $with = [
'category'
];
protected $appends = [
'catName'
];
// relation
public function category()
{
return $this->hasOne(PostCat::class, 'id', 'id_cat');
}
//getters
public function getCatNameAttribute()
{
return $this->category->name;
}
best regards

Laravel 4 Present Model

In Laravel 4, I have a model linking to a database table. Let's call it Model.
Say that this model has a database column called Property A and Property B.
When I make get request call to my model, i.e. Model::all() or Model::find($id), I don't want to return Property A or Property B, but some kind of function of the two, which appears to the front-end as some kind of read-only field, i.e. Property C.
Do I need to use a presenter library here, or is there a way by overriding model functions within Laravel 4?
The key for me here is that the property shows up when I call Model::all()
EDIT:
From my understanding this should return an attribute with the name foo constantly with the value "foo":
Model
class DiscountLink extends Eloquent {
protected $table = 'discountLinks';
protected $hidden = array('tag');
protected $fillable = array('name', 'currency', 'language', 'price', 'instalments', 'expires', 'active', 'foo');
public function getFooAttribute()
{
return "foo";
}
}
Controller
class DiscountLinkController extends \BaseController {
public function index()
{
return DiscountLink::all();
}
}
Use an accessor in your Model. To concatenate A and B, for instance:
public function getPropertyCAttribute()
{
return $this->attributes['property_a'] . ' ' . $this->attributes['property_b'];
}
And then you can access Model::find($id)->propertyC.
If you want the attribute to be automatically included in your model's results array (e.g. if you're sending the results of Model::all() or Model::get() as JSON, for example), add an $appends declaration to the top of your model:
protected $appends = array('PropertyC');
If the function is something that can be done in the database (like concatenation, sum, etc.), you could also add a DB::raw command to your query, like:
Model::select(*, DB::raw('CONCAT(PropertyA, " ", PropertyA) AS PropertyC'))->...

Laravel Eloquent Save to DB Using Create - Unhelpful Error

I get a very unhelpful error when I try and insert a new record in to my db. Does anyone have any idea where to start to solve this error?
user_id
That's all it says. (This is the name of one of my required fields in the table I'm saving to but it's not very descriptive.)
For reference here's my code:
$data = array(
'user_id'=>1,
'post_type'=>'0',
'post_title'=>'blah',
'description'=>'blah');
//it fails on this line
$id = Post::create($data);
Here's what my model looks like:
class Post extends Eloquent{
public static $rules = array(
'user_id'=>'required',
'post_type'=>'required',
'post_title' => 'required|max:25',
'description'=>'required'
);
public static function validate($data){
return Validator::make($data, static::$rules);
}
public function user(){
return $this->hasOne('User', 'id', 'user_id');
}
}
Tried getting rid of all relationships and validation in the model. That didn't work.
This is called mass-assignment in Laravel, what you need to do to get it working on your model is to set a protected array called $guarded to be empty. Add this to your model.
protected $guarded = array();
What this array does, it can prevent attributes to be mass-assigned with an array, if you don't want an attribute to be filled with the Model::create() method, then you need to specify that attribute in the $guarded array.
If instead you want to specify only the fillable attributes, Laravel's Eloquent also provides an array called $fillable where you specify only the attributes that you want to fill via the mass-assigment way.
Further reading:
Mass Assignment:
http://laravel.com/docs/eloquent#mass-assignment
Create Method:
http://laravel.com/docs/eloquent#insert-update-delete

Categories