LaravelのEloquent ORMは複合主キーに対応していなかった

スポンサーリンク

注意:Laravel4の話です

2013-09-19追記
こちらの記事ですが、以下の記事の内容を行うと、insert等ができなくなるようになってしまいました・・・。
どうやら最後のIDが取得出来ないからこうなってしまうようです。

互換性などの点からもIDは付けるようにしましょう。複合ユニークキーとインデックスをつければそれなりにちゃんと速くなるはず

こんにちは。ファガイです。

先日から個人的にサイトを制作していたのですが、衝撃の事実がわかりまして・・・

Eloquent ORMは複合主キーに対応していない!

うん、複合主キーに対応していない。

皆見たらわかると思うんだ、Illuminate\Database\Eloquent\Builderのfindメソッドを見てみよう。

public function find($id, $columns = array('*'))
{
    $this->query->where($this->model->getKeyName(), '=', $id);

    return $this->first($columns);
}

あとは、Illuminate\Database\Eloquent\Modelとかも。

public static function find($id, $columns = array('*'))
{
    $instance = new static;

    if (is_array($id))
    {
        return $instance->newQuery()->whereIn($instance->getKeyName(), $id)->get($columns);
    }

    return $instance->newQuery()->find($id, $columns);
}

そもそも、primaryKeyは1つであると確定しているのも問題じゃないのかと思ってる。
リレーションを組もうとしたが、この仕様だと、primaryKeyは1つでなければいけない。複合主キーで組もうと思ったらリレーションが組めない。
結構大問題だった。
大問題でも無かった。Query Builderで取ってきてくっつければ普通に取得できた。
Eloquent Modelのfindメソッドの動き的にid複数を取得して来れる的な仕様と見れる。だがQuery Builderには実装されていない。まだ粗はありそう。

対処方法

上記で出したメソッドにidがarrayで来たとかprimaryKeyがarrayだったとか判定が必要。
あと、コアとなっているEloquent\Relations\BelongsTo等のaddConstrationsをいじる必要がある。
これを修正しないと、リレーションを組むことが出来ませんでした。

自分の場合はこのような形に変更しています。例えば、BelongsToの場合。

public function addConstraints()
{
    if (static::$constraints)
    {
        $key = $this->related->getKeyName();

        $table = $this->related->getTable();

        $count = count($key);
        if($count > 1) {
            for($i = 0; $i < $count; ++$i) {
                $this->query->where($table.'.'.$key[$i], '=', $this->parent->{$this->foreignKey[$i]});
            }
        }else{
            $this->query->where($table.'.'.$key, '=', $this->parent->{$this->foreignKey});
        }
    }
}

コアとなっている部分をいじってしまっているので、composer updateをやってしまうともう一度直さなきゃいけなさそうですね。
Eloquent Modelの方は一つBaseEloquentとかモデルを用意して、それを継承させる形にすればよいでしょう。

ではではー。

2013-09-08追記
複合主キーは拡張性が無くなったり、変更に強くないため主キーが1つなのかもしれませんね・・。しかしながらスキーマビルダーは複合主キーが定義出来る・・・なんだろうこの矛盾

コメント

  1. HiroKws より:

    こんにちは。
    理由は簡単です。Laravelのデータベース操作はEloquent ORMだけではないからです。クエリービルダーで操作することもできるからです。
    基本的に、スキーマーは、その他の通り、テーブルのスキーマーを(完全ではないが)簡単に定義するだけのものです。Eloquentとの整合性を考えているのではなく、サポートしているDBエンジン間の違いを埋め、できるだけ共通に定義できるようにするためのものです。(とはいっても、MySQL専用とか、ありますが。)

    • fagai より:

      こんにちは。コメントありがとうございます。
      Query Builderを使うことで改変せずに情報を取得し、データに含めることが出来ました。
      また、複合キーをリレーションで組む事自体にも色々と問題があるようですね。取得方法に差異が出てきますが、今後は複合で取る際にはクエリービルダーを使用することにします。

      • HiroKws より:

        本当に私も最初は悩みました。けど、それほどシリアスなシステムを作成しているわけでないので、初めからid主キー一本だけと、割りきって使い出し、それから便利に使っています。(というより、SQLはもう直接書きたくなくなりました。)
        まあ、Eloquentを使い始める時は、誰でも通る道でしょうね。

  2. 匿名 より:

    通りすがりです。
    自分は複合keyを使う時はYii、それ以外はLaravelで使い分けます。
    PHP縛りの場合のみですが。。

タイトルとURLをコピーしました