Eager loading the first item from multiple value from a related table in laravel 5.4 - php

I have two tables 'purchases' and 'accounts_purchase_history'. purchase has many accounts history. So I need to get all the purchases with the latest history.
What i have done so far
$purchases = Purchases::with(['accounts_purchase_historie' => function($query){
return $query->orderBy('id','DESC')->first();
}])->orderBy('id','DESC')->where('status','extended')->get();
But this query only gets the first purchase history, How can i solve this?

You can use a HasOne relationship:
public function latest_history() {
return $this->hasOne(...)->orderBy('id','DESC');
}
$purchases = Purchases::with('latest_history')
->orderBy('id','DESC')
->where('status','extended')
->get();
foreach($purchases as $purchase) {
$purchase->latest_history
}
However, this will still fetch all the histories from the database in the background.

Related

return if id not null, laravel elequent

I have connected tables with a hasMany & belongsTo relationship.
Bridal Model:
public function plans()
{
return $this->hasMany("App\Plan");
}
Plan Model:
public function bridal()
{
return $this->belongsTo("App\Bridal");
}
And I have an query that returns those data to endpoint.
public function bridal()
{
$bridals = Bridal::with(["plans" => function($query){
$query->orderBy("plans.plan_price");
}])
->groupBy("place_name")
->get();
return $bridals;
}
Everything is fine, except one thing. In Bridal table some of ID doesn't have plan. So when I return datas, some of bridal id comes with an empty Plans array.
I want to prevent that. If a bridal id doesn't have a plan, then I don't want to return that bridal id. How can I achieve what I want here?
You can use Has. If you just use has('relation') that means you only want to get the models that have at least one related model in this relation.
$bridals = Bridal::with(["plans" => function($query){
$query->orderBy("plans.plan_price");
}])->has('plans')
->groupBy("place_name")
->get();
Check more info SO answer
Add whereHas() in your query : so you will get bridal records which has plans
$bridals = Bridal::with(["plans" => function($query){
$query->orderBy("plans.plan_price");
}])
->whereHas('plans')
->groupBy("place_name")
->get();

Laravel - Get users for tickers

I understand this might be really simple but I cannot get my head around it.
Say I am fetching all tickets
$tickets = Ticket::all();
And with a single line, I want to fetch all the users associated with each ticker, (every ticket has a field with user_id), is there a single line of code, which can do it in Laravel. I can do the old way to loop though each ticket and fecth the details for each user as below, but am just looking for best practices here.
foreach($tickets as $ticket):
$ticket->user = User::find($ticket->user_id);
endforeach;
i'm assuming you have defined a correct relationship in your models.
You can use eloquent relationship of laravel to get all tickets of users
$tickets = User::with('tickets')->get();
to get data if user has at least one ticket you can use the below code
$tickets = User::has('tickets')->get();
To get all tickets only:
$tickets = Ticket::all();
and then in your blade you can do:
#foreach($tickets as $ticket)
// getting user using relationship
{{ $ticket->user->name }}
#endforeach
or to get tickets associated with user you can use
$tickets = Ticket::whereHas('user', function ($q) {
$q->with('user');
})
->get();
to know more about relationships visit this
The first, you declare relation model Ticket with model User:
This is code in model Ticket:
public function user()
{
return $this->hasOne('App\Model\User', 'user_id', 'id'); //id is the primary key in User table
}
then fetching all tickets with user info
$tickets = Ticket::load('user')->all();
or
$tickets = Ticket::with('user')->all();
If you want to get the list of all tickets where has user, with the user associated with each ticket, try this:
$tickets = Ticket::whereHas('user', function ($query) {
$query->with('user');
})
->get();
If you are sure that all tickets have an associated user, use this:
$tickets = Ticket::with('user')->get();

Laravel Eloquent get where doesnt have, search where has when not null

