How to reuse parts of Eloquent builder in Laravel - laravel

Is there a way to reuse parts of a query in Laravel? For example if I have two queries which share some arbitrarily long section, can I create a function/variable that can be used in place of that section?
Toy queries:
$a = TableA::join('tableb', 'tablea.bid', '=', tableb.id)
->join('talbec', 'tableb.cid', '=', tablec.id)
->join('tabled', 'tablec.did', '=', tabled.id)
->where('tablea_col', '=', true)->get();
$b = TableA::join('tableb', 'tablea.bid', '=', tableb.id)
->join('talbec', 'tableb.cid', '=', tablec.id)
->join('tabled', 'tablec.did', '=', tabled.id)
->where('tableb_col', '=', true)->get();
I would like something like:
$shared = TableA::join('tableb', 'tablea.bid', '=', tableb.id)
->join('talbec', 'tableb.cid', '=', tablec.id)
->join('tabled', 'tablec.did', '=', tabled.id);
$a = shared->where('tablea_col', '=', true)->get();
$b = shared->where('tableb_col', '=', true)->get();

From Laravel 8+, you can clone it then reuse as you wish:
$shared = TableA::join('tableb', 'tablea.bid', '=', tableb.id)
->join('talbec', 'tableb.cid', '=', tablec.id)
->join('tabled', 'tablec.did', '=', tabled.id);
$a = $shared->clone()->where('tablea_col', '=', true)->get();
$b = shared->clone()->where('tableb_col', '=', true)->get();
before Laravel 8, use
$a = clone $shared;
$b = clone $shared;
$a = $a->where('tablea_col', '=', true)->get();
$b = $b->where('tableb_col', '=', true)->get();

You should be able to use a scope for this:
function scopeWithBCandD($query) {
$query->join('tableb', 'tablea.bid', '=', 'tableb.id')
->join('talbec', 'tableb.cid', '=', 'tablec.id')
->join('tabled', 'tablec.did', '=', 'tabled.id');
}
Which would permit:
TableA::withBCandD()->where('tablea_col', true)->get();
TableA::withBCandD()->where('tableb_col', true)->get();

Related

Laravel Eloquent multiple wheres gives wrong results

Why this code below is returning wrong results?
$player->wins = Game::where('home_player_1', '=', $player->id)
->orWhere('home_player_2', '=', $player->id)
->orWhere('home_player_3', '=', $player->id)
->orWhere('home_player_4', '=', $player->id)
->where('result', '=','1') // 1 = win
->get();
$player->losses = Game::where('home_player_1', '=', $player->id)
->orWhere('home_player_2', '=', $player->id)
->orWhere('home_player_3', '=', $player->id)
->orWhere('home_player_4', '=', $player->id)
->where('result', '=','0') // 0 = lose
->get();
$player->matches = Game::where('home_player_1', '=', $player->id)
->orWhere('home_player_2', '=', $player->id)
->orWhere('home_player_3', '=', $player->id)
->orWhere('home_player_4', '=', $player->id)
->get();
What i get is like 5matches / 5 wins / 5losses when in real its 5/5/0. How should i do it?
Move your home_player_x clauses into a nested where condition clause:
$player->wins = Game::where(function ($query) use ($player) {
return $query->where('home_player_1', $player->id)
->orWhere('home_player_2', $player->id)
->orWhere('home_player_3', $player->id)
->orWhere('home_player_4', $player->id);
})->where('result', 1)
->get();
...
This way, it doesn't conflict with that last ->where('result') clause.
Additionally, since you're reusing this clause multiple times, define it as a variable, and reuse it:
$conditional = function ($query) use ($player) {
return $query->where(function ($subQuery) use ($player) {
return $subQuery->orWhere('home_player_1', $player->id)
->orWhere('home_player_2', $player->id)
->orWhere('home_player_3', $player->id)
->orWhere('home_player_4', $player->id);
})
});
$player->wins = Game::where($conditional)->where('result', 1)->get();
$player->losses = Game::where($conditional)->where('result', 0)->get();
$player->matches = Game::where($conditional)->get();
Or, consider defining this as a Scope on the Game model, so you can call Game::playerMatches($player)->where('result', 1)->get(), etc. See the Documentation for that:
https://laravel.com/docs/8.x/eloquent#local-scopes
I won't really touch too much on this, but defining numbered columns, like home_player_1, home_player_2, etc. is generally considered bad data practice. You should define a home_players table, or games_players, with a type column for home or away table and associate Game and Players that way.
Check out the post on Logical Grouping in the Laravel Docs. I think based on that, your statement for matches should be:
$player->matches = Game::where(function ($query) use ($player) {
$query->where('home_player_1', '=', $player->id)
->orWhere('home_player_2', '=', $player->id)
->orWhere('home_player_3', '=', $player->id)
->orWhere('home_player_4', '=', $player->id);
})->get();
I am not sure if this helps as I do not have the rest of the code, but please test it and let me know.
Also from the docs:
"In fact, you should generally always group calls to the orWhere method in parentheses in order to avoid unexpected query behavior."
Need to modify the orWhere() condition, Instead of above you can use below where clause:
$player->wins = Game::where('result', '=','1') // 1 = win
->where(function ($query) use ($player) {
$query->where('home_player_1', '=', $player->id)
->orWhere('home_player_2', '=', $player->id)
->orWhere('home_player_3', '=', $player->id)
->orWhere('home_player_4', '=', $player->id);
})->get();
$player->losses = Game::where('result', '=', '0') // 0 = lose
->where(function ($query) use ($player) {
$query->where('home_player_1', '=', $player->id)
->orWhere('home_player_2', '=', $player->id)
->orWhere('home_player_3', '=', $player->id)
->orWhere('home_player_4', '=', $player->id);
})->get();
$player->matches = Game::
where(function ($query) use ($player) {
$query->where('home_player_1', '=', $player->id)
->orWhere('home_player_2', '=', $player->id)
->orWhere('home_player_3', '=', $player->id)
->orWhere('home_player_4', '=', $player->id);
})->get();
When there were multiple orWhere() then normal conditions do not work, Need to add them under a single where.

