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);
Related
I have a many to many relationship for users and roles. A user can have multiple roles, but I only want with to grab the FIRST role.
Consider the following code:
User::with('roles')->get()
Works great for all roles, but I only want the first role.
I've set this up in my model but doesn't work:
public function role()
{
return $this->roles()->first();
}
How do I load with for only the first result?
You should be able to call first directly on the eager loaded relationship like this:
User::with(['roles' => function ($query) {
$query->first();
})->get();
first() actually executes the query and returns the results as a collection. Relationships must return a query builder, which can then be chained or executed, so using first() in a relationship won't work.
UPDATE
I realised you want to use role in with, so you need to create a relationship to do that. Create a new relationship on your User model (you can use any limit described in the docs, not just oldest()):
public function role()
{
return $this->hasOne('App\Role')->oldest();
}
And then you can use it in with:
$users = User::with('role')->get();
I have a user and a department, a user can belong to many departments. I'm trying to define a scope on the user model to only show users of departments that the current user is a part of:
public function scopeVisible($query) {
$user = Auth::user();
return $user->departments()->users();
}
I've looked at the documentation and can't seem to find a way to do this.
I tried mapping but got strange errors where a method call would be on a query builder instead of a collection even though the object is a collection.
As Laravel docs says you can use whereHas to add customized constraints for more specific queries:
If you need even more power, you may use the whereHas and orWhereHas
methods to put "where" conditions on your has queries. These methods
allow you to add customized constraints to a relationship constraint.
Then this should work:
public function scopeVisible($query) {
$user_id = Auth::user()->id;
return $query->whereHas('departaments', function($query) use ($user_id) {
$query->where('user_id', '=', $user_id);
});
}
The error you are getting about "the method call must be on a query builder instead of a collection" it's because on your code you are returning the result of $user->departments()->users(), which is a collection of Departments, you should return the query builder instead.
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
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');
}
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.