I have three models
A user created a meeting and other members are invited to that meeting
I need to get the meeting with the detail of the user who created the meeting for the user that is invited to that meeting
So I have an meeting table
Model Meeting
function creator() {
return $this->belongsTo( 'User', 'created_by' );
}
function attendees() {
return $this->hasMany('MeetingAttendees');
}
Model User
function invitedMeetings() {
return $this->hasMany( 'MeetingAttendees' );
}
Model Meeting Attendees
function meeting() {
return $this->belongsTo('Meeting');
}
and finally the code to load the relation. where $user is the User Object
$user->invitedMeetings()
->with(['meeting.creator' => function ($query) use ($status) {
if (!empty($status)) {
$query->where('meetings.status', '=', $status);
}
}])->get();
I need to get only meetings where the $status has a certain value, but the where is adding a query to user table. I cannot seem to add a where in the relation load. How can I achieve this?
You should be able to do this much easier
$user->invitedMeetings()
->with(['meeting', 'meeting.creator'])
->where('status', '=', 'accepted')
->get()
//If you want to do a check on another table
$user->invitedMeetings()
->with(['meeting', 'meeting.creator'])
->where('meeting.status', '=', 'accepted')
->get()
Have a look here to get a better understanding about nesting eloquent relations: Eloquent Nested Relation with Some Constraint
I have figured out the answer for this, so
$user->invitedMeetings()
->with(['meeting.creator' => function ($query) use ($status) {
if (!empty($status)) {
$query->where('meetings.status', '=', $status);
}
}])->get();
should be changed to
$user->invitedMeetings()
->with(['meeting' => function ($query) use ($status) {
$query->where('meetings.status', '=', $status)->with('creator');
}])->get();
This solved my issue
Related
Having a hard time understanding how to order my Laravel model by a nested relationship.
Here are the Models.
User.php
// Has many small_groups through a pivot table
public function small_groups()
{
return $this->belongsToMany('App\Models\SmallGroup')->withPivot('type')->withTimestamps();
}
SmallGroup.php
// Has many SmallGroupLessons
public function small_group_lessons()
{
return $this->hasMany('App\Models\SmallGroupLesson');
}
SmallGroupLessons.php
// Has many SmallGroupLessonComments
public function small_group_lesson_comments()
{
return $this->hasMany('App\Models\SmallGroupLessonComment');
}
SmallGroupLessonsComment.php
// Belongs to SmallGroupLesson
public function small_group_lesson()
{
return $this->belongsTo('App\Models\SmallGroupLesson');
}
What's I'm trying to do, is pull all of the user's small groups, ordered by the most recent SmallGroupLessonComment if one exists. I've been doing some research, and it sounds like using Laravels ORM in this use case will not work. However, I'm not entirely sure on how to create the join on the nested relationship.
I tried the following, but this only pulls in the most latest SmallGroupLessonComment, however, it does not order the entire result set.
$small_groups = $user->small_groups()->with([
'small_group_lessons' => function($q) {
$q->with([
'latest_comment' => function($q) {
$q->orderBy('created_at', 'asc');
}
]);
}
])->paginate($limit);
Update
Was able to solve it via the following...
$small_groups = $user->small_groups()->with([
'small_group_lessons' => function($q) {
$q->with([
'latest_comment' => function($q) {
$q->orderBy('created_at', 'asc');
}
]);
}
])
->leftJoin('small_group_lessons', 'small_group_lessons.small_group_id', '=', 'small_groups.id')
->leftJoin('small_group_lesson_comments', 'small_group_lesson_comments.small_group_lesson_id', '=', 'small_group_lessons.id')
->orderBy('small_group_lesson_comments.created_at', 'desc')
->paginate($limit);
Update #2
The above doesn't work. I get multiple small groups back that are the same item.
Update #3
This query is pretty close, but it's just ordered by the most recent SmallGroupLesson. Ideally, we order by the SmallGroupLessonComment 🤔
$small_groups = $user->small_groups()->with(
[
'small_group_lessons' => function($q) {
$q->with('latest_comment');
$q->orderBy('created_at', 'desc');
}
],
)
->orderBy(
SmallGroupLesson::select('created_at')
->whereColumn('small_group_id', 'small_groups.id')
->orderBy(SmallGroupLessonComment::select('created_at')
->whereColumn('small_group_lesson_id', 'small_group_lessons.id')
->orderBy('created_at', 'desc')
->limit(1), 'desc')
->limit(1), 'desc'
)
->paginate();
$data=User::select('*')->leftJoin('small_group_lessons','small_group_lessons.user_id','user.id')
->ordeBy('small_group_lessons.created_at','DESC')->get();
try like this
I was able to solve it via the following. Ordering based off the latest comment now works correctly.
$small_groups = $user->small_groups()->with([
'small_group_lessons' => function($q) {
$q->with('latest_comment');
$q->orderBy('created_at', 'desc');
}],
)
->orderBy(
SmallGroupLesson::select('small_group_lesson_comments.created_at')
->join('small_group_lesson_comments', 'small_group_lessons.id', '=', 'small_group_lesson_comments.small_group_lesson_id')
->whereColumn('small_group_id', 'small_groups.id')
->latest()
->limit(1), 'desc'
)
->paginate();
I have a set up like this,
QUIZ MODEL
public function scores()
{
return $this->hasMany('App\Score');
}
SCORE MODEL
public function quiz()
{
return $this->belongsTo('App\Quiz');
}
public function user()
{
return $this->belongsTo('App\User');
}
USER MODEL
public function scores()
{
return $this->hasMany('App\Score');
}
Some background, a quiz should only be playable by a user if that user does not already have a score for said quiz, what I am wanting to do is that if a user has a relationship with a quiz via having a score I want to stop that quiz being return in the query, here is my attempt,
$quiz = Quiz::with('questions.answers')
->has('scores.user', 2)
->whereDate('date_playable', '=', $date)
->first();
However this returns no quizes regardless of whether the user has a score for it or not. Can anyone enlighten me on how to only return quizes that a user does not currently have a score for?
You are currently searching for a quiz that does not have more than 2 scores for any user.
What you need is whereDoesntHave instead:
$quiz = Quiz::with('questions.answers')
->whereDoesntHave('scores', function ($query) use ($user) {
$query->where('user_id', $user->id);
})
->whereDate('date_playable', '=', $date)
->first();
Where $user is the App\User instance that you are querying for.
There could be multiple approaches to achieve that outcome. I am thinking about creating a many to many relationship between Quizzes and Users, taking scores as the middle table.
User
{
public function quizzes()
{
return $this->belongsToMany(Quiz::class, 'scores');
}
}
Then to get the desired quiz:
$quiz = Quiz::with('questions.answers')
->whereKeyNot($user->quizzes()->pluck('id')->all())
->whereDate('date_playable', '=', $date)
->first();
I have an 'implementation' table that contains relationships to retrieve projects and scores.
The scores table contains a" user_id "field.
I would like to collect all the implementations but with only the score which contains the user_id.
My original query.
public function getImplementations($id, $student)
{
$data = Implementation::where('event_id', $id)->where('student_id', $student)->with('project', 'score')->get();
return response()->json($data);
}
My test for get only the score from specific user (only one score per user per implementation, so no conflict possible)
$data = Implementation::where('event_id', $id)
->where('student_id', $student)->with('project', 'score')
->whereHas('score', function ($query) {
$query->where('user_id', Auth::user()->id);
})->get();
But that does not give the expected result. This is not the right method and I do not know the right one.
Thanks for your help
I am not sure if there's a need to eager-load and constrain a relationship simultaneously. Does the following provide you with the expected result?
$data = Implementation::where('event_id', $id)
->with([
'project',
'score' => function ($query) {
$query->where('user_id', Auth::id());
}
])
->where('student_id', $student)
->get();
I have one to many relation based two tables users and games and there is also bridge table users_games (linking user_id to games).
I want to fetch a single record from games table based on provided game_id for specific user. I did some research and found whereHas() which is returning all games which are belongs to specific user. But I need to fetch one based on game_id. Can some one kindly let me know how can I fix issue in below script
$GameInfo = User::with('games')->whereHas('games', function ($query) use($request)
{
$query->where('game_id', '=', $request->game_id);
})->find(request()->user()->id);
Is this what you're trying to do?
$GameInfo = $request
->user()
->games()
->where('game_id', $request->game_id)
->first();
try this:
$GameInfo = User::with(['games' => function ($query) use($request)
{
$query->where('game_id', $request->game_id);
}])->whereHas('games', function ($query) use($request)
{
$query->where('game_id', '=', $request->game_id);
})->find(request()->user()->id);
If your relation 'games' is a hasMany() with table 'users_games', You can try this code
$GameInfo = User::with(['games' => function ($query) use($request)
{
$query->where('game_id', $request->game_id);
}])
->where('users.id', <user_id_variable>)
->first();
And the relation 'games' in User Model as
public function games()
{
return $this->hasMany('App\Models\UserGames', 'user_id', 'id');
}
I've got a standard many-to-many relationship
class User {
public function roles() {
return $this->belongsToMany('Role');
}
}
class Role {
public function users() {
return $this->belongsToMany('User');
}
}
And it works very well.
But I need to select all the users that has exactly two specific roles.
$roleAdmin = Role::where('name', 'admin')->first();
$roleUser = Role::where('name', 'user')->first();
$users = //which users has BOTH $roleAdmin and $roleUser ??
Is it possible to achieve this using eloquent or I need a raw query?
PS the use-case is stupid, I know, it's just an abstraction of my real problem (that doesn't concern users and roles)
The best solution I found is to get admins and users and then use intersect() helper to get only users who are present both in $users and admins collections:
$users = User::whereHas('roles', function ($q) use($otherRoles) {
$q->where('name', 'user')->whereNotIn('name', $otherRoles);
})->get();
$admins = User::whereHas('roles', function ($q) use($otherRoles) {
$q->where('name', 'admin')->whereNotIn('name', $otherRoles);
})->get();
$result = $admins->intersect($users);
If you want to save some memory, you could pluck() only IDs, intersect() these and only then get users with whereIn().
This is not an eloquent solution(by all means)
$users = \DB::table('users')
->select(\DB::raw("GROUP_CONCAT(roles.name SEPARATOR '-') as `role_names`"), 'users.name')
->join('role_user', 'users.id', '=', 'role_user.user_id')
->join('roles', 'roles.id', '=', 'role_user.role_id')
->groupBy('users.id')
->having('role_names', '=', 'admin-user')
->get();
admin-user can be user-admin, which roles names comes first in the database. Please change table and column names as per your requirement