I'm having some trouble with this query and I'm out of ideas, let me explain:
Here is what I have:
Table of users (Columns that matter for this question: name, description)
Table of services (Columns that matter for this question: name, description, subcategory_id, category_id)
Pivot table for the user-services relationship
Here is what I want to happen:
The app must be able to search by text in the name/description of the user and his related services.
Also the app can send me one category_id and multiple subcategory_ids for the search, so the related services must have that id
First search works alright, problem comes when I try the second one. Is like it doesn't take into account every parameter of the query. I want it to search by text ONLY if that category_id or subcategory_id matches, but it shows results without having that match.
Here is what I've got so far:
$professionals = \DB::table('users')->where('role_id', Role::PROFESSIONAL_USER)
->join('professional_service', 'users.id', '=', 'professional_service.user_id', 'left')
->join('services', 'services.id', '=', 'professional_service.service_id', 'left')
->select('users.id', 'users.name as name', 'users.last_name as last_name', 'users.gender as gender', 'users.description as description', 'users.city', 'users.latitude', 'users.longitude', 'users.wpicture')
->where('users.name', 'like', '%'.$request->search.'%')
->where('services.category_id', '=', $request->category_id)
->orWhere('users.description', 'like', '%'.$request->search.'%')
->orWhere('services.name', 'like', '%'.$request->search.'%')
->orWhere('services.description', 'like', '%'.$request->search.'%')
->whereIn('services.subcategory_id', $request->subcategories)
->groupBy('users.id')
->paginate($request->per_page);
Any help would be appreciated. Thanks!
Edited (using closure as suggested in comments):
$professionals = \DB::table('users')->where('role_id', Role::PROFESSIONAL_USER)
->join('professional_service', 'users.id', '=', 'professional_service.user_id', 'left')
->join('services', 'services.id', '=', 'professional_service.service_id', 'left')
->select('users.id', 'users.name as name', 'users.last_name as last_name', 'users.gender as gender', 'users.description as description', 'users.city', 'users.latitude', 'users.longitude')
->where(function($query) use ($search){ $query->where('users.name', 'like', '%'.$search.'%'); $query->orWhere('users.description', 'like', '%'.$search.'%'); $query->orWhere('services.name', 'like', '%'.$search.'%'); $query->orWhere('services.description', 'like', '%'.$search.'%');})
->where('services.category_id', '=', $request->category)
->whereIn('services.subcategory_id', explode(',', $request->subcategories))
->groupBy('users.id')
->paginate($request->per_page);
Problem now is, if the user only search by text and category / subcategories are null, no results are shown. How can I ignore them if they are null?
Found the solution (I'm sure it can be done in a better way, but this is working for me):
$professionals = \DB::table('users')->where('role_id', Role::PROFESSIONAL_USER)
->join('professional_service', 'users.id', '=', 'professional_service.user_id', 'left')
->join('services', 'services.id', '=', 'professional_service.service_id', 'left')
->select('users.id', 'users.name as name', 'users.last_name as last_name', 'users.gender as gender', 'users.description as description', 'users.city', 'users.latitude', 'users.longitude')
->where(function($query) use ($search){ $query->where('users.name', 'like', '%'.$search.'%'); $query->orWhere('users.description', 'like', '%'.$search.'%'); $query->orWhere('services.name', 'like', '%'.$search.'%'); $query->orWhere('services.description', 'like', '%'.$search.'%');})
->when($category, function($query) use ($category) { $query->where('services.category_id', '=', $category);})
->when($subcategories, function($query) use ($subcategories) { $query->whereIn('services.subcategory_id', $subcategories);})
->groupBy('users.id')
->paginate($request->per_page);
Thanks for the tip about closures.
Related
I have a query where I have joined several tables together and as they have fields with identical names I have used the select query to only pick out the fields that I require. However, i also need to include the results obtained from the with query but I do not know how to specify it in the select query.
$display_tickets = ManualTicket::select('u.name as name', 'i.name as initiator', 'manual_tickets.status as status', 'manual_tickets.description as description', 'manual_tickets.location as location', 'manual_tickets.created_at as created_at', 'manual_tickets.initiator_id as initiator_id', 'manual_tickets.id as manual_ticket_id','manual_tickets.manual_ticket_log as manual_ticket_log_id')
->leftJoin('users as u', 'u.id', '=', 'manual_tickets.user_id')
->leftJoin('users as i', 'i.id', '=', 'manual_tickets.initiator_id')
->where(function ($checkClients) use($target_client_id){
$checkClients->where('u.client_id', '=', $target_client_id)
->orWhere('i.client_id', '=', $target_client_id);
})
->whereBetween('manual_tickets.created_at', [$start_date->toDateString(), $end_date->addDays(1)->toDateString()])
->with('manual_ticket_log')
->orderBy("created_at", "DESC")->get();
I tried to include the manual ticket logs through the use of the code below but it says that there is no such field in the manual ticket table.
'manual_tickets.manual_ticket_log as manual_ticket_log_id')
In short, how can i include the results of the with relationship from ->with('manual_ticket_log') into the select statement
--EDIT-- (Tried to replace join with with queries)
However there appears to be any error with my SQL query which i believe stems from the whereHas portion of the code which results in this error being shown.
strtolower() expects parameter 1 to be string, object given
$display_tickets = ManualTicket::select('*')
->with('user')
->with('initiator')
->with('manual_ticket_log')
->where(function ($checkClients) use($target_client_id){
$checkClients->whereHas('user', function ($checkClient) use($target_client_id){
$checkClient->where('client_id', '=', $target_client_id);
})
->orWhere($checkClients->whereHas('initiator', function ($checkClient2) use($target_client_id){
$checkClient2->where('client_id', '=', $target_client_id);
}));
})
->whereBetween('manual_tickets.created_at', [$start_date->toDateString(), $end_date->addDays(1)->toDateString()])
->orderBy("created_at", "DESC")->get();
you should join that table to get fields form:
$display_tickets = ManualTicket::select('u.name as name', 'i.name as initiator', 'manual_tickets.status as status', 'manual_tickets.description as description', 'manual_tickets.location as location', 'manual_tickets.created_at as created_at', 'manual_tickets.initiator_id as initiator_id', 'manual_tickets.id as manual_ticket_id','manual_tickets.manual_ticket_log as manual_ticket_log_id',
'manual_tickets.manual_ticket_log as manual_ticket_log_id')
->leftJoin('users as u', 'u.id', '=', 'manual_tickets.user_id')
->leftJoin('users as i', 'i.id', '=', 'manual_tickets.initiator_id')
->leftJoin('manual_ticket_logs',function ($join){
$join->on('manual_ticket_logs.manual_ticket_id', '=', 'manual_tickets.id')
->on('manual_ticket_logs.id', '=', \DB::raw("(select max(id) from manual_ticket_logs WHERE manual_ticket_logs.manual_ticket_id = manual_tickets.id)"));
})
->where(function ($checkClients) use($target_client_id){
$checkClients->where('u.client_id', '=', $target_client_id)
->orWhere('i.client_id', '=', $target_client_id);
})
->whereBetween('manual_tickets.created_at', [$start_date->toDateString(), $end_date->addDays(1)->toDateString()])
->with('manual_ticket_log')
->orderBy("created_at", "DESC")->get();
please make sure of the fields names in the newest join clause.
You can take advantage of Laravel relationships to make the code more readable and avoid an expensive leftJoin:
$display_tickets = ManualTicket::select('*')
->with([ 'user', 'initiator', 'manual_ticket_log' ])
->where(function ($checkClients) use($target_client_id){
$checkClients->whereHas('user', function ($checkClient) use($target_client_id){
$checkClient->where('client_id', '=', $target_client_id);
})->orWhereHas('initiator', function ($checkClient2) use($target_client_id){
$checkClient2->where('client_id', '=', $target_client_id);
}));
})->whereBetween('created_at', [$start_date->toDateString(), $end_date->addDays(1)->toDateString()])
->orderBy("created_at", "DESC")
->get();
You can try this
$display_tickets = ManualTicket::select('u.name as name', 'i.name as initiator', 'manual_tickets.status as status', 'manual_tickets.description as description', 'manual_tickets.location as location', 'manual_tickets.created_at as created_at', 'manual_tickets.initiator_id as initiator_id', 'manual_tickets.id as manual_ticket_id','manual_tickets.manual_ticket_log as manual_ticket_log_id')
->leftJoin('users as u', 'u.id', '=', 'manual_tickets.user_id')
->leftJoin('users as i', 'i.id', '=', 'manual_tickets.initiator_id')
->where(function ($checkClients) use($target_client_id){
$checkClients->where('u.client_id', '=', $target_client_id)
->orWhere('i.client_id', '=', $target_client_id);
})
->whereBetween('manual_tickets.created_at', [$start_date->toDateString(), $end_date->addDays(1)->toDateString()])
->with(['manual_ticket_log' => function($query) {
$query->select('field1', 'field2');
}])
->orderBy("created_at", "DESC")->get();
Or do this like
->with('manual_ticket_log:field1, field2')
I am trying to select rows from table students using laravel query for searching but on searching it's returning all rows from table.
$data = DB::table('students')
->select(['name', 'username'])->where('institute', $inst)
->where('username', 'LIKE', '%'.$search.'%')
->orWhere('name', 'LIKE', '%'.$search.'%')
->orWhere('email', 'LIKE', '%'.$search.'%')
->orWhere('contact', 'LIKE', '%'.$search.'%')
->orderBy('id', 'DESC')
->paginate(10);
This is the thing, at least for what I can see, you have two kind of filters:
Students that are in a given institute
Students that have a similar username, name, email or contact than the given search term
If so, the problem in your query are the orWhere() clauses. Let's simplify your query to illustrate the issue:
$data = DB::table('students')
->where('institute', $inst)
->orWhere('name', 'LIKE', '%'.$search.'%')
->get();
In the above code, the statement will get all the students that have the institute equals to $inst OR have a name similar to %search%. So, the results could include students that are not in the given institute. You see the issue now?
In order to first filter by institute AND get records that have a similar similar username, name, email or contact, you could wrap the second conditions in a closure. Try this:
$data = DB::table('students')
->select(['name', 'username'])
->where('institute', $inst)
->where(function ($query) use ($search) { // <<<
$query->where('username', 'LIKE', '%'.$search.'%')
->orWhere('name', 'LIKE', '%'.$search.'%')
->orWhere('email', 'LIKE', '%'.$search.'%')
->orWhere('contact', 'LIKE', '%'.$search.'%');
})
->orderBy('id', 'DESC')
->paginate(10);
What I am trying to achieve is to allow teachers to import a student into different classes.
Note: A student can be multiple classes.
The problem is that when I show the list of students in a select dropdown it should return all students except for students that are not in this class (the class being the page that I am on, app.com/classes/5 for example).
$students = User::join('group_user', 'users.id', '=', 'group_user.user_id')
->role('student')
->where('group_user.group_id', '!=', $id)
->orderBy('users.name', 'asc')
->get();
This works and shows all students that are not in this specific class BUT if a student that's in this class and another class their name appears in the list and as duplicate names.
What can I do?
When MySQL's only_full_group_by mode is turned on, it means that strict ANSI SQL rules will apply when using GROUP BY
You should try to select fields from schema on which you can apply group by instead of select *.
$students = User::join('group_user', 'users.id', '=', 'group_user.user_id')
->role('student')
->where('group_user.group_id', '!=', $id)
->select('users.id', 'other fields you used')
->orderBy('users.name', 'ASC')
->groupBy('users.id')
->get();
Not IN is also useful in your case
User::select('fields you used')
->role('student')
->whereNotIn('id', DB::table('group_user')->where('group_id', $id)->pluck('user_id')) // $id = 5
->orderBy('name', 'ASC')
->get();
Modify your query to use distinct() like so;
$students = User::join('group_user', 'users.id', '=', 'group_user.user_id')
->role('student')
->where('group_user.group_id', '!=', $id)
->orderBy('users.name', 'ASC')
->distinct()
->get();
You could also groupBy('users.id')
$students = User::join('group_user', 'users.id', '=', 'group_user.user_id')
->role('student')
->where('group_user.group_id', '!=', $id)
->orderBy('users.name', 'ASC')
->groupBy('users.id')
->get();
I am querying vehicles with model name and type but these where clauses are not at the end after addingorWhere. When I comment or remove these orWhere Clauses then its work.
$models = Vehicle::join('vmodels', 'vmodels.vehicle_id', '=', 'vehicles.id')
->join('vehicletypes', 'vehicletypes.id', '=', 'vehicles.vehicletype_id')
->join('brands', 'brands.id', '=', 'vehicles.make')
->join('companies', 'brands.id', '=', 'companies.name')
->select('vehicles.slug as vslug', 'brands.name as brandname', 'vehicles.id as vid', 'vmodels.*', 'vehicletypes.name as vtype', 'companies.status as cstatus')
->where('brands.name', 'LIKE', "%{$term}%")
->orWhere('vmodels.name', 'LIKE', "%{$term}%")
->orWhere('vmodels.year', 'LIKE', "%{$term}%")
->orWhere('vehicletypes.name', 'LIKE', "%{$term}%")
->orWhere('vehicles.model', 'LIKE', "%{$term}%")
->where('companies.status', '=', 1 )
->where('vmodels.status', '=', 1)
->where('vehicles.status', '=', 1)
->paginate(30);
Group your orWhere's like,
$models = Vehicle::join('vmodels', 'vmodels.vehicle_id', '=', 'vehicles.id')
->join('vehicletypes', 'vehicletypes.id', '=', 'vehicles.vehicletype_id')
->join('brands', 'brands.id', '=', 'vehicles.make')
->join('companies', 'brands.id', '=', 'companies.name')
->select('vehicles.slug as vslug', 'brands.name as brandname', 'vehicles.id as vid', 'vmodels.*', 'vehicletypes.name as vtype', 'companies.status as cstatus')
->where(function($query) use($term){
$query->where('brands.name', 'LIKE', "%{$term}%")
->orWhere('vmodels.name', 'LIKE', "%{$term}%")
->orWhere('vmodels.year', 'LIKE', "%{$term}%")
->orWhere('vehicletypes.name', 'LIKE', "%{$term}%")
->orWhere('vehicles.model', 'LIKE', "%{$term}%");
})
->where('companies.status', '=', 1 )
->where('vmodels.status', '=', 1)
->where('vehicles.status', '=', 1)
->paginate(30);
Reference : https://laravel.com/docs/5.7/queries#parameter-grouping
if you want to simulate AND/OR statement for your SQL queries with Laravel query builder, you should do it like this:
((A or B) and (C or D)) or (E)
ModelName::join(...)->join(...)
->where(function($query){
$query->where( A )
->orWhere( B )
})
->where(function($query){
$query->where( C )
->orWhere( D )
})->orWhere( E )
How about this one:
$models = Vehicle::join('vmodels', 'vmodels.vehicle_id', '=', 'vehicles.id')
->join('vehicletypes', 'vehicletypes.id', '=', 'vehicles.vehicletype_id')
->join('brands', 'brands.id', '=', 'vehicles.make')
->join('companies', 'brands.id', '=', 'companies.name')
->select('vehicles.slug as vslug', 'brands.name as brandname', 'vehicles.id as vid', 'vmodels.*', 'vehicletypes.name as vtype', 'companies.status as cstatus')
->where('brands.name', 'LIKE', "%{$term}%")
->where('companies.status', '=', 1 )
->where('vmodels.status', '=', 1)
->where('vehicles.status', '=', 1)
->orWhere(function($query) use ($term){
$query->where('vmodels.name', 'LIKE', "%{$term}%")
->where('vmodels.year', 'LIKE', "%{$term}%")
->where('vehicletypes.name', 'LIKE', "%{$term}%")
->where('vehicles.model', 'LIKE', "%{$term}%")
})
->paginate(30);
I'm trying to search all the users registered for a particular course using the following query using the laravel.
$users = DB::table('users')
->leftJoin('registrationrequests', 'users.id', '=', 'registrationrequests.user_id')
->where('firstname', 'LIKE', "%$search%")
->orWhere('lastname', 'LIKE', "%$search%")
->orWhere('username', 'LIKE', "%$search%")
->where('registrationrequests.course_id', $course_id)
->where('registrationrequests.registered', 1)
->get();
In the table registrationrequests, I have columns for course_id, user_id and registered (binary value). course_id and user_id are foreign keys for respective tables.
I'm getting an array of output. But it's not checking the condition where('registrationrequests.course_id', $course_id)
What might be the reason?
Using http://laravel.com/docs/4.2/queries#advanced-wheres and https://stackoverflow.com/a/18660266/689579 your query would look something like -
$users = DB::table('users')
->leftJoin('registrationrequests', 'users.id', '=', 'registrationrequests.user_id')
->where('registrationrequests.course_id', $course_id)
->where('registrationrequests.registered', 1)
->where(function($users) use($search){
$users->where('firstname', 'LIKE', "%$search%")
->orWhere('lastname', 'LIKE', "%$search%")
->orWhere('username', 'LIKE', "%$search%")
})
->get();