Help us understand the problem. What is going on with this article?

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

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

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away