LoginSignup
5
1

More than 5 years have passed since last update.

【Laravel5.7】belongsToのリレーション先の型がintになって死んだ

Last updated at Posted at 2019-02-22

idがstring型で定義されたテーブルA、およびA.idにリレーションを張ってるテーブルBがあるとします。

テーブル

テーブルA

CREATE TABLE table_a (
 `id` varchar(16) NOT NULL COMMENT '主キー',
 `name` varchar(16) NOT NULL COMMENT '名前',
 PRIMARY KEY (`id`)
)

テーブルB

CREATE TABLE table_b (
 `id` int NOT NULL COMMENT '主キー',
 `a_id` varchar(16) NOT NULL COMMENT 'table_a.id',
 PRIMARY KEY (`id`),
 CONSTRAINT `a` FOREIGN KEY (`a_id`) REFERENCES `table_a` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
)

モデル

モデルA

class A extends Model
{
    protected $table = 'table_a';

    // IDは文字列型
    protected $casts = [
        'id' => 'string',
    ];
}

モデルB

class B extends Model
{
    protected $table = 'table_b';

    // a_idは文字列型
    protected $casts = [
        'a_id' => 'string',
    ];

    // Aへのリレーション
    public function table_a()
    {
        return $this->belongsTo('Path/To/Model/A', 'a_id', 'id');
    }
}

コントローラ

public function hogeAction(Request $request){
    // BとAを取得
    $BwithA = B::where('id', 1)->with(['table_a'])->get();
}

発行されたSQL

DB::getQueryLogで確認できます。

array:2 [
  0 => array:3 [
    "query" => "select * from `b` where `id` = ?"
    "bindings" => array:1 [
      0 => 1
    ]
  ]
  1 => array:3 [
    "query" => "select * from `a` where `a`.`id` in (999)"
    "bindings" => []
  ]
]

おい"どこ行った。

現象

b.a_idがどのような形であろうが、数値として扱われてしまいます。
a_idに"hoge"とか文字列が入っていたとしても気にせず数値にするのでwhere a.id IN (0)になってしまいます。
結果として正常にリレーションを持って来れません。

調査

Illuminate\Database\Eloquent\Relations\BelongsTo

BelongsToクラス内のaddEagerConstraintsでWHERE IN句を生成しているようだ。

BelongsTo.php
public function addEagerConstraints(array $models){
    $whereIn = $this->whereInMethod($this->related, $this->ownerKey);
    $this->query->{$whereIn}($key, $this->getEagerModelKeys($models));
}

この$whereInに、whereIntegerInRawとかいういかにもintを返すっぽい値が入っていた。

Illuminate\Database\Eloquent\Relations\Relation

Relationクラス内のwhereInMethodメソッドが、whereIntegerInRawもしくはwhereInという値を返している。

Relation.php
    protected function whereInMethod(Model $model, $key)
    {
        return $model->getKeyName() === last(explode('.', $key))
                    && $model->getIncrementing()
                    && in_array($model->getKeyType(), ['int', 'integer'])
                        ? 'whereIntegerInRaw'
                        : 'whereIn';
    }

こういう書き方やめてくれよ。

さて$model->getKeyType()が悪さをしているようだ。
コントローラでちょっと確認してみる。

public function hogeAction(Request $request){
    $a = new A();
    var_dump($a->getKeyType());
}

$aintになった。
なんで?

解決

主キーの型指定は$castsではなく$keyType設定が必要だったとかいう落ち。

主キーが整数でない場合は、モデルのprotectedの$keyTypeプロパティへstring値を設定してください。

感想

最初から主キーの型という方向で調べればすぐわかったと思うのだが、withbelongsToから調べ始めてしまったために無駄に時間がかかった。

ところでこれ、今回は外部キーの向き先がテーブルAの主キーだったから$keyTypeで解決できたけど、主キーではなく他のカラムだった場合はどうなるんだろう?

5
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1