Laravel model won't find messages by user id - php

I'm working on a website with a messages system written in Laravel. To identify a users messages the messages table has a to_user attribute which - obviously - holds the id of the user to which the message is sent. However, even though there are plenty of sample messages in this table, my Message model won't return any results when I search for messages by this attribute.
I've tried several things. First off, $messages = Message::where('to_user', USER_ID);, this results in an empty array (except for the standard Eloquent stuff, no actual results from the table). Next, I tried a prettier approach. In my User model, I added a fancy relationship like this:
/**
* Get the users messages
*/
public function messages() {
return $this->hasMany('App\Models\Message', 'to_user');
}
Unfortunately though, $messages = $currentUser->messages() returns the same empty result as it did on my first try.
Last night when I was having a shower I had an eureka moment (this is definitely essential information needed to solve the problem). I was sure I had the solution, either $currentUser->id is a string or the to_user attribute must return the id as a string for some reason. Unfortunately though, this wasn't the case. var_dump shows that both are integers.
Any ideas? I'd really appreciate it.
Thanks!
Roemer

This is a standard one-to-many relationship, the right way to handle this situation is to create the relation in both of your models:
User Model
public function messages() {
return $this->hasMany('App\Models\Message', 'to_user');
}
Message Model:
public function user() {
return $this->belongsTo('App\Models\User', 'to_user');
}
Then get the messages with:
$messages = User::find(<your_user_id>)->messages()->get()
check here for more info

You aren't fetching the results of the query.
$messages = Message::where('to_user', USER_ID); This won't return anything
$messages = Message::where('to_user', USER_ID)->get(); This will return a collection of messages

Your message method returns the HasMany class, it is not returning a resultset yet.
Try:
$messages = $currentUser->messages()->get();
or
$messages = Message::where('to_user', USER_ID)->get();
I think your problem is you do not finished the query builder.

Did you create a "belongsTo" method in the related class?

Related

get a values using many to many relations on laravel 5 models

i'm really new working with laravel 5.0, so I got this problem when I try to retrieve a result using a model. I have a users table, with a list of users who can be a manager or not, they can have assigned one or more companies, or none, a company table with companies which can have one or many managers, and a pivot table that I called companies_managers. I set up the relations in every model like this:
/***User model***/
public function companies()
{
return $this->belongsToMany('App\Company', 'companies_managers','id', 'manager_id');
}
and the same in Company model
public function managers()
{
return $this->belongsToMany('App\User', 'companies_managers', 'id', 'company_id');
}
I want to get the managers assigned to a company using a company id to get it, but it just gave me an huge object without the values I looking for (the names of the managers assigned to that company). This is the code that I tried:
$managers = Company::find($id)->managers();
I would appreciate any help you can give me
Using ->managers() (with the brackets) doesn't actually return the associated managers, but rather a Builder instance (the "huge object"), which you can then chain with additional parameters before finally retrieving them with ->get() (or another closure, like ->first(), ->paginate(), etc)
Using ->managers (without the brackets), will attempt to access the associated managers, and execute any additional logic to retrieve them.
So, you have 2 options:
$company = Company::with(["managers"])->findOrFail($id);
$managers = $company->managers;
Or
$company = Company::findOrFail($id);
$managers = $company->managers()->get();
Both of those will perform the necessary logic to pull the managers. ->with() and no brackets is slightly more efficient, doing it all in a single query, so bear that in mind.
You just need to split out your code;
// this will find the company based on the id, or if it cannot find
// it will fail so will abort the application
$company = Company::findOrFail($id);
// this uses the active company record and gets the managers based
// on the current company
$managers = $company->managers;
Thank you for your help guys, I solved the issue fixing the relations in the models to this:
return $this->belongsToMany('App\Company', 'companies_managers', 'manager_id', 'company_id');
and this
return $this->belongsToMany('App\User', 'companies_managers', 'company_id', 'manager_id');
The IDs that I had set were not the correct ones for belongsToMany function
And this
$managers = Company::find($id)->managers();
was a problem too, was a dumb mistake of my part. I solved the return of Builder instance using just return instead of dd(), in that way I got the values I looking for. Thanks everyone!

Laravel policy is returning unauthorised

I have a policy that checks if the authenticated user can delete a consultant.
Unfortunately, the response is always unauthorised, and I'm unsure why!
Policy function (ClinicConsultantPolicy):
public function delete(User $user, Consultant $consultant)
{
$consultant_clinic_id = $consultant->clinic_id;
return $user->clinic->id === $consultant_clinic_id;
}
Controller calling the above function (ClinicConsultantController):
public function destroy($id)
{
$consultant = Consultant::find($id);
$this->authorize('delete', $consultant);
Consultant::find($id)->delete();
return redirect('clinic/consultants');
}
If I output the two variables the policy is trying to match (user's clinic ID and the consultant's clinic id), both are equal to 2.
However, clearly one of them is either not 2, or perhaps undefined, when it reaches the policy, but I'm unsure why? Many thanks for your help.
To me it looks like a model relationship issue when calling $user->clinic->id. It would be very helpful if you would provide the relationship implementation. I can only suggest writing
$user->clinic()->first()->id if your relationship is set as one to many. Cheers

