
Para testar um valor nulo, usa whereNull() e whereNotNull(). Para saber se uma linha existe, exists() em vez de count() > 0: a base devolve um booleano em vez de contar. Numa coleção já carregada, testa isEmpty() e nunca if ($collection), que é sempre verdadeiro.
Há quatro perguntas que voltam em ciclo assim que se escrevem consultas Eloquent: esta coluna está a null, este registo existe, quantos são, e como trazer só as colunas úteis. Este guia trata as quatro, com o SQL realmente emitido e as armadilhas que custam uma consulta a mais.
Este artigo faz parte do percurso Desenvolvimento web. Todos os exemplos foram executados em Laravel 13.30.1 com PHP 8.4.25, sobre uma tabela tasks com as colunas id, title, assignee (nullable), active, priority e as colunas de timestamps, cuja ausência provoca um erro logo no primeiro registo, e cuja conversão em objetos Carbon é automática.
Testar se uma coluna está a null: whereNull e whereNotNull
Em SQL escreve-se IS NULL e IS NOT NULL, porque = NULL nunca devolve verdadeiro. O Eloquent expõe as duas condições sob a forma de métodos:
// as tarefas sem responsável
Task::whereNull('assignee')->get();
// as tarefas que têm um
Task::whereNotNull('assignee')->get();O SQL produzido é exatamente o esperado:
select * from "tasks" where "assignee" is not nullO mesmo funciona no construtor de consultas, sem passar por um modelo:
use Illuminate\Support\Facades\DB;
DB::table('tasks')->whereNull('assignee')->get();Podes passar um array para testar várias colunas de uma vez, ficando a condição a incidir sobre todas:
Task::whereNull(['assignee', 'closed_at'])->get();Lê-se muitas vezes que where('assignee', null) não funciona. É falso há muito tempo: o Laravel deteta o valor nulo e gera is null. Verificado em Laravel 13, Task::where('assignee', null) produz select * from "tasks" where "assignee" is null e devolve as mesmas linhas que whereNull(). Ainda assim, prefere whereNull(): a intenção fica explícita e não depende dessa normalização.
As variantes seguem a lógica habitual do construtor, com or e a negação:
Task::whereNull('assignee')->orWhereNotNull('closed_at')->get();Testar se um registo existe
Para saber se há uma linha correspondente, não vás buscá-la. O exists() pede à base de dados uma resposta booleana:
if (Task::where('title', 'Publier')->exists()) {
// pelo menos uma linha corresponde
}
if (Task::where('title', 'Publier')->doesntExist()) {
// nenhuma linha
}A diferença face ao count() vê-se no log das consultas. Eis o que as três escritas enviam de facto à base:
-- exists()
select exists(select * from "tasks" where "active" = ?) as "exists"
-- count() > 0
select count(*) as "aggregate" from "tasks" where "active" = ?
-- get()->isNotEmpty()
select * from "tasks" where "active" = ?As três respondem à pergunta, mas a terceira traz e hidrata todas as linhas em objetos para deitar fora o conteúdo logo a seguir. Numa tabela volumosa, é a diferença entre uma consulta de custo constante e uma consulta que engorda com os dados.
Criar só se o registo não existir
O par «verifico e depois insiro» tem nome: firstOrCreate(). O primeiro array serve para procurar, o segundo fornece os valores usados apenas em caso de criação.
$post = Post::firstOrCreate(
['slug' => $slug], // critério de pesquisa
['title' => $title, 'body' => $body], // usado apenas na criação
);Se a linha já existir, o segundo array é ignorado: o registo é devolvido tal e qual, sem escrita. Duas variantes completam a família:
// a mesma lógica, mas sem gravar na base de dados
$post = Post::firstOrNew(['slug' => $slug], ['title' => $title]);
$post->exists; // false enquanto não chamares save()
// encontra e atualiza, ou cria
$post = Post::updateOrCreate(['slug' => $slug], ['title' => $title]);O firstOrCreate() executa um SELECT e depois um INSERT. Duas consultas simultâneas podem passar o SELECT ao mesmo tempo e tentar duas inserções. Põe uma restrição de unicidade na base sobre a coluna em causa, através de uma migração: é ela, e não o código PHP, que garante a unicidade.
Contar e detetar uma coleção vazia
É preciso distinguir dois count(). No construtor de consultas, é um COUNT(*) executado pela base de dados. Numa coleção já carregada, é uma simples contagem em memória.
// COUNT(*) na base de dados, nenhuma linha trazida
Task::where('active', true)->count(); // 3
// as linhas são carregadas e depois contadas em PHP
$tasks = Task::where('active', true)->get();
$tasks->count(); // 3Se já precisas das linhas, a segunda forma é a certa: não lances outra consulta só para contar. Se queres apenas o número, fica-te pela primeira.
Para testar se uma coleção está vazia, há dois métodos explícitos:
$tasks = Task::where('title', 'Inexistant')->get();
$tasks->isEmpty(); // true
$tasks->isNotEmpty(); // falseif ($tasks) é sempre verdadeiro, mesmo quando a coleção não contém nada: é um objeto. O teste tem de incidir sobre isEmpty(), isNotEmpty() ou blank(). É o erro mais frequente desta secção.
Repara, já agora, que isEmpty() não recebe argumento nenhum. Encontra-se com frequência $posts->isEmpty($posts), que funciona por acidente, porque o argumento é ignorado.
Numa view Blade, o @forelse trata o caso vazio sem condição explícita:
@forelse ($tasks as $task)
<li>{{ $task->title }}</li>
@empty
<li>Aucune tâche pour le moment.</li>
@endforelseSelecionar só as colunas úteis
Por omissão, o Eloquent faz um select *. Numa tabela larga, ou quando só mostras dois campos, é transferência e hidratação para nada. Três escritas permitem restringir as colunas:
// 1. select() no construtor
Task::select('title', 'priority')->where('active', true)->get();
// 2. como argumento de get() ou de first()
Task::where('active', true)->get(['title']);
Task::where('assignee', 'Damien')->first(['title', 'priority']);
// 3. acrescentando a uma seleção existente
Task::select('id')->addSelect('title')->first();Quando queres apenas um valor, dois métodos evitam passar por um modelo completo:
// um único valor escalar
Task::where('assignee', 'Damien')->value('title'); // 'Écrire le guide'
// uma lista de valores
Task::where('active', true)->pluck('title'); // ['Écrire le guide', 'Relire']
// uma lista indexada por outra coluna
Task::pluck('title', 'id'); // [1 => 'Écrire le guide', …]O value() substitui com vantagem o first(['title'])->title que se escreve por reflexo: devolve diretamente a string, e null se nenhuma linha corresponder, enquanto a outra escrita rebenta com um erro sobre um objeto nulo.
Depois de um first(['title']), o atributo priority não existe no modelo. Aceder-lhe devolve null sem erro, o que esconde o problema até à apresentação. Lembra-te também de incluir a chave primária na seleção: sem id, as relações e o save() deixam de conseguir encontrar a linha.
Erros frequentes
isEmpty(), isNotEmpty() ou blank().first(['title']), ler priority devolve null sem erro. Inclui sempre a chave primária, senão as relações e o save() deixam de funcionar.count() no construtor: é um COUNT(*) que não traz linha nenhuma.$posts->isEmpty($posts) vê-se com frequência e funciona por acidente, já que o argumento é ignorado.

