laravel retrieving data from database takes too much time - php

I am working with laravel project which uses a mysql database.It has some tables which has over 5 million data.it takes too much time to get these data to frontend. following is the function i use to get data. i have some filters used such as date range (from,to) search by name($seach) pagination amount(records_number) etc. but when i try to get all records it takes too much time. is there any solutions/optimizations for this matter?
Thanks.
public function transactionListBetween($from, $to, $sort, $search, $records_number, $filter, $previous_sort) {
$query = $this->accountTransactions
->with('transactionType', 'giver', 'recipient')
->leftJoin('tbdb_users as recipient', 'recipient.id', '=', 'tbdb_account_transaction.recipient_id')
->leftJoin('tbdb_users as giver', 'giver.id', '=', 'tbdb_account_transaction.giver_id')
->leftJoin('tbdb_account_transaction_type', 'tbdb_account_transaction.account_transaction_type_id', '=', 'tbdb_account_transaction_type.id')
->where('recipient.name', 'like', "%$search%")
->select('tbdb_account_transaction.*');
if($filter) {
if($filter == -1) {
$transactionTypeModel = \App::make('App\Models\AccountTransactionTypeModel');
$depositTypeIds = $transactionTypeModel->whereIn('name', ['PayPal Deposit', 'Eway Deposit', 'Bank Deposit', 'BPay Deposit', 'Poli Deposit'])->lists('id');
$query = $query->whereIn('tbdb_account_transaction.account_transaction_type_id', $depositTypeIds);
} else {
$query = $query->where('tbdb_account_transaction.account_transaction_type_id', $filter);
}
}
if($from) {
$query = $query->where('tbdb_account_transaction.created_date', '>=', $from);
}
if($to) {
$query = $query->where('tbdb_account_transaction.created_date', '<=', $to->endOfDay());
}
if($records_number == 'no_paginate') {
return $query->orderBy($sort, $previous_sort)
->get();
} else {
$totalAmount = $query->sum('amount');
$collection = $query->orderBy($sort, $previous_sort)->paginate($records_number);
$collection->totalAmount = number_format($totalAmount / 100, 2);
return $collection;
}
}

