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
Usermodel.
How do I create the profile table?
php artisan make:model Profile -mSchema::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();
});php artisan migrateThe 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?
// app/Models/User.php
use Illuminate\Database\Eloquent\Relations\HasOne;
public function profile(): HasOne
{
return $this->hasOne(Profile::class);
}// 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?
$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?
$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.
// 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?
// 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?
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
?->orwithDefault(). - 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?
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.



