Somewhere in my Laravel application, the following query is likely to return a very large results set:
$data = $query->join('accommodation_rooms', 'accommodations.id', '=', 'accommodation_rooms.accommodation_id')
->join('discounts', 'accommodation_rooms.id', '=', 'discounts.accommodation_room_id')
->select('accommodation_rooms.id')
->orderBy('discounts.amount', 'desc')
->select('discounts.amount', 'accommodations.*')
->groupBy('discounts.amount', 'accommodation_rooms.id');
return $data;
I wondered how I could load a part of the data faster but only load the rest later maybe using some pagination mechanism or something.
Given the data is sent from an API, I want to know how I could chunk this data.
Thank you.
You can use paginate method for this purpose
$data = $query->join('accommodation_rooms', 'accommodations.id', '=', 'accommodation_rooms.accommodation_id')
->join('discounts', 'accommodation_rooms.id', '=', 'discounts.accommodation_room_id')
->select('accommodation_rooms.id')
->orderBy('discounts.amount', 'desc')
->select('discounts.amount', 'accommodations.*')
->groupBy('discounts.amount', 'accommodation_rooms.id')->paginate(15);
You can change the numbers of record to fetch in given parameter to paginate function e.g 15.
Related
When I do a query like this with multiple joins and retrieve a single column on the nested join, in that example h.mv ,it looses it's type information in laravel and gets treated as a string whereas the column in the mysql database is a DECIMAL.
Account::select('accounts.*', 'accounts.id', 'h.mv', 'h.mv_local')
->with('currency')
->join('sem', 'sem.entity_id', '=', 'accounts.id')
->join('s', 's.id', '=', 'sem.security_id')
->join('h', 'h.security_id', '=', 's.id')
->join('st', 'st.id', '=', 's.type1_id')
->join('hA', 'hA.id', '=', 'h.entity_id')
->where('st.name', '!=', 'CA') //
->where('h.date', $this->T)
->whereIn('accounts.type', ['pa', 'f'])
->where('hA.type', '!=', 'subMandate')
->whereRaw('`hA`.`asset_id` = `accounts`.`asset_id`')
->when($this->mandate_ids, function ($query) {
return $query->whereIn('accounts.id', $this->custodial_ids);
})
->get();
Is that expected behaviour when dealing with multiple joins? I can find old articles stating that the pdo bridge will treat every column as a string and laravel is doing the type conversion so I'm wondering what the constraints are on type conversion or even if that is actually what's happening?
Any experts on eloquent or the pdo layer who could give me some advice would be greatly appreciated
I'm trying to fetch some data with a subquery using Eloquent but dding returns nothing. Separately, this
$discountArticles = $discountTableItemIdIn
->where('recipient_type', '=', 'article')
->toArray();
or this
$discountArticles = $discountTableItemIdIn
->where('recipient_id', '=', $articleId)
->toArray();
work fine.
However when I try something like this, it fails (or rather, returns nothing):
$discountArticles = $discountTableItemIdIn->where(function ($subQuery) {
$subQuery
->where('recipient_type', '=', 'article')
->where('recipient_id', '=', $articleId);
})->toArray();
I know I can do separate queries on the same collection and do an array_merge but I'd like to get this way working instead. Not sure what's happening.
So $discountTableItemIdIn is a collection of the entire table? That means you're gonna need a different function, as the ->where() logic on a collection is different from how it functions on a builder (eloquent) instance.
Try using filter():
$discountArticles = $discountTableItemIdIn->filter(function ($item) use($articleId) {
return $item->recipient_type == "article" && $item->recipient_id == $articleId;
})->toArray();
What this will do is filter your $discountTableItemIdIn collection for records that have a type of article and a recipient_id of whatever $articleId contains, return a new collection and convert that to an array.
Just a note, this is quite inefficient; you should try to avoid loading the whole table into a collection and just query the table directly using the subquery logic in your question.
its there's a way to optimize this code. I already google it but I don't know what the exact keyword to search, so I always failed to find the answer.
At this code I get the Approver List of ID 512 (requestor)
$approver_list = DB::table('users')
->leftjoin('approver_group_list', 'approver_group_list.user_id', '=', 'users.id')
->leftjoin('approval_roles', 'approval_roles.as_id', '=', 'approver_group_list.as_id')
->leftjoin('approver_requestor_list', 'approver_requestor_list.at_id', '=', 'approval_roles.at_id')
->where('approver_requestor_list.user_id', 512)
->get();
Then I use array_push to extract the data of approver_list, then I use the value of $result to get value in LeaveMain table.
$result = array();
foreach($approver_list as $al)
{
array_push($result , $al->user_id);
}
$leave_list = LeaveMain::whereIn('requestor_id', $result)->get();
My problem is, it is always need to use array_push to to extract data, or laravel have a way to optimize this code.
$approver_list = DB::table('users')
->leftjoin('approver_group_list', 'approver_group_list.user_id', '=', 'users.id')
->leftjoin('approval_roles', 'approval_roles.as_id', '=', 'approver_group_list.as_id')
->leftjoin('approver_requestor_list', 'approver_requestor_list.at_id', '=', 'approval_roles.at_id')
->where('approver_requestor_list.user_id', 512)
->pluck('id');
$leave_list = LeaveMain::whereIn('requestor_id', $approver_list)->get();
Basically you can pluck the id from the table itself, rather than fetching all the data and then taking the id later, and whereIn accepts collection as the second argument. so no need to cast to an array
The whereIn method filters the collection by a given key / value
contained within the given array: Link
You don't need to loop the approver_list values, you can use pluck method to retrieves all of the values for user_id.
$result = $approver_list->pluck('user_id')->toArray();
$leave_list = LeaveMain::whereIn('requestor_id', $result)->get();
I have a table called List which i planned to be displayed into view with this command : $lists= List::with('user', 'product.photodb', 'tagCloud.tagDetail')->get();. But, i want the data displayed is only those that has TagID equal to the one user inputted. Those data can be retrieved from TagCloud table.
What i am currently doing is :
$clouds = TagCloud::select('contentID')
->where('tagDetailID', '=', $tagID)
->get();
$lists = List::with('user', 'product.photodb', 'tagCloud.tagDetail')
->where('id', '=', $clouds->contentID)
->get();
But when i tried to run it, it only return a null value, even though when i am doing return $clouds, it does returned the desired ID.
Where did i do wrong ? Any help is appreciated !
A couple of gotchas with your current solution.
Using get() returns an Illuminate\Database\Eloquent\Collection object. Hence you can't use $clouds->contentID directly since $clouds is a collection (or array if you prefer). See Collection Documentation.
where(...) expects the third parameter to be a string or integer, aka single value. Instead, you are passing a collection, which won't work.
The correct way is to use whereHas() which allows you to filter through an eager loaded relationship.
Final Code:
$lists = List::with('user', 'product.photodb', 'tagCloud.tagDetail')
->whereHas('tagCloud',function($query) use ($tagID) {
return $query->where('contentID','=',$tagID);
})
->get();
See WhereHas Documentation.
What you want is whereHas()
$list = List::with(...)
->whereHas('relation', function($q) use($id) {
return $q->where('id', $id);
})->get();
Apply Where condition in you tagCloud model method tagDetail
public function tagDetail(){
return $q->where('id', $id);
}
Of course I can use order_by with columns in my first table but not with columns on second table because results are partial.
If I use 'join' everything works perfect but I need to achieve this in eloquent. Am I doing something wrong?
This is an example:
//with join
$data = DB::table('odt')
->join('hdt', 'odt.id', '=', 'hdt.odt_id')
->order_by('hdt.servicio')
->get(array('odt.odt as odt','hdt.servicio as servicio'));
foreach($data as $v){
echo $v->odt.' - '.$v->servicio.'<br>';
}
echo '<br><br>';
//with eloquent
$data = Odt::get();
foreach($data as $odt){
foreach($odt->hdt()->order_by('servicio')->get() as $hdt){
echo $odt->odt.' - '.$hdt->servicio.'<br>';
}
}
In your model you will need to explicitly tell the relation to sort by that field.
So in your odt model add this:
public function hdt() {
return $this->has_many('hdt')->order_by('servicio', 'ASC');
}
This will allow the second table to be sorted when using this relation, and you wont need the order_by line in your Fluent join statement.
I would advise against including the order by in the relational method as codivist suggested. The method you had laid is functionally identical to codivist suggestion.
The difference between the two solutions is that in the first, you are ordering odt ( all results ) by hdt.servicio. In the second you are retrieving odt in it's natural order, then ordering each odt's contained hdt by servico.
The second solution is also much less efficient because you are making one query to pull all odt, then an additional query for each odt to pull it's hdts. Check the profiler. Considering your initial query and that you are only retrieving one column, would something like this work?
HDT::where( 'odt_id', '>', 0 )->order_by( 'servico' )->get('servico');
Now I see it was something simple! I have to do the query on the second table and get contents of the first table using the function odt() witch establish the relation "belongs_to"
//solution
$data = Hdt::order_by('servicio')->get();
foreach($data as $hdt){
echo $hdt->odt->odt.' - '.$hdt->servicio.'<br>';
}
The simple answer is:
$data = Odt::join('hdt', 'odt.id', '=', 'hdt.odt_id')
->order_by('hdt.servicio')
->get(array('odt.odt as odt','hdt.servicio as servicio'));
Anything you can do with Fluent you can also do with Eloquent. If your goal is to retrieve hdts with their odts tho, I would recommend the inverse query for improved readability:
$data = Hdt::join('odt', 'odt.id', '=', 'hdt.odt_id')
->order_by('hdt.servicio')
->get(array('hdt.servicio as servicio', 'odt.odt as odt'));
Both of these do exactly the same.
To explain why this works:
Whenever you call static methods like Posts::where(...), Eloquent will return a Fluent query for you, exactly the same as DB::table('posts')->where(...). This gives you flexibility to build whichever queries you like. Here's an example:
// Retrieves last 10 posts by Johnny within Laravel category
$posts = Posts::join('authors', 'authors.id', '=', 'posts.author_id')
->join('categories', 'categories.id', '=', 'posts.category_id')
->where('authors.username', '=', 'johnny')
->where('categories.name', '=', 'laravel')
->order_by('posts.created_at', 'DESC')
->take(10)
->get('posts.*');