You seem to be doing 3 left joins and getting the same data as you did with the with.
You can drop them:
public function transactionListBetween($from, $to, $sort, $search, $records_number, $filter, $previous_sort) {
$query = $this->accountTransactions->->with('transactionType', 'giver', 'recipient')
->whereHas('recipient', function ($query) use ($search) { //Query the related model
$query->where('name', 'LIKE', "%$search%"); //Note this will not use an index
});
if ($filter) {
if ($filter == -1) {
$query->whereHas('transactionType', function ($query) {
$query->whereIn('id', \DB::raw("(SELECT id FROM tbdb_account_transaction_type WHERE name IN ('PayPal Deposit', 'Eway Deposit', 'Bank Deposit', 'BPay Deposit', 'Poli Deposit')");
});
} else {
$query->whereHas('transactionType', function ($query) use ($filter) {
$query->where('id', $filter);
});
}
}
if($from) {
$query = $query->where('created_date', '>=', $from);
}
if($to) {
$query = $query->where('created_date', '<=', $to->endOfDay());
}
if($records_number == 'no_paginate') {
return $query->orderBy($sort, $previous_sort)
->get();
} else {
$totalAmount = $query->sum('amount');
$collection = $query->orderBy($sort, $previous_sort)->paginate($records_number);
$collection->totalAmount = number_format($totalAmount / 100, 2);
return $collection;
}
}
This will reduce the main result set greatly making it easier to work with.

In my case i have also having this problem when using mysql database. These much records in table with relationship takes too much time. You have to use raw query to get result. You have to use pagination to show the data. because fetching all the data will slow down.
But i highly recommended you to normalize your table structure. In my case i did normalize of my tables by split it with yearly.

Found out why this is happening. it is because of pagination. it takes lot of time to paginate. there are more than 50000 pages so it takes time. i used simplePaginate() instead of paginate(). then it loads faster than usual

Related

Optimise Laravel Eloquent Query Result

I have a homework table in laravel-5.7 where 5000 records in the table also have some Relational records that are coming through HasMany() or HasOne() Relation. I tried many types of Eloquent Queries to get fast results. but Postman result time becomes 10200ms to 10700ms but when i direct dispaly this into postman then i am getting this into 500ms to 1100ms. i want to get it in near about 800ms after binding form Laravel Resource or Normal Array.
problem is, when i try to show the Eloquent result direct then it coming around 600ms to 1000ms. but when i bind into an Array and display in postman then its taking 6200ms why? i do not know?
$page = $req->page ?$req->page:1; // set starting value for look query limit.
$user = Auth::user()->student()->first();
$studentProfile = Auth::user()->student()->first();
// collecting all homework id that have assigned to student.
$studentHWList = StudentHomeWork::where("student_id",$studentProfile->id)
->select('home_work_id')
->get()->pluck('home_work_id');
// collecting page by page of homework id.
$hwLimitList = Homework::where('session_code', dnc($req->require('sid')))
->whereIn('id',$studentHWList )
->where('approved', '1')
->select('id')
->orderBy('updated_at','desc')
->get();
$hwIndexes = $hwLimitList->pluck('id')->forPage($page,$this->recordLimit);
$paginated = Homework::whereIn('id', $hwIndexes)
->with( "user:id,username,name",
'subject:id,subject_name,subject_code',
'approveByUser','publishBy')
->with(["likes"=>function($erw){
$erw->select('id','home_work_id','complete_status','likes')
->where("student_id", $studentProfile->id);
}])
->with(['comment'=>function($qur){
$qur->where('parent_id',0)
->where('user_id',$user->id);
}])
->orderBy('id','desc')
->get( );
if( count($paginated))
{
$paginationData = customPagination('getAllHW',$hwLimitList , $page , $this->recordLimit , $user, $studentProfile );
return response()->json(["error"=>0,"errmsg"=>"","paginationData"=>$paginationData ,
"response"=>['homework_list'=>$this->customResourceHWBinding($paginated , $req )],'auth'=>userType()]);
private function customResourceHWBinding($queryData , $request, $user, $studentProfile )
{
$document_list =[]; $is_seen=0; $resultData =[];
foreach ( $queryData as $query )
{
if( count($query->document) )
{
foreach($query->document as $document){
if( $document->changed_filename )
{
$file=""; $fileName ="";
$path =env('AWS_URL')."/uploads/".dnc($request->header('dbauth'))."/".$query->session_code."/homeWorks/";
if(is_s3FileExist( $path.$document->changed_filename ) )
{
$fileName =$document->changed_filename;
}
$document_list[] = [
'oname'=> $document->changed_filename,
'ext'=>$fileName?explode('.', $document->changed_filename):"",
'url'=>$file,
'file_url'=>$document->changed_filename?$path.$document->changed_filename:""
];
}
}
}
$resultData[] = [
'id'=>enc($query->id),
'ids'=>$query->id,
'pin_user_id'=>"",
'pin_enabled'=>0,
'created_by'=>$query->user->name,
'created_by_image'=>getUserImage($query->user,$query->user->privilege,$request),
'assignment_date'=>getDateFormat($query->assignment_date,0),
'assigment_date_edit'=>"",
'submission_date'=>getDateFormat($query->submission_date,1),
'submission_date_edit'=>"",
'class_code'=>$query->class_code,
'subject'=>$query->subject?$query->subject->subject_name:"",
'topic'=>$query->topic,
'is_student_seen'=> $this->studentHWSeen($query, $user, $studentProfile),
'updated_at'=> date('d-m-Y H:i:s' , strtotime($query->updated_at)),
'approved'=>$query->approved,
'approve_by'=> '',
'can_approve'=>0,
'comment_count'=>0,
'total_like'=>0,
'documents_count'=>count($document_list)?count($document_list):0,
'is_draft'=> $query->draft?$query->draft:0,
];
}
return $resultData;
}
private function studentHWSeen( $query , $user, $studentProfile)
{
if(count($query->studentSeen))
{
foreach($query->studentSeen as $seen){
if( user->privilege == 1 )
{
if($seen->student_id == $studentProfile->id )
return 1;
}
}
}
return 0;
}
I try to use Resource but it's also taking 3+seconds. I try too many others optimize a solution but not work in my case. someone told Use query builder instead of Eloquent to optimize queries. find here Optimising Laravel query . is it a good answer for me? I am not sure. please help me.
please check my image.
Eloquent Query Result
First of all, try to optimize this:
$paginated = Homework::whereIn('id', $hwIndexes)
->with( "user:id,username,name",'subject:id,subject_name,subject_code',
'approveByUser','publishBy')
->with(["likes"=>function($erw){
$erw->select('id','home_work_id','complete_status','likes')
->where("student_id",Auth::user()->student()->first()->id);
}])
->with(['comment'=>function($qur){
$qur->where('parent_id',0)
->where('user_id',Auth::id());
}])
->orderBy('id','desc')
->get( );
You run same code in nested query: Auth::user()->student()->first()->id.
Optimized version:
$studentId = Auth::user()->student()->first()->id;
$paginated = Homework::whereIn('id', $hwIndexes)
->with("user:id,username,name", 'subject:id,subject_name,subject_code', 'approveByUser', 'publishBy')
->with(["likes"=>function($erw) use ($studentId) {
$erw->select('id','home_work_id','complete_status','likes')
->where("student_id", $studentId);
}])
->with(['comment'=>function($qur) {
$qur->where('parent_id',0)
->where('user_id',Auth::id());
}])
->orderBy('id', 'desc')
->get();
Remember to add indexes to fields that you're using in where conditions.

Laravel Eloquent split large query in to chunk and reuse it

I am having really a very big query formation like below. i want to split this and need to re-use for many other ajax call's
$buildquery=Hotel::has('room');
$buildquery->whereHas('room', function($query) use ($request) {
// If amenities is there add it to query
if($request->filled('amenities')){
$amenities = $request->amenities;
$count = count($amenities);
$query->withCount(['amenities' => function($query) use ($amenities, $count){
$query->whereIn('amenities_id', $amenities);
}])
->having('amenities_count', $count);
}
/* filter based on guest */
if($request->filled('guestsCount')){
$memberCount = $request->guestsCount + $request->childCount;
$query->Where('capacity', '>=', $memberCount);
}else{
$query->Where('capacity', '>=', 1);
}
});
$buildquery->with(['room' => function ($query) use ($request) {
// If amenities is there add it to query
if($request->filled('amenities')){
$amenities = $request->amenities;
$count = count($amenities);
$query->withCount(['amenities' => function($query) use ($amenities, $count){
$query->whereIn('amenities_id', $amenities);
}])
->having('amenities_count', $count);
}
/* filter based on guest */
if($request->filled('guestsCount')){
$memberCount = $request->guestsCount + $request->childCount;
$query->Where('capacity', '>=', $memberCount);
}else{
$query->Where('capacity', '>=', 1);
}
$query->with('roomtype')->with('floorroomcount')->with('image')->with('amenities');
$query->OrderBy('price');
$query->Where('astatus', 1)->Where('status', 0);
}]);
/* client must be active */
$buildquery->whereHas('client', function($query) {
$query->Where('status', 1);
});
/* search based on rating */
if ($request->filled('rating')) {
if($request->rating > 0){
$rating = $request->rating;
$buildquery->where('star', $rating);
}
}
/* search based on hotel */
if ($request->filled('location_id')) {
$buildquery->Where('city', $request->location_id);
}
#include('roomlist.area');
$buildquery->Where('astatus', 1)->where('status', 0); //actually its hotel
$hotels = $buildquery->simplePaginate(20);
$hotels = $this->addRates($hotels, $request->checkin_date, $request->checkout_date);
$hotels = $this->addAvailableCount($hotels, $request->checkin_date, $request->checkout_date);
$hotels = $hotels->transform(function (Hotel $hotel){
$hotel->setRelation('room', $hotel->room->sortBy('price')->flatten());
return $hotel;
});
return view('roomlist.loadmore', compact('hotels'));
please see this line #include('roomlist.area'); in that roomlist/area.blade.php file i am having the following code
<?php
if($request->filled('type')){
if($request->type == "Area"){
//get the area first
$hotel = Hotel::select('area')->where('city', $request->location_id)->first();
if(isset($hotel)){
if($hotel->area != null){
$buildquery->where('area', $hotel->area);
}
}
}
}
?>
Is there any way that i can include this code from a blade or in any other manner.
Note: i need to re-use many things like this.
First thing you could do is break some of this functionality into scopes: https://laravel.com/docs/5.6/eloquent#query-scopes
For instance, you could change this:
/* search based on hotel */
if ($request->filled('location_id')) {
$buildquery->Where('city', $request->location_id);
}
Into this:
if ($request->filled('location_id')) {
$buildquery->inCity($request->location_id);
}
Or this:
/* client must be active */
$buildquery->whereHas('client', function($query) {
$query->Where('status', 1);
});
into this:
$buildquery->withActiveClient();
This is a small change but it allows you to use inCity in other places without re-writing as much, and for the other scopes it might be more code you can extract.
You could also make a Transformer class to change this:
$hotels = $hotels->transform(function (Hotel $hotel){
$hotel->setRelation('room', $hotel->room->sortBy('price')->flatten());
return $hotel;
});
To this:
$hotels = (new HotelRoomTransformer())->transform($hotels);
This type of extracting code could make this file much more readable, and that way if you need to reuse parts of it you have them in separate, reusable files.
Lastly, this type of functionality can all be extracted into a repository if you want to entirely remove Eloquent from your controllers. Here is a short guide on the repository pattern: https://medium.com/#connorleech/use-the-repository-design-pattern-in-a-laravel-application-13f0b46a3dce

Laravel 5.5 Eloquent - Optionally chain where statements

I want the following to happen - search by fields if fields are different from "all". Something like this:
// If $request['field'] != 'all' add query
if(isset($request['types'])) {
$query = Offer::whereHas('types', function ($query) use ($request) {
$typeArray = $request->get('types');
$query->whereIn('type', $typeArray);
});
}
if ($request['country'] != 'all') {
query->where('country_id', $country);
}
At the end I want to order and paginate the results like so:
$offers = $query->orderBy('created_at', 'desc')->paginate(9);
Is there any way to achieve this? If my question isn't clear enough tell me and I will edit it and try to explain better.
You can use eloquent when method to check conditions and append query. in when method you can check your conditions.
Offer::when(isset($request['types']), function($query) {
$query->whereHas('types', function ($query) {
$query->whereIn('type', request()->get('types'));
});
})
->when(request()->country != 'all', function($query) {
$query->where('country_id', request()->country);
})
->orderBy('created_at', 'desc')->paginate(9);
Can you try this:
<?php
$query = Offer::where(function($q) use ($request){
if ( !empty($request['country']) && is_numeric($request['country']) ) {
$query->where('country_id', $request['country']);
}
if( !empty($request['types']) && is_array($request['types']) ) {
$q->whereHas('types', function ($q) use ($request) {
$typeArray = $request->get('types');
$q->whereIn('type', $typeArray);
});
}
});
$offers = $query->orderBy('created_at', 'desc')->paginate(9);

Laravel and php best multiple search method

I have a controller that get the four inputs from a search form.
SearchController.php code
public function results(Request $request) {
$text = $request -> text;
$pet = $request -> pet;
$category = $request -> category;
$city = $request -> city;
$searchArray = [];
if(empty($text) && empty($pet) && empty($category) && empty($city)) {
Session::flash('danger', "You didn't select any search any search.");
return redirect() -> back();
}
//SEARCH CODE HERE
}
What I am trying to do
I am trying to search 4 columns in my database
Problem is
I need also to search the 4 columns in one query.
That means that I need to check if the $text variable is not empty and $pet variable is not empty then I have to do this query:
if(!empty($text) && !empty($pet))
$result = Post::where('text', 'like', '%'.$text.'%') -> where('text', $pet) -> get();
This method will work fine but I will have multiple if statements that will check all the possibilities.
Is there faster and optimal solution?
Option 1
Build logic manually. This is the best way in many situations. An example:
$result = Post::query();
if (!empty($text)) {
$result = $result->where('text', 'like', '%'.$text.'%');
}
if (!empty($pet)) {
$result = $result->where('pet', $pet);
}
if (!empty($category)) {
$result = $result->where('category', $category);
}
if (!empty($city)) {
$result = $result->where('city', 'like', '%'.$city.'%');
}
$result = $result->get();
Option 2
Use conditional clauses. Example:
Post::when($text, function ($q) use ($text) {
return $q->where('text', 'like', '%'.$text.'%');
})
->when($pet, function ($q) use ($pet) {
return $q->where('pet', $pet);
})
->when($category, function ($q) use ($category) {
return $q->where('category', $category);
})
->when($city, function ($q) use ($city) {
return $q->where('city', 'like', '%'.$city.'%');
})
->get();

Building dynamic queries in Laravel - how to do a multiple option search

So I am trying to set up a search page and it has multiple get options But I am curious as to how to set this up correctly, I know this is far from correct as I am doing if statements inside of setting a variable, But I am so lost right now.
Any help would be greatly appreciated.
public function index()
{
$queryUsername = Request::get('u');
$queryPostcode = Request::get('p');
$queryOrderbyPhotos = Request::get('o1');
$queryOrderbyOnline = Request::get('o2');
$queryOrderbyTypes = Request::get('o3');
$users = User::rightJoin('user_profiles','users.id', '=', 'user_profiles.user_id')
if ($queryUsername)
{
->where('users.username', '=', "$queryUsername")
}
if ($queryPostcode) {
->where('user_profiles.postcode', '=', "$queryPostcode")
}
if ($queryOrderbyPhotos) {
->whereNotNull('user_profile.avatar')
}
if ($queryOrderbyOnline) {
->orderBy('users.last_online', 'DESC')
}
if ($queryOrderbyType) {
->orderBy('users.type', 'DESC')
}
->get();
return view('view', compact('users'));
}
This is how I'll approach the problem. I'll create a variable holding the query builder and then call all the additional query methods on it.
With Eloquent and actually with any class that allows Method Chaining you can do this:
$query = User::select(...)->join(..);
$query->where(...);
$query->get(...);
So in your case I'll be trying to achieve what you want in this manner:
public function index()
{
$input = Request::all();
$query = User::rightJoin('user_profiles', 'users.id', '=', 'user_profiles.user_id');
if (isset($input['u']) && $input['u'])
$query->where('users.username', '=', $input['u']);
if (isset($input['p']) && $input['p'])
$query->where('user_profiles.postcode', '=', $input ['p']);
if (isset($input['o1']) && $input['o1'])
$query->whereNotNull('user_profile.avatar');
if (isset($input['o2']) && $input['o2'])
$query->orderBy('users.last_online', 'DESC');
if (isset($input ['o3']) && $input['o3'])
$query->orderBy('users.type', 'DESC');
$users = $query->get();
return view('view', compact('users'));
}
Of course it will be a good idea that you have an additional check for valid input on each input parameter. But this can be achieved in many ways. You can read more about Laravel Controller Validation or Laravel Form Request Validation
By the way I'll suggest to move all this code in your model or in separate class as I prefer keeping controllers slim.
You can try :
$users_query = new User;
$users_query->rightJoin(....);
if ($queryUsername)
{
$users_query->where('users.username', '=', "$queryUsername")
}
// Your other conditions .....
....
$users = $users_query->get();
multiple option search
This is a trait that can be used by any models
This function will remove code repetitions into your project
public function scopeSearch($query, $keyword, $columns = [], $relativeTables = [])
{
if (empty($columns)) {
$columns = array_except(
Schema::getColumnListing($this->table), $this->guarded
);
}
$query->where(function ($query) use ($keyword, $columns) {
foreach ($columns as $key => $column) {
$clause = $key == 0 ? 'where' : 'orWhere';
$query->$clause($column, "LIKE", "%$keyword%");
if (!empty($relativeTables)) {
$this->filterByRelationship($query, $keyword, $relativeTables);
}
}
});
return $query;
}
Filter into relationship also
private function filterByRelationship($query, $keyword, $relativeTables)
{
foreach ($relativeTables as $relationship => $relativeColumns) {
$query->orWhereHas($relationship, function($relationQuery) use ($keyword, $relativeColumns) {
foreach ($relativeColumns as $key => $column) {
$clause = $key == 0 ? 'where' : 'orWhere';
$relationQuery->$clause($column, "LIKE", "%$keyword%");
}
});
}
return $query;
}

Categories