CakePHP3 Entityについて


はじめに

これまた備忘録ではあるのですが、キッカケとしては、今個人的な成果物のような物を作成してまして、その際Modelを触ったのですが(普段も触っていますが)、新しくTableとEntityを作成する事になり、その際、そもそも論のような事が発生したのがキッカケです。
なんとなく触っていた、という印象なので、改めて作成する、となった時に手が止まりました。特にEntityとは?感が強かったので、調べた内容をアウトプットして、まとめたいと思います。

Modelとは

まず最初に大枠であるModelについて簡単に触れます。

Cakeに限らずWebフレームワークは、M(Model)、V(View)、C(Controller)の三つの要素から構成されています。

上記の図のような構成になります。

Modelはデータ部分にあたり、DBとの密接な関係があり、DBへアクセスする部分と捉えてもいいのではないかと思います。

(調べて初めて知りましたが)Cake3.x系からTableとEntityという二つのクラスが存在するようになりました。

CakePHPのクックブックには、TableとEntityについてこの様に書かれています。

[参考]
https://book.cakephp.org/3.0/ja/orm.html

CakePHP では、2種類の主要なオブジェクトを使ってデータベースのデータを操作します。 1種類目は リポジトリー や テーブルオブジェクト です。これらのオブジェクトを利用して、 データのコレクションへアクセスします。これらを利用することで、新しいレコードを保存したり、 既存データの編集/削除、リレーションの定義、そして一括処理ができます。 2種類目は エンティティー です。エンティティーは、個々のレコードを意味し、 行/レコードレベルの振る舞いや機能の定義を可能にします。

この様にTableとEntityには違いがあります。

DBへアクセスする部分

と書きましたが、この二つの説明を見る限りでは、Tableオブジェクトがそれに当たりそうで、DBとのインターフェースとも言えます。

Tableオブジェクトに関しては、結構イメージしやすいというか、馴染み深いので、比較的自分の中での理解、がすぐできました。

対してEntityは、いまいちその立ち位置、役割がわかりません。結局Entityはどういうもの何ができるのか?を書いていきます。

Entityとは

まずEntityとはなんなのか?です。
上記の説明にある様に、

エンティティーは、個々のレコードを意味し、 行/レコードレベルの振る舞いや機能の定義を可能にします。

広くDBというより、個々のレコードだという事がわかります。
DBの中でも更にフォーカスしたものだという事ですね。
なので1つ目の疑問だった、どういうものでという問いに対しては、レコードに当たるという事がわかりました。

Entityは何できるのか

次にEntityは何ができるのか?
役割としては

行/レコードレベルの振る舞いや機能の定義を可能にします

という事で、レコードに対して色々出来るよ、という事ですね。
CakePHPにある標準機能を使用すると、カラムに対してもできる様です。

実際にコードを書いてみました。

User.php
namespace App\Model\Entity;

use Cake\ORM\Entity;
use Cake\Auth\DefaultPasswordHasher;

class User extends Entity
{
    // パスワードをハッシュ化
    protected function _setPasswordHash($password)
    {
        return (new DefaultPasswordHasher)->hash($password);
    }
}

DefaultPasswordHasherというCakePHP3の標準機能を用いたパスワードをハッシュ化するコードです。

ちゃんとハッシュ化されているのか試してみます

UsersController
$user = $this->Users->newEntity();
if ($this->request->is('post')) {
    $user = $this->Users->patchEntity($this->request->data);
    $this->Users->save($user);
}

DB確認↓

mysql
[vagrant@local hoge]$ mysql -u root
~
mysql> use sample
~
mysql> select password from users;

+--------------------------------------------------------------+
| password                                                     |
+--------------------------------------------------------------+
| $2y$10$xvUM0mgJlU93WErsfdnCFuwWPDTRwb0ZIrsglt0e1NJMxImvw0OLS |
+--------------------------------------------------------------+
1 row in set (0.00 sec)

ちゃんとハッシュ化されてました。

