Edited at

CakePHP3でWebアプリ開発

More than 1 year has passed since last update.


概要

本記事では、CakePHPを使って、顧客管理システムを作ることを目的として、システムへの認証機能から順を追って説明していきます。


前提

CakePHPがインストールされていること。


下準備

アプリを新規作成する。

$ composer create-project --prefer-dist cakephp/app [アプリ名]

TIPSとして、上記コマンドはやや長いので、エイリアスを貼ることをおすすめします。

$ alias cakenewapp='composer create-project --prefer-dist cakephp/app'

$ cakenewapp [アプリ名]

MySQLに新規データベースを作成する。

mysql> create database [データベース名]

Query OK, 1 row affected (0.03 sec)

config設定を変更する。(config/app.php)

    'Datasources' => [

'default' => [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => 'localhost',
/**
* CakePHP will use the default DB port based on the driver selected
* MySQL on MAMP uses port 8889, MAMP users will want to uncomment
* the following line and set the port accordingly
*/

//'port' => 'nonstandard_port_number',
'username' => 'my_app', <=これ
'password' => 'secret', <=これ
'database' => 'my_app', <=これ
'encoding' => 'utf8',
'timezone' => 'UTC',
'cacheMetadata' => true,
'log' => false,

組み込みのWEBサーバを起動して、テストページにアクセスし、設定内容が問題無いか確認する。

$ bin/cake server -H 0.0.0.0


認証機能の実装

MVCを順に作成していくことで、認証機能が簡単に実装できます。(作成する順番としては、M->C->Vですが)


Model

userテーブルを作成する。

mysql> CREATE TABLE users (

-> id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
-> username VARCHAR(50),
-> password VARCHAR(255),
-> role VARCHAR(20),
-> created DATETIME DEFAULT NULL,
-> modified DATETIME DEFAULT NULL
-> );
Query OK, 0 rows affected (0.38 sec)

UsersTableクラスを定義するため、src/Model/Table/UsersTable.phpを作成する。


<?php

namespace App\Model\Table;

use Cake\ORM\Table;
use Cake\Validation\Validator;

class UsersTable extends Table
{

public function validationDefault(Validator $validator)
{
return $validator
->notEmpty('username', 'A username is required')
->notEmpty('password', 'A password is required')
->notEmpty('role', 'A role is required')
->add('role', 'inList', [
'rule' => ['inList', ['admin', 'user']],
'message' => 'Please enter a valid role'
]);
}

}

Userクラスのエンティティを定義するため、src/Model/Entity/User.phpを作成する。(Usersではなく、Userであることに注意!)

<?php

namespace App\Model\Entity;

use Cake\Auth\DefaultPasswordHasher;
use Cake\ORM\Entity;

class User extends Entity
{

protected $_accessible = [
'*' => true,
'id' => false
];

protected function _setPassword($password)
{
return (new DefaultPasswordHasher)->hash($password);
}
}


Controller

認証機能を有効にするため、AppControler(src/Controller/AppController.php)を修正する。

    public function initialize() /* modify */

{
$this->loadComponent('Flash');
$this->loadComponent('Auth', [
'authorize' => ['Controller'],
'loginRedirect' => [
'controller' => 'Users',
'action' => 'index'
],
'logoutRedirect' => [
'controller' => 'Users',
'action' => 'login'
]
]);
}

public function isAuthorized($user) /* add */
{
return false;
}

UsersControllerを定義するため、src/Controller/UsersController.phpを作成する。

<?php

namespace App\Controller;

use App\Controller\AppController;
use Cake\Event\Event;

class UsersController extends AppController
{

public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
$this->Auth->allow(['add', 'logout']);
}

public function login()
{
if ($this->request->is('post')) {
$user = $this->Auth->identify();
if ($user) {
$this->Auth->setUser($user);
return $this->redirect($this->Auth->redirectUrl());
}
$this->Flash->error(__('Invalid username or password, try again'));
}
}

public function logout()
{
return $this->redirect($this->Auth->logout());
}

public function isAuthorized($user)
{
return true;
}

public function index()
{
$this->set('users', $this->Users->find('all'));
}

public function view($id)
{
$user = $this->Users->get($id);
$this->set(compact('user'));
}

public function add()
{
$user = $this->Users->newEntity();
if ($this->request->is('post')) {
$user = $this->Users->patchEntity($user, $this->request->data);
if ($this->Users->save($user)) {
$this->Flash->success(__('The user has been saved.'));
return $this->redirect(['action' => 'add']);
}
$this->Flash->error(__('Unable to add the user.'));
}
$this->set('user', $user);
}

}


View

ユーザ登録画面のテンプレートを定義するため、src/Template/Users/add.ctpを作成する。

<div class="users form">

<?= $this->Form->create($user) ?>
<fieldset>
<legend><?= __('Add User') ?></legend>
<?= $this->Form->input('username') ?>
<?= $this->Form->input('password') ?>
<?= $this->Form->input('role', [
'options' => ['admin' => 'Admin', 'user' => 'user']
]) ?>
</fieldset>
<?= $this->Form->button(__('Submit')); ?>
<?= $this->Form->end() ?>
</div>

ログイン画面のテンプレートを定義するため、src/Template/Users/login.ctpを作成する。

<div class="users form">

<?= $this->Flash->render('auth') ?>
<?= $this->Form->create() ?>
<fieldset>
<legend><?= __('Please enter your username and password') ?></legend>
<?= $this->Form->input('username') ?>
<?= $this->Form->input('password') ?>
</fieldset>
<?= $this->Form->button(__('Login')); ?>
<?= $this->Form->end() ?>
</div>

ユーザ一覧画面のテンプレートを定義するため、src/Template/Users/index.ctpを作成する。

<h1>users</h1>

<table>
<tr>
<th>Id</th>
<th>Username</th>
</tr>
<?php foreach ($users as $user): ?>
<tr>
<td><?= $user->id ?></td>
<td><?= $this->Html->link($user->username, ['action' => 'view', $user->id]) ?></td>
</tr>
<?php endforeach; ?>
</table>

ユーザ詳細画面のテンプレートを定義するため、src/Template/Users/view.ctpを作成する。

<h1><?= h($user->id) ?></h1>

<p><?= h($user->username) ?></p>

これで、簡単に認証の仕組みができあがります!

参考:Cookbook ブログチュートリアル


顧客管理機能の実装

認証機能が実装できたので、顧客管理機能を追加します。


Model

customersテーブルを作成する。

CREATE TABLE customers (

id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
first_name VARCHAR(20) NOT NULL,
last_name VARCHAR(20) NOT NULL,
telephone_number VARCHAR(20) NOT NULL,
mailaddress VARCHAR(50) NOT NULL,
created DATETIME DEFAULT NULL,
modified DATETIME DEFAULT NULL
);

CustomersTableクラスを定義するため、src/Model/Table/CustomersTable.phpを作成する。

<?php

namespace App\Model\Table;

use Cake\ORM\Table;
use Cake\Validation\Validator;

class CustomersTable extends Table
{

public function validationDefault(Validator $validator)
{
return $validator
->notEmpty('first_name', 'First name is required')
->notEmpty('last_name', 'Last name is required');
}

}

Customerクラスのエンティティを定義するため、src/Model/Entity/Customer.phpを作成する。

<?php

namespace App\Model\Entity;

use Cake\ORM\Entity;

class Customer extends Entity
{

protected $_accessible = [
'*' => true,
'id' => false
];

}


Controller

CustomersControllerを定義するため、src/Controller/CustomersController.phpを作成する。

<?php

namespace App\Controller;

use App\Controller\AppController;
use Cake\Event\Event;

class CustomersController extends AppController
{

public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
}

public function isAuthorized($user)
{
return true;
}

public function index()
{
$this->set('customers', $this->Customers->find('all'));
}

public function view($id)
{
$customer = $this->Customers->get($id);
$this->set(compact('customer'));
}

public function add()
{
$customer = $this->Customers->newEntity();
if ($this->request->is('post')) {
$customer = $this->Customers->patchEntity($customer, $this->request->data);
if ($this->Customers->save($customer)) {
$this->Flash->success(__('The customer has been saved.'));
return $this->redirect(['action' => 'add']);
}
$this->Flash->error(__('Unable to add the customer.'));
}
$this->set('customer', $customer);
}

}


