Situation
So this just happend to me. I have 3 models that have a relation with eachother. My schema is like this:
-- RollingStocks
id
name
[...]
-- Users
id
name
[..]
-- Tasks
id
description
projectleader_id // Corresponds with App\User or App\Group
projectleader_type // Corresponds with App\User or App\Group
rolling_stock_id
[...]
My models have a relationship with eachother:
RollingStock.php
public function task()
{
return $this->hasMany(Task::class);
}
User.php
public function task()
{
return $this->morphMany(Task::class, 'projectleader');
}
Task.php
public function RollingStock()
{
return $this->belongsTo(RollingStock::class);
}
public function projectleader()
{
return $this->morphTo();
}
In my User model I have set the 'password' and 'remember_token' as $hidden which looks like this:
User.php
protected $hidden = [
'password', 'remember_token',
];
Problem
With this little introduction, I will now bring you to my problem. When I fetch in my RollingStocksController all tasks WITH the projectleaders with the following query, the results include the 'hidden' fields in the User model (as projectleader) as well.
$rollingStock = RollingStock::with('task.projectleader')->find($id); // ID matches the ID of the RollingStock I'm trying to fetch)
Result
If I die and dump (dd()) the object has it's relations, BUT then the fields 'password' and 'remember_token' from the User model are vissible and printable to the screen if I loop through the object.
Is there a way to hide the fields, even if the model is (eager) loaded as a relation?
$hidden only hides fields when the result is returned as JSON. It is easy to forget, but the section of the docs is titled "Hiding Attributes From JSON".
In your controller, try:
return $rollingStock;
Laravel will convert that to JSON, and your hidden fields will not show up. Change it to:
dd($rollingStock);
and they will appear, as the result is not JSON, just a dumped variable.
Related
so i just wondered, if something like this is possible, since my code does not work.
protected $appends = ['position_name'];
public function getPositionNameAttribute()
{
return $this->belongsTo('App\EmployeePosition', 'employee_position_id')->name;
}
Can I append the name of Eloquen relationship model?
edit: so far, i am using this:
foreach ($employees as $e) {
$e->position_name = $e->position->name;
}
So, I needed to use the relation defined before.
protected $appends = ['position_name'];
public function position()
{
return $this->belongsTo('App\EmployeePosition', 'employee_position_id');
}
public function getPositionNameAttribute()
{
return $this->position->name;
}
Based on your comments i'd suggest to use the laravel default solution for your problems API resrouces
eg
class EmployeeResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'position_name' => $this->position->name,
];
}
}
note: using the with as other people suggested to preload information can increase performance by reducing the amount of queries, if you are returning a collection of employees.
Creating an accessor that looks up a value in another model and appending this field by using $appends is bad practice and will lead to n+1 queries whenever you fetch your Employee model. You should avoid doing this and just use $employee->position->name.
You should also make sure to use Employee::with('position') when you need to show the position name, so that the position model is fetched in a single query.
If the position name is something that you need in all your Employee queries, then you can set the Employee to always eager load the position by defining the following inside your Employee model:
/**
* The relationships that should always be loaded.
*
* #var array
*/
protected $with = ['position'];
I think you can just create a model with position names and reference it to the position id in the other mode by using eloquent relationships.
I use eloquent relationship between Two models tweets and users table.and run this snippets:
$list = User::with('tweets')->all();
return response($list,200);
my data looks like this one:
{
id:1,
name:John,
tweets:
[
{id:1,content:"test"},
{id:2,content:"test2"}
]
}
I have 2 questions:
How to change the tweets key to the for example comments in the response data?
How to get just content key from tweets?
assuming this is one to many relations
question 1 : change tweets() to comments() in parent model
class parent_model extends Model
{
protected $hidden = ['primary_key'];
public function tweets(){ //change to comments
return $this->hasMany(Child::class);
}
//rest of the code
}
question 2 : User::with('comments:{foreign key must included},selected_table')->all();
if you want to hide primary_key You better use the $hidden property on your Eloquent model then u can select desired columns to be displayed in response.
also set relation belongsTo function in child model to be included in $hidden property.
class child_relation_model extends Model
{
protected $hidden = ['primary_key', 'user'];
public function user(){
return $this->belongsTo(Parent::class);
}
//rest of the code
}
I have a Post model:
class Post extends Model
{
protected $fillable = [
'title',
'user_id',
'token',
'body'
];
public function favorites()
{
return $this->hasMany(Favorite::class);
}
public function addFavorite($state = 1)
{
$this->favorites()->create(compact('state'));
}
}
Favorite model:
class Favorite extends Model
{
protected $fillable = ['user_id', 'post_id', 'state'];
}
When I test in tinker:
$post = Post::first();
$post->addFavorite();
It returns me an error below:
Illuminate/Database/QueryException with message 'SQLSTATE[HYOOO]: General error: 1364 Field 'user_id' doesn't have a default value (SQL: insert into favorites...
Why it ask user_id when it is given in the post? Question is do I necessarily need to input the user_id to achieve this?
The question of whether user_id is necessary is up to you. Will it come in handy later on? Does having it on the posts table suffice?
It is asking for user_id because you do not have a default value field on that field in the favorites table. You can either set a default value, remove it from the table (if you decide you don't need it), OR provide it when creating via the relationship:
class Post extends Model
{
protected $fillable = [
'title',
'user_id',
'token',
'body'
];
public function addFavorite($state = 1)
{
$this->favorites()->create([
'state' => $state,
'user_id' => $this->user_id,
]);
}
public function removeFavorite()
{
$this->addFavorite(0);
}
}
Don't forget to include the relationship definition of favorites on the Post model.
Based on the plural name, it seems that a post has many favorites, but then your removeFavorite() method calls the addFavorite method?? This would not actually remove anything - it would create a new record.
Since Favorite model is related to Post model and you create it via relation()->create(), you can skip specifying post_id as Laravel can deduce it. But you do have to specify user_id, because there's no way for your code to know that favourite.user_id and post.user_id is the same. So in short: yes, you have to specify user_id.
I have a laravel application in which the setting_types and user's settings are saved into different models.
User.php:
/*
* Getting the user's notification setting.
*/
public function notificationSetting()
{
return $this->hasMany('App\NotificationSetting');
}
/*
* Controller function to get the user's settings
*/
public function getSetting(Request $request)
{
$userSetting = $user->notificationSetting;
// check new settings are inserted for user or not.
if (someCondition) {
// add new settings for user.
$user->notificationSetting()->save(new NotificationSetting(['user_id' => $user_id, "notification_type_id" => 121]));
print_r($user->notificationSetting); // still rec. Old values.
}
return $user->notificationSetting;
}
As you can see that I insert the relation object but I didn't receive on the same time. and if I hit again (this time my someCondition become false) so it will return the update records.
Since the save() method returns a boolean, you could write it like this:
$user->notificationSetting()
->save(
$notificationSetting = new NotificationSetting([
'user_id' => $user_id,
'notification_type_id' => 121
])
);
return $notificationSetting;
You might also be able to use the create() method instead, that will return the instance of the model, but only if the attributes are fillable of course.
If you want to retrieve all the related records of a model at any time, you can use the load() method like this:
$user->load('notificationSetting');
It is also important the use the plural form for a hasMany relation in order to distinguish it from a hasOne or a belongsTo relation:
public function notificationSettings()
{
return $this->hasMany('App\NotificationSetting');
}
I have a relationship 1:1 a message can have one author (actually User model),
models/message.php
public function author()
{
return $this->hasOne('User', 'id', 'user_id');
}
This is fine, I get the user model back in this relationship, however I want to add an attribute to the model return in the relationship, basically I want to check if the user model has a password value, if it doesn't set attribute named guest to true.
Is this possible?
You can do this
public function author()
{
$user = $this->hasOne('User', 'id', 'user_id');
if($user->password == "")
{
$user['guest'] = 1; //// or something
}
return $user;
}
This is quick hack hope this helps you
Usually, password field is restricted in User model like this:
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = array('password', 'remember_token');
If I understood it right, you want to get password value from User model through relationship. You can remove 'password' from $hidden array in User model, but it's recommended not to do it, because your password field can be modified, which can make some problems to your application security. After removing 'password' from $hidden array you will have access to this field.