【CakePHP入門】Modelについて詳しく解説!

こんにちは。ライターの亀田です。

PHPには様々なフレームワークがあります。

中でもCakePHPは歴史も長く多くのプログラマーに愛されています。

最近はFuel,Laravelなどの新興勢力が台頭してきましたが、Cakeを愛好する人は少なくありません。

そのため、ニーズに応えて2.0系列から3.0系列へとバージョンアップを遂げてきました。

ここでは、特にModelに着目し、2.0と比較しつつ、3.0の特徴について説明していくことにします。

まず、

・Modelとは

で、Modelとは何かについて説明し、以下で、詳しくその詳細の機能について説明していきます。

特に、CakePHP2と3の違いについて説明します。

その後、

・findでEntityを検索し表示する方法
・saveでデータを保存する方法
・バリデーション(validation)でデータをチェックする
・joinでテーブルを結合する
・deleteでテーブルレコードを削除
・updateallでテーブルを一括更新する
・アソシエーションでモデル同士を関連付ける
・containで関連するテーブルデータを検索する

で、さらにModelの詳細について説明をしていきます。

目次

Modelとは

CakePHPのModelについて説明する前に、そもそもModelとは何かについて説明します。

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

これらの違いを分かりやすく説明すると、

Viewレイアウト部分、つまりユーザーに見える部分であり、Modelデータ部分、そして、Controllerはこの2つを結びつけるものになります。

特にModelは、データベースと密接に関係があり、ほぼデータベースへアクセスする部分と言えます。

cake2.0および3.0でも、基本的にこの仕組みは変わりません。

しかし、この2つで大きな違いは、cake2.0がこの部分にModelクラスを用いているのに対し、3.0では、TableEntityという2つのクラスに分かれた点です。

ここでは、具体的な例を挙げながら、その仕組みについて説明してくことにします。

findでEntityを検索し表示する方法

では、具体的にTableEntityは、それぞれどのような役割をするかを説明しましょう。

まず、Tableクラスですが、これはその名の通り、データベースのテーブルへのアクセスを行います。

2.0のModelクラスに近いといえるでしょう。

Entitiyの使い方

3.0から導入された、Entityでは、データベースの1レコードの操作ができるようになりました。

これにより、2.0の頃よりも柔軟にデータベースの操作が実現できるようになっています。

例として2.0のものと比較しながらその意味を説明しましょう。2.0系列で、例えば

$user = $this->Users->find('first');
echo $user['User']['name'];

という処理をしたとします。2.0系列では、DBのデータを配列で取得しているからです。

つまり、findメソッドでは配列が得られるわけです。

この処理では、ユーザーテーブルのデータが丸ごと取得されてしまいます。

プログラム的にも、DBへの操作という観点からみても、大変効率が悪いコードになります。

それに対し、3.0系列では同等の操作を

$user = $this->Users->find('first');
echo $user->name;

という形で記述できます。見てわかる通り、データベースの処理から配列が排除され、

かわりに、$userというオブジェクトがfindメソッドで取得されています。これが、Entityです。

なお、Tableは複数形の単語、Entityは同じ単語の単数形を用いてください。

例えば、usersというテーブルがっあったとしたら、TableクラスはUsersTable、EntityはUserになります。

これにより、データベースへの操作がよりスマートになったわけです。

その理由は色々ありますが、一番便利になった点は、メソッドチェーンが使えるようになった点です。

メソッドチェーンとは、メソッドをつなげて処理することをいいます。例えば、

$q->mehtod1();
$q->mehtod2();
$q->mehtod3();

という処理に、メソッドチェーンが使えると、

$q->mehtod1()->mehtod2()->mehtod3();

と記述できます。

CakePHPでも、Modelに限らずこのメソッドチェーンが利用できるため、処理を効率的に記述することが可能です。

なお、findでEntityを検索する仕組みについては、以下で詳しく説明しています。参考にしてみてください。

バリデーション(validation)でデータをチェックする

次は、validationについて説明しましょう。

validationとは、入力されたデータを評価する仕組みで、画面入力されたフィールドデータを、データベースに書き込む際などに、その書式が正しいかチェックします。

たとえば、メールアドレスの欄に記述する部分が、正しい書式になっているか、などです。

そして、間違っていればエラーを発してデータの書き込みができないようにします。

validationの基本的な使い方

cakephp3でのvalidatorの記述は、Tableクラス内で行います。

例えば、

$validator
    ->requirePresence('title')
    ->notEmpty('title', 'Please fill this field')
    ->add('title', [
        'length' => [
            'rule' => ['minLength', 10],
            'message' => 'Titles need to be at least 10 characters long',
        ]
    ]);

といったように記述します。

この場合は、titleのフィールドの評価で、空白の禁止やエラーメッセージ、長さに関する規制などを設定しています。

ちなみに、2.0にもValidatorはありましたが、定義は配列で行っていたため大変見ずらいものでした。

3.0系列ではその問題点が解決されています。

なお、validateについては、以下で詳しく説明しています。参考にしてみてください。

joinでテーブルを結合する

続いて、SQLのクエリを直接実行することで行える様々な操作の方法を紹介していきます。

手始めに、joinで複数のテーブルを結合する仕組みについて説明します。

これは、SQLの内部結合及び外部結合に相当する処理です。

joinの基本的な使い方

そもそも、テーブルクラスで、belongsToを使うと、テーブルの結合はできます。

しかし、コントローラ内で直接DB操作をしたいときなどは、以下の方法で実現できます。

        
$query =  $this->AcceptOrder->find()
    ->join([
    'table' => 'customer',
    'alias' => 'c',
    'type' => 'LEFT',
    'conditions' => 'c.id = AcceptOrder.customer_id',
  ])->select([
    'id' => 'AcceptOrder.id',
    'customer_name' => 'c.customer_name',
    'product_id' => 'AcceptOrder.product_id',
    'quantity' => 'AcceptOrder.quantity',
  ]);