Laravel get all conversations with participants current user participates in

I have four tables. User, Conversations, Message and Conversation_Participants.
(I hope you don't find a relationship error in this image )
I tried to add a function
public function conversations(){
return $this->belongsToMany(Conversation::class, 'conversation_participants', 'user_id', 'conversation_id');
}
to User::class but if i call User::with('conversations')->get() i only get all existing Users. What am I doing wrong? First i want to get all conversations the current user participates in and second I want to get all receivers of the conversations.
I also tried
public function participants()
{
return $this->belongsToMany(User::class, 'conversation_participants', 'user_id', 'conversation_id');
}
in the Conversation::class but Conversation::with('participants')->get() gets me all Conversation even those the user isn't participating in.
I'm really confused atm :/
Add the following in your User model:
public function conversations() {
return $this->belongsToMany(Conversation::class, 'conversation_participants', 'users_id', 'conversations_id');
}
And this to your Conversation model:
public function participants() {
return $this->belongsToMany(User::class, 'conversation_participants', 'conversation_id', 'users_id');
}
If you want to link your tables easier, read up on conventions.
To get all the conversations a user is participating in, run the following (assuming you've loaded the user): $user->conversations() to get all the conversations a user is in.
If you want all users, with all their conversations, with all the participants connected, do the following: $users = Users::with('conversations.participants')->get(). You can now loop through this as follows:
foreach($users as $user) {
foreach($user->conversations as $conversation) {
foreach($conversations->participants as $participant) {
// do fancy stuff here
}
}
}
Notice that the user from which you start is also a participant, so maybe you need to filter that one out again.
If you want to get even more fancy (but use more resources) you could even query all the messages a conversation has too!
User::with('conversations.participants', 'conversations.messages.user')->get()
But this only works when you set up a second set of relationships along the upper table in your image (conversations <-> messages <-> users)
Edit
In the comments, OP asked if it was possible to limit the amount of messages retrieved from the database, which is possible to my knowledge, but I don't now if this is the best way:
Remove the messages from the first eager loading part:
User::with('conversations.participants')
After that, when looping through the conversations, lazy load the messages:
$conversation->load(['messages' => function($query){
$query->take(5);
}, 'users']);
Access them after that with $conversation->messages.
Note
I think this could be done more easily in one go, but I don't have a setup right now to test this for you.
Edit 2
After Ronon added another comment, here's what I came up with:
Add a new relationship in the Conversation model:
public function last_messages() {
return $this->hasMany(Message::class, 'conversation_id', 'id')->latest()->limit(2);
}
Because of that, you now can do this:
User::with('conversations.participants', 'conversations.last_messages.users')->get()
If you still want all the messages, you can use the messages relationship. If you want less, use the last_messages one.
Calling User::with('conversations')->get() does not specify a User. I might be misreading but I think you are looking for something like the following:
$user = User::with('conversations')->find(1);
$userConversations = $user->conversations;
which will provide you with a user and their conversations.

GroupBy and OrderBy Laravel Eloquent

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.

Laravel nested relationship not working

I have a user model which stores basic user information such as username, password etc.
There are also 3 types of user, Student, Staff and Parent. Each type also has a seperate model. For example, there is a Student model which belongs to a User model.
I also have a relationships table, which stores relationships between students and parents. This relationship is stored in the User model.
If I do something like:
App\Student::first()->user->relations;
It happily returns a collection of related parents.
In my Students model, I have a method called hasParent() which accepts a given user ID, and checks to ensure the student has a parent with that id. In that method, I have the following:
public function hasParent($parent)
{
return $this->user->relations->where('id', $parent)->count() === 1;
}
However, this returns an error Cannot call 'where' on a non-object. If I debug further, $this->user->relations returns an empty array.
The problem is, like above, if I call the methods separately, I get the results I want.
So to clarify, if I run:
App\Student::first()->user->relations;
This returns a collection of users just fine.
In my Student model however, if I call:
$this->user
Then I get the correct student
If I call
$this->user->relations
I get an empty array. Which doesn't make sense! Can anyone shed any light on this, or what I might be doing wrong? If you need any further info, please let me know.
You need to call where on the relation like below.
public function hasParent($parent)
{
return $this->user->relations()->where('id', $parent)->count() === 1;
}
See the parenthesis after the relations. If you call the relation without the parenthesis Laravel returns you a collection. To get the builder you need to call the relation with the parenthesis.
I'd suggest - to avoid creating a huge query overhead (which you'll do by calling where and count on the Query builder, not the collection) - to do what you're doing already, except using Illuminate Collections filter-method:
public function hasParent($parent)
{
return $this->user->relations->filter(function($relation) use ($parent){return $entity->id === $parent;})->count() === 1;
}

Categories