This question is really hard to word, so I apologise for the title!
Lets say I have a Rooms model and a Bookings model, 1 room can have many bookings.
I am building a table with filters, and I need to be able to run a filter on the booking relationship IF there is a booking, else grab the record anyway.
Lets say my room has a booking relationship like so
public function latestBooking(){
return $this->hasOne('App\Models\Bookings', 'room_id', 'id')
->orderBy('expiry_date', 'DESC')->limit(1);
}
The above relationship gets me the latest booking.
I'm running a 'vacant from' filter like so
if ($request->has('filters.vacant_from')) {
$rooms = $rooms->whereHas('latestBooking', function ($query) use ($request) {
$query->where('expiry_date', '<', Carbon::createFromFormat('d/m/Y',$request->input('filters.vacant_from'))->format('Y-m-d'));
});
}
The issue is that this only gets rooms that have a booking and are available after the date specified. I need it to be able to search all rooms, if it does have a booking, check if the expiry date is after the date specified, if it doesn't have a latest booking, get it anyway as its a vacant room.
Basically how would I structure my filter function so that it runs like this
Search all rooms, if it has a latest booking, check its expiry date is after the date specified and only grab it if its true/vacant, if it doesn't have a latest booking then grab it anyway as it is vacant
I have a suggestion for you regarding your latestBookingRelationship. You should create another relationship and add a greater than today condition to it and maybe name it activeLatestBooking.
Then, your latest Booking should comprise all booking rooms irrespective of whether their booking is active or not.
public function activeLatestBooking(){
return $this->hasOne('App\Models\Bookings', 'room_id', 'id')
->where('expiry_date', '>', date('d/m/Y', strtotime('today')))->orderBy('expiry_date', 'DESC')->limit(1);
}
public function latestBooking(){
return $this->hasOne('App\Models\Bookings', 'room_id', 'id')
->orderBy('expiry_date', 'DESC')->limit(1);
}
Then, to answer your question:
if ($request->has('filters.vacant_from')) {
$rooms = $rooms->whereHas('latestBooking', function ($query) use ($request) {
$query->where('expiry_date', '<', Carbon::createFromFormat('d/m/Y',$request->input('filters.vacant_from'))->format('Y-m-d'));
})->orWhereDoesntHave('latestBooking');
}
What the filter query does is this: It gets all latest bookings whose expiry_date is less than the vacant_from date...and the orWhereDoesntHave gets all rooms that have never been booked.
the booking relationship should be like this,
public function latestBooking(){
return $this->hasMany('App\Models\Bookings')->orderBy('expiry_date', 'DESC');
}
as this is a one to many relationship. laravel relationship
and you can write your filter like this,
if ($request->has('filters.vacant_from')) {
$rooms = $rooms->whereHas('latestBooking', function ($query) use ($request) {
$query->whereDate('expiry_date', '<', Carbon::createFromFormat('d/m/Y',$request->input('filters.vacant_from'))->format('Y-m-d'));
});
}
you can use whereDate to run date queries in laravel. Hope this helps
Assuming you have hasMany relation between Rooms and Bookings model with name bookings
$rooms = Rooms::whereHas('bookings', function ($query) use ($request) {
$query->whereDate('expiry_date', '<', Carbon::createFromFormat('d/m/Y',$request->input('filters.vacant_from'))->format('Y-m-d'));
})->orWhereDoesntHave('bookings')->get();

Eloquent HasMany relationship, with limited record count

I want to limit related records from
$categories = Category::with('exams')->get();
this will get me exams from all categories but what i would like is to get 5 exams from one category and for each category.
Category Model
public function Exams() {
return $this->hasMany('Exam');
}
Exam Model
public function category () {
return $this->belongsTo('Category');
}
I have tried couple of things but couldnt get it to work
First what i found is something like this
$categories = Category::with(['exams' => function($exams){
$exams->limit(5);
}])->get();
But the problem with this is it will only get me 5 records from all categories. Also i have tried to add limit to Category model
public function Exams() {
return $this->hasMany('Exam')->limit(5);
}
But this doesnt do anything and returns as tough it didnt have limit 5.
So is there a way i could do this with Eloquent or should i simply load everything (would like to pass on that) and use break with foreach?
There is no way to do this using Eloquent's eager loading. The options you have are:
Fetch categories with all examps and take only 5 exams for each of them:
$categories = Category::with('exams')->get()->map(function($category) {
$category->exams = $category->exams->take(5);
return $category;
});
It should be ok, as long as you do not have too much exam data in your database - "too much" will vary between projects, just best try and see if it's fast enough for you.
Fetch only categories and then fetch 5 exams for each of them with $category->exams. This will result in more queries being executed - one additional query per fetched category.
I just insert small logic inside it which is working for me.
$categories = Category::with('exams');
Step 1: I count the records which are coming in response
$totalRecordCount = $categories->count()
Step 2: Pass total count inside the with function
$categories->with([
'exams' => function($query) use($totalRecordCount){
$query->take(5*$totalRecordCount);
}
])
Step 3: Now you can retrieve the result as per requirement
$categories->get();

Laravel 4 - How to get the latest in with() for each record in one query

I'm working on a messaging system in laravel 4. I have the following tables users, conversations, conversation_user(pivot) and messages. users and converations have many to many relationship, conversations have many messages and users have many messages. I get the conversations of a user like so:ยจ
$usersConversations = Auth::user()->conversations;
Then I loop over them to get their ids to be able to select only those messages users and messages like so:
if(0 < $usersConversations->count()) {
foreach ($usersConversations as $conversation) {
$conversationIds[] = $conversation->id;
}
$conversations = Conversation::with('users', 'messages')->whereIn('id', $conversationIds)->get();
} else {
$conversations = false;
}
but this returns all users and all messages for each of the conversations. I wan't all the users but only the latest message for each conversation. So I tried doing this instead:
$conversations = Conversation::with('users', 'latestMessage')->whereIn('id', $conversationIds)->get();
// and have this in my Conversation model
public function latestMessage() {
return $this->hasMany('Message')->orderBy('created_at', 'desc')->take(1);
}
This does get me the latest message, but only the very latest, and only one, no matter how many convesations there are. I wan't one message for each of the conversations.
Can anyone help me with how to construct that query? Thanks in advance.
Here is what you are looking for.
$conversations = Conversation::with(array('users', 'messages' => function($query){
$query->orderBy('created_at', 'desc')
->take(1);
})->get();
There is no need to use the latestMessage function.
EDIT: The item returned will be a collection if the relationship is not a 1-1. Even if you use first() instead of take(1). Honestly I think it's not intended, but to work around this, make sure to output your messages as such:
foreach($conversations as $conversation){
echo $conversation->messages->first()->YOUR_FIELD;
}

Categories