How to join two queries as one in whereIn

In my Laravel-5.8 I have these queries:
$manager = DB::table('hr_employees')
->select('line_manager_id')
->where('hr_status',0)
->whereIn('employee_code', $employee_with_goals)
->get();
$employee_with_goals = DB::table('hr_employees')
->join('appraisal_goals', 'hr_employees.employee_code', '=', 'appraisal_goals.employee_code')
->select('hr_employees.employee_code')
->where('hr_employees.company_id', $userCompany)
->where('hr_employees.hr_status',0)
->where('appraisal_goals.is_published', '=', '1')
->where('appraisal_goals.is_approved', '=', '3')
->groupBy('hr_employees.employee_code')
->get();
$manager is the main query
How do I combine these two queries as one using $employee_with_goals as the variable in whereIn()?
You can use the pluck method to retrieve all values for a given key in a collection:
$manager = DB::table('hr_employees')
->select('line_manager_id')
->where('hr_status',0)
->whereIn('employee_code', $employee_with_goals->pluck('employee_code'))
->get();
You can actually use a subquery too:
$manager = DB::table('hr_employees')
->select('line_manager_id')
->where('hr_status',0)
->whereIn('employee_code', function ($query) use ($userCompany) {
$query
->table('hr_employees')
->select('hr_employees.employee_code')
->join('appraisal_goals', 'hr_employees.employee_code', '=', 'appraisal_goals.employee_code')
->where('hr_employees.company_id', $userCompany)
->where('hr_employees.hr_status',0)
->where('appraisal_goals.is_published', '=', '1')
->where('appraisal_goals.is_approved', '=', '3')
->groupBy('hr_employees.employee_code')
})
->get();
The code above is untested.

Laravel 5.2 : How to write query builder with 2 ON in this right join

I have this sql syntax
SELECT vehicles.license_plate, vehicles.year, vehicles.status, vehicles.zone_id, vehicles.car_class_id, harga_sewa.value, harga_sewa.description
FROM `vehicles`
RIGHT JOIN `harga_sewa` ON vehicles.zone_id = harga_sewa.zone_id
AND vehicles.car_class_id = harga_sewa.car_class_id
How I can write something like this?
DB::table('vehicles')
->rightJoin('harga_sewa', 'vehicles.zone_id', '=', 'harga_sewa.zone_id', AND vehicles.car_class_id = harga_sewa.car_class_id)
->select(*)
Try below code
DB::table('vehicles')
->rightJoin('harga_sewa', function ($join) {
$join->on('vehicles.zone_id', '=', 'harga_sewa.zone_id');
$join->on('vehicles.car_class_id', '=', 'harga_sewa.car_class_id');
})
->get();
On mobile, so can't test, but from memory this should work:
DB::table('vehicles')
->rightJoin('harga_sewa', 'vehicles.zone_id', '=', 'harga_sewa.zone_id')
->rightJoin('harga_sewa', 'vehicles.car_class_id', '=', 'harga_sewa.car_class_id')
->select('*')
->get();

Eloquent WHERE AND with "sub" OR

