Uczymy się liczyć soft deleted models za pomocą Eloquenta oraz fasady DB. Poznajemy ograniczenia w zakresie SQL Query Buildera. Do dzieła.

Ok, rzućmy okiem na tabelę notes. Jest tam deleted_at, ustawione na null dla wszystkich, bo żadna notatka nie została usunieta.

Możemy policzyć notatki komendą:

SELECT count(*) 
FROM `notes`;

Tylko to daje nam wszystkie, w tym usunięte. Nieusunięte zbieramy kodem:

SELECT count(*) 
FROM `notes` 
WHERE deleted_at is null; 

Możemy je sobie zobaczyć:

SELECT * 
FROM `notes`
WHERE deleted_at is null;

Ok, napiszmy komendę artisana z Eloquentem:

Artisan::command('notescnt', function () {
    $cnt = Note::count();
    $this->comment($cnt);
});

A teraz z fasadą DB:

Artisan::command('notescnt2', function () {

    $sql = DB::table('notes')
    ->select(DB::raw('COUNT(*)'))
    ->whereNull("deleted_at")
    ->count();

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

Tutaj mamy dwa organiczenia:

  • w metodzie select musimy użyć DB raw jak chcemy się bawić w coś takiego jak COUNT(*)
  • musimy zwrócić count, nie get

Co się stanie, gdy nasza metoda zwróci toSql() zamiast count()? Przypominam, że to końcowe ->count() to metoda klasy Collection.

select COUNT(*) 
from `notes` 
where `deleted_at` 
is null

Wygląda dobrze, więc czemu nie ->get()? Zobaczmy co nam ->get() zwróci:

Artisan::command('notescnt3', function () {

    $sql = DB::table('notes')
    ->select(DB::raw('COUNT(*) as cnt'))
    ->whereNull("deleted_at")
    ->get();

    $this->comment($sql);
});
//[{"cnt":88}]

Niby fajnie, ale spróbujmy się do tego odwołać po indeksie, po kluczu, cokolwiek. Będzie błąd:

Artisan::command('notescnt3', function () {

    $sql = DB::table('notes')
    ->select(DB::raw('COUNT(*) as cnt'))
    ->whereNull("deleted_at")
    ->get();

    var_dump($sql);

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

// object(Illuminate\Support\Collection)#911 (2) {
//     ["items":protected]=>
//     array(1) {
//       [0]=>
//       object(stdClass)#912 (1) {
//         ["cnt"]=>
//         int(88)
//       }
//     }
//     ["escapeWhenCastingToString":protected]=>
//     bool(false)
//   }

//ERROR

Var_dump już nam więcej powiedział. Mamy klasę Collection. Mamy też items, ale items jest protected, zapomnij, że się do niego dokopiesz.

Teoretycznie, jak nam ten count przeszkadza (metoda klasy Collection) to możemy spróbować z toArray:

Artisan::command('notescnt3', function () {

    $sql = DB::table('notes')
    ->select(DB::raw('COUNT(*) as cnt'))
    ->whereNull("deleted_at")
    ->get();

    var_dump($sql->toArray());

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

// array(1) {
//     [0]=>
//     object(stdClass)#912 (1) {
//       ["cnt"]=>
//       int(88)
//     }
//   }

//88

Ok, teraz robimy zebranie wszystkich notatek (razem z usuniętymi):

Artisan::command('notes-with-trashed', function () {
    $cnt = Note::withTrashed()->count();
    $this->comment($cnt);
});

A teraz z fasadą DB:

Artisan::command('notes-with-trashed2', function () {

    $sql = DB::table('notes')
    ->select(DB::raw('COUNT(*)'))
    ->count();

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

Teraz weźmiemy tylko te usunięte (gdzie deleted at nie jest nullem):

Artisan::command('notes-trashed', function () {
    $cnt = Note::onlyTrashed()->count();
    $this->comment($cnt);
});

Ok, fasada DB:

Artisan::command('notes-trashed2', function () {

    $sql = DB::table('notes')
    ->select(DB::raw('COUNT(*)'))
    ->whereNotNull("deleted_at")
    ->count();

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

Odpowiednik w czystym SQL:

SELECT count(*) 
FROM `notes` 
WHERE deleted_at is not null;  

Na tym poziomie powinniśmy już ogarniać:

  • Jak pisać czyste SQL queries
  • Jak używać fasady DB
  • Znać ograniczenia fasady DB oraz sposoby ich obejścia (DB raw)
  • Znać konstrukcję tabel tworzonych przez Laravela
  • Wiedzieć które metody są metodami eloquenta, a które metodami klasy Collection (np. ->count(), które zwraca int z kolekcji, nie żaden SQL)
  • Ogarniać czym jest aliasowanie zwracanych kolumn w SQL
  • Znać metody klasy collection, np. toArray() albo count()
  • Znać metody Query Buildera oraz Eloquent Models

Łatwiej powiedzieć niż zrobić, ale wszystko przychodzi z praktyką. Polecam dużo małych ćwiczeń z jednym jedynym celem.