CakePHP3のMVC入門[Model]モデル[Table/Entity]を用いデータベースからデータ取得を行い表示させる
- 公開:
- カテゴリ: PHP CakePHP
- タグ: PHP,Beginner,MVC,CakePHP,3.5,Model,Table,Entity
前回の記事では、MVCの中のコントローラとビューを使い、HelloWorldの表示までを行いました。
今回は、MVCの「M」=「Model」モデルを使い、データベースからデータを取得し表示するまでを行います。
開発環境
今回の開発環境は以下の通りです。
- 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の世界へ、行ってらっしゃい。