Eloquent order by one-to-one-related column - php

Good morning everyone!
I'm using Laravel 5.0 and Eloquent to build a displaying page for results of some replies on the database.
Replies are in reservations table, which "belongs" to users table since every reservation is linked to a person.
class Reservation extends Model {
public function user()
{
return $this->belongsTo('App\User');
}
}
I would like to display results, the reservations, ordered by the last name column of the user. So like:
$reservations = Reservation::orderBy( /*users.last_name*/ )->get();
But I dont'know how. Thank you in advance for your time.

You'll need to join the tables to order by a foreign column.
$reservations = Reservation::join('users', 'users.id', '=', 'users.reservation_id')
->orderBy('users.last_name', 'asc')->get();

Related

Foreach according to collection attribute

I have a existing laravel project on php 7.4.
It has users table, patient_appointmnets table.
In users table it contains both patient and doctor with role_id
And there is a doctor_details table which contain only basic info.
The patient_appointments table has
user_id, doctor_id, date, start_time and end_time
The problem is if do
PatientAppointment:whereDate('date', Carbon:now()->format('Y-m-d')->get() ;
And use foreach in blade
I get appointments wise column.
So e.g if a doctor has multiple appointments on that day. I got same doctor column multiple times.
But i want all appointments go in single column for particular dooctir id.
There is no direct doctor tablebwhich contains appointment. On patient_appointmnets table has doctor_id and user_id
So i want collection according to doctor where it contains all appintment
I could do join like:
$var = DoctorDetail:join('users.id', '=', 'doctor_derail.user_id')->select('doctor_detail.*')->get();
then,
for(i=0; i <= count($var) ; i++)
{
$var[$i->appointments] = PatientAppointment::whereDate('date', Carbon::now()->format('Y-m-d')->get();
}
return $var;
but not work. But in dd it show appointments inside doctor_detail collection. But if i do foreach in blade, it shows builder:get() expects 1 for very first get() query..
Any other solution to do same thing.
I believe the key to solve your problem is to use the groupBy method after retrieving all the appointments.
I assume you got the following relationships in the User and PatientAppointment table.
class User extends Model
{
public function doctorDetail()
{
return $this->hasOne(DoctorDetail::class);
}
}
class PatientAppointment extends Model
{
public function doctor()
{
$this->belongsTo(User::class, 'doctor_id');
}
public function patients()
{
$this->belongsTo(User::class, 'patient_id');
}
}
In the controller, you can retrieve all the appointments on that day and group by the doctor_id field, so you got all the appointments belonging to the same doctor:
public function index()
{
$appointmentsGroupedByDoctors = PatientAppointment:whereDate('date', Carbon:now()->format('Y-m-d'))
->get()
->groupBy('doctor_id');
return view(..., compact('appointmentsGroupedByDoctors'));
}
In the view, you can do something like:
#foreach($appointmentsGroupedByDoctors as $doctorId => $appointments)
<p>Doctor Name: {{$appointments->first()->doctor?->doctorDetail->name}}</p>
#foreach($appointments as $appointment)
// Listing the appointments belonging to the same doctor here.
#endforeach
#endforeach

Get only 3 records using with from many to many relationship Laravel

I'm using laravel 7 and
I have 3 tables shown below. I want first three students data instead of all student. tables have many to many relationship.
groups
id
name
students
id
name
group_student_pivot
group_id
student_id
created_at
I have below relationship in models
Groups model
public function students()
{
return $this->belongsToMany(Student::class,'group_student_pivot')->withPivot(['status'])->withTimestamps();
}
Student model
public function groups()
{
return $this->belongsToMany(Group::class,'group_student_pivot')->withPivot(['status'])->withTimestamps();
}
$groups = Group::whereIn('id',$groupIds)->with('students')->get();
In above query I want first 3 students data instead of all students.
You can get 3 records like this:
$groups = Group::whereIn('id',$groupIds)->with('students', function($q){
$q->take(3);
})->get();
You can use with and whereHas method
$groups = Group::with('students')->whereHas('students', function($q){
$q->take(3);
})->whereIn('id',$groupIds)->get();

Laravel - Displaying fields with many to many relationship according field in pivot table

I have this database structure
table users table office_user table offices
----------- ----------------- -------------
id * id * id *
full_name user_id name
office_id
joined_at
So in my project every office has many users and user can be joined to many offices in date (joined_at)
User.php model
public function offices()
{
return $this->belongsToMany('App\Office)->withPivot('joined_at');
}
Office.php model
public function users()
{
return $this->belongsToMany('App\User)->withPivot('joined_at');
}
OfficeController.php
public function show(Office $office)
{
$users = User::with(array('phones', 'offices' , function($query)
{
$query->orderBy('joined_at', 'desc');
}))->get();
return view('dashboard.offices.show', compact(['office', 'users']));
}
I need two things :
1- Get current users list for every office
2- Count of current users in every office
I already achieve this:
<h3>{{ $office->name }}</h3><span>{{ $office->users->count() }}</span>
#foreach ($office->users as $user)
<li>{{ $user->full_name }}</li>
#endforeach
But the result is not as expected it gives me all users in certain office and count of them regardless there joined date
I want the list of last joined users to this office and count of them according joined_at field in pivot table
Thank you and Sorry for my english
But the result is not as expected it gives me all users in certain office and count of them regardless there joined date
When you do $office->users->count() that is the expected behavior because you are retrieve all the associated users of every office at any time, so given that you returned all this users, the count() executed in the collection will count all of them.
Your pivot attribute is just a timestamp, so how would you reduce the number of users returned? users that joined the office today/in the last hour/in the last 15 min maybe?
If so, you can add constrains to your count() method to get the results you want.
As an example, in the following lines we are gonna constraint the associated offices that has a joined_at that belongs to today:
public function show(Office $office)
{
$users = User::with([
'phones',
'offices' => function ($offices) {
$offices->whereDate('joined_at', '>', now()->startOfDay());
},
])->get();
return view('dashboard.offices.show', compact([office, 'users']));
}
Check this section of the documentation:
Constraining Eager Loads
Sometimes you may wish to eager load a relationship, but also specify
additional query conditions for the eager loading query. Here's an
example:
$users = App\User::with(['posts' => function ($query) {
$query->where('title', 'like', '%first%');
}])->get();
In this example, Eloquent will only eager load posts where the post's
title column contains the word first. You may call other query
builder methods to further customize the eager loading operation:
$users = App\User::with(['posts' => function ($query) {
$query->orderBy('created_at', 'desc');
}])->get();

Select a table based on select result from another table

I have a table Registrationrequests where I have course_id and user_id and some other field.
$users_id = Registrationrequest::where('course_id', $query_course_user)->where('registered', 1)->get();
From the above query it gives me an array of result. But I need to take the details of these user_id from another table Users. I'm using Laravel. Table models are Registrationrequest and User
How can I get the user details from the above select result? I'm not that good in Joins. Any advice?
Use Eloquent's whereHas method:
$courseId = Request::get('course_id');
$users = User::whereHas('registrationRequests', function($query) use ($courseId)
{
$query->where('course_id', $courseId)->where('registered', 1);
});
This assumes you have set up the proper relationship in your User model. If not, add this method to your user model:
public function registrationRequests()
{
return $this->hasMany('Registrationrequest');
}

Laravel Eloquent Join vs Inner Join?

So I am having some trouble figuring out how to do a feed style mysql call, and I don't know if its an eloquent issue or a mysql issue. I am sure it is possible in both and I am just in need of some help.
So I have a user and they go to their feed page, on this page it shows stuff from their friends (friends votes, friends comments, friends status updates). So say I have tom, tim and taylor as my friends and I need to get all of their votes, comments, status updates. How do I go about this? I have a list of all the friends by Id number, and I have tables for each of the events (votes, comments, status updates) that have the Id stored in it to link back to the user. So how can I get all of that information at once so that I can display it in a feed in the form of.
Tim commented "Cool"
Taylor Said "Woot first status update~!"
Taylor Voted on "Best competition ever"
Edit #damiani
So after doing the model changes I have code like this, and it does return the correct rows
$friends_votes = $user->friends()->join('votes', 'votes.userId', '=', 'friend.friendId')->orderBy('created_at', 'DESC')->get(['votes.*']);
$friends_comments = $user->friends()->join('comments', 'comments.userId', '=', 'friend.friendId')->orderBy('created_at', 'DESC')->get(['comments.*']);
$friends_status = $user->friends()->join('status', 'status.userId', '=', 'friend.friendId')->orderBy('created_at', 'DESC')->get(['status.*']);
But I would like them all to happen at once, this is because mysql sorting thousands of records in order is 100x faster then php taking 3 lists, merging them and then doing it. Any ideas?
I'm sure there are other ways to accomplish this, but one solution would be to use join through the Query Builder.
If you have tables set up something like this:
users
id
...
friends
id
user_id
friend_id
...
votes, comments and status_updates (3 tables)
id
user_id
....
In your User model:
class User extends Eloquent {
public function friends()
{
return $this->hasMany('Friend');
}
}
In your Friend model:
class Friend extends Eloquent {
public function user()
{
return $this->belongsTo('User');
}
}
Then, to gather all the votes for the friends of the user with the id of 1, you could run this query:
$user = User::find(1);
$friends_votes = $user->friends()
->with('user') // bring along details of the friend
->join('votes', 'votes.user_id', '=', 'friends.friend_id')
->get(['votes.*']); // exclude extra details from friends table
Run the same join for the comments and status_updates tables. If you would like votes, comments, and status_updates to be in one chronological list, you can merge the resulting three collections into one and then sort the merged collection.
Edit
To get votes, comments, and status updates in one query, you could build up each query and then union the results. Unfortunately, this doesn't seem to work if we use the Eloquent hasMany relationship (see comments for this question for a discussion of that problem) so we have to modify to queries to use where instead:
$friends_votes =
DB::table('friends')->where('friends.user_id','1')
->join('votes', 'votes.user_id', '=', 'friends.friend_id');
$friends_comments =
DB::table('friends')->where('friends.user_id','1')
->join('comments', 'comments.user_id', '=', 'friends.friend_id');
$friends_status_updates =
DB::table('status_updates')->where('status_updates.user_id','1')
->join('friends', 'status_updates.user_id', '=', 'friends.friend_id');
$friends_events =
$friends_votes
->union($friends_comments)
->union($friends_status_updates)
->get();
At this point, though, our query is getting a bit hairy, so a polymorphic relationship with and an extra table (like DefiniteIntegral suggests below) might be a better idea.
Probably not what you want to hear, but a "feeds" table would be a great middleman for this sort of transaction, giving you a denormalized way of pivoting to all these data with a polymorphic relationship.
You could build it like this:
<?php
Schema::create('feeds', function($table) {
$table->increments('id');
$table->timestamps();
$table->unsignedInteger('user_id');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->morphs('target');
});
Build the feed model like so:
<?php
class Feed extends Eloquent
{
protected $fillable = ['user_id', 'target_type', 'target_id'];
public function user()
{
return $this->belongsTo('User');
}
public function target()
{
return $this->morphTo();
}
}
Then keep it up to date with something like:
<?php
Vote::created(function(Vote $vote) {
$target_type = 'Vote';
$target_id = $vote->id;
$user_id = $vote->user_id;
Feed::create(compact('target_type', 'target_id', 'user_id'));
});
You could make the above much more generic/robust—this is just for demonstration purposes.
At this point, your feed items are really easy to retrieve all at once:
<?php
Feed::whereIn('user_id', $my_friend_ids)
->with('user', 'target')
->orderBy('created_at', 'desc')
->get();

Categories