Muhammad Yasir Amin

Loading websiteโ€ฆ

More from Muhammad Yasir Amin

Build Along With Me

Practical walkthroughs on Laravel, React, APIs and deployment workflows.

Subscribe on YouTube

Laravel 13 One to One Relationship: hasOne and belongsTo Example

๐Ÿ› ๏ธ Laravel Tutorials4 min read

Laravel 13 One to One Relationship: hasOne and belongsTo Example

Muhammad Yasir Amin

Muhammad Yasir Amin

Senior Full Stack Engineerโ€ขNov 9, 2026
Laravel 13 One to One Relationship: hasOne and belongsTo Example

A one-to-one relationship in Laravel 13 links one model to exactly one other model, such as a User with one Profile. Define hasOne() on the model that owns the other, belongsTo() on the model that holds the foreign key, and add a unique foreign key column.

Below you will build the tables, define both sides, create and update related data, and handle users who do not have a profile yet.

What are the prerequisites?

  • Laravel 13 (current stable at the time of writing) with a supported PHP version.
  • A database connection.
  • The default User model.

How do I create the profile table?

Code
php artisan make:model Profile -m
PHP
Schema::create('profiles', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->unique()->constrained()->cascadeOnDelete();
    $table->string('phone')->nullable();
    $table->string('city')->nullable();
    $table->text('bio')->nullable();
    $table->timestamps();
});
Code
php artisan migrate

The unique() call matters. Without it, nothing stops the database from storing two profiles for one user, and the relationship would quietly become one-to-many.

How do I define hasOne and belongsTo?

JavaScript
// app/Models/User.php
use Illuminate\Database\Eloquent\Relations\HasOne;

public function profile(): HasOne
{
    return $this->hasOne(Profile::class);
}
JavaScript
// app/Models/Profile.php
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Profile extends Model
{
    protected $fillable = ['phone', 'city', 'bio'];

    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }
}

The foreign key defaults to user_id. For another name use hasOne(Profile::class, 'owner_id').

How do I save a related record?

JavaScript
$user = User::find(1);

// create
$user->profile()->create([
    'phone' => '+92 300 0000000',
    'city'  => 'Lahore',
]);

// update or create in one call
$user->profile()->updateOrCreate(
    [],
    ['city' => 'Karachi', 'bio' => 'Backend developer']
);

An empty first argument in updateOrCreate matches the existing row for this user, because the relationship already scopes the query by user_id.

How do I read the related record?

PHP
$user = User::find(1);
echo $user->profile->city;

$profile = Profile::find(1);
echo $profile->user->name;

// eager load for lists
$users = User::with('profile')->get();

What if the user has no profile?

$user->profile returns null and $user->profile->city then throws an error. Two clean fixes exist.

JavaScript
// 1. null-safe operator
echo $user->profile?->city ?? 'Not set';

// 2. a default empty model
public function profile(): HasOne
{
    return $this->hasOne(Profile::class)->withDefault([
        'city' => 'Not set',
    ]);
}

withDefault() returns an unsaved Profile instance so Blade templates do not need null checks.

How do I query users by their profile?

JavaScript
// users with a profile
$users = User::has('profile')->get();

// users without one
$users = User::doesntHave('profile')->get();

// users from a city
$users = User::whereHas('profile', fn ($q) => $q->where('city', 'Lahore'))->get();

How do I build a controller and form?

JavaScript
public function update(Request $request)
{
    $data = $request->validate([
        'phone' => ['nullable', 'string', 'max:30'],
        'city'  => ['nullable', 'string', 'max:80'],
        'bio'   => ['nullable', 'string', 'max:1000'],
    ]);

    $request->user()->profile()->updateOrCreate([], $data);

    return back()->with('status', 'Profile saved.');
}

Using $request->user() means each person can edit only their own profile. Never accept a user_id from the form.

What are the common mistakes?

  • No unique index. Duplicates become possible.
  • Calling the property on null. Use ?-> or withDefault().
  • Foreign key on the wrong table. The key lives on the table of the model that uses belongsTo.
  • Trusting a user id from the request. Always resolve it from the authenticated user.

How do I check that it works?

JavaScript
public function test_user_has_one_profile(): void
{
    $user = User::factory()->create();
    $user->profile()->create(['city' => 'Lahore']);

    $this->assertSame('Lahore', $user->fresh()->profile->city);
    $this->assertSame($user->id, $user->profile->user->id);
}

FAQ

When should I use one-to-one instead of extra columns?

Use it when the data is optional, large, or rarely read, so the main table stays small. Otherwise plain columns are simpler.

How is hasOne different from belongsTo?

The model with the foreign key column uses belongsTo. The other side uses hasOne.

Can I delete the profile with the user?

Yes. cascadeOnDelete() on the foreign key removes the profile when the user row is deleted.

References

Article Index & Topic Tags
LaravelDatabaseEloquentRelationships

YouTube Channel

Build Along With Me

Practical walkthroughs on Laravel, React, APIs and deployment workflows.

Muhammad Yasir Amin
Available for Contracts
Author & Solutions Architect

Senior Full Stack Engineer

Independent Senior Full Stack Engineer working on Laravel, React and Next.js applications, APIs and delivery workflows.

Laravel & PHP ApplicationsReact & Next.js InterfacesREST APIs & IntegrationsDatabase Queries & DebuggingDocker Development WorkflowsClear Scope & Handover
Discuss Your Next Step

What Does Your Application Need Next?

Share your current system, the problem you want to solve and what a useful result looks like. We can identify the right starting point and agree on a practical scope.

Technical Audit

Find the Next Useful Fix

Application Development

Scoped, Reviewable Milestones

Ongoing Support

Agreed Capacity and Priorities

Share your goals, current stack and requirements before the call.