PHP
laravel

LaravelのbelongsToでBelongsToオブジェクトが戻ってきた話

概要

EloquentのリレーションbelongsToがある以下のModelがあるとする(適当なのは許して

ModelA.php
namespace App\Models;

class ModelA extends Model {

    public function model_b()
    {
        return $this->hasMany('App\Models\ModelB');
    }

}
ModelB.php
namespace App\Models;

class ModelB extends Model {

    public function model_a()
    {
        return $this->belongsTo('App\Models\ModelA');
    }

}

これで ModelB::first()とかで取得したModelBオブジェクト$model_bに対して、以下のように書くと

$model_b->model_a()->id;

ここに書いてある「1対多(Inverse)」とは違う挙動になる。

(プライマリキーとしてidがあるものとする)

ちなみにtinkerで実行したらこんな文言で怒られた

PHP Notice: Undefined property: Illuminate/Database/Eloquent/Relations/BelongsTo::$id in Psy Shell code on line 1

何が起きてるのか?

リファレンスとかチュートリアルとして書いてあるのはこう

$model_b->model_a->id;

上で書いたのはこう

$model_b->model_a()->id;

おわかりいただけただろうか……

ポイントはmodel_aをメソッド呼び出ししているかプロパティ参照しているかの違い。多分わかる人は上に書いたPHPメッセージでわかるかもしれない。  
ちなみにチュートリアル通りにやるなら、プロパティ参照のほうが正解。

$model_bに対してmodel_a()とメソッド呼び出しをすると、上のソースコード通りなら$this->belongsTo('App\Models\ModelA');が実行される。
Laravel公式APIリファレンスによると、このメソッドではBelongsToオブジェクトを戻す。

もちろんModelオブジェクトじゃないのでModelAの情報をプロパティで持っていない。なのでその後のidプロパティ参照で怒られてしまったのだ……。

余談

ちなみに合っているかの保証が出来ないけれど、以下のような書き方ならmodel_a()とメソッド呼び出しでbelongsTo側のModelAオブジェクトを引っ張ってこれる

$model_b->model_a()->first();

……でもおとなしく$model_b->model_aで参照しときましょ

あとがき

Laravel使ったプロジェクトで1対多が絡むところの表示部分を書いてたときにうっかりなtypoで遭遇してしまった事象。
relation貼るときはメソッド定義だけど、実際に使うときはプロパティ参照と、ちょっとソースコードを見るだけではこの部分だけハマりそうってことでメモってみました