RitoLabo

CakePHP3のMVC入門[Model]モデル[Table/Entity]を用いデータベースからデータ取得を行い表示させる

  • 公開:
  • カテゴリ: PHP CakePHP
  • タグ: PHP,Beginner,MVC,CakePHP,3.5,Model,Table,Entity

前回の記事では、MVCの中のコントローラとビューを使い、HelloWorldの表示までを行いました。

今回は、MVCの「M」=「Model」モデルを使い、データベースからデータを取得し表示するまでを行います。

アジェンダ
  1. 開発環境
  2. CakePHP3のモデルについて
    1. Table(テーブル)
    2. Entity(エンティティ)
  3. コントローラ作成
  4. ビューテンプレート作成
  5. モデル作成
  6. 取得データを表示する
  7. 動作確認

開発環境

今回の開発環境は以下の通りです。

  • 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の世界へ、行ってらっしゃい。