今回のお題
表題の通りです。
久しぶりにCakePHP3を使ってコーディングをする機会がありました。
既にCakePHP4も出ていますが、CakePHP3.8からはそんなに変化がないとのことなので
Cake4にもすんなり入れると良いなあ、などと勝手に考えており。。
需要があるかはさておき、備忘録として残しておきます。
環境
PHP: 5.6.40
CakePHP: 3.8
OS: CentOS7.2
DB: MySQL5.7.21
全体的にレガシーな環境。。
(なのでCakePHP3を採用したという経緯)
テーブル構成
ありきたりで恐縮ですが、ユーザーテーブルへの登録です。
年齢は実年齢ではなく、10代・20代のようなレンジでの管理となります。
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL,
`age` tinyint(4) NOT NULL,
`gender` enum('male','female') NOT NULL,
`email` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`last_login_at` datetime DEFAULT NULL,
`is_deleted` tinyint(4) NOT NULL DEFAULT '0',
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CakePHP3実装
Model
Table
validationのコードも載せています。
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class UsersTable extends Table
{
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')
->allowEmptyString('id', null, 'create');
$validator
->scalar('name')
->maxLength('name', 10, '名前は10文字以内で設定してください。')
->requirePresence('name', 'create')
->notEmptyString('name');
$validator
->add('name', 'unique', [
'rule' => 'validateUnique',
'provider' => 'table',
'message' => '名前がすでに登録されています。'
]);
$validator
->scalar('age')
->requirePresence('age', true)
->notEmptyString('age');
$validator
->scalar('gender')
->requirePresence('gender', true)
->notEmptyString('gender');
$validator
->email('email', false, 'メールアドレスが正しくありません。')
->requirePresence('email', true)
->notEmptyString('email', 'メールアドレスを入力してください。');
$validator
->add('email', 'unique', [
'rule' => 'validateUnique',
'provider' => 'table',
'message' => 'メールアドレスがすでに登録されています。'
]);
$validator
->scalar('password')
->minLength('password', 8, 'パスワードは半角英数字8文字以上で入力してください。')
->requirePresence('password', 'create', 'パスワードを入力してください。')
->allowEmptyString('password', 'パスワードを入力してください。', 'update');
$validator
->scalar('password_confirm')
->minLength('password_confirm', 8, '確認用パスワードは半角英数字8文字以上で入力してください。')
->sameAs('password', 'password_confirm', '異なるパスワードが入力されています。')
->requirePresence('password_confirm', 'create', '確認用パスワードを入力してください。')
->allowEmptyString('password_confirm', '確認用パスワードを入力してください。', 'update');
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;
}
}
Entity
hashしたパスワードを保存するためにDefaultPasswordHasherを使用しています。
use Cake\Auth\DefaultPasswordHasher;
use Cake\ORM\Entity;
class User extends Entity
{
protected $_accessible = [
'name' => true,
'age' => true,
'gender' => true,
'email' => true,
'password' => true,
'last_login_at' => true,
'is_deleted' => true,
'created' => true,
'modified' => true,
];
protected $_hidden = [
'password',
];
protected function _setPassword($password)
{
return (new DefaultPasswordHasher)->hash($password);
}
}
Controller
画面は以下の通りです。
add: 登録画面
confirm: 確認画面
complete: 完了画面
年齢と性別はconfigで選択肢を管理しています。
確認画面へ遷移した際にvalidationチェックを行い、通ったらセッションに保存をします。
完了画面は「完了しました」が表示されるだけです。
use App\Controller\AppController;
use Cake\Core\Configure;
use Cake\Event\Event;
use Cake\I18n\Time;
class UsersController extends AppController
{
public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
$this->Auth->allow([
'add',
'confirm',
'complete',
]);
}
public function initialize()
{
parent::initialize();
}
public function add()
{
$user = $this->Users->newEntity();
$ages = Configure::read('User.age');
$genders = Configure::read('User.gender');
if ($this->request->is('post')) {
$user = $this->Users->patchEntity($user, $this->request->data);
if ($user->errors()) {
$this->Flash->error('入力内容を確認してください。');
} else {
$this->request->session()->write('session.user_add', $user);
return $this->redirect(['action' => 'confirm']);
}
}
if ($this->request->session()->check('session.user_add')) {
$user = $this->request->session()->consume('session.user_add');
}
$this->set(compact('user', 'ages', 'genders'));
}
public function confirm()
{
if (!$this->request->session()->check('session.user_add')) {
return $this->redirect(['action' => 'add']);
}
$ages = Configure::read('User.age');
$genders = Configure::read('User.gender');
$user = $this->request->session()->read('session.user_add');
$this->set(compact('user', 'ages', 'genders'));
}
public function complete()
{
if ($this->request->session()->check('session.user_add')) {
$user = $this->request->session()->consume('session.user_add');
$result = $this->Users->save($user);
if (!$result) {
$this->Flash->error('保存できませんでした。');
$this->request->session()->write('errors', $user);
return $this->redirect($this->referer());
}
}
}
}
config (選択肢)
bootstrap.phpにファイルを定義をすると使用することができます。
Configure::load('user', 'default', false);
定数はこのように定義します。
<?php
return [
"User"=> [
"age" => [
1 => '20歳未満',
2 => '20-29歳',
3 => '30-39歳',
4 => '40-49歳',
5 => '50-59歳',
6 => '60-69歳',
7 => '70-79歳'
],
"gender" => [
'male' => '男性',
'female' => '女性'
],
]
];
view
登録画面
ここでは細かいデザインは置いておいて、大まかなフォームの設定を記述します。
<?= $this->Form->create($user, ['type' => 'post', 'autocomplete' => 'off']) ?>
名前
<?= $this->Form->control('name', ['type' => 'text', 'label' => false, 'required' => false]) ?>
年齢(セレクトボックス)
<?= $this->Form->control('age', ['type' => 'select', 'label' => false, 'options' => $ages, 'required' => false, 'empty' => '選択してください']) ?>
性別(ラジオボタン)
<?= $this->Form->control('gender', ['type' => 'radio', 'label' => false, 'required' => false, 'options' => $genders]) ?>
メールアドレス
<?= $this->Form->control('email', ['type' => 'text', 'label' => false, 'required' => false]) ?>
パスワード
<?= $this->Form->control('password', ['type' => 'password', 'label' => false, 'div' => false, 'required' => false, 'value' => '']) ?>
パスワード確認用
<?= $this->Form->control('password_confirm', ['type' => 'password', 'label' => false, 'div' => false, 'required' => false, 'value' => '']) ?>
<?= $this->Form->button('確認画面へ', ['type' => 'submit']) ?>
<?= $this->Form->end() ?>
確認画面
ここでも設定を書き残しておきます。
セッションから値を取得して表示します。
<?= $this->Form->create($user, ['type' => 'post', 'url' => ['controller' => 'Users', 'action' => 'complete']]) ?>
名前
<?= $user['name'] ?>
年齢
<?= $ages[$user['age']] ?>
性別
<?= $genders[$user['gender']] ?>
メールアドレス
<?= $user['email'] ?>
<?= $this->Form->button('登録完了', ['type' => 'submit']) ?>
<?= $this->Form->end() ?>
以上です。
完了画面は「完了しました」が表示されるだけなので省略します。
同じCakePHP3でもフォームヘルパーの書き方が変わっていました。
CakePHP4でもこのような感じでいけるのではないかと(願望込み)。。