As one can see above, there is a 'eagerLoad' section and the querybuilder accepts calling $query->with('relation') but produces the following error: Method addEagerConstraints does not exist.
I've tried to find some documentation on this matter but didn't find much. Is it possible at all to use eager loading in this case? If so, could anyone tell how?
Update
As some people pointed out it is possible and the error is caused by another error in my code. Here are some samples:
// Querybuilder
$query->select([
'persons.id as alumni_id',
...
]);
$query->where('...'); // Based on search parameters
$query->groupBy('alumni_id');
$query->with('relation');
$result = collect($query->get());
// Model
public function relation()
{
// Note: relation does have a column person_id
return $this->hasMany(Relation::class, 'person_id', 'alumni_id')->get();
}
Produces
BadMethodCallException in Macroable.php line 81: Method addEagerConstraints does not exist.
As far as I've learned, It is not possible to use eager loading to load Model relations with the querybuilder. (as the querybuilder is not leveraging Eloquent, it doesn't know about the relations)
If anyone knows this to be (partially) incorrect, please let me know.
This should work
// Querybuilder
$query->select([
'persons.id as alumni_id',
...
]);
$query->where('...'); // Based on search parameters
$query->groupBy('alumni_id');
$query->with('relation');
$result = $query->get(); // already returns a collection
// Model
public function relation()
{
// Note: relation does have a column person_id
// A relation should return an Eloquent object (HasMany in this case), not a collection
return $this->hasMany(Relation::class, 'person_id', 'alumni_id');
}
Related
I have a model called Shift on my application, and I've defined my relationships and scopes on it like this:
Relationship:
public function status()
{
return $this->belongsTo(ShiftStatus::class);
}
With scope:
public function scopeWithStatus($query)
{
$query->with('status');
}
Now, when I retrieve shifts and try to call these scopes, I do the following:
$shifts = Shift::all()
->withStatus()
->withProfession()
...
->get();
return response([
'message' => 'Shifts retrieved.',
'shifts' => $shifts
]);
However, I get this error...
"message": "Method Illuminate\\Database\\Eloquent\\Collection::withStatus does not exist.",
I'm not sure why this is happening? It should pick up the scope shouldn't it?
It is not possible to use a query scope in a Collection, since query scope is a concept used in Eloquent to add constraints to a database query while Collections are just a collection of things (data, objects, etc).
So, remove all(). Also i think you are missing return statement in scope (not a part of this question, but you need to update the code )
when you call Shift::all() you get all the shifts table record from db as a collection , then you load the relation on that collection which makes that error.
you should not be loading the result from db unless your query is ready, you should tell the query builder to load the relation then call the result:
$shifts = Shift::withStatus()
->withProfession()
...
->get();
This is my relation method in my model:
public function relation()
{
return $this->belongsTo('App\Table', 'table_2_id', 'table_2_id')->where('table_1_id', $this->table_1_id);
}
So, both the model above and Table2 have the same column, but there is the possibility of many entries, so I want to then filter on a second column that is shared by both tables, table_1_id.
It seems to work perfectly on the command line, but when eager loading the relationship is empty.
If I remove the additional ->where() from the relationship method then the eager loading works.
The way I am eager-loading is by doing
->with('relation.nestedRelation');
I've also tried
->with(['relation' => function ($q) {
$q->with('nestedRelation');
}])
I've just tried adding it as an attribute to see if that helped but still no joy.
You can use the whereColumn() function :
->whereColumn('table_1.table_1_id', 'table_2.table_1_id')
Or you can use that :
public function filterRelation(){
return $this->relation->where('table_1_id', $this->table_1_id);
}
In my Profile model I setted this relationship
public function lease()
{
return $this->belongsTo(Lease::class, 'lease_id', 'id');
}
And in my Lease model I seeted this way
public function profile()
{
return $this->hasOne(Profile::class, 'lease_id', id);
}
As longs as I know in laravel you could do
$profile = factory(App\Profile::class)->create();
$profile->lease()->get();
And then responds correctly with the model inside of a collection
And if I do
$profile->lease
Responds correctly directly with the model
It isn't supposed that dynamic propertis execute the query right away like a shortcut of ->lease()->get()? Why it gives different formatted results?
When you are calling get on a builder you are getting a collection always. When you call first on a builder like that you will get a model or null. The dynamic property for the relationship, based upon the relationship object, will either query with get or first respectively when it loads it. Which is why $model->relationship is returning you the result you expect.
The relationships that are singular, cause a find and the ones that are many cause a get.
Laravel 5.4 - Docs - Eloquent - Relations - Relationship Methods vs Dynamic Properties
Building a chat application with a dashboard and am trying to get a notification of the last message the that other user sent.
Here is my Model relationships:
public function messages() {
return $this->hasMany('App\Message', 'author_id');
}
public function lastMessage() {
return $this->hasMany('App\Message', 'recipient_id')->orderBy('created_at', 'DESC')->groupBy('author_id');
}
On thing I cant figure out is instead of returning the last message as it should be sorted by using orderBY, it returns the first record of that group that exists in the database.
Looked around online but cant seem to find any info on this. The only thing I found is a post by someone who said that orderBy and groupBy in laravel don't play well together.
Any help is appreciated!
Instead of redefining the relationship in lastMessage, you might try calling messages from it and then running your query from that.
Without seeing more of your model schema (ie: where are these relationships defined??), this might not be perfect, but it's a start:
public function lastMessage()
{
return $this->messages() // <-- Notice the ()...this creates a query instead of immediately returning the relationship
->where('recipient_id', $this->id) // Not sure if this is correct, might need to adjust according to how you have defined the tables
->orderBy('created_at', 'desc')
->first();
}
It will query the messages relationship with the chained constraints that are listed. And by returning first() it returns only one record as opposed to a collection.
I have 3 tables / models
User (has an id)
Articles (has an id )
UserArticles (has an id, article_id and user_id)
I am a little confused on how I would set up the relationship so that I will be able to get all articles connected to a user so I can set up a call like so in my controller:
$articles = User::find(Auth::user()->id)->articles->paginate(20);
I figured this was a manytomany relationship so I am playing around with this code inside the User model:
public function articles()
{
return $this->belongsToMany('App\Article', 'user_saved_articles', 'user_id', 'article_id');
}
Now this works, as long as I don't call paginate() on the controller function I'd like to use above. Here is where my real issue lies now, so it works with
$articles = User::find(Auth::user()->id)->articles;
but with this:
$articles = User::find(Auth::user()->id)->articles->paginate(20);
it comes up with the error:
FatalErrorException in UserController.php line 217:
Call to undefined method Illuminate\Database\Eloquent\Collection::paginate()
I can't figure out why I can't paginate on this, as I can with all my other queries.
If you call an eloquent relation as an attribute User::find(Auth::user()->id)->articles it will automatically execute the SQL, and return a Collection. The error is telling you that you have a Collection, which you can't call paginate() on.
If you want to reference the relationship to add more statements you need to call it as a function User::find(Auth::user()->id)->articles(). This will return a QueryBuilder instance that you can add statements to, and/or paginate.
So this should work:
$articles = User::find(Auth::user()->id)->articles()->paginate(20);