I have a question regarding the WHERE clause
in the query I would like to say
SELECT ...
FROM ...
WHERE link.hos_id = $hos_id
AND outcome.otc_otrdischargedate = $td
AND outcome.otc_outcome LIKE ['%ICU%', '%I.C.U%', '%L3%']
but instead it reads like this
SELECT ...
FROM ...
WHERE link.hos_id = $hos_id
AND outcome.otc_otrdischargedate = $td
OR outcome.otc_outcome LIKE ['%ICU%', '%I.C.U%', '%L3%']
how should the laravel query be structured to read like the first example?
$matchThese = ['link.hos_id' => $hos_id, 'outcome.otc_otrdischargedate' => $td];
$icu = DB::table('link')
->join('demographic', 'link.lnk_dmgid', '=', 'demographic.dmg_id')
->join('admission', 'link.lnk_admid', '=', 'admission.adm_id')
->join('outcome', 'link.lnk_otcid', '=', 'outcome.otc_id')
->where($matchThese)
->orWhere('outcome.otc_outcome', 'like', '%ICU%')
->orWhere('outcome.otc_outcome', 'like', '%I.C.U%')
->orWhere('outcome.otc_outcome', 'like', '%L3%')
->orWhere('outcome.otc_outcome', 'like', '%Level3%')
->orWhere('outcome.otc_outcome', 'like', '%Level 3%')
->orWhere('outcome.otc_outcome', 'like', '%Intensive Care Unit%')
->get();
$icuSize = sizeof($icu);
SOLUTION -- THANKS #Mehravish Temkar
different query, but same principal
$array_conditions = array('%ICU%', '%I.C.U%','%L3%','%Level3%','%Level 3%','%Intensive Care Unit%');
$step_down_icu = DB::table('link')
->join('daily_link', 'link.lnk_id', '=', 'daily_link.dlk_lnkid')
->join('admission', 'link.lnk_admid', '=', 'admission.adm_id')
->join('outcome', 'link.lnk_otcid', '=', 'outcome.otc_id')
->where('admission.adm_referraldate', '=', $td)
->where('outcome.otc_outcome', 'like', '%ICU%')
->Where(function ($query) use($array_conditions) {
for ($i = 0; $i < count($array_conditions); $i++){
$query->orwhere('outcome.otc_outcome', 'like', '%' . $array_conditions[$i] .'%');
}
})->get();
$step_down_icuSize = sizeof($step_down_icu);
This should work:
$array_conditions = array('%ICU%', '%I.C.U%','%L3%');
$matchThese = ['link.hos_id' => $hos_id, 'outcome.otc_otrdischargedate' => $td];
$icu = DB::table('link')
->join('demographic', 'link.lnk_dmgid', '=', 'demographic.dmg_id')
->join('admission', 'link.lnk_admid', '=', 'admission.adm_id')
->join('outcome', 'link.lnk_otcid', '=', 'outcome.otc_id')
->where($matchThese)
->whereIn('outcome.otc_outcome', 'like', $array_conditions)
->get();
$icuSize = sizeof($icu);
Just switch the orWhere() with where(). The query builder will chain the SQL clauses with AND.
$matchThese = ['link.hos_id' => $hos_id, 'outcome.otc_otrdischargedate' => $td];
$icu = DB::table('link')
->join('demographic', 'link.lnk_dmgid', '=', 'demographic.dmg_id')
->join('admission', 'link.lnk_admid', '=', 'admission.adm_id')
->join('outcome', 'link.lnk_otcid', '=', 'outcome.otc_id')
->where($matchThese)
->where('outcome.otc_outcome', 'like', '%ICU%')
->where('outcome.otc_outcome', 'like', '%I.C.U%')
->where('outcome.otc_outcome', 'like', '%L3%')
->where('outcome.otc_outcome', 'like', '%Level3%')
->where('outcome.otc_outcome', 'like', '%Level 3%')
->where('outcome.otc_outcome', 'like', '%Intensive Care Unit%')
->get();
$icuSize = sizeof($icu);

Specifying where-and in Laravel's query builder

I have the following code which works:
$name = 'jhon';
$users = DB::table('account')->where('login', '=', $name)->get();
How would I specify the AND parameter so that I can query on multiple conditions?
$login = $request->login;
$password = $request->password;
$users = DB::table('account')->where([
['login', '=', $login],
['password', '=', $password]
])->get();
Doesn't work.
$users = DB::table('account')
->where('login', '=', $login and 'password', '=', $password)->get();
To clarify, I'm looking specifically for a query builder solution, not an Eloquent solution.
Use where method twice:
$users = DB::table('account')
->where('login', '=', $login)
->where('password', '=', $password)
->get();

Resources