Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

alshamsan's avatar

Error setting up One-to-Many relationship with custom foreign key

Issue

I am trying to set up a One-to-Many relationship between Country and User models, using a custom foreign key, instead of the conventional country_id column (users->country references countries->code).

Consider the following tables:

countries table:

| id | name         | code | language |
|----|--------------|------|----------|
| 1  | Canada       | ca   | English  |
| 2  | India        | in   | Hindi    |
| 3  | France       | fr   | French   |

users Table:

| id | name         | email           | country |
|----|--------------|-----------------|---------|
| 1  | Craig Harris | [email protected] | ca      |
| 2  | Tracy Hunt   | [email protected] | fr      |
| 3  | Lalita Gopal | [email protected] | in      |

How can I tell laravel to reference users->country to countries->code and be able to load the Country model of of the User? Something similar to the below:

How i want to use it is like this:

{{ $user->country->name }}

This is my attempt:

Migrations

Users Table Migration:

Schema::create('users', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('email')->unique();
    $table->string('password');
    $table->string('country');
    $table->foreign('country')->references('code')->on('countries')->onUpdate('cascade');
    $table->timestamps();
});

Countries Table Migration:

Schema::create('countries', function (Blueprint $table) {
    $table->id();
    $table->string('code')->unique();
    $table->string('name');
    $table->string('language');
    $table->timestamps();
});

Models

User Model User.php

public function country()
{
    return $this->belongsTo(Country::class, 'country', 'code');
}

Country Model Country.php

public function users()
{
    return $this->hasMany(User::class, 'country', 'code');
}

Note: I used a custom foreign_key and local_key as I understood from the docs here.

View & Rote

Route file web.php

Route::get('/', function () {
    $users = User::with('country')->paginate(10);
    return view('users.index', ['users' => $users]);
});

my view file users/index.blade.php

<ul>
    @foreach ($users as $user)
    <li>{{ $user->country->name }}</li>
    @endforeach
</ul>

Error

I get the following error:

Trying to get property 'name' of non-object (View: ... /views/users/index.blade.php)

What am I doing wrong? :/

0 likes
6 replies
tykus's avatar
tykus
Best Answer
Level 104

You have a property country on the User model that is the same as the relationship country; so whenever you use $user->country, you get the property, e.g . ca, not the related County Model. This is why the convention has a _id suffix for foreign IDs.

To fix the problem, you need to change one of the column (property) name or relationship name. For example, you could make the database column name (and resulting property on the User Model) country_code, and them use country as the relationship name. Or, you could change the relationship name to countryModel

1 like
alshamsan's avatar

hi @tykus

Thanks for the answer.

How is this a problem since I'm explicitly defining the foreign key name?

Edit

I updated the column name to country_code as suggested, and it actually worked.

For anyone wondering, this is what I changed.

Schema::create('users', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('email')->unique();
    $table->timestamp('email_verified_at')->nullable();
    $table->string('password');
    $table->string('country_code'); // changed country to country_code
    $table->foreign('country_code')->references('code')->on('countries')->onUpdate('cascade'); //same
    $table->rememberToken();
    $table->timestamps();
});

and in the User.php model:

public function country()
{
    return $this->belongsTo(Country::class, 'country_code', 'code');
}

I still can't wrap it around my head, why should the original code not work? What is the problem with having the property name same as the relationship name? (since we have explicitly told Laravel about the custom foreign key name )

tykus's avatar

It is a problem because

whenever you use $user->country, you get the property, e.g . ca, not the related County Model.

It has nothing whatsoever to do with the foreign key name; it has to do with the relationship having the same name as a property (column) of the User model

1 like
tykus's avatar

The problem with a property being the same as a relationship comes whenever you try to use a relationship as a property not as a method. Eloquent's getAttribute method will check for a property (coming from the column name) first, then casts, mutators, and finally relationships. Whenever you call $user->country you get the property, and never reach the relation.

Please consider marking the Best Reply above @alshamsan

1 like
alshamsan's avatar

Got it.

So properties have precedence over relationships.

Thanks man @tykus

tykus's avatar

If you source-dive the getAttribute Model method, you will see how it works.

Please or to participate in this conversation.