Laravel Eloquent selecting field from relationship after joining with other tables - php

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

Related

Laravel SQL Query whereIn + where + orWhere

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.

Laravel query count total rows

I have this query:
$counts = DB::table('projects')
->join('prject_links', 'prject_links.project_id', '=', 'projects.id')
->join('links', 'links.id', '=', 'prject_links.link_id')
->join('segments', 'segments.id', '=', 'links.segment_id')
->join('hthree_regions', 'hthree_regions.id', '=', 'segments.hthree_id')
->join('areas', 'areas.id', '=', 'hthree_regions.area_id')
->join('zone_regions', 'zone_regions.id', '=', 'areas.zone_id')
->select(
'zone_regions.id as regions',
'areas.id as provinces',
'hthree_regions.id as cities',
'segments.id as segments'
)
->get();
what I'm looking to have as result is something like:
counts => [
regions => 20,
provinces => 10,
cities => 7,
segments => 5
];
What do i get currently is confusing result like:
any idea about how to fix my query?
Use groupBy and count(distinct field) to get the field's count in each project:
->selectRaw(
"CONCAT('project ', projects.id) as project,
COUNT(DISTINCT zone_regions.id) as regions,
COUNT(DISTINCT areas.id) as provinces,
COUNT(DISTINCT hthree_regions.id) as cities,
COUNT(DISTINCT segments.id) as segments"
)
->groupBy('projects.id')
->get();
This might be your solution. Run it and let me know if that helps
$counts = DB::table('projects')
->join('prject_links', 'prject_links.project_id', '=', 'projects.id')
->join('links', 'links.id', '=', 'prject_links.link_id')
->join('segments', 'segments.id', '=', 'links.segment_id')
->join('hthree_regions', 'hthree_regions.id', '=', 'segments.hthree_id')
->join('areas', 'areas.id', '=', 'hthree_regions.area_id')
->join('zone_regions', 'zone_regions.id', '=', 'areas.zone_id')
->groupBy('projects.id')
->selectRaw(
'count(zone_regions.id) as regions',
'count(areas.id) as provinces',
'count(hthree_regions.id) as cities',
'count(segments.id) as segments'
)
->get();

How to hide repetitive data Laravel

