CakePHP 3 で仮想フィールドを使う方法と注意点

はじめに

CakePHP 3 のエンティティには仮想フィールドの機能があります。この機能を使うことで、コントローラやテンプレート内のコードを簡潔にし、保守性を高めることができます。

公式ドキュメントにも実装方法が掲載されているのですが、文量が少ないためか、見逃す場合もあるようです。また toArray() した際に、その配列に仮想フィールドを含める方法がちょっと離れたところに書かれていて、こちらも見逃しやすいかもしれません。

今日は仮想フィールドの実装方法と、toArray() 時にそれを含む方法、そして実装上の注意点をご紹介します。

今回使用した CakePHP のバージョンは 3.8.5 です。

目次
  1. 実装方法と使い方
  2. toArray() の結果に含める
  3. 注意点

1. 実装方法と使い方

例として、users テーブルに tel_1、tel_2、tel_3 のフィールドがあるものとします。
(varchar型、未入力時はNULL)

Entity/User.php に下記のように追加します。

/src/Model/Entity/User.php
class User extends Entity
{
    // ~~~省略~~~

    /**
     * TEL番号をハイフンで結合したものを取得
     *
     * @return string TEL番号
     */
    protected function _getTel()
    {
        if ($this->_properties['tel_1'] === NULL) {
            return '';
        }

        return implode('-', [
            $this->_properties['tel_1'],
            $this->_properties['tel_2'],
            $this->_properties['tel_3']
        ]);
    }
}

あとは、コントローラやテンプレートで通常のフィールドと同様に呼び出すことができます。
(下記コードは実装例です)

/src/Controller/UsersController.php
public function detail($userId)
{
  $user = $this->Users->get($userId);
  $this->set(compact('user'));
}
/src/Template/Users/detail.ctp
<dt>Tel</dt>
<dd><?= $user->tel ?></dd>

2. toArray() の結果に含める

デフォルトでは、toArray() の結果に仮想フィールドは含まれません。試しに下記のように debug() するとわかります。

/src/Controller/UsersController.php
public function detail($userId)
{
  $user = $this->Users->get($userId)
    ->toArray();
  debug($user);
  exit;
}

toArray() の結果に含めるためには、エンティティに下記を追記します。

/src/Model/Entity/User.php
class User extends Entity
{
    // ~~~省略~~~

    /**
     * toArray()の結果に含める仮想フィールド
     */
    protected $_virtual = [
        'tel'
    ];

    // ~~~省略~~~
}

これで再度上記の debug() のコードを実行してみてください。
「tel」のフィールドが追加されているはずです。

3. 注意点

仮想フィールドを使用する際の注意ですが、これは使用する度に関数が実行されます。今回の例だと $user->tel を呼び出す度に _getTel() が実行されます。仮想フィールドの取得処理が重い場合で、それを複数回呼び出す場合には、一度別の変数に入れるとよさそうです。

また書籍「リーダブルコード」によると get●●●() というメソッドには、軽い処理を書くべきとあります。そもそも重くなる処理は仮想フィールドにせず、Table や Component などに書いた方がいいのかもしれません。