find以降のメソッドは、そのままsqlのキーワードに一致します。

この処理はメソッドチェーンになっており、joinで結合をした後、selectで検索をかけています

なお、この操作の前提となるテーブル設計、操作、joinおよび、テーブルの結合について詳しく知りたい方は、以下を参考にしてください。

deleteでテーブルレコードを削除

次は、レコードを削除するdeleteについて説明します。

レコードを削除する方法

CakePHP3.0系列で、delete処理を行うには、次のような書式になります。

$query = $this->Articles->get(2);
$result = $this->Articles->delete($query);

これは、Articlesテーブルから、2番目のレコードを取得し、それを削除します。

非常に簡単に実現できることが分かります。

なお、deleteに関しては、以下で詳しく説明しています。参考にしてみてください。

updateallでテーブルを一括更新する

更にテーブルを一括更新する方法についてみてみましょう。

SQLには、特定のテーブルを更新するupdateという命令が存在します。

しかし、しばしば複数のデータを一斉更新しなくてはならないケースもあります。

そういう時に、CakePHP3のモデルには、updateallメソッドが用意されています。

テーブルを一括更新する方法

実装例は以下の通りです。

$this->Articles->updateAll(
  [ 'Author' => "Yamada"],
  [ 'age' => 30 ]
);

これで、ArticlesテーブルのAuthorカラムとageカラムが同時に更新されます。この例で行くと、すべての記事の筆者AuthorがYamadaさん30歳という情報に書き換わるわけですね。

テーブルの一括変換については、以下で詳しく説明しています。

興味のある方は、参考にしてみてください。

アソシエーションでモデル同士を関連付ける

アプリケーションで、異なるテーブル同士の関連を定義することはよくあることです。

例えば、ブログのようなシステムを作ったとき、記事テーブルは複数のコメントにより構成されています。

そして、一つの記事は、著者テーブルに属しています。

こういったテーブル同士を関連付けるのが、アソシエーションと言われるものです。

モデル同士を関連付ける方法

では、実際にアソシエーションはどのように記述する例を見てみることにしましょう。

アソシエーションを行うメソッドは、belongsTobelongsToManyhasOnehasManyといったものが存在します。

以下、それらを順次紹介していきましょう。

belongsTo

まず、belongsToメソッドは、あるデータが他のデータに属することを意味します。

例えば、一つの記事(Article)が、一人の著者(Author)に属するとします。その時は以下のように記述することが出来ます。

class ArticlesTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsTo('Authors');
    }
}

belongsToMany

次に、belongsToManyですが、指定したデータが複数のデータに属することを示します。

例えば、以下のようなケースは、記事(Article)は、複数のタグ(Tag)を持ち、同時にタグも複数の記事に関連付けられるということを意味します。

class ArticlesTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsToMany('Tags');
    }
}
class TagsTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsToMany('Articles');
    }
}

hasOne

次にhasOneメソッドですが、これは
あるデータが指定したデータを1つ持っている
ことを意味します。

以下のようなケースは、1ユーザーが(User)が、1つの住所(Address)を持っているようなケースの記述例です。

class UsersTable extends Table
{
    public function initialize(array $config)
    {
        $this->hasOne('Addresses');
    }
}

hasMany

最後のに、1つのデータが複数のデータを持つ場合は、hasManyメソッドを用います。
以下の場合一つの記事(Aritcle)は複数のコメント(Comment)を持つことを表します。

class ArticlesTable extends Table
{
    public function initialize(array $config)
    {
        $this->hasMany('Comments');
    }
}

これらのメソッドは、更にメソッドチェーンでより詳しい条件を追加することも可能です。

なお、アソシエーションについては、以下で詳しく説明してます。興味のある方はご覧ください。

containで関連するテーブルデータを検索する

最後にcontainsを紹介します。

containsは、テーブルデータを検索する時に、関連するテーブルデータも一緒に取得するために用います。

containの基本的な使い方

たとえば、Controllerの中でarticlesテーブルと、それに関連するauthorsテーブルのデータをcontainを使って検索するといった表現は以下のようになります。

$query = $this->Articles->find('all', ['contain' => 'Authros']);

ここにメソッドチェーンで更に複雑な条件をつけての検索も可能です。

なお、containについて更に詳しく知りたい方は、以下を参考にしてみてください。

まとめ

以上が、CakePHP3系統のModelの解説です。

概要から、2.0系列との違い、そして使い方までを以下の流れで説明しました。

・Modelとは
・findでEntityを検索し表示する方法
・saveでデータを保存する方法
・バリデーション(validation)でデータをチェックする
・joinでテーブルを結合する
・deleteでテーブルレコードを削除
・updateallでテーブルを一括更新する
・アソシエーションでモデル同士を関連付ける
・containで関連するテーブルデータを検索する

CakePHP3.0では、2.0の考え方を引き継ぎつつ、TableとEntityを分けることにより、柔軟なデータ構造へのアクセスを可能にしました。

CakePHPは、現在ではPHPのフレームワークの中では老舗といえる存在です。

しかし、時代の変化に対応するために常に進化を続けているのです。

この記事を書いた人

エンジニアとして独自の製品やサービスを開発する傍ら、エンジニア教育にも力を入れています。企業研修や専門学校での非常勤講師もしながら、独自の言語学習サイト「一週間でわかるシリーズ」を運営し、エンジニアになりた人をサポートする活動をライフワークにしています。
【一週間で学べるシリーズ】
http://sevendays-study.com/

目次