Hey there! If you're working with Laravel 12 and want to understand how to set up a one-to-one relationship between two database tables, you're in the right place. In this beginner-friendly guide, I'll walk you through the process of creating a one-to-one relationship using Laravel's Eloquent ORM.
We'll use a simple example: a User
who has one Profile
. By the end, you'll know how to define models, migrations, and relationships, and how to retrieve related data.
In a one-to-one relationship, a single record in one table is associated with exactly one record in another table. For example:
User
has one Profile
.Profile
belongs to one User
.This is useful for organizing data, like storing user details (e.g., bio, phone number) in a separate profiles
table.
Before we begin, ensure you have:
- A Laravel 12 project set up.
- A database configured (e.g., MySQL, SQLite).
- Basic knowledge of Laravel models, migrations, and controllers.
If you don't have a project yet, run:
laravel new example-app
We'll create two tables: users
and profiles
. The profiles
table will have a foreign key linking to the users
table.
Laravel includes a default users
migration. If you haven't modified it, it should already exist in database/migrations
. Ensure it looks like this:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('users');
}
};
Run the following command to create a migration for the profiles
table:
php artisan make:migration create_profiles_table
Open the generated migration file in database/migrations
and update it:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('profiles', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id')->unique();
$table->string('phone')->nullable();
$table->text('bio')->nullable();
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
}
public function down(): void
{
Schema::dropIfExists('profiles');
}
};
Explanation:
user_id
is a foreign key linking to the users
table.unique()
constraint ensures each user has only one profile.onDelete('cascade')
deletes the profile if the associated user is deleted.Run the migrations to create the tables in your database:
php artisan migrate
Next, we need to define the User
and Profile
models and set up their relationships.
Open app/Models/User.php
and add the hasOne
relationship:
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Database\Eloquent\Relations\HasOne;
class User extends Authenticatable
{
protected $fillable = ['name', 'email', 'password'];
public function profile(): HasOne
{
return $this->hasOne(Profile::class);
}
}
Explanation: The hasOne
method defines that a User
has one Profile
.
Run this command to create the Profile
model:
php artisan make:model Profile
Open app/Models/Profile.php
and add the belongsTo
relationship:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Profile extends Model
{
protected $fillable = ['user_id', 'phone', 'bio'];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}
Explanation: The belongsTo
method defines that a Profile
belongs to a User
.
Let’s create a controller to test the one-to-one relationship and a route to access it.
Run:
php artisan make:controller UserController
Open app/Http/Controllers/UserController.php
and add the following code:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\User;
use App\Models\Profile;
class UserController extends Controller
{
public function index()
{
// Create a user
$user = User::create([
'name' => 'John Doe',
'email' => '[email protected]',
'password' => bcrypt('password'),
]);
// Create a profile for the user
$user->profile()->create([
'phone' => '123-456-7890',
'bio' => 'Web developer and tech enthusiast.',
]);
// Retrieve the user with their profile
$userWithProfile = User::with('profile')->first();
return response()->json([
'user' => $userWithProfile->name,
'email' => $userWithProfile->email,
'profile' => [
'phone' => $userWithProfile->profile->phone,
'bio' => $userWithProfile->profile->bio,
],
]);
}
}
Explanation:
with('profile')
to eager-load the profile data.Open routes/web.php
and add:
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserController;
Route::get('/test-relationship', [UserController::class, 'index'])->name('test.relationship');
Start your Laravel server:
php artisan serve
Visit http://localhost:8000/test-relationship
in your browser. You should see a JSON response like:
{
"user": "John Doe",
"email": "[email protected]",
"profile": {
"phone": "123-456-7890",
"bio": "Web developer and tech enthusiast."
}
}
This confirms the one-to-one relationship is working!
You can access related data in various ways:
$user = User::find(1);
$profile = $user->profile; // Returns the associated Profile
echo $profile->phone; // Outputs: 123-456-7890
$profile = Profile::find(1);
$user = $profile->user; // Returns the associated User
echo $user->name; // Outputs: John Doe
Create a Blade file resources/views/user.blade.php
:
<!DOCTYPE html>
<html>
<head>
<title>Laravel 12 One-to-One Relationship</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<h3>User Profile</h3>
<p><strong>Name:</strong> {{ $user->name }}</p>
<p><strong>Email:</strong> {{ $user->email }}</p>
<p><strong>Phone:</strong> {{ $user->profile->phone }}</p>
<p><strong>Bio:</strong> {{ $user->profile->bio }}</p>
</div>
</body>
</html>
Update the UserController
to return the view:
public function index()
{
$user = User::with('profile')->first();
return view('user', compact('user'));
}
Visit http://localhost:8000/test-relationship
to see the user’s profile displayed.
Setting up a one-to-one relationship in Laravel 12 is straightforward with Eloquent ORM! In this tutorial, you learned how to create migrations, define models, set up relationships, and retrieve related data.
Whether you're building user profiles or other linked data, this approach keeps your database organized and efficient. I hope this guide was easy to follow and helps you implement one-to-one relationships in your Laravel projects.
Q1: What’s the difference between hasOne
and belongsTo
?
A: hasOne
is used on the model that owns the relationship (e.g., User
has one Profile
). belongsTo
is used on the model that is owned (e.g., Profile
belongs to a User
).
Q2: Why use a separate profiles
table instead of adding columns to users
?
A: A separate table keeps the users
table clean and is useful when profile data is optional or complex. It also improves performance for queries that don’t need profile data.
Q3: What does onDelete('cascade')
do?
A: It ensures that if a user is deleted, their associated profile is also deleted automatically, maintaining data integrity.
Q4: How do I handle cases where a user has no profile?
A: Check if the profile exists: $user->profile ? $user->profile->phone : 'No profile'
. You can also use optional($user->profile)->phone
to avoid errors.
Q5: Can I create the profile automatically when a user is created?
A: Yes! Use a model event or observer. For example, in User.php
:
protected static function booted()
{
static::created(function ($user) {
$user->profile()->create(['phone' => 'N/A', 'bio' => 'No bio yet']);
});
}
You might also like: