¿Cuántas veces has usado first() asumiendo que solo existe un registro? Si por algún error hay duplicados, first() te devolverá uno cualquiera sin avisarte. Aquí es donde sole() brilla.
Compatibilidad
Laravel: 8.23+
PHP: 7.4+
¿Qué hace sole()?
El método sole() devuelve exactamente un registro. Si no cumple esta condición, lanza una excepción:
Resultados | Comportamiento |
|---|---|
0 |
|
1 | Devuelve el modelo ✓ |
2+ |
|
Uso básico
<?php
use App\Models\User;
// Obtener el único usuario con este email
$user = User::where('email', '[email protected]')->sole();Compáralo con first():
<?php
// Con first(): si hay duplicados, devuelve uno cualquiera (bug silencioso)
$user = User::where('email', $email)->first();
// Con sole(): si hay duplicados, lanza excepción (detectas el problema)
$user = User::where('email', $email)->sole();Variantes útiles
soleValue()
Cuando solo necesitas un campo específico:
<?php
// Obtener solo el nombre, garantizando un único resultado
$name = User::where('email', $email)->soleValue('name');En colecciones
sole() también funciona en colecciones de Laravel:
<?php
$numbers = collect([1, 2, 3, 4, 5]);
// Funciona: solo hay un número mayor que 4
$result = $numbers->where(fn ($n) => $n > 4)->sole(); // 5
// Lanza excepción: hay múltiples números mayores que 2
$result = $numbers->where(fn ($n) => $n > 2)->sole(); // MultipleRecordsFoundExceptionCasos de uso ideales
Buscar por campos únicos
<?php
// Configuraciones del sistema donde solo debe existir una
$config = Setting::where('key', 'app_timezone')->sole();
// Registro de auditoría específico
$log = AuditLog::where('transaction_id', $transactionId)->sole();Validar integridad en comandos
<?php
namespace App\Console\Commands;
use App\Models\User;
use Illuminate\Console\Command;
class VerifyAdminCommand extends Command
{
protected $signature = 'verify:admin';
public function handle(): int
{
try {
$admin = User::where('role', 'super_admin')->sole();
$this->info("Admin verificado: {$admin->email}");
return self::SUCCESS;
} catch (\Illuminate\Database\MultipleRecordsFoundException $e) {
$this->error('Error: Existen múltiples super admins');
return self::FAILURE;
}
}
}En tests para asegurar datos correctos
<?php
namespace Tests\Feature;
use App\Models\Order;
use Tests\TestCase;
class OrderTest extends TestCase
{
public function test_solo_existe_una_orden_pendiente_por_usuario(): void
{
$user = User::factory()->create();
Order::factory()->pending()->for($user)->create();
// Si el sistema permite duplicados por error, este test fallará
$order = Order::where('user_id', $user->id)
->where('status', 'pending')
->sole();
$this->assertNotNull($order);
}
}Manejo de excepciones
Puedes capturar las excepciones específicas:
<?php
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Database\MultipleRecordsFoundException;
try {
$user = User::where('email', $email)->sole();
} catch (ModelNotFoundException $e) {
// No existe ningún registro
return response()->json(['error' => 'Usuario no encontrado'], 404);
} catch (MultipleRecordsFoundException $e) {
// Existen múltiples registros (problema de integridad)
Log::critical('Duplicado detectado', ['email' => $email]);
return response()->json(['error' => 'Error interno'], 500);
}sole() vs first() vs firstOrFail()
Método | 0 resultados | 1 resultado | 2+ resultados |
|---|---|---|---|
|
| Modelo | Primer modelo |
| Excepción | Modelo | Primer modelo |
| Excepción | Modelo | Excepción |
Usa sole() cuando la unicidad sea un requisito de negocio.
Conclusión
sole() es una herramienta defensiva. No previene duplicados, pero te avisa inmediatamente cuando existen. Úsalo en consultas donde asumes unicidad y quieres fallar rápido si esa suposición es incorrecta.
Curso Laravel 12
Completo 2026
El único curso 100% actualizado que incluye Laravel 12, Livewire 3, Vue 3, React 19 e Inertia 2. Aprende con proyectos reales y las últimas funcionalidades.
star Incluido en cualquier suscripción