わすれっぽいきみえ

みらいのじぶんにやさしくしてやる

Eloquent ORMで特定カラムの値を処理して取り出すために$appendsを使う

簡単に間違いコードを書くと

<?php

use Illuminate\Database\Eloquent\Model as Eloquent;

class Example extends Eloquent
{
    protected $table = 'examples';

    public function getPath() {
        return parse_url($this->url);
    }
}

ここで url というのは examples テーブルの1カラムとする。

要は url を単にURLではなくて、後ろのパスだけを取り出すメソッドが書きたいと思ったときにどうしたらいいの?というのを調べてた。
正直、上みたいに書いただけで簡単に動いてほしかったんだが、実際には以下のような例外が飛んだ。

[BadMethodCallException]
  Call to undefined method Illuminate\Database\Query\Builder::getPath()

まさか Query\Builder からメソッドが呼び出されるとは思ってなくて驚いたんだが、Eloquentはすべてのattributeをあるテーブルのカラムと紐付けて取り出そうとしてしまうからのようで、上のサンプルコードのようなことをやりたかったら実際のテーブルには存在しない仮想的なカラムみたいなのを用意して実装してやる必要があると分かった。

なので上のコードを修正すると

<?php

use Illuminate\Database\Eloquent\Model as Eloquent;

class Example extends Eloquent
{
    protected $table = 'examples';
    protected $appends = ['path'];

    public function getPathAttribute() {
        return parse_url($this->url);
    }
}

これで例えば $exampleExample クラスのインスタンス$example->url = 'http://example.com/hoge/fuga' とすると、 $example->path'/hoge/fuga' が取れるようになる。

一応公式ドキュメントにもこうやったらできることは書かれてるんだが、見つけるのが難しいところにある。

laravel.com

After creating the accessor, add the attribute name to the appends property on the model.

しかもサンプルコードはあるけど、これしか書いてない。あるカラムのaccessorを作ると、そのカラムを取り出すときに必ずaccessorに指定した処理が実行されて取り出される。これが上の例でいう getPathAttribute なんだが、 path というカラムがテーブルには存在しない場合 protected $appends = ['path']; と書いて、仮想的なカラムを用意してねとある。これ、できればMutatorsの方に説明を書いてほしい…。

stackoverflow.com

ググってたらこの質問に行き着いて、2013年に立てられたものなので使えるのか不安だったが、今の公式ドキュメントと照らし合わせるとなるほどって感じだった。

ちなみにこの仕組を使って私が書いたコードは ここ においてる。