Sprawdzamy istnienie relacji w modelu na kilku przykładach, kontynuacja lekcji poprzednich. Staramy się wyrobić w korzystaniu z baz danych w Laravelu.

Ok, pierwsze podejście:

Artisan::command('person-has-note {id}', function (int $id) {

    $person = Person::withCount('notes')->findOrFail($id);
    $this->comment($person->notes_count ? 'true' : 'false');
    
});

Jak to działa – wyjaśniam:

  • metoda withCount zwraca w result secie pole notes_count zawierające ilość relacji notes
  • gdy ta ilość jest równa 0 to 0 jako falsy value psuje warunek i mamy false

Inny, bardziej toporny przykład:

Artisan::command('person-has-note2 {id}', function (int $id) {

    $person = Person::with('notes')->findOrFail($id);
    $this->comment($person->notes()->count() ? 'true' : 'false');
    
});

Jak to działa:

  • Tutaj nie ładujemy kolumny notes_count
  • Za pomocą with „ochoczo” ładujemy (eager loading) wszystkie notatki (czego w sumie nie potrzebujemy) jako kolekcję
  • Zwracany wynik ma kolekcję notes, która jak każda kolekcja ma metodę count

Ok, ciekawsze podejście:

Artisan::command('person-has-note3 {id}', function (int $id) {

    $person = Person::withExists('notes')->findOrFail($id);
    $this->comment($person->notes_exists ? 'true' : 'false');
    
});

Wyjaśnienie:

  • tutaj nie ładujemy ani kolekcji notes ani pola notes_count, jesteśmy tak oszczędni, jak to możliwe
  • za pomocą metody withExists ładujemy pole notes_exists, które zawiera prawdę lub fałsz

Ok, zróbmy jeszcze ćwiczenie z fasadą DB:

Artisan::command('person-has-note4 {id}', function (int $id) {

    $sql = DB::table('notes')
    ->select("id")
    ->where('person_id', $id)
    ->count();

    $this->comment($sql);
    
});

Jak widać działa i nawet DB raw nie potrzebowaliśmy. Już tłumaczę dlaczego. Zamieńmy count na toSql i sprawdźmy:

select `id` 
from `notes` 
where `person_id` = ?

I tylko tyle. Dostajemy kolekcję z numerami id, pod warunkiem że w polu person_id (które nie jest zaciągane) person_id jest równe temu przekazanemu do funkcji.

Dalej metoda ->count() sprawdza, ile wyników dostaliśmy. Ale my chcielibyśmy coś takiego:

SELECT count(*) FROM `notes` WHERE person_id = 1; 

I takie coś zwraca:

COUNT(*)
7

Czyli jedna kolumna, jeden rekord, nie jedna kolumna ID i 7 rekordów przekazywanych do Collection tylko po to, by wywołać ->count().

Ok, możemy zrobić to tak:

Artisan::command('person-has-note5 {id}', function (int $id) {

    $sql = DB::table('notes')
    ->select(DB::raw('COUNT(*) as cnt'))
    ->where('person_id', $id)
    ->toSql();

    $this->comment($sql);
    
});

//select COUNT(*) as cnt from `notes` where `person_id` = ?

Musimy tylko pamiętać, że ->get() zwróci nam Collection. Ona będzie miała items, które są protected, więc musimy pokombinować z toArray:

Artisan::command('person-has-note5 {id}', function (int $id) {

    $sql = DB::table('notes')
    ->select(DB::raw('COUNT(*) as cnt'))
    ->where('person_id', $id)
    ->get();

    $this->comment($sql->toArray()[0]->cnt);
    
});

//7

Mam nadzieję, że jesteśmy na takim poziomie, że to co tu teraz piszę w ogóle nas nie przeraża.