CakePHP3まとめ（Entityクラス編）

[CakePHP3](http://book.cakephp.org/3.0/en/orm.html)から新しく追加されるEntityクラスの簡単なまとめ  
環境構築については[CakePHP dev-3 + Windows7 + IIS + MySQLの環境構築](http://qiita.com/masashi0127/items/ee1218202fff331c318b)を参照

- 概要
- ファイル名・クラス名
- Getter と Setter
- Eager loading と Lazy loading
- その他

# 概要
- 2.xまでのModelクラスは、3.xではTableクラスとEntityクラスに分離される
- Tableクラスは主に下記のような機能を有する（2.xまでのModelクラスと機能的にはほぼ同じ）
  - データのCRUD
  - アソシエーション
  - バリデーション
- Entityクラスはクエリ結果の各レコードにアクセスするためのクラス

```php
namespace App\Model\Table;
use Cake\ORM\Table;
class ArticlesTable extends Table {

public function initialize(array $config) {
    $this->belongsTo('Users');
    $this->hasMany('Comments');
}

public function index() {
    $query = $articles->find();
    // クエリ結果のデータ型は配列ではなくオブジェクト
    // フェッチで結果を取得
    foreach ($query as $row) {        
        echo $row->title;
        // or
        echo $row->get('title');

        // アソシエーションテーブルから取得
        // UserEntityクラスは$row->userに相当
        echo $row->user->name

        foreach ($row->comments as $comment)
            // CommentEntityクラスは$commentに相当
            echo $comment->comment;
        }
    }
}
```

# ファイル名・クラス名

- ファイル名・クラス名ともに対応するTableクラスの単数形
- 別のクラス名のEntityクラスを指定したい場合は、Tableクラスのinitializeメソッドで指定

# Getter と Setter

```php
namespace App\Model\Entity;

use Cake\ORM\Entity;

class Article extends Entity {
    // フィールド名の先頭に'get'をつける
    public function getTitle($title) {
        return ucwords($title);
    }

    // フィールド名の先頭に'set'をつける
    public function setTitle($title) {
        // set(key, value)で$_properties配列にデータをセット
        $this->set('slug', Inflector::slug($title));
        return $title;
    }

    // 複数フィールドの値を加工して取得したい場合、メソッド名のget,setの後ろは自由
    public function getFullName() {
        // クエリで取得した値、もしくはset()でセットした値を$_propertiesから取得
        return $this->_properties['first_name'] . '  ' .
            $this->_properties['last_name'];
    }
}
```

# Eager loading と Lazy loading

- Eager loading = 一括取得
- Lazy loading = 都度取得
- 両者のメリット・デメリットについては割愛

```php
// Eager loading はアソシエーションによるデータの一括取得
// Lazy loading はEntityのGetterでアソシエーションテーブルのデータを取得

namespace App\Model\Entity;

use Cake\ORM\Entity;
use Cake\ORM\TableRegistry;

class Article extends Entity {

    public function getComments() {
        // Commentsテーブルクラスをインスタンス化
        $comments = TableRegistry::get('Comments');
        // Articleのidと紐づくCommentsデータを取得
        return $comments->find('all')
            ->where(['article_id' => $this->id])
            ->all();
    }

}

$article = $this->Articles->findById($id);
// $article->commentsで上記のgetCommentsを実行
foreach ($article->comments as $comment) {
    echo $comment->body;
}
```

# その他

- 関数の再利用
- バリデーションエラー
- 仮想フィールドの設定
- 隠しフィールの設定

## 関数の再利用
- Traitを使用

```php
// SoftDelete/Model/Entity/SoftDeleteTrait.php

namespace SoftDelete\Model\Entity;

trait SoftDeleteTrait {

    public function softDelete() {
        $this->set('deleted', true);
    }
}

namespace App\Model\Entity;

use Cake\ORM\Entity;
use SoftDelete\Model\Entity\SoftDeleteTrait;

class Article extends Entity {
    use SoftDeleteTrait;
}
```

## バリデーションエラー

```php
// すべて取得
$errors = $user->errors();

// フィールドを指定して取得
$errors = $user->errors('password');

// メッセージをセット
$user->errors('password', ['Password is required.']);
```

## 仮想フィールドの設定
Entityクラスの`$_virtual`プロパティにセット

```php
namespace App\Model\Entity;
use Cake\ORM\Entity;
class User extends Entity {
    protected $_virtual = ['full_name'];
}

...
// テーブルオブジェクトからセットする場合、virtualProperties()を使用
$user->virtualProperties(['full_name', 'is_admin']);
```

## 隠しフィールドの設定
Entityクラスの`$_hidden`プロパティにセット

```php
namespace App\Model\Entity;
use Cake\ORM\Entity;
class User extends Entity {
    protected $_hidden = ['password'];
}

...
// テーブルオブジェクトからセットする場合、hiddenProperties()を使用
$user->hiddenProperties(['password', 'recovery_question']);
```