Laravel 5.4 Eager loading belongsToMany Relationship null Binding - laravel

I have an existing model that is lazy loading a belongsToMany relationship just fine.
My problem is, when i try to eager load the relationship, i get an empty result.
When I inspect the query, it shows that the binding for the relationship query is null.
Here is my (simplified) code:
// Controller
public function filter(Request $request, App\Programs $program)
{
$program = $program->newQuery();
$program->select(
'slug',
'title',
'season'
);
$program->with([
'sports'
]);
return $program->get();
}
// Model
class Programs extends Model
{
public function sports(){
return $this->belongsToMany('App\Sport', 'program_sport', 'program_id', 'sport_id');
}
}

The problem is that you don't take id from database so Eloquent cannot get related models. Instead of:
$program->select(
'slug',
'title',
'season'
);
you should use:
$program->select(
'id',
'slug',
'title',
'season'
);
Be aware however you could use simpler syntax (and don't inject model in controller like this):
return Program::select('id','slug', 'title', 'season')->with('sports')->get();
Also I have no idea why you use route model binding in that case

Related

Laravel getAttribute() on eloquent?

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.

How to create a data associatively? laravel eloquent

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.

Laravel class isn't in polymorphic relationship with other class

I am working on a polymorphic relationship between these classes in my small project: Submissions (same as posts), Comments and Users.
The problem is, I am able to gather all comments by relying on Submission model, for example:
$submissions = Submission::with('comments')->get()->find( $submission );
But, if I try something like this in tinker:
$users = User::with( 'comments' )->get();
I get all the users data, but no comments show up:
comments: Illuminate\Database\Eloquent\Collection {#3003
all: [],
},
Here's all the code of relations between models:
class Comment extends Model
{
protected $fillable = [ 'commentable_id', 'commentable_type', 'text' ];
public function commentable() {
return morphTo();
}
}
Now this is being added inside Submission and User model:
public function comments() {
return $this->morphMany( Comment::class, 'commentable' );
}
In Submission model I am using protected $guarded = []; to deal with Mass Assignment and I don't call it inside user model.
I hope you can tell me what am I doing wrong since somehow I can't access user model by comment model and vice versa.
Thanks in advance!
edit: Here's a code of creation of a comment:
public function store( Submission $submission ) {
$data = request()->validate([
'text' => [ 'required', 'string', 'max:255' ]
]);
$id = $submission->comments()->create([ 'text' => request()->text ]);
dd( $submission, $data );
}
Relation morphMany is one-to-many relation, like hasMany. So one comment can belong to a submission or a user, not both. One option is to create separate comments for submission and user (for the user do the same as for submission $user->comments()->->create([...])). Second option is to use many-to-many polymorphic relation, where one comment can belong to multiple submissions and users.

Can eloquent ignore irrelevant data in Laravel 5.7

This is basically the same question as this here from 2013. Except the answer doesn't work for me.
I have a Model App\Post:
class Post extends Model
{
protected $fillable = ['title'];
// This Model doesn't contain an 'authorname' field
public function author()
{
return $this->belongsTo('App\Author');
}
}
and a Model App\Author:
class Author extends Model
{
protected $fillable = ['name'];
public function posts()
{
return $this->hasMany('App\Post');
}
}
And an array I want to save to that Model:
$posts = [
['title'=>'one post', 'authorname' => 'Mickey'],
['title'=>'another post', 'authorname' => 'Minny'],
];
foreach($posts as $post){
$authorModel=App\Author::firstOrCreate(['name'=>$post['authorname']]);
App\Post::create($post)->author()->associate($authorModel)->save();
}
According to this question, that should work, but I get an
SQL error 42522: Column not found: 1054 Unknown column 'authorname' in 'field list'
which suggests Laravel forwards the whole array to mySQL. Is there a way to make this work without unsetting the authorname key?
Obviously this is a simpified version of what I want to do and keeping track of what to unset seems unnecessary - as would be assigning all array keys to their respective database fields manually.
The only idea I have here is that you run this code in DatabaseSeeder (which automatically unguards models) or you somewhere manually call Eloquent::unguard() (or code similar to this). This would explain why any other fields are used when creating model no matter of $fillable property.

laravel select statement calling sub model using "with" statement

When I call a select statement using a with clause calling supplier model I want the supplier class to call the user related model. how do I do that
$products = Product::select(['id', 'title', 'slug', 'unit_price',
'sell_price', 'created_at', 'updated_at', 'created_by',
'updated_by', 'supplier_id'])->with('supplier');
// when calling supplier model call user model as well
class Supplier extends Model
{
protected $table = "suppliers";
protected $fillable = ['company', 'email'];
public function user() {
return $this->belongsTo('App\User', 'email', 'email');
}
}
According to the documentation you can perform nested eager loading using the dot notation.
So in you example
...->with('supplier');
becomes
...->with('supplier.user');

Resources