View

顧客登録画面のテンプレートを定義するため、src/Template/Customers/add.ctpを作成する。

<div class="customers form">

<?= $this->Form->create($customer) ?>
<fieldset>
<legend><?= __('Add Customer') ?></legend>
<?= $this->Form->input('first_name') ?>
<?= $this->Form->input('last_name') ?>
<?= $this->Form->input('telephone_number') ?>
<?= $this->Form->input('mailaddress') ?>
</fieldset>
<?= $this->Form->button(__('Submit')); ?>
<?= $this->Form->end() ?>
</div>

顧客一覧画面のテンプレートを定義するため、src/Template/Customers/index.ctpを作成する。

<h1>Customers</h1>

<table>
<tr>
<th>Id</th>
<th>First name</th>
<th>Last name</th>
</tr>
<?php foreach ($customers as $customer): ?>
<tr>
<td><?= $this->Html->link($customer->id, ['action' => 'view', $customer->id]) ?></td>
<td><?= $customer->first_name ?></td>
<td><?= $customer->last_name ?></td>
</tr>
<?php endforeach; ?>
</table>

顧客詳細画面のテンプレートを定義するため、src/Template/Customers/view.ctpを作成する。

<h1><?= h($customer->id) ?></h1>

<p><?= h($customer->first_name) ?></p>
<p><?= h($customer->last_name) ?></p>
<p><?= h($customer->telephone_number) ?></p>
<p><?= h($customer->mailaddress) ?></p>


