Understand the sole() query constructor method

Original text: Understanding the sole() Query Builder Method

catalogue

preface

Laravel 8.23 introduces a sole() method on the query constructor, which retrieves a single record, but also has additional assertions.

Sole is useful when you need to query a single line and assert that the query matches only one record. This is helpful when you expect only one record. Laravel throws an exception when there are multiple or no records.

We will demonstrate how to use it and what to get from three possible scenarios:

  1. A record that matches the query
  2. There are no records matching the query
  3. Multiple records matching query

Quick start

Let's create a quick demo application so that you can continue the experiment as needed.
First, create a Laravel project:

laravel new sole-demo
cd sole-demo

Next, create a model that we can use to demonstrate how sole() works:

php artisan make:model -m Book

Finally, we will define the book database table for our model:

database/migrations/xxxx_xx_xx_xxxxxx_create_books_table.php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateBooksTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('books', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('summary');
            $table->dateTime('published_at')->nullable();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('books');
    }
}

Don't forget to make sure that the fields we create are writable:

app/Models/Book.php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Book extends Model
{
    use HasFactory;

    /**
     * The attributes that are mass assignable.
     *
     * @var string[]
     */
    public $fillable = [
        'title',
        'summary'
    ];
}

Run migration file:

php artisan migrate:fresh

You should now have a books table, which we will use to demonstrate some examples.

Use the sole() method

To experiment with sole(), we will use the php artisan tinker command to create records and query the database.
First, let's see what happens when we use the first() and get() methods.

use App\Models\Book;

Book::create ( [
    'title' => 'War of the Worlds',
    'summary' => 'Example summary' ,
] );

// All records matching the query
Book::where ( 'title', 'like', '%War%' )->get();
/**
Illuminate\Database\Eloquent\Collection {#4264
    all: [
        App\Models\Book {#4209
            id: 1,
            title: "War of the Worlds",
            summary: "Example summary",
            published_at: null,
            created_at: "2021-09-26 18:14:29",
            updated_at: "2021-01-21 18:14:29",
        },
    ],
}
*/

// Get the first record of the query
// Even if the query has multiple matches, the first one is returned
Book::where ( 'title', 'like', '%War%' )->first();
/**
App\Models\Book {#4210
    id: 1,
    title: "War of the Worlds",
    summary: "Example summary",
    published_at: null,
    created_at: "2021-09-26 18:14:29",
    updated_at: "2021-09-26 18:14:29",
}
*/

get() and first() methods are quite common in Laravel, but sole() is useful when you expect only one record to exist:

/**
App\Models\Book {#3647
    id: 1,
    title: "War of the Worlds",
    summary: "Example summary",
    published_at: null,
    created_at: "2021-09-26 18:14:29",
    updated_at: "2021-09-26 18:14:29",
}
*/

If there is no record in the database table, we can expect to throw ModelNotFoundException:

Book::where('title', 'like', '%The War')->sole();
// Illuminate\Database\Eloquent\ModelNotFoundException

If there are multiple records in the database table, we will also get an exception, but this time it is MultipleRecordsFoundException:

// Create a second title that contains the word 'War'.
Book::create ( [
    'title'  =>  'War and Peace',
    'summary'  =>  'Example summary',
] );

Book::where ( 'title', 'like', '%War%' )->sole();
// Illuminate\Database\MultipleRecordsFoundException

Tags: Laravel

Posted on Sun, 26 Sep 2021 05:49:56 -0400 by Rumour