Laravel: Eloquent - One to One Relationship

Carlos Costa

One-to-One relationship is a very basic relation. It can be described as a relationship between two tables. Each record in the first table can have only one record in the second table. And each record in the second table can have only one record in the first table.

First, let’s create two models, Car and Drive. A car can have one drive and a drive can have one car.

In this note we will create the models, the migrations, the factories and tests to demonstrate the one to one relation.

Ok, let’s start by creating the models and the migrations.

php artisan make:model Car -mf --pest
php artisan make:model Driver -mf --pest

Migrations


Now, let’s create a migration for each model.

// Car
Schema::create('cars', function (Blueprint $table) {
    $table->id();
    $table->timestamps();
    $table->string('name');
    $table->string('model');
    $table->string('year');
    $table->string('color');

    //foreign key
    $table->unsignedBigInteger('driver_id')->unique();
    $table->foreign('driver_id')->references('id')->on('drivers');
});

// Driver
Schema::create('drivers', function (Blueprint $table) {
    $table->id();
    $table->timestamps();
    $table->string('name');
    $table->string('email');
    $table->string('phone');
});

Models


In One To One relationship the rule to models is:

hasOnebelongsTo
idforeign key

Let’s define the models relationship.

// Car
class Car extends Model
{
    use HasFactory;

    protected $fillable = [
        'name',
        'model',
        'year',
        'color',
    ];

    public function driver()
    {
        return $this->belongsTo(Driver::class);
    }
}

// Driver
class Driver extends Model
{
    use HasFactory;

    protected $fillable = [
        'name',
        'email',
        'phone',
    ];

    public function car()
    {
        return $this->hasOne(Car::class);
    }
}

Factory


Now, let’s define a factory for each model.

// Car
class CarFactory extends Factory
{
    protected $model = Car::class;

    public function definition()
    {
        return [
            'name' => $this->faker->name,
            'model' => $this->faker->name,
            'year' => $this->faker->year,
            'color' => $this->faker->colorName,
            'driver_id' => Driver::factory(),
        ];
    }
}

// Driver
class DriverFactory extends Factory
{
    protected $model = Driver::class;

    public function definition()
    {
        return [
            'name' => $this->faker->name,
            'email' => $this->faker->unique()->safeEmail,
            'phone' => $this->faker->phoneNumber,
        ];
    }
}

Tests


Now, let’s create a test to demonstrate the one to one relationship.

Car model test:

use App\Models\Car;
use App\Models\Driver;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

test('Car: crud', function () {
    //create
    $car = Car::factory()->create();

    $this->assertTrue($car->exists);
    $this->assertNotNull($car->id);

    //update
    $car->name = 'Tesla';
    $car->model = 'Model S';
    $car->year = 2022;
    $car->color = 'Red';
    $car->save();

    $this->assertEquals('Tesla', $car->name);
    $this->assertEquals('Model S', $car->model);
    $this->assertEquals(2022, $car->year);
    $this->assertEquals('Red', $car->color);

    //delete car
    $car->delete();
    $cars = Car::all();
    $this->assertEquals(0, $cars->count());
});

test('Car: relationship with driver', function () {
    $car = Car::factory()->create();

    $this->assertInstanceOf(Driver::class, $car->driver);
});

Driver model test:

use App\Models\Driver;
use Illuminate\Foundation\Testing\RefreshDatabase;
use App\Models\Car;

uses(RefreshDatabase::class);

test('Driver: crud', function () {
    //create
    $driver = Driver::factory()->create();

    $this->assertTrue($driver->exists);
    $this->assertNotNull($driver->id);

    //update
    $driver->name = 'John';
    $driver->email = 'john@example.com';
    $driver->phone = '1234567890';
    $driver->save();

    $this->assertEquals('John', $driver->name);
    $this->assertEquals('john@example.com', $driver->email);
    $this->assertEquals('1234567890', $driver->phone);

    //get all drivers
    $driver->delete();
    $drivers = Driver::all();

    $this->assertEquals(0, $drivers->count());
});

test('Driver: relationship with car', function () {
    $driver = Driver::factory()->create();

    $driver->car()->create(
        [
            'name' => 'Tesla',
            'model' => 'Model S',
            'year' => 2022,
            'color' => 'Red',
        ]
    );

    $this->assertInstanceOf(Car::class, $driver->car);
});

If we try to create two car for the same driver, we will get the following exception:

test('Driver: relationship', function () {
    $driver = Driver::factory()->create();

    $driver->car()->create(
        [
            'name' => 'Tesla',
            'model' => 'Model S',
            'year' => 2022,
            'color' => 'Red',
        ]
    );

    $driver->car()->create(
        [
            'name' => 'BMW',
            'model' => '750i',
            'year' => 2012,
            'color' => 'Blue',
        ]
    );

    $this->assertInstanceOf(Car::class, $driver->car);
});

SQLSTATE[23000]: Integrity constraint violation: 19 UNIQUE constraint failed: cars.driver_id

References