1. Home
  2. PHP
  3. CakePHP
  4. CakePHP3のMVC入門 Model, Table/Entity を用いデータベースからデータ取得を行い表示させる

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

  • 公開日
  • 更新日
  • カテゴリ:CakePHP
  • タグ:PHP,Beginner,MVC,CakePHP,Model,Table,Entity
CakePHP3のMVC入門 Model, Table/Entity を用いデータベースからデータ取得を行い表示させる

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

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

Contents

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

Author

rito

  • Backend Engineer
  • Tokyo, Japan
  • PHP 5 技術者認定上級試験 認定者
  • 統計検定 3 級