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ą