Laravel Eloquent Save to DB Using Create - Unhelpful Error - php

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

Related

Remove specific field from model before creating after processing laravel

I am trying to make a trait for storing images for models. I am not able to remove the thumbnail from the model.
Is there any way to remove the thumbnail field from the model because there is no field like a thumbnail in the table?
trait ModelHelpers
{
protected static $thumbnail;
public static function boot()
{
parent::boot();
self::creating(function($model){
$collection = collect($model);
self::$thumbnail = $collection->only('thumbnail');
$collection->except(['thumbnail']);
$model->ignoreField('thumbnail');
// ... code here
});
}
}
OR
Is there any way to add the data in the model that don't process while mysql query but is available in the model for processing before or after creating?
Right now I am adding the thumbnail key in fillable to get into the model but it is processed while the insert query that i don't want to:
protected $fillable = ['user_id', 'title', 'meta_title', 'slug', 'summary', 'published','published_time', 'thumbnail'];
$fillbale is used to define the properties you want to use in inserting.
If you want to skip some properties to insert it, you must use $guarded

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.

How to authorize then update a Laravel model

I'm calling this controller to update a model:
public function update(Request $request, $id)
{
$question = Question::find($id);
$this->authorize('edit', $question); // uses Laravel's built-in Policy framework
$updateArray = [
'question' => $request->question,
'type_based_id' => $request->type_based_id,
];
//$question = Question::where('id', $id);
$question = $question->update($updateArray);
// Add the id into the array for return to the client
$updateArray["id"] = $id;
return ['message' => 'Question Updated', 'data' => $updateArray];
}
The code above throws a MassAssignmentException on the call to $question->update(). If I uncomment $question = Question::where('id', $id); it works.
I did some logging, and it seems that find() returns an instance of my model (App\Question) and where() returns a builder (Illuminate\Database\Eloquent\Builder)
How can I satisfy both authorize() and update() without making two separate database requests?
Thank you!
The reason it works using the Query Builder is because it by-passes the mass assignment checks of the model. You are running your own query and not using the Model's update method.
Question::where()->update is calling update on the Query Builder, not the Model.
There is no reason to use the query builder when you already have the model instance you are updating, but this isn't actually running any additional SQL queries.
MassAssignmentException usually means one of the attributes you're passing is guarded in the model. To unguard attributes, either remove them from the $guarded property or add them to the $fillable property of the model. Do NOT use both $guarded and $fillable, you must use one or the other. Read the full documentation here:
https://laravel.com/docs/5.5/eloquent#mass-assignment
MassAssigntmentException is due to the fields you are updating not being fillable and therefor being guarded from assignment, to achieve this you need to set these on the Question class.
public class Question
{
protected $fillable = [
'question',
'type_based_id',
];
}

Laravel - Model ID guarding

Say we have a Topic submission html form which uses the following input fields names:
name
text
At the controller level we may have written something like this:
public function create(Request $request) {
// Validation logic ...
Topic::create($request->all());
}
What if a client user add an id input field:
id
name
text
Will Laravel populate also the id field of the new Model object?
Can I guard the id of a Model, or should I use $request->only()?
You do not need to use $fillable array or $guarded for primary key which is id by default. Eloquent will not populate primary key when you're using create() method.
Laravel has two ways of achieving this:
class Topic extends Model {
protected $fillable = [
"name" , "text"
];
}
Alternatively there is the opposite:
class Topic extends Model {
protected $guarded = [ "id" ];
}
$fillable contains what is allowed to be mass assigned (via fill or the constructor etc) in the model and $guarded contains what should never be mass assigned.
More info at https://laravel.com/docs/5.5/eloquent#mass-assignment

Get specific columns using with() in laravel with appends fields in model

In my User model, I have an appends fields:
protected $appends = [
'is_admin'
];
It will appends is_admin field in every request by using with() eager loading. However, in some scenario I don't want to return is_admin field, I am trying to use the follows:
$this->belongsTo('App\Models\User')
->select(['id', 'username', 'first_name', 'last_name']);
But it doesn't work.
Is the appends field always be appended even I use custome select field ?
appends is used when the model is serialized; it 'appends' attributes to the output.
If you have a single model and you want to not have the appends accessors added to the serialized data, you can set them hidden.
Example:
$test = new class extends Illuminate\Database\Eloquent\Model {
protected $appends = ['is_admin'];
public function getIsAdminAttribute() {
return rand(0, 1);
}
};
dump($test->toArray()); // will contain 'is_admin'
$test->makeHidden('is_admin');
dump($test->toArray()); // will not contain 'is_admin'
// This can be applied to Eloquent Collections.
$model->relation->makeHidden('is_admin');
One way of doing it.
Simply put this result to a variable
$data = $this->belongsTo('App\Models\User')->select(['id', 'username', 'first_name', 'last_name']);
and use directly
$data->makeHidden("is_admin");
This will work for you

Categories