Introducing the Laravel One-to-One Relationship

  • March 05, 2015

Welcome to the incredibly popular Easy Laravel 5 companion blog. To celebrate the new edition's release (updated for Laravel 5.5!) use the discount code easteregg to receive 20% off the book or book/video package! » Buy the book

One-to-one relationships link one row in a database table to one (and only one) row in another table. In my opinion there are generally few uses for a one-to-one relationship because the very nature of the relationship indicates the data could be consolidated within a single record. However, for the sake of demonstration let's suppose your application offered user authentication and profile management, and you wanted to separate the user's authentication (e-mail address, password) and profile (name, phone number, gender) data into two separate tables. This relationship is depicted in the below diagram.

An example one-to-one relationship

To manage this relationship in Laravel you'll associate the User model (created automatically with every new Laravel 5 project; I'll formally introduce this model in Chapter 7) with the model responsible for managing the profiles, which we'll call Profile. To create the model you can use the Artisan generator:

$ php artisan make:model Profile

You'll find the newly generated model inside app/Profile.php:

<?php namespace todoparrot;

use Illuminate\Database\Eloquent\Model;

class Profile extends Model {

    //

}

Next, open up the newly created migration and modify the up method to look like this:

public function up()
{   
  Schema::create('profiles', function(Blueprint $table)
  {
    $table->integer('user_id')->unsigned()->nullable();
    $table->foreign('user_id')->references('id')->on('users');
    $table->string('name');
    $table->string('telephone');
    $table->timestamps();    
  });
}

The bolded lines are the only two you'll need to add. The first line results in the addition of an integer-based column named user_id. The second line identifies this column as being a foreign key which references the users table's id column.

T> You must specify an integer column as unsigned when it's T> intended to be used as a foreign key, otherwise the migration T> will fail.

After saving the changes run the following command to create the table:

$ php artisan migrate
Migrated: 2015_01_20_201647_create_profiles_table

With the tables in place it's time to formally define the relations within the Laravel application.

Defining the One-to-One Relation

You'll define a one-to-one relation by creating a public method typically having the same name as the related model. The method will return the value of the hasOne method, as demonstrated below:

class User extends Model {

  public function profile()
  {
    return $this->hasOne('todoparrot\Profile');
  }

}

Once defined, you can retrieve a user's profile information by calling the user's profile method. Because the relations can be chained, you could for instance retrieve a user's telephone number like this:

$user = User::find(212)->profile->telephone;

To retrieve the telephone number, Laravel will look for a foreign key in the profiles table named user_id, matching the ID stored in that column with the user's ID.

The above example demonstrates how to traverse a relation, but how is a relation created in the first place? I'll show you how to do this next.

Creating a One-to-One Relation

You can easily create a One-to-One relation by creating the child object and then saving it through the parent object, as demonstrated in the below example:

$profile = new Profile;
$profile->telephone = '614-867-5309';

$user = User::find(212);
$user->profile()->save($profile);

Deleting a One-to-One Relation

Because a profile should not exist without a corresponding user, you'll just delete the associated profile record in the case you want to end the relationship:

$user = User::find(212);
$user->profile()->delete();

However, if a user record were to be deleted from the database you wouldn't want its corresponding profile record to be orphaned. One way to avoid this is by deleting the related profile record after deleting the user record (via Eloquent's delete method), but chances are this two step process will eventually be neglected, leaving orphaned records strewn about the database. Instead, you'll probably want to automate this process by taking advantage of the underlying database's ability to delete child tables when the parent table is deleted. You can specify this requirement when defining the foreign key in your table migration. I've modified the relevant lines of the earlier migration used to create the profiles table, attaching the onDelete option to the foreign key:

$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')
      ->on('users')->onDelete('cascade');

With the cascading delete option in place, deleting a user from the database will automatically result in the deletion of the user's corresponding profile.