Hello i have this function
public function readData(){
$TableB1 = \DB::table('users')
->join('group_user', 'users.id', '=', 'group_user.user_id')
->join('groups', 'groups.id', '=', 'group_user.group_id')
->join('meetings', 'users.id', '=', 'meetings.owned_by_id')
->select(
'users.name as name',
'group_user.user_id as id',
'groups.name as groupname',
'meetings.owned_by_id as meetings'
)
->get();
return $TableB1;
}
it returns data like this :
[{"name":"Mohamed Adel","id":6,"groupname":"Team Mohamed","meetings":6},{"name":"Mohamed Adel","id":6,"groupname":"Team Mohamed","meetings":6},{"name":"Norman Osborn","id":2,"groupname":"Team Mohamed","meetings":2},{"name":"Harry Osborn","id":3,"groupname":"Team Harry","meetings":3}]
as you can see i have 2 Mohamed Adel objects how to hide them
or how to query right so i don't get repetition each time
i edited the function
public function readData(){
$TableB1 = \DB::table('users')
->join('group_user', 'users.id', '=', 'group_user.user_id')
->join('groups', 'groups.id', '=', 'group_user.group_id')
->join('meetings', 'users.id', '=', 'meetings.owned_by_id')
->select(
'users.name as name',
'group_user.user_id as id',
'groups.name as groupname',
'meetings.owned_by_id as meetings'
)
->groupBy('users.id')
->get();
i get this error : https://ibb.co/FqPd1Wz
You can simply use groupBy with query builder
public function readData()
{
$TableB1 = \DB::table('users')
->join('group_user', 'users.id', '=', 'group_user.user_id')
->join('groups', 'groups.id', '=', 'group_user.group_id')
->join('meetings', 'users.id', '=', 'meetings.owned_by_id')
->select(
'users.name as name',
'group_user.user_id as id',
'groups.name as groupname',
'meetings.owned_by_id as meetings'
)
->groupBy('users.name')
->get();
return $TableB1;
}
You can read more about groupBy here
You need to use groupBy method like this :
->groupBy('group_user.user_id')
See this documentation link Laravel Query Builder Group By

Reducing database query sizes in laravel

I'm looking to reduce the my query size in laravel.
My query looks something like this (I shortened it, it's about 10 times this amount of lines):
$users = User::where("interface_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', $unavailableCheck)
->orWhere("interface_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', 1)
->orWhere("web_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', $unavailableCheck)
->orWhere("web_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', 1)
->orWhere("illustration_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', $unavailableCheck)
->orWhere("illustration_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', 1)
->orWhere("brush_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', $unavailableCheck)
->orWhere("brush_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', 1)
->orWhere("typography_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', $unavailableCheck)
->orWhere("typography_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', 1)
->orWhere("identity_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', $unavailableCheck)
->orWhere("identity_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', 1)
->orWhere("vector_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', $unavailableCheck)
->orWhere("vector_art", '=', 1)->where('role', '=', 2)->where('commstatus', '=', 1)
->orderBy($orderByString, 'desc')
->paginate(1);
As you can see, it's a bit redundant.
For every art type, I'm looking to get users by the role of "2", if their commstatus is equal to "1" or "$unavailable".
At first, I tried to shorten it by not adding "role" or "commstatus" at the end of each "where" clause, and at the bottom writing another $users = $users::where("role", "=", "2"), for example, but I can't seem to be able to find the right syntax that.
Is there any way to shorten this query?
You certainly shouldn't need to duplicate where('role', '=', 2)->where('commstatus', '=', $unavailableCheck) for every single type of art, as they're effectively ANDed conditions; and consider whereIn('commstatus', [$unavailableCheck, 1]) rather than having two equality checks.
Something like:
$users = User::where('role', '=', 2)
->whereIn('commstatus', [$unavailableCheck, 1])
->where("interface_art", '=', 1)
->orWhere("web_art", '=', 1)
->orWhere("illustration_art", '=', 1)
->orWhere("brush_art", '=', 1)
->orWhere("typography_art", '=', 1)
->orWhere("identity_art", '=', 1)
->orWhere("vector_art", '=', 1)
->orderBy($orderByString, 'desc')
->paginate(1);
As others have stated, your schema would likely benefit from refactoring towards normalization. However, I believe you can still refactor your existing query to be less redundant and more readable.
Laravel has the ability to handle Advanced Where Clauses which include nested parameter groupings. From the documentation:
The Closure will receive a query builder instance which you can use to set the constraints that should be contained within the parenthesis group.
With that in mind, you should be able to refactor the query like this:
$users = User::whereIn('commstatus', [$unavailableCheck, 1])
->where('role', 2)
->where(function ($query) {
$query->where("interface_art", 1)
->orWhere("web_art", 1)
->orWhere("illustration_art", 1)
->orWhere("brush_art", 1)
->orWhere("typography_art", 1)
->orWhere("identity_art", 1)
->orWhere("vector_art", 1);
})
->orderBy($orderByString, 'desc')
->paginate(1);
That will create a SQL query that does:
SELECT * FROM users
WHERE commstatus IN ($unavailableCheck, 1)
AND role = 2
AND (interface_art = 1 OR illustration_art = 1 OR ... etc)

syntax to join a table twice in laravel

I'm trying to join the below 3 tables. The issue is in the part where I want to join posts.id to the shares.post_id using 'AND' operator which is not correct. What is the correct syntax to do that ?
$sharedPosts = DB::table('shares')
->join('users', 'users.id', '=', 'shares.user_id')
->join('posts', 'posts.user_id', '=', 'users.id' , 'AND' , 'posts.id', '=', 'shares.post_id')
->where('shares.user_id', '=', $myFriends)
->get();
Try aliasing:
$sharedPosts = DB::table('shares')
->join('users', 'users.id', '=', 'shares.user_id')
->join('posts as p1', 'p1.user_id', '=', 'users.id' , 'AND' , 'p1.id', '=', 'shares.post_id')
->join('posts as p2', 'p2.id', '=', 'shares.post_id')
->where('shares.user_id', '=', $myFriends)
->get();
You cannot join a table twice with no alias.
Although if you have 2 clauses in your join, I think you ought to do as in the documentation :
$sharedPosts = DB::table('shares')
->join('users', 'users.id', '=', 'shares.user_id')
->join('posts as p1', , function($join){
$join->on('p1.user_id', '=', 'users.id')
->on('p1.id', '=', 'shares.post_id')
})
->join('posts as p2', 'p2.id', '=', 'shares.post_id')
->where('shares.user_id', '=', $myFriends)
->get();
Edit (with one posts join):
$sharedPosts = DB::table('shares')
->join('users', 'users.id', '=', 'shares.user_id')
->join('posts', , function($join){
$join->on('posts.user_id', '=', 'users.id')
->on('posts.id', '=', 'shares.post_id')
})
->where('shares.user_id', '=', $myFriends)
->get();

Categories