ハッシュ化されるタイミングは、DBに格納される直前でなので、例えば、バリデーションに引っかからないのか?、という問題は気にしなくて大丈夫です。

この方法ではなくてもハッシュ化する事ができます。

User.php
namespace App\Model\Entity;

use Cake\ORM\Entity;
use Cake\Auth\DefaultPasswordHasher;

class User extends Entity
{
    // パスワードをハッシュ化
    protected function _getPasswordHash($password)
    {
        return (new DefaultPasswordHasher)->hash($password);
    }
}
UsersController
$user = $this->Users->newEntity();
if ($this->request->is('post')) {
    $user = $this->Users->patchEntity($this->request->data);
    $user->password_hash;
}

$user->password_hashをデバックして見るとハッシュ化されている事が確認できると思います。

二つの方法でパスワードをハッシュ化してみましたが、前者はミューテーターで、後者はアクセサーというメソッドを使用した方法です。

前者のミューテーターは、_set+メソッド名とする事で定義できます。
これは上記でも記述しましたが、どの様に保存されるかを定義する事ができます。

後者のアクセサーは、_get+メソッド名とする事で定義できます。
これは、どの様に取得するかを定義する事ができます。

※メソッド名の命名はCakeのルールになります。
[参考]
https://book.cakephp.org/3.0/ja/orm/entities.html#id5

他にもできる事があったので、一部紹介します。

has()を使用してプロパティが存在するかの確認。

use App\Model\Entity\User;

// Entityのインスタンス生成
$user = new User([
    'name' => 'john',
    'age' => 25,
    'hobby' => null
]);

$user->has('name'); // 実行結果→true
$user->has('hobby'); // 実行結果→false

has()はプロパティに値が存在すれば、true無ければfalseを返します。
(UserEntityのインスタンス生成時に連想配列の形で、インスタンス生成に必要なプロパティを渡す事ができます。)

配列やJSONへの変換

UserEntityを配列に変換してみます。

use App\Model\Entity\User;

$user = new User([
    'name' => 'john',
    'age' => 25,
    'hobby' => 'soccer',
]);

$user = $user->toArray();

// 出力結果
[
    "name" => "john",
    "age" => "25",
    "hobby" => "soccer"
]

次は、JSONに変換してみます。

use App\Model\Entity\User;

$user = new User([
    'name' => 'john',
    'age' => 25,
    'hobby' => 'soccer',
]);

$user = json_encode($user);

// 出力結果
{"name":"john","age":"25","hobby":"soccer"}

[参考]
https://book.cakephp.org/3.0/ja/orm/entities.html#json

プロパティを隠す

上記の変換時に変換されたくないプロパティが存在する場合などに、Entityクラスに定義する事によって、防ぐ事ができます。

namespace App\Model\Entity;

use Cake\ORM\Entity;

class User extends Entity
{
    // hobbyプロパティを隠す
    protected $_hidden = ['hobby'];
}
use App\Model\Entity\User;

$user = new User([
    'name' => 'john',
    'age' => 25,
    'hobby' => 'soccer'
]);

$user = json_encode($user);

//実行結果
{"name":"john","age":"25"}

また、hiddenProperties()を実行時に使用する事で、対象のプロパティーを変更する事ができます。

[参考]
https://book.cakephp.org/3.0/ja/orm/entities.html#id15

実用性の有無は置いといて、こんな事がEntityでは出来るんだよ程度に一部紹介という形で書きました。
Entityが出来る事は他にもあります。
CakePHPのクックブックを参考に、色々試してみて下さい。
[参考]
https://book.cakephp.org/3.0/ja/orm/entities.html

まとめ

実際にコードを書いて試してみましたが、色々できるんだなという印象です。
特にアクセサーとミューテーターは実際にインターン先でもよく使用しているので、理解を深める事ができてよかったと思います。
どういうもので何が出来るのかの疑問も解消されました。
後者の何が出来るのかについてはまだまだ調べている段階ではあるものの、今回調べた事で新たな発見があったので、良しとしつつ、他に出来る事がないか調べてみようと思います。