How can I convert this SQL to Laravel5.5 Eloquent format
select * from 'arm_articles' where ('article_tag' like '%standard%' or 'article_topic' like '%standard%' or 'article_details' like '%standard%' or 'article_type' like '%standard%') and ( ('id' between 287 and 296) and 'article_active' = 1) order by 'id' desc
Please observer the braces in the SQL
This is the one i wrote that returns a different sql when i tested the output using ->toSql
$post= PostModel::where('article_tag','like','%'.$contributor_id.'%')->orWhere('article_topic','like','%'.$contributor_id.'%')->orWhere('article_details','like','%'.$contributor_id.'%')->orWhere('article_type','like','%'.$contributor_id.'%')->whereBetween('id', [$end, $start-1])->where('article_active',1)->orderBy('id', 'desc')->take(10)->get();
Find the SQL output from the query above
select * from 'arm_articles' where 'article_tag' like ? or 'article_topic' like ? or 'article_details' like ? or 'article_type' like ? and 'id' between ? and ? and 'article_active' = ? order by 'id' desc limit 10
This output looks like the needed SQL, but the different is the braces on the SQL. so one coin the Eloquent Query Builder to come out with the braces on the query ?
Use the where() closure for parameter grouping:
PostModel::where(function($q) use($contributor_id) {
$q->where('article_tag', 'like', '%' . $contributor_id . '%')
->orWhere('article_topic', 'like', '%' . $contributor_id . '%')
->orWhere('article_details', 'like', '%' . $contributor_id . '%')
->orWhere('article_type', 'like', '%' . $contributor_id . '%');
})
->whereBetween('id', [$end, $start - 1])
->where('article_active', 1)
->orderBy('id', 'desc')
->take(10)
->get();
Try with this
$post = PostModel::where(function ($query) use ($contributor_id) {
$query->orWhere('article_topic','like','%'.$contributor_id.'%')
->orWhere('article_details','like','%'.$contributor_id.'%')
->orWhere('article_type','like','%'.$contributor_id.'%')
->orwhere('article_tag','like','%'.$contributor_id.'%');
})->whereBetween('id', [$end, $start-1])
->where('article_active',1)
->orderBy('id', 'desc')
->take(10)
->get();
Alexey Mezenin did it before me.
Related
I am trying to search relevant jobs from a list on jobs on my DB according to what user searches for and if the search term matches the job title its highly relevant search result so I am trying to assign the relevancy value to job objects right after their where condition gets satisfied. Also cleaner way of writing orWhere conditions?
public function search()
{
$searchText = $_GET['searchText'];
$jobs = Jobs::where('name', 'LIKE', '%' . $searchText . '%') //how to do $jobs->relevancy = 100 here? if this where condition is satisfied?
->orwhere('skills', 'LIKE', '%' . $searchText . '%')
->orwhere('desc', 'LIKE', '%' . $searchText . '%') //$jobs->relevancy = 10 here
->orwhere('desc1', 'LIKE', '%' . $searchText . '%')
->orwhere('desc2', 'LIKE', '%' . $searchText . '%')
->orwhere('desc3', 'LIKE', '%' . $searchText . '%')
->orwhere('desc4', 'LIKE', '%' . $searchText . '%')
->get();
return view('results', compact('jobs'));
}
If you want to group orWhere conditions to then apply another WHERE (AND) statement, you can use a closure for the grouping.
Imagine that you want something like this:
WHERE A
AND (WHERE B OR WHERE C)
AND (WHERE D OR WHERE E)
Then you could do:
$results = Model::where('A', 'something')
->where(function ($query) {
$query->where('B', 'something')
->orWhere('C', 'something');
})
->where(function ($query) {
$query->where('D', 'something')
->orWhere('E', 'something');
})
->get();
There's no easy way to know which part of the where clauses actually matched the query. You'd need to re-do the text comparisons after you got the results, which is probably not ideal.
In these sort of situations you might want to consider a full text search.
If you're using MySQL you can create a FULLTEXT index and then use MATCH ... AGAINST. (other DBMS have a different syntax but generally there is support).
$searchText = request()->searchText;
$jobs = Jobs::whereRaw('MATCH(name) AGAINST (? IN NATUAL LANGUAGE MODE)', [$searchText])
->orWhereRaw('MATCH(desc) AGAINST (? IN NATUAL LANGUAGE MODE)', [$searchText])
// ...
selectRaw('(100*MATCH(name) AGAINST (? IN NATUAL LANGUAGE MODE) + 10*MATCH(name) AGAINST (? IN NATUAL LANGUAGE MODE)) as score', [$searchText,searchText])
->addSelect("*")
->orderBy('score', 'desc')
->get();
MATCH ... AGAINST will return a relevance score so by doing e.g. 100*MATCH(name).. you are adding a weight of 100 to that match. The overall result should be ordered based on the total relevance of the search.
More on full text search in MySQL in the manual
If you are using this kind of condition in many places then you can do the following!
1- In your AppServiceProvider inside boot methode
use Illuminate\Database\Query\Builder;
public function boot()
{
Builder::macro('search', function($field, $string){
return $string ? $this->where($field, 'like', '%'.$string.'%') : $this;
});
Builder::macro('orSearch', function($field, $string){
return $string ? $this->orWhere($field, 'like', '%'.$string.'%') : $this;
});
}
2- Do your Query like the following
$jobs = Jobs::where(function($query) {
if($searchText != "") {
$query->search('skills', $searchText);
$query->orSearch('desc', $searchText);
$query->orSearch('desc1', $searchText);
$query->orSearch('desc2',$searchText);
$query->orSearch('desc3',$searchText);
}
})->get();
I think this is a cleaner way of doing this kind of query.
I have a query like so
$data = City::with('hotel')->orwherehas('hotel', function ($query) use ($user_input) {
//here i want to limit this result to 5
$query->where('name', 'LIKE', '%' . $user_input . '%')->take(5);
// $query->take(5); i have tried this too
})->orWhere('name', 'LIKE', '%' . $user_input . '%')->get();
inside the whereHas clause, I have a query that I want to limit to 5, now I tried limit, take but no luck after that where nothing is working I don't know why
You can pass your query to the ->with() query builder method:
$data = City::with(['hotel' => function($query) use ($user_input) {
$query->where('name', 'LIKE', '%' . $user_input . '%')->limit(5);
}])
->where('name', 'LIKE', '%' . $user_input . '%')
->get();
This will get all hotels associated with a city which have the user input, where the city contains the user input.
Note that the ->orWhere() is not used here.
What I'm trying to do is setup a server side configuration for a table data. So I have a model CounterLog that has 3 relationships set [belongsTo] category, location, user. I want a query to filter all CounterLog data including relationships, with offset, limit and orderBy methods set and in the same time retrieve all the filtered rows ignoring offset and limit. Here is what I managed until now and maybe understand better what I want:
$search_query = function($q) use ($search) {
$q->where('name', 'like', '%' . $search . '%');
};
$query = CounterLog::where('created_at', 'like', '%' . $search . '%')
->orWhereHas('category', $search_query)
->orWhereHas('location', $search_query)
->orWhereHas('user', $search_query);
$logs = $query->offset($offset)->limit($limit)->get();
$logs_total = $query->offset(0)->count();
In the last line I'm using $query->offset(0) because for some reason if offset is set to a number $logs_total becomes 0. I'm not sure this is the proper way to do it.. but even like this I have no idea how to use orderBy for ex. category.name.
I know I can always use raw queries in eloquent but I want to know if there is a way to use ORM and relationships. I would really appreciate if you could help me with this..cuz the struggle is real.
Thanks a lot :)
Apparently I haven't got a solution with ORM so I did it with "raw" queries:
$query = $this->db->table('counter_logs')
->leftJoin('categories', 'counter_logs.category_id', '=', 'categories.id')
->leftJoin('locations', 'counter_logs.location_id', '=', 'locations.id')
->leftJoin('users', 'counter_logs.user_id', '=', 'users.id');
->where('counter_logs.created_at', 'like', '%' . $search . '%')
->orWhere('categories.name', 'like', '%' . $search . '%')
->orWhere('locations.name', 'like', '%' . $search . '%')
->orWhere('users.name', 'like', '%' . $search . '%');
->select('counter_logs.id as id', 'categories.name as category', 'locations.name as location', 'users.name as user', 'counter_logs.created_at as date');
$json['total'] = $query->count();
$logs = $query->offset($offset)->limit($limit)->orderBy($sort, $order)->get();
Try to swap statements:
$logs_total = $query->count();
$logs = $query->offset($offset)->limit($limit)->get();
Or clone base query, like this:
$total_count_query = clone $query;
$logs = $query->offset($offset)->limit($limit)->get();
$logs_total = $total_count_query->count();
I'm experiencing difficulty in writing this query in laravel 5.1,
select * from `host_requests` where `host_id` = 5 and (`email` LIKE "%v%" or `request_url` LIKE "%v%")
The purpose of this query is to fetch records that are associated with host_id = 5, also email and request_url are optional if they set then LIKE clause should be used.
I wrote the query in laravel like this
$searchname = "v";
$hostid = "5";
$data = HostRequest::where('email', 'LIKE', '%' . $searchname . '%')
->where('host_id', $hostid)
->orwhere('request_url', 'LIKE', '%' . $searchname . '%')
->latest('id')
->paginate($size);
It is fetching all the records that are also not associated with host_id = 5.
Any help is much appreciated..Thanks
You should use where() with closure like this:
$data = HostRequest::where('host_id', $hostid)
->where(function($q) use($searchname) {
$q->where('email', 'like', '%'.$searchname.'%')
->orWhere('request_url', 'like', '%'.$searchname.'%');
})->orderBy('id', 'desc')
->paginate($size);
Try this code:
$data = HostRequest::where(function ($query) use ($searchname) {
$query->orwhere('email', 'like', '%'.$searchname.'%')
->orWhere('request_url', 'like', '%'.$searchname.'%');
})
->where('host_id', $hostid)
->latest('host_id')
->paginate($size);
I'm building up a filterable list with Laravel 5.0 and I'm crashing on following Problem.
I get the filter parameters from a HTML form and pass them to the Query Builder, but if a form input stays empty all rows of the table filtered by the other filters should be returned.
Example code:
$collection = Model::withTrashed()
->where('attr1', 'LIKE', \Request::input('attr1', '%'))
->where('attr2', 'LIKE', \Request::input('attr2', '%'))
->where('attr3', 'LIKE', \Request::input('attr3', '%'))
->get();
This seems to be most correct code for me, but it doesn't work as expected. Do you know a good solution for my Problem? I don't want to integrate a messy switch/case statement for proofing for existence and building the collection up manually. :(
Thanks for your help.
It seems that your problem is that you are always passing the filters to the query and you should only pass them if they are not empty.
Maybe somethig like this would work for you:
$results = Model::withTrashed();
if (\Request::input('attr1')) {
$results->where('attr1', 'LIKE', \Request::input('attr1', '%'));
}
if (\Request::input('attr2')) {
$results->where('attr2', 'LIKE', \Request::input('attr2', '%'))
}
if (\Request::input('attr3')) {
$results->where('attr3', 'LIKE', \Request::input('attr3', '%'))
}
$collection = $results->get();
You need to tweak your query a bit like this:
$collection = Model::withTrashed()
->where('attr1', 'LIKE', '%' . \Request::input('attr1') . '%')
->where('attr2', 'LIKE', '%' . \Request::input('attr2') . '%')
->where('attr3', 'LIKE', '%' . \Request::input('attr3') . '%')
->get();
This will make sure that the LIKE clause is still correct when an empty input parameter is sent.