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();
Related
How can I search in two column in my mysql table example : I want search product name and with sku
I found some function but I cant entegreted , now only search with product name.
public function search(Builder $builder)
{
$result = [];
$query = DB::table($builder->model->searchTable());
$columns = implode(',', $builder->model->searchColumns());
$query->whereRaw("MATCH ({$columns}) AGAINST (? IN BOOLEAN MODE)", $this->getSearchKeyword($builder));
if ($builder->callback) {
$query = call_user_func($builder->callback, $query, $this);
}
$result['count'] = $query->count();
if (property_exists($builder, 'orders') && ! empty($builder->orders)) {
foreach ($builder->orders as $order) {
$query->orderBy($order['column'], $order['direction']);
}
}
if ($builder->limit) {
$query = $query->take($builder->limit);
}
if (property_exists($builder, 'offset') && $builder->offset) {
$query = $query->skip($builder->offset);
}
$result['results'] = $query->pluck($builder->model->searchKey());
return $result;
}
private function getProducts(Product $model)
{
return $model->search(request('query'))
->query()
->limit(10)
->withName()
->withBaseImage()
->withPrice()
->addSelect([
'products.id',
'products.slug',
'products.in_stock',
'products.manage_stock',
'products.qty',
'products.sku',
])
->with(['files', 'categories' => function ($query) {
$query->limit(5);
}])
->when(request()->filled('category'), $this->categoryQuery())
->get();
}
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
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);
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;
}
Keep in mind I have minimal SQL experience, so I may be doing it all wrong.
I'm trying to set up search for a table but simply searching for the string a user enters is not ideal because though something may come up for "steve", if a user types "steve jobs story", nothing will match if that doesn't exist, as it'll be looking for that whole string instead of looking for each word respectively.
I tried to break it down by exploding the string into an array and then imploding that and separating it with %'s, so a search like "steve jobs story" would be "steve%jobs%story", and with my query it would be "%steve%jobs%story%". When I try searching this, I get no error but the query finds nothing, whereas when I just type one word it works, so I'm guessing what I'm trying to do isn't allowed.
Can someone point me in the right direction? Here is my controller/model:
// Controller
$query = $request->get('q');
$explodedQuery = explode(" ", $query);
$newQuery = implode("%", $explodedQuery);
$articles = Article::isLikeTitle($newQuery)
->isLikeBody($newQuery)
->latest('published_at')
->published()
->paginate(10);
// Model
public function scopeIsLikeTitle($query, $q)
{
return $query->where('title', 'LIKE', "%$q%");
}
public function scopeIsLikeBody($query, $q)
{
return $query->where('body', 'LIKE', "%$q%");
}
You can use loop to append the query with orWhere like this
$query = $request->get('q');
$explodedQuery = explode(" ", $query);
$articles = Article::isLikeTitle($explodedQuery)
->isLikeBody($explodedQuery)
->latest('published_at')
->published()
->paginate(10);
// Model
public function scopeIsLikeTitle($query, $q)
{
foreach($q as $eachQueryString)
{
$query->orWhere('title', 'LIKE', '%'.$eachQueryString .'%');
}
return $query;
}
public function scopeIsLikeBody($query, $q)
{
foreach($q as $eachQueryString)
{
$query->orWhere('body', 'LIKE', '%'.$eachQueryString .'%');
}
return $query;
}