CakePHP3のMVC入門 Model, Table/Entity を用いデータベースからデータ取得を行い表示させる
- 公開日
- 更新日
- カテゴリ:CakePHP
- タグ:PHP,Beginner,MVC,CakePHP,Model,Table,Entity
前回の記事 では、MVC の中のコントローラとビューを使い、HelloWorld の表示までを行いました。
今回は、MVC の「M 」=「Model 」モデルを使い、データベースからデータを取得し表示するまでを行います。
Contents
開発環境
今回の開発環境は以下の通りです。
- Linux CentOS 7
- Apache 2.4
- PHP 7.1
- CakePHP 3.5
CakePHP のバージョンは 3 系であれば同一手順で進めていけます。
また、CakePHP のルートディレクトリを「cakephp/」とします。
データベースと、データの入ったテーブル(users)がある前提で進めます。もしまだ用意していない場合は、以下を参考にして users テーブルとデータをさくっと作成してください。
CakePHP3 のモデルについて
CakePHP3 のモデルは、2 種類のオブジェクトを用いてデータベースのデータ操作を行います。
Table(テーブル)
-
データのコレクションへアクセスする。
-
アソシエーションを加えたりメソッドを定義したい場合。
-
新しいレコードを保存
-
既存データの編集・削除
-
リレーションの定義
-
一括処理
Entity(エンティティ)
- 行/レコードレベルの振る舞いや機能の定義を行う。
- アクセッサーとミューテーターメソッドを定義できる。
- 個別や複数レコードにカスタムロジックを定義できる。
CakePHP3 では、命名規則でテーブルクラスとエンティティークラスが関連づけられます。
コントローラ作成
それでは作業を行っていきます。まずはコントローラを生成します。 cakephp のルートディレクトリへ移動し、以下の Users コントローラを生成する bake コマンドを叩きます。
# CakePHP のルートディレクトリへ移動する
cd /path/to/cakephp
# bake コマンドでコントローラを生成する
bin/cake bake controller Users
# 実行結果
[demo@localhost cakephp]# bin/cake bake controller Users
Baking controller class for User...
Creating file /var/www/html/cakephp/src/Controller/UsersController.php
Wrote `/var/www/html/cakephp/src/Controller/UsersController.php`
Bake is detecting possible fixtures...
Baking test case for App\Controller\UsersController ...
Creating file /var/www/html/cakephp/tests/TestCase/Controller/UsersControllerTest.php
Wrote `/var/www/html/cakephp/tests/TestCase/Controller/UsersControllerTest.php`
cakephp/src/Controller 配下に UsersController.php が生成されます。
cakephp
├─ src
│ ├─ Controller
│ │ ├─ AppController.php
│ │ ├─ UsersController.php
- cakephp/src/Controller/UsersController.php
-
<?php namespace App\Controller; use App\Controller\AppController; /** * Users Controller * * * @method \App\Model\Entity\User[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = []) */ class UsersController extends AppController { /** * Index method * * @return \Cake\Http\Response|void */ public function index() { $users = $this->paginate($this->Users); $this->set(compact('users')); } /** * View method * * @param string|null $id User id. * @return \Cake\Http\Response|void * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. */ public function view($id = null) { $user = $this->Users->get($id, [ 'contain' => [] ]); $this->set('user', $user); } /** * Add method * * @return \Cake\Http\Response|null Redirects on successful add, renders view otherwise. */ public function add() { $user = $this->Users->newEntity(); if ($this->request->is('post')) { $user = $this->Users->patchEntity($user, $this->request->getData()); if ($this->Users->save($user)) { $this->Flash->success(__('The user has been saved.')); return $this->redirect(['action' => 'index']); } $this->Flash->error(__('The user could not be saved. Please, try again.')); } $this->set(compact('user')); } /** * Edit method * * @param string|null $id User id. * @return \Cake\Http\Response|null Redirects on successful edit, renders view otherwise. * @throws \Cake\Network\Exception\NotFoundException When record not found. */ public function edit($id = null) { $user = $this->Users->get($id, [ 'contain' => [] ]); if ($this->request->is(['patch', 'post', 'put'])) { $user = $this->Users->patchEntity($user, $this->request->getData()); if ($this->Users->save($user)) { $this->Flash->success(__('The user has been saved.')); return $this->redirect(['action' => 'index']); } $this->Flash->error(__('The user could not be saved. Please, try again.')); } $this->set(compact('user')); } /** * Delete method * * @param string|null $id User id. * @return \Cake\Http\Response|null Redirects to index. * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. */ public function delete($id = null) { $this->request->allowMethod(['post', 'delete']); $user = $this->Users->get($id); if ($this->Users->delete($user)) { $this->Flash->success(__('The user has been deleted.')); } else { $this->Flash->error(__('The user could not be deleted. Please, try again.')); } return $this->redirect(['action' => 'index']); } }
今回は index アクションで進めるので他は放っておいてよいですが、一点、クラス最上部に initialize() メソッドを追加してください。 initialize() メソッドは、クラスのコンストラクタです。そこで、今回は使わないのでひとまずテンプレートの使用を無効にしておきます。
class UsersController extends AppController
{
public function initialize()
{
// レイアウトを無効にする
$this->viewBuilder()->layout(false);
}
/**
* Index method
*
* @return \Cake\Http\Response|void
*/
public function index()
{
$users = $this->paginate($this->Users);
$this->set(compact('users'));
}
ビューテンプレート作成
次に、ビューを作成します。 CakePHP ではテンプレートファイルです。
cakephp/src/Template 配下に Users ディレクトリを作成し、index.ctp を作成します。
cakephp
├─ src
│ ├─ Template
│ │ ├─ Users
│ │ └─ index.ctp
index.ctp を以下のようにコーディングします。
- cakephp/src/Template/Users/index.ctp
-
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>Sample Users index</title> </head> <body> <p>Users index</p> </body> </html>
この時点で一旦、画面表示まで流れが通ります。
http://YOUR-DOMAIN/users
モデル作成
モデル、つまり Table クラスと Entity クラスのファイルを作成します。この2つのファイルは、bake コマンドを叩けば一発で生成できます。
cakephp のルートディレクトリに移動し、以下の bake コマンドを叩きます。
# CakePHP のルートディレクトリへ移動する
cd /path/to/cakephp
# bake コマンドでモデルを生成する
bin/cake bake model Users
# 実行結果
[demo@localhost cakephp]# bin/cake bake model Users
One moment while associations are detected.
Baking table class for Users...
Creating file /var/www/html/cakephp/src/Model/Table/UsersTable.php
Wrote `/var/www/html/cakephp/src/Model/Table/UsersTable.php`
Deleted `/var/www/html/cakephp/src/Model/Table/empty`
Baking entity class for User...
Creating file /var/www/html/cakephp/src/Model/Entity/User.php
Wrote `/var/www/html/cakephp/src/Model/Entity/User.php`
Deleted `/var/www/html/cakephp/src/Model/Entity/empty`
Baking test fixture for Users...
Creating file /var/www/html/cakephp/tests/Fixture/UsersFixture.php
Wrote `/var/www/html/cakephp/tests/Fixture/UsersFixture.php`
Deleted `/var/www/html/cakephp/tests/Fixture/empty`
Bake is detecting possible fixtures...
Baking test case for App\Model\Table\UsersTable ...
Creating file /var/www/html/cakephp/tests/TestCase/Model/Table/UsersTableTest.php
Wrote `/var/www/html/cakephp/tests/TestCase/Model/Table/UsersTableTest.php`
cakephp/src/Model 配下のそれぞれのディレクトリにファイルが生成されます。
cakephp
├─ src
│ ├─ Model
│ │ ├─ Entity
│ │ │ └─ User.php
│ │ └─ Table
│ │ └─ UsersTable.php
- cakephp/src/Model/Table/UsersTable.php
-
<?php namespace App\Model\Table; use Cake\ORM\Query; use Cake\ORM\RulesChecker; use Cake\ORM\Table; use Cake\Validation\Validator; /** * Users Model * * @method \App\Model\Entity\User get($primaryKey, $options = []) * @method \App\Model\Entity\User newEntity($data = null, array $options = []) * @method \App\Model\Entity\User[] newEntities(array $data, array $options = []) * @method \App\Model\Entity\User|bool save(\Cake\Datasource\EntityInterface $entity, $options = []) * @method \App\Model\Entity\User patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = []) * @method \App\Model\Entity\User[] patchEntities($entities, array $data, array $options = []) * @method \App\Model\Entity\User findOrCreate($search, callable $callback = null, $options = []) * * @mixin \Cake\ORM\Behavior\TimestampBehavior */ class UsersTable extends Table { /** * Initialize method * * @param array $config The configuration for the Table. * @return void */ public function initialize(array $config) { parent::initialize($config); $this->setTable('users'); $this->setDisplayField('name'); $this->setPrimaryKey('id'); $this->addBehavior('Timestamp'); } /** * Default validation rules. * * @param \Cake\Validation\Validator $validator Validator instance. * @return \Cake\Validation\Validator */ public function validationDefault(Validator $validator) { $validator ->integer('id') ->allowEmpty('id', 'create'); $validator ->scalar('name') ->maxLength('name', 255) ->requirePresence('name', 'create') ->notEmpty('name'); $validator ->email('email') ->requirePresence('email', 'create') ->notEmpty('email') ->add('email', 'unique', ['rule' => 'validateUnique', 'provider' => 'table']); $validator ->scalar('password') ->maxLength('password', 255) ->requirePresence('password', 'create') ->notEmpty('password'); $validator ->integer('role') ->requirePresence('role', 'create') ->notEmpty('role'); $validator ->dateTime('last_login_at') ->requirePresence('last_login_at', 'create') ->notEmpty('last_login_at'); return $validator; } /** * Returns a rules checker object that will be used for validating * application integrity. * * @param \Cake\ORM\RulesChecker $rules The rules object to be modified. * @return \Cake\ORM\RulesChecker */ public function buildRules(RulesChecker $rules) { $rules->add($rules->isUnique(['email'])); return $rules; } }
- cakephp/src/Model/Entity/User.php
-
<?php namespace App\Model\Entity; use Cake\ORM\Entity; /** * User Entity * * @property int $id * @property string $name * @property string $email * @property string $password * @property int $role * @property \Cake\I18n\FrozenTime $last_login_at * @property \Cake\I18n\FrozenTime $created * @property \Cake\I18n\FrozenTime $modified */ class User extends Entity { /** * Fields that can be mass assigned using newEntity() or patchEntity(). * * Note that when '*' is set to true, this allows all unspecified fields to * be mass assigned. For security purposes, it is advised to set '*' to false * (or remove it), and explicitly make individual fields accessible as needed. * * @var array */ protected $_accessible = [ 'name' => true, 'email' => true, 'password' => true, 'role' => true, 'last_login_at' => true, 'created' => true, 'modified' => true ]; /** * Fields that are excluded from JSON versions of the entity. * * @var array */ protected $_hidden = [ 'password' ]; }
両ファイルとも生成された時点で色々と記述されていますが、内容は至ってシンプルです。ですが、現時点ではまだこの辺はいじらなくて OK なので、見なかったことにしてそっとファイルを閉じておきます。
取得データを表示する
実はモデルの作成まで終えると、既にデータ取得が行えている状態になっています。
コントローラの index アクションを見てみてください。実は既にデータ取得の処理が記述されており、この時点でデータの取得が行われており、ビューへのセットまで済んでいます。
- cakephp/src/Controller/UsersController.php
-
public function index() { // データ取得 $users = $this->paginate($this->Users); // データセット $this->set(compact('users')); }
それでは受け取ったデータをビューに描画します。ビューテンプレートを以下のようにコーディングします。
- cakephp/src/Template/Users/index.ctp
-
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>Sample Users index</title> </head> <body> <p>Users index</p> <ul> <?php foreach ($users as $user): ?> <li> <ul> <li><?= $user->name ?></li> <li><?= $user->email ?></li> </ul> </li> <?php endforeach; ?> </ul> </body> </html>
受け取った変数 $users をループさせ、1件ずつ名前とメールアドレス表示している。という流れになります。
動作確認
それではブラウザからアクセスし、表示確認を行います。
http://YOUR-DOMAIN/users
問題なくコントローラでデータベースから取得したデータをビューへ渡し表示する事が出来ました。
まとめ
以上で作業は完了です。ひとまず MVC の流れをさっとさらいつつ、モデルを使ってデータベースからデータを取得して表示させてみる。という感じにふさわしく、さらっと最後までいけたと思います。
CakePHP は、それが定める命名規則に沿って bake コマンドでクラス・ファイルを作成していくと、各々が規則によって呼応し合い、動作する PHP フレームワークです。若干押しつけがましい部分もありますが、慣れればそれも一興、ですね。
これで MVC が一通り体感できたと思います。これからより深い CakePHP3 の世界へ、行ってらっしゃい。