Uczymy się robić join oraz leftJoin w Laravelu. Powinniśmy to znać, bo to podstawa SQL, ale w dzisiejszych czasach ludzie często zabierają się za frameworki bez poznania podstaw, do dzieła!
Ok, przypomnijmy sobie tę metodę:
Artisan::command('with-address {id}', function (int $id) {
$person = Person::with('address')->findOrFail($id);
$this->comment("Name: {$person->firstName} {$person->lastName}");
$this->comment("Age: {$person->age}");
$this->comment("Created at: {$person->created_at}");
if($person->address){
$this->comment("City: {$person->address->city}");
$this->comment("Street: {$person->address->street}");
}
});
Mamy tutaj:
- eager loading relacji address
- findOrFail, czyli będzie błąd, jak nie istnieje ->first albo ->find
- find/findOrFail(id) można teoretycznie zamienić na whereKey([$id]) albo whereKey($id) – to już powinniśmy znać
- Do adresu odwołujemy się przez person->address
Dobra, zróbmy join:
Artisan::command('join', function () {
$sql = DB::table('people as ppl')
->join('addresses as addr', 'ppl.id', '=', 'addr.person_id')
->select('ppl.*', 'addr.city', 'addr.street')
->get();
$this->comment($sql);
});
Dużo nam tego wywali, więc możemy użyć albo pętli albo wrzucić gdzieś i przeformatować. Albo debugowanie przez toSql:
Artisan::command('join-inspect', function () {
$sql = DB::table('people as ppl')
->join('addresses as addr', 'ppl.id', '=', 'addr.person_id')
->select('ppl.*', 'addr.city', 'addr.street')
->toSql();
$this->comment($sql);
});
Nasz sql wygląda tak:
select `ppl`.*, `addr`.`city`, `addr`.`street`
from `people` as `ppl`
inner join
`addresses` as `addr`
on `ppl`.`id` = `addr`.`person_id`
Możemy wejść w phpMyAdmin, w tabelkę people, SQL, wkleić i dać go.
Co nam się rzuci w oczy?
- Po pierwsze, result set zwrócił tylko te rekordy, które pokrywają się po obu stronach, czyli osoby, które posiadają adres, takich, które nie posiadają po prostu nie ma
- Po drugie, wyniki są (jak w sql przystało) nazwami kolumn (żadnego aliasu nie dawaliśmy), czyli jest city i name na prawo od tabeli lewej. Oznacza to, że odnosilibyśmy się do tych wyników przez $result->city, nie $result->address->city
Tak działa join, tabela po lewej, tabela po prawej, połącz po pk id z lewej odpowiadającej fk nazwa_id po prawej, zwraca wyniki, które sobie odpowiadają.
Jeżeli ktoś chce zabrać wszystkie rekordy people (tabela po lewej) i dołączyć do result setu adresy (jak istnieją, a ja nie to null) to musi wykonać leftJoin:
Artisan::command('leftjoin', function () {
$sql = DB::table('people as ppl')
->leftJoin('addresses as addr', 'ppl.id', '=', 'addr.person_id')
->select('ppl.*', 'addr.city', 'addr.street')
->get();
foreach($sql as $person){
$this->comment("{$person->firstName} {$person->lastName}");
$this->comment("Age: {$person->age}");
if($person->city)
$this->comment("City: {$person->city}");
if($person->street)
$this->comment("Street: {$person->street}");
$this->comment("----------------------------------");
}
});
Left join, czyli:
- Weź wszystkie wyniki z tabeli po lewej
- Zrób join (warunek jak przy joinach)
- Po prawej doklejasz do, co pasuje, jak nie to null
Pamiętamy także, że wynik to jest SQL, zatem miasto będzie pod $result->city, nie pod $result->address->city.
Ok, wykonajmy inspekcję:
rtisan::command('leftjoin-inspect', function () {
$sql = DB::table('people as ppl')
->leftJoin('addresses as addr', 'ppl.id', '=', 'addr.person_id')
->select('ppl.*', 'addr.city', 'addr.street')
->toSql();
$this->comment($sql);
});
SQL wygląda tak:
select `ppl`.*, `addr`.`city`, `addr`.`street`
from `people` as `ppl`
left join
`addresses` as `addr`
on `ppl`.`id` = `addr`.`person_id`;
I kawałek z wyniku (powinniśmy sami to sprawdzić) wygląda tak:
id firstName lastName age created_at updated_at city street
1 Telly Hermann 19 2024-08-15 12:22:14 2024-08-16 10:47:32 London London Street
2 Laney Terry 38 2024-08-15 12:22:14 2024-08-16 10:47:32 London London Street
3 Kaitlyn Kertzmann 36 2024-08-15 12:22:14 2024-08-16 10:47:32 Warsaw Warsaw Street
4 Corine Auer 35 2024-08-15 12:22:14 2024-08-16 10:47:32 NULL NULL
5 Juana Pfeffer 19 2024-08-15 12:22:14 2024-08-16 10:47:32 NULL NULL
6 Camille Gulgowski 27 2024-08-15 12:22:14 2024-08-16 10:47:32 NULL NULL
7 Thora Conn 24 2024-08-15 12:22:14 2024-08-16 10:47:32 NULL NULL
8 Jakayla Hoppe 38 2024-08-15 12:22:14 2024-08-16 10:47:32 NULL NULL
9 Angeline Mante 43 2024-08-15 12:22:14 2024-08-16 10:47:32 NULL NULL
10 Elliot Reilly 40 2024-08-15 12:22:14 2024-08-16 10:47:32 East Pansy McClure Viaduc
Kolejność wyznaczają pk tabeli lewej, po prawej mamy pasujące wyniki z tabeli prawej albo nulle.