Laravel 5.5 Eloquent - Optionally chain where statements - php

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);

Related

Laravel: search and filter data

i want to multi filter data in laravel but i show this error:
Too few arguments to function Illuminate\Support\Collection::get()
Please help me to solve this issue.
public function searchLanding(Request $request)
{
$landings = Landing::all();
if(count($landings) && !is_null($request->title)) {
$landings = $landings->where("name", "LIKE", "%{$request->title}%")->get();
}
if (count($landings) && !is_null($request->start_at)) {
$landings = $landings->where('start_at', '>=', $request->start_at)->get();
}
if (count($landings) && !is_null($request->end_at)) {
$landings = $landings->where('end_at', '<=', $request->end_at)->get();
}
}
public function searchLanding(Request $request)
{
$landings = Landing::query();
if(!is_null($request->title)) {
$landings->orWhere("name", "LIKE", "%{$request->title}%");
}
if (!is_null($request->start_at)) {
$landings->orWhere('start_at', '>=', $request->start_at);
}
if (!is_null($request->end_at)) {
$landings->orWhere('end_at', '<=', $request->end_at);
}
return $landings->get();
}
Note:
You shouldn't call all() or get() when you are still building your query, only call them when you want to get the result.
Use where() when you want all conditions to be true,
or use orWhere() when you want one of the conditions to be true.
In the example above, only one of the conditions needs to be true e.g. search found in title or after start_at or before end_at.

helper vs controller performace in laravel 5.*

Lets assume I have a helper called engine.
if( ! function_exists('engine') )
{
function engine($user_id_1, $user_id_2, $league, $log = true)
{
/*
* variables
*/
$questionsLevel = 1;
$user1 = \App\User::where('id', $user_id_1)->first();
$user2 = \App\User::where('id', $user_id_2)->first();
$league = \App\Models\League::where('id', $league)->first();
$users = $league->all_users;
/*
* check if users joined to league correctly
*/
$user1_ok = $user2_ok = false;
foreach($users as $user)
{
if( $user->id == $user_id_1 )
{
$user1_ok = true;
}
if( $user->id == $user_id_2)
{
$user2_ok = true;
}
$check_users = [
$user1_ok,
$user2_ok
];
}
if( in_array(false, $check_users) )
{
return [
'errcode' => 404,
'errmessage' => 'one ro both user/s did not joined to league'
];
}
//DO SOME STUFF
}//function engine
}
As you know, I can write a controller to do same.
Does anyone know Which is faster based on Laravel architecture? and how can I test performance in both cases?
I'm using laravel 5.7.*
Fastest would be a solution that does not load unnecessary data:
if(! function_exists('engine'))
{
function engine(int $user1, int $user2, int $league)
{
return League::query()
->where('id', $league)
->whereHas('users', function($query) use ($user1) {
$query->where('id', $user1);
})
->whereHas('users', function($query) use ($user2) {
$query->where('id', $user2);
})
->exists();
}
}
In general, I don't think such a function should return complex results like an error message though. For this particular job, you should use a controller action. The helper method should only return true or false in my opinion. You could also place the same logic in a service class though (probably the best solution).
About the performance, I do not think there will be any changes as same query will be executed as well as same number of operation will be done, for example the foreach() loop it is O(n) in both the cases, so what will be difference, but you may like to change your code to something like below or may use ternary operator also. The difference will be in organization of codes. In controller the testing will be easier.
Note: But how id will be both $user1 and $user2, so let's make it OR in stead of AND
foreach($users as $user)
{
if( $user->id == $user_id_1 )
{
$user1_ok = true;
}elseif( $user->id == $user_id_2)
{
$user2_ok = true;
}
$check_users = [
$user1_ok,
$user2_ok
];
}
to
foreach($users as $user)
{
if( in_array($user->id, [$user_id_1, $user_id_2])
{
$user1_ok = true;
}
$check_users = [
$user1_ok,
$user2_ok
];
}
or
foreach($users as $user)
{
if( $user->id == $user_id_1 )
{
$check_users['user1_ok'] = $user->id == $user_id_1 ? true : false;
}
if( $user->id == $user_id_2)
{
$check_users['user2_ok'] = true;
}
}
If you want to get enginees with user1_ok, user2_ok, you may like to run a query as #Namoshek has suggested.
or you may like to change the query to something like
League::query()->where('id', $league)
->whereHas('users', function($query) use ($user1, $user2) {
$query->where('id', $user1)->where('id', $user2);
})->exists();
but, how id will be both $user1 and $user2?so, if it is OR instead of AND.
League::query()->where('id', $league)
->whereHas('users', function($query) use ($user1, $user2) {
$query->whereIn('id', [$user1, $user2]);
})->exists();
or even $users = [$user1, $user2]; and
League::query()->where('id', $league)
->whereHas('users', function($query) use ($users) {
$query->whereIn('id', $users);
})->exists();
By the way, if you want this using these query, you just may put it in Engine model and user or reuse it when ever required. Also for helper, it can be reusable where as in controller it is not.

laravel retrieving data from database takes too much time

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

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