How to search by laravel Eloquent Many to Many - laravel

three tables
user
id name
1 a
2 b
3 c
role
id name
1 aa
2 bb
3 cc
4 dd
role_user
role_id user_id
1 1
2 3
2 1
3 4
3 3
3 2
3 1
Laravel Code:
User::with('roles')->whereHas('roles', function($query) {
$query->whereIn('role_id', [2, 4]);
})->get();
this query return user has role 2 or 4,
I want get user has role 2 and 4;
what can I do?

To achieve what you're after you can use havingRaw():
User::with('roles')->whereHas('roles', function ($query) {
$query
->select('user_id')
->whereIn('role_id', [2, 4])
->groupBy('user_id')
->havingRaw('count(distinct(role_id))=2'); // 2 = The number of different roles i.e. count([2,4])
})->get();
When using whereIn you're essentially asking for retsults that include at least one of the values you pass to it. Using having in conjunction with group by allows you to stipulate that you want to include a certain number of them (in this case all of them).

Related

Laravel: Sum column based on relationship condition

Let's say we have two tables:
Payments
id
reason_id
amount
1
1
100
2
2
10
3
1
30
Payment Reasons
id
title
is_discount
1
Payment for something
0
2
Discount for something
1
I'd like to query payments table and sum amount column based on its relationship payment_reasons.is_discount value:
total_received
total_discounted
130
10
How to achieve this?
If you want to get the data in one db-request, I don't think there is a clean laravel-way to solve this. I've been trying out some raw mysql to get it into a working query and it would look like this:
select
sum(if(pr.is_discount = 0, p.amount, 0)) as total_received,
sum(if(pr.is_discount = 1, p.amount, 0)) as total_discounted
from payments p
join payment_reasons pr on pr.id = p.reason_id
The if statement will render if discount needs to be used per row and the sum wil simply sum all the end-results together.
Doing this in laravel with a model does not really make sence, since there are no fields in the result which your model could use anyway. In this case, using eloquent would be more (unmaintainable) code than a simple Query Builder:
$data = DB::select('
select
sum(if(pr.is_discount = 0, p.amount, 0)) as total_received,
sum(if(pr.is_discount = 1, p.amount, 0)) as total_discounted
from payments p
join payment_reasons pr on pr.id = p.reason_id
')
If you don't mind doing multiple queries to fetch the data, models make more sense:
$totalReceived = Payment
::whereHas('paymentReason', fn($q) => $q->where('is_discount', 0))
->sum('amount');
$totalDiscounted = Payment
::whereHas('paymentReason', fn($q) => $q->where('is_discount', 1))
->sum('amount');
Please note that this example will perform 4 queries, 2 on payments, and 2 on payment_reasons

insert data with many to many relation in Laravel Eloquent

I have created Many To Many relationships on both levels (database and Models) for (User) and (Role) Models, now how can I assign many roles to the user id = 2 for instance: like below?
$user = User::find(2);
????
return view('index', compact(''));
By using attach and insert array for multiple roles
$roleId = [1, 2, 3];
$user->roles()->attach($roleId);
There's also sync method, the difference is sync remove all roles associated by user only if its not included in array of roleId
// User has role_id 1, 2, 3
$roleId = [1, 3];
$user->roles()->sync($roleId); // Role id 2 removed
Docs

Laravel/Eloquent whereIn/where with a pair of sets

I have 3 tables in my database.
The first two tables are just normal tables with an ID and some other columns like:
Table 1
ID
col01
1
...
2
...
Table 2
ID
col01
1
...
2
...
The third table is some kind of a relation/assignment table:
Table 3
ID
table1_id
table2_id
text
1
1
1
..
2
1
2
..
3
1
3
..
4
2
1
..
5
3
3
..
Now I do have a SQL statement which does exactly what I want:
SELECT * FROM table_3 where (table1_id, table2_id) in ( (1, 1), (2, 1), (3, 3));
So Im sending following Request Body to the API:
{
"assignments": [
{
"table1_id": 1,
"table2_id": 1
},
{
"table1_id": 2,
"table2_id": 1
},
{
"table1_id": 3,
"table2_id": 3
}
]
}
I do validate my the request with
->validate($request,
[
'assignments' => 'required|array',
'assignments.*.table1_id' => 'required|integer|min:1|max:20',
'assignments.*.table2_id' => 'required|integer|min:1|max:20'
]
Now Im kinda stuck how to use the eloquent commands (e.g. whereIn) to get my desired output.
Thanks in advance!
EDIT
So I took the workaround of arcanedev-maroc mentioned here: https://github.com/laravel/ideas/issues/1021
and edited it to fit my Request.
Works like a charm.
Laravel does not provide any functions by default. The core team said that they would not maintain this feature. You can read the post here.
But you can create your own query to accomplish this. I am providing a function that you can use as per your specification:
public function test(Request $request)
{
$body=$request->input('data');
$data=json_decode($body)->assignments;
$query='(table1_id, table2_id) in (';
$param=array();
foreach($data as $datum)
{
$query=$query."(".$datum->table1_id.",".$datum->table2_id."), ";
}
$query = rtrim($query, ", ");
$query = $query.")";
$result=DB::table('table3')->whereRaw($query)->get();
return $result;
}
So I took the workaround of arcanedev-maroc mentioned here: https://github.com/laravel/ideas/issues/1021
and edited it to fit my Request.
Works like a charm.

Laravel group by on model hierarchy

I've searched this about two days in the site but was unable to find a similar example.
I'm trying to code the equivalent of this Oracle command in Eloquent:
select d.code, d.grp, d.cname, sum(d.poso1)
from pers_data e, misth_master m, misth_detail d
where e.per_emploeeno = m.emp_no
and m.misaa = d.misaa
and e.per_emploeeno = '04004'
and m.year = '2019'
group by d.code, d.grp, d.cname;
It basically joins 3 models in hierarchical fashion ( e -(has_many)-> m -(has_many)-> d), applies constraints on 1st and 2nd and groups/sums data from the 3rd.
The result set is smt like this:
CODE GRP CNAME SUM(POSO1)
---- --- ----- ----------
153 1 cname1 0
2 0 cname2 3480
2 1 .. 1003,2
162 1 .. 250,8
5 .. 2464,67
30 4 .. 172,68
102 1 .. 949,08
105 1 .. 37,5
111 1 .. 25
0 0 .. 21600
11 1 cnamen 3976,26
In Laravel, I've declared the relevant models (e=Employee with relationships to m=misthoi and d=details).
After many experiments, I've come to this lines of code to achieve this:
(Please note that Employee model has a global scope and thus needs not be constrained in this piece of code, i.e. e.per_emploeeno = '04004' is implied)
$mdata_flat = Employee::with(['misthoi' => function ($query) {
$query->where('year', '2019');
}, 'misthoi.details'])->first()->misthoi->pluck('details')->flatten();
$mdata_grouped = $mdata_flat->groupBy(function ($item, $key) {
return $item['grp'].'.'.$item['code'];
})->map(function ($item) {
return [$item->max('cname'), $item->sum('poso1')];
})->toArray();
It works, indeed, but seems to me complicated and I'm fan of simple, readable code.
I'm convinced there is a neat way to do this with Eloquent.
Could it be written in simpler manner?
It could be generalized as:
Let A, B, C be 3 hierarchical related models (A->B->C).
I want to group data of C only, based on constraints on A and B.
Thank you in advance!
I don't 100% understand the Oracle syntax, but it looks like joins to me.
I would "translate" it to Laravel's query builder as:
DB::table('pers_data')
->join('misth_master', 'pers_data.per_emploeeno', '=', 'misth_master.emp_no')
->join('misth_detail', 'misth_master.misaa', '=', 'misth_detail.misaa')
->where('pers_data.per_emploeeno', '=', '04004')
->where('misth_master.year', '=', '2019')
->select(DB::raw('SUM(misth_detail.poso1) as total'))
->groupBy('misth_detail.code', 'misth_detail.grp', 'misth_detail.cname')

How to get count of data based on the pivot table laravel?

I have three tables,
User cities user_cities
id name title id name user_id city_id
1 aaa designer 1 cityA 2 1
2 bbb developer 2 cityB 2 3
3 ccc designer 3 cityC 1 2
4 ddd designer 3 3
5 rrr developer 3 2
4 2
4 3
Now i am going to filter user by their title=designer. Then the filter data will be,
user
id name title
1 aaa designer
3 ccc designer
4 ddd designer
Along with this data i have to get the count of cities like this,
data: {
0: {
name:cityB,
id:2,
count:3
},
1: {
name:cityC,
id:3,
count:2
}
}
How can i get this with laravel eloquent?
You can do
$users = App\User::where('title', 'designer')->get();
and use
foreach($users as $user) {
$user->cities->count(); // collection count
}
But the better way will be Counting related models
$users = App\User::withCount('cities')->where('title', 'designer')->get();
and then
foreach($users as $user) {
$user->cities_count; // relationship count
}

Resources