Kontynuujemy naukę Laravela, tym razem przebijając się do coraz to bardziej zaawansowanych zagadnień. Staramy się osiągnąć mistrzostwo. Do dzieła.
Ok, policzmy osoby z notatkami:
Artisan::command('person-hasnotes', function () {
$cnt = Person::has('notes')->count();
$this->comment("There are $cnt profiles with notes");
});
Teraz policzmy osoby bez notatek:
Artisan::command('person-nonotes', function () {
$cnt = Person::doesntHave('notes')->count();
$this->comment("There are $cnt profiles without notes");
});
Policzmy osoby, które mają równo 5 notatek:
Artisan::command('person-hasfivenotes', function () {
$cnt = Person::has('notes', "=", 5)->count();
$this->comment("There are $cnt profiles with 5 notes");
});
Policzmy osoby, które mają więcej niż jedną notatkę:
Artisan::command('person-morethan1note', function () {
$cnt = Person::has('notes', ">", 1)->count();
$this->comment("There are $cnt profiles with more than one note");
});
Te rzeczy powinny być dla nas już mega proste. Ok, zróbmy whereHas:
Artisan::command('person-wherehas', function () {
$people = Person::whereHas('notes', function($query){
$query->where('content', 'like', 'Lorem%');
})->get();
foreach($people as $person){
$this->comment($person->full_name);
$this->comment($person->id);
}
});
Ten kod da nam osoby, które mają notatki i te notatki muszą zaczynać się od Lorem. Pytanie czy wszystkie?
Dodajmy do osoby, której dodawaliśmy Loremy coś innego:
Artisan::command('person-add-lorem {id}', function (int $id) {
$person = Person::findOrFail($id);
$note = new Note(["title" => "Bla", "content" => "Bla bla bla"]);
$person->notes()->save($note);
$this->comment("note added");
});
Teraz jeszcze raz wykonujemy metodę person-wherehas i jak się okazuje nie wszystkie muszą spełniać kryteria.
Jeżeli kogoś denerwuje, że whereHas wymaga callbacka i Builderem (którego nie trzeba już wstrzykiwać przez DI, ale można) to jest nowa alternatywa:
Artisan::command('person-whereRelation', function () {
$people = Person::whereRelation("notes", "content", "like", "Lorem%")->get();
foreach($people as $person){
$this->comment($person->full_name);
$this->comment($person->id);
}
});
Ok, przypomnijmy raz jeszcze doesntHave:
Artisan::command('person-nonotes', function () {
$cnt = Person::doesntHave('notes')->count();
$this->comment("There are $cnt profiles without notes");
});
Mamy analogiczną do whereHas metodę whereDoestHave:
Artisan::command('person-wheredoesnthave', function () {
$cnt = Person::whereDoesntHave('notes', function (Builder $query) {
$query->where('content', 'like', 'Lorem%');
})->count();
$this->comment($cnt);
});
Pytanie, co ta metoda liczy? Ile z tych, co notatki mają posiada content niezaczynający się od Lorem? Czy ile nie ma ani notatek a jak ma to nie zaczynają się od lorem? Co w przypadku, gdy przynajmniej jedna od lorem się nie zaczyna?
Rzut oka na sql:
select * from `people`
where not exists
(select * from `notes`
where `people`.`id` = `notes`.`person_id`
and `content` like ?
and `notes`.`deleted_at` is null)
Wyjaśnienie:
- count tu nie mamy, bo to metoda klasy Collection, ale dla uproszczenia użyjemy zaraz określenia „liczy”
- nasz kod liczy te osoby, które nie mają notatek, oraz te, które jakieś mają, ale nie mają notatek zaczynających się od Lorem
- jeżeli jakiś rekord ma przynajmniej jedną zaczynającą się od Lorem to nie jest liczony jako ten, który nie posiada
- krótko mówiąc zbierane są te i tylko te, które albo notatek nie mają, albo ani jednej od Lorem się zaczynającej nie mają