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

chadhortonwowza's avatar

Eloquent Relations - getting parent data from child not working

I am testing one-to-many relationships and have come across a situation where I cannot figure out how to get data for a parent object from a child object.

Table 1 (parent): test1

pkid (primary key, auto increment)
Id (varchar)

Model for test1

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class SalesforceTest1 extends Model
{
    public $timestamps = false;
    protected $connection = 'esb-salesforce';
    protected $table = 'test1';
    protected $primaryKey = 'pkid';
    protected $guarded = [];

    public function test2()
    {
        return $this->hasMany('App\Models\SalesforceTest2', 'test1Id', 'Id');
    }
}

Table 2 (child): test2

pkid (primary key, auto increment)
test1Id (varchar, foreign key to test1.Id)
name (varchar)

Model for test2

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class SalesforceTest2 extends Model
{
    public $timestamps = false;
    protected $connection = 'esb-salesforce';
    protected $table = 'test2';
    protected $primaryKey = 'pkid';
    protected $guarded = [];

    public function test1()
    {
        return $this->belongsTo('App\Models\SalesforceTest1', 'test1Id', 'Id');
    }
}

I have sample data in both tables.

test1

pkid: 1
Id: ab12345600k

test2

pkid: 1
test1Id: ab12345600k
name: John Doe

Executing the following works great and I get results as expected and can access test2:

>>> SalesforceTest1::where('Id','=','ab12345600k')->first()->test2;
=> Illuminate\Database\Eloquent\Collection {#680
     all: [
       App\Models\SalesforceTest2 {#705
         pkid: 1,
         test1Id: "ab12345600k",
         name: "John Doe",
         created_at: null,
         updated_at: null,
       },
     ],
   }

But executing the following and trying to access test1, I get a null:

>>> SalesforceTest2::where('Test1Id','=','ab12345600k')->first()->test1;
=> null

What's interesting is even if I comment out the test1 method from SalesforceTest2, I still get a null instead of the collection I'd expect that contains:

pkid: 1
Id: ab12345600k
0 likes
15 replies
geowrgetudor's avatar

In SalesforceTest2, the 1'st parameter after the class should be the foreign key.

public function test1()
    {
        return $this->belongsTo('App\Models\SalesforceTest1', 'id', 'test1Id');
    }
chadhortonwowza's avatar

Hi geowrge,

I changed this as well, but the way I have it is actually correct for the belongsTo.

Test:

return $this->belongsTo('App\Models\SalesforceTest1', 'Id', 'test1Id');

and I get:

>>> SalesforceTest2::where('Test1Id','=','ab12345600k')->first()->test1;
Illuminate\Database\QueryException with message 'SQLSTATE[42S22]: Column not found: 1054 Unknown column 'test1.test1Id' in 'where clause' (SQL: select * from `test1` where `test1`.`test1Id` is null limit 1)'
geowrgetudor's avatar

Can you post the db schema for those 2 tables?

Do you get results just for this?

dd(SalesforceTest2::where('Test1Id','=','ab12345600k')->first());
chadhortonwowza's avatar
CREATE TABLE `test1` (
  `pkid` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `Id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`pkid`),
  UNIQUE KEY `test1_id_unique` (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `test2` (
  `pkid` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `test1Id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`pkid`),
  KEY `test2_test1id_foreign` (`test1Id`),
  CONSTRAINT `test2_test1id_foreign` FOREIGN KEY (`test1Id`) REFERENCES `test1` (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
chadhortonwowza's avatar

I do get results for

dd(SalesforceTest2::where('Test1Id','=','ab12345600k')->first());
>>> dd(SalesforceTest2::where('Test1Id','=','ab12345600k')->first());
App\Models\SalesforceTest2 {#705
  +timestamps: false
  #connection: "esb-salesforce"
  #table: "test2"
  #primaryKey: "pkid"
  #guarded: []
  #keyType: "int"
  #perPage: 15
  +incrementing: true
  #attributes: array:5 [
    "pkid" => 1
    "test1Id" => "ab12345600k"
    "name" => "John Doe"
    "created_at" => null
    "updated_at" => null
  ]
  #original: array:5 [
    "pkid" => 1
    "test1Id" => "ab12345600k"
    "name" => "John Doe"
    "created_at" => null
    "updated_at" => null
  ]
  #relations: []
  #hidden: []
  #visible: []
  #appends: []
  #fillable: []
  #dates: []
  #dateFormat: null
  #casts: []
  #touches: []
  #observables: []
  #with: []
  +exists: true
  +wasRecentlyCreated: false
}
geowrgetudor's avatar

I think what you want won't be possible.

It works with hasMany(), but it might not work backwards using belongsTo() because the Id field in the first table is not a primary key.

So probably, as a work around, you will need to use hasOne() in the child table.

chadhortonwowza's avatar

So laravel is actually testing indexes in the tables? I thought the point to being able to pass the keys in the 2nd and 3rd parameters allows one to specify the field relationships, which means laravel doesn't/shouldn't care what the primary key is.

Also, many normalized RDBMS allow any table to have any number of foreign key relations to other tables that are associated without those fields being primary key (they just have to be indexed).

chadhortonwowza's avatar

What you are saying is I should do away with belongsTo() and replace it with hasOne()? But also keep the hasMany() in my Test1 model?

geowrgetudor's avatar
Level 4

Table 1 > hasMany() > Table 2 Table 2 > hasOne() -> Table 1 (instead of belongsTo())

chadhortonwowza's avatar

@geowrge I believe a bug has been uncovered in Laravel with regard to belongsTo(). I should be allowed to use any column that isn't a primary key.

But you are correct, as soon as I switched from belongsTo to hasOne, I was able to access data. And then as soon as I switched to using the default primary key of Id, I was able to change the test2() method back to belongsTo() and it also worked.

1 like
JarekTkaczyk's avatar

@chadhortonwowza hasOne/Many comes with counterpart of belongsTo. hasOne is wrong here. There's no bug, and you can specify whatever columns you want on this types of relation.

For example:

// User [users: id, uuid]
public function posts()
{
    return $this->hasMany(Post::class, 'user_uuid', 'uuid');
}

// Post [posts: id, user_uuid]
public function belongsTo()
{
    return $this->belongsTo(User::class, 'user_uuid', 'uuid');
}

Your setup with hasOne and swapped keys (here would be hasOne(User::class, 'uuid', 'user_uuid') ) is ok in just one case for $post->user call, however it will break when saving, eager loading and so on and so forth.

I guess you experienced some problem because of misleading names etc (or if it is just for the forum purposes, then show your real models).

chadhortonwowza's avatar

@JarekTkaczyk That's what I had initially and it wasn't working. The hasOne() was tested after recommendation by @geowrge but that didn't work so well.

I resorted to no longer using my customer primary keys and using the laravel default of id (which is fine in this case) and things are working now (utilizing the hasMany and belongsTo).

So, I still believe there's a possible bug in Laravel's hasMany / belongsTo when a non-standard Laravel primary key name is used.

joaomario's avatar

I had this same issue, but now I got it right:

#Model for test1 ... public function test2() { return $this->hasMany('App\Models\SalesforceTest2', 'test1Id', 'Id'); }

  • This works, function name could either be tests2, or whatever

#Model for test2 public function test1() { return $this->belongsTo('App\Models\SalesforceTest1', 'test1Id', 'Id'); } *This doesn't work. Function name should be exactly parents name, or underscored:

public function sales_force_test1() { return $this->belongsTo('App\Models\SalesforceTest1', 'test1Id', 'Id'); } *This worked for me

Best regards!

Please or to participate in this conversation.