Eloquent pod pokličkou: whereHas()

Podobně jako metoda has(), metoda whereHas() nám umožňuje získat data z databáze, kde se daný vztah shoduje s danými podmínkami. Metoda whereHas() je však víceméně pohodlnější, jelikož ve skutečnosti deleguje metodu has(). Její přidanou hodnotou je vytvářet náš kód více explicitní a lépe čitelný. Metoda má také zpřeházené parametry v signatuře, takže hodnoty vkládáme v pohodlnějším pořadí.

Mějme jednoduchý příklad:

Kód pak může vypadat například takto:

$usersWithOverdueTasks = $users->filter(function ($user) {
    foreach ($user->tasks as $task) {
        if ($task->due_at <= Carbon::now()) {
            return true;
        }
    }

    return false;
});

Ačkoliv kód bude fungovat, není moc čitelný a také znamená, že budeme muset načíst všechny uživatele a jejich úkoly do paměti nezávisle na tom, jestli je budeme vůbec při dalším zpracování potřebovat nebo ne. Co kdybychom ale přesunuli tuto logiku na databázovou úroveň?

V následující příkladu můžeme vidět metodu whereHas() v akci, kdy se databázový dotaz upraví tak, že vybíráme pouze uživatele se zpožděnými úkoly.

User::whereHas('tasks', function ($query) {
    $query->where('due_at', '<=', Carbon::now());
})->get();

Za zmínku také stojí, že closure v druhém parametru vkládá instanci Eloquent query builderu, takže máme plný přístup ke všem Eloquent metodám, včetně  k jakýmkoliv vlastním metodám, které jsme si definovali v modelu relace, v našem případě tedy v modelu Task.

Všimněte si, že do poddotazu, který kontroluje existenci našeho vztahu, přibyla navíc klauzule WHERE. To pak znamená, že úkol bude správně vyhodnocen jen v případě, že jeho termín již uplynul. Tím pádem získáme pouze uživatele, jejichž úkoly jsou po termínu.

public function doesntHave($relation, $boolean = 'and', Closure $callback = null)
{
    return $this->has($relation, '<', 1, $boolean, $callback);
}
public function whereDoesntHave($relation, Closure $callback = null)
{
    return $this->doesntHave($relation, 'and', $callback);
}