注文管理機能の実装

顧客管理機能が実装できたので、さらに顧客からの注文を管理する機能を追加します。


Model

requestsテーブルを作成する。

1人の顧客が複数の注文をできるという要件で実装してみるため、requestsテーブルにcustomer_idを持たせて、外部キーを設定します。

CREATE TABLE requests (

id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
customer_id INT UNSIGNED NOT NULL,
created DATETIME,
modified DATETIME,
FOREIGN KEY customer_key (customer_id) REFERENCES customers(id)
);

RequestsTableクラスを定義するため、src/Model/Table/RequestsTable.phpを作成する。

<?php

namespace App\Model\Table;

use Cake\ORM\Table;

class RequestsTable extends Table
{
public function initialize(array $config)
{
$this->addBehavior('Timestamp');
}
}


Controller

RequestsControllerを定義するため、src/Controller/RequestsController.phpを作成する。

<?php

namespace App\Controller;

class RequestsController extends AppController
{

public function initialize()
{
parent::initialize();

$this->loadComponent('Flash'); // Include the FlashComponent
}

public function isAuthorized($user)
{
return true;
}

public function index()
{
$requests = $this->Requests->find('all');
$this->set(compact('requests'));
}

public function view($id = null)
{
$request = $this->Requests->get($id);
$this->set(compact('request'));
}

public function add()
{
$request = $this->Requests->newEntity();
if ($this->request->is('post')) {
$request = $this->Requests->patchEntity($request, $this->request->query);
if ($this->Requests->save($request)) {
$this->Flash->success(__('Your request has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('Unable to add your request.'));
}
$this->set('request', $request);
}
}

顧客詳細画面に注文のリストを表示するため、CustomersControllerのviewメソッドを修正する。

use Cake\ORM\TableRegistry;




public function view($id = null)
{
$customer = $this->Customers->get($id);
$this->set(compact('customer'));

$requests = TableRegistry::get('Requests');
$this->set('requests', $requests->find('all'));
}


View

注文登録画面のテンプレートを定義するため、src/Template/Requests/add.ctpを作成する。

<h1>Add Request</h1>

<?php
echo $this->Form->create($request);
echo $this->Form->button(__('Save Request'));
echo $this->Form->end();
?>

注文一覧画面のテンプレートを定義するため、src/Template/Requests/index.ctpを作成する。

<h1>requests</h1>

<table>
<tr>
<th>Id</th>
<th>CustomerId</th>
</tr>

<?php foreach ($requests as $request): ?>
<tr>
<td>
<?= $this->Html->link($request->id, ['action' => 'view', $request->id]) ?>
</td>
<td>
<?= $this->Html->link($request->customer_id, ['controller' => 'customers', 'action' => 'view', $request->customer_id]) ?>
</td>
</tr>
<?php endforeach; ?>
</table>

注文詳細画面のテンプレートを定義するため、src/Template/Requests/view.ctpを作成する。

<h1><?= h($request->id) ?></h1>

<p>CustomerId: <?= h($request->customer_id ?></p>

さらに顧客詳細画面のテンプレートに、その顧客に属する注文リストを追加する。

<h1><?= h($customer->title) ?></h1>

<p>FirstName: <?= h($customer->first_name) ?></p>
<p>LastName: <?= h($customer->last_name) ?></p>
<p>TelephoneNumber: <?= h($customer->telephone_number) ?></p>
<p>Mailaddress: <?= h($customer->mailaddress) ?></p>

<p><?= $this->Html->link('Add Request', ['controller' => 'requests', 'action' => 'add', '?' => ['customer_id' => $customer->id]]) ?></p>

<p>requests</p>
<table>
<tr>
<th>Id</th>
</tr>

<?php foreach ($requests as $request): ?>
<tr>
<td> <?= $this->Html->link($request->id, ['action' => 'view', $request->id]) ?> </td>
</tr>
<?php endforeach; ?>
</table>

こちらの記事を参考にすすめていくと、簡単に管理機能の実装ができあがります。

Blog Tutorial part2

Blog Tutorial part3

今後も引き続き追加していきます。。。