cakePHP 3 を 直接 触って 覚えてみることにした No.4
この記事は 2017年 2月 14日 に書かれた記事です。
わかってしまえば 大した事ないのですが 前回から ず〜と 悩んでました…
やっと ログイン画面を 作成することができたので 忘れないように メモしておきます
テーブル作成
管理者用 の ユーザ を 保存するための テーブル を 作成します
ユーザ名 メールアドレス パスワード を 1セット として Admins を作成します
bin/cake bake migration CreateAdmins username:string:unique email:string:unique password:string created modified -p Admin bin/cake migrations migrate -p Admin
コントローラー
コントローラー は Users として 作成します
plugins/Admin/src/Controller/UsersController.php
<?php namespace Admin\Controller; use Admin\Controller\AppController; /** * Users Controller * * @property \Admin\Model\Table\AdminTable $Admins */ class UsersController extends AppController { /** * Index method * * @return \Cake\Network\Response|null */ public function index() { $this->set( 'title_for_layout', __( 'Admin List' ) ); $this->loadModel( 'Admin.Admins' ); $admins = $this->paginate( $this->Admins ); $this->set( compact( 'admins' ) ); $this->set( '_serialize', ['admins'] ); } /** * View method * * @param string|null $id Admin id. * @return \Cake\Network\Response|null * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. */ public function view( $id = null ) { $this->set( 'title_for_layout', __( 'Admin Details' ) ); $this->loadModel( 'Admin.Admins' ); $admin = $this->Admins->get( $id, [ 'contain' => [] ] ); $this->set( 'admin', $admin ); $this->set( '_serialize', ['admin'] ); } /** * Add method * * @return \Cake\Network\Response|null Redirects on successful add, renders view otherwise. */ public function add() { $this->set( 'title_for_layout', __( 'Add Admin' ) ); $this->loadModel( 'Admin.Admins' ); $admin = $this->Admins->newEntity(); if( $this->request->is( 'post' ) ) { $admin = $this->Admins->patchEntity( $admin, $this->request->data ); if( $this->Admins->save( $admin ) ) { $this->Flash->success( __( 'The admin has been saved.' ) ); return $this->redirect( [ 'action' => 'index' ] ); } $this->Flash->error( __( 'The admin could not be saved. Please, try again.' ) ); } $this->set( compact( 'admin' ) ); $this->set( '_serialize', ['admin'] ); } /** * Edit method * * @param string|null $id Admin id. * @return \Cake\Network\Response|null Redirects on successful edit, renders view otherwise. * @throws \Cake\Network\Exception\NotFoundException When record not found. */ public function edit( $id = null ) { $this->set( 'title_for_layout', __( 'Edit Admin' ) ); $this->loadModel( 'Admin.Admins' ); $admin = $this->Admins->get( $id, [ 'contain' => [] ] ); if( $this->request->is( [ 'patch', 'post', 'put' ] ) ) { $admin = $this->Admins->patchEntity( $admin, $this->request->data ); if( $this->Admins->save( $admin ) ) { $this->Flash->success( __( 'The admin has been saved.' ) ); return $this->redirect( [ 'action' => 'index' ] ); } $this->Flash->error( __( 'The admin could not be saved. Please, try again.' ) ); } $this->set( compact( 'admin' ) ); $this->set( '_serialize', ['admin'] ); } /** * Delete method * * @param string|null $id Admin id. * @return \Cake\Network\Response|null Redirects to index. * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. */ public function delete( $id = null ) { $this->set( 'title_for_layout', __( 'Delete Admin' ) ); $this->loadModel( 'Admin.Admins' ); $this->request->allowMethod( [ 'post', 'delete' ] ); $admin = $this->Admins->get( $id ); if( $this->Admins->delete( $admin ) ) { $this->Flash->success( __( 'The admin has been deleted.' ) ); } else { $this->Flash->error( __( 'The admin could not be deleted. Please, try again.' ) ); } return $this->redirect( [ 'action' => 'index' ] ); } /** * Login method */ public function login() { $this->set( 'title_for_layout', __( 'Login' ) ); $this->viewBuilder()->layout( 'admin.login' ); if( $this->request->is( 'post' ) ) { $admin = $this->Auth->identify(); if( $admin ) { $this->Auth->setUser( $admin ); return $this->redirect( $this->Auth->redirectUrl() ); } $this->Flash->error( __( 'Invalid username or password, try again.' ) ); } } /** * Logout method */ public function logout() { return $this->redirect( $this->Auth->logout() ); } }
モデル
モデル は bake で 作ってしまいましょう
DB を 元に 作ってくれるので 非常に便利です!!
bin/cake bake model Admins -p Admin
単純に 作成しただけでは password は ハッシュ化されないので Entity という 機能を使って DB に 入れる直前に ハッシュ化 されるようにします
省略した形で書きます
plugins/Admin/src/Model/Entity/Admin.php
~ bake された 記述 ~ use Cake\Auth\DefaultPasswordHasher; class Admin extends Entity { ~ bake された 記述 ~ protected function _setPassword( $password ) { return ( new DefaultPasswordHasher )->hash( $password ); } }
ビュー
ビュー部分は Bootstrap の コーディングが必要ですので ソースを ガシガシ 載せます!!
plugins/Admin/src/Template/Layout/admin.login.ctp
<!DOCTYPE html> <html lang="ja"> <head> <meta name='robots' content='noindex,follow'> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="format-detection" content="telephone=no,address=no,email=no"> <?= $this->Html->css( 'Admin.bootstrap.min' ).PHP_EOL ?> <?= $this->Html->css( 'Admin.bootstrap.diff' ).PHP_EOL ?> <?= $this->Html->css( 'Admin.admin.login' ).PHP_EOL ?> <title><?= $title_for_layout ?></title> <?= $this->Html->meta( 'icon', 'Admin./favicon/admin-favicon.ico' ).PHP_EOL ?> <?= $this->Html->meta( [ 'rel' => 'apple-touch-icon-precomposed', 'sizes' => '16x16', 'link' => 'Admin./favicon/admin-favicon.ico', ] ).PHP_EOL ?> </head> <body> <?= $this->fetch( 'content' ) ?> </body> <?= $this->Html->script( 'Admin.jquery.min' ).PHP_EOL ?> <?= $this->Html->script( 'Admin.tether.min' ).PHP_EOL ?> <?= $this->Html->script( 'Admin.bootstrap.min' ).PHP_EOL ?> </html>
plugins/Admin/src/Template/Users/index.ctp
<section class="container"> <header class="form-signin-heading"> <h1><?= $title_for_layout ?></h1> </header> <?= $this->Flash->render() ?> <table class="table table-hover table-responsive"> <thead> <tr> <th><?= $this->Paginator->sort( 'id' ) ?></th> <th><?= $this->Paginator->sort( 'username' ) ?></th> <th colspan="3"><?= __( 'Actions' ) ?></th> </tr> </thead> <tbody> <?php foreach( $admins as $admin ) : ?> <tr> <td><?= $this->Number->format( $admin->id ) ?></td> <td><?= h( $admin->username ) ?></td> <td><?= $this->Html->link( __( 'View' ), [ 'action' => 'view', $admin->id ], [ 'class' => 'btn btn-secondary' ] ) ?></td> <td><?= $this->Html->link( __( 'Edit' ), [ 'action' => 'edit', $admin->id ], [ 'class' => 'btn btn-secondary' ] ) ?></td> <td><?= $this->Form->postLink( __( 'Delete' ), [ 'action' => 'delete', $admin->id ], [ 'class' => 'btn btn-secondary' ], [ 'confirm' => __( 'Are you sure you want to delete # {0}?', $admin->id ) ] ) ?></td> </tr> <?php endforeach; ?> </tbody> </table> <nav aria-label="Page navigation example"> <ul class="pagination"> <?= $this->Paginator->first( '<< ' . __( 'first' ) ).PHP_EOL ?> <?= $this->Paginator->prev( '< ' . __( 'previous' ) ).PHP_EOL ?> <?= $this->Paginator->numbers().PHP_EOL ?> <?= $this->Paginator->next( __( 'next' ) . ' >' ).PHP_EOL ?> <?= $this->Paginator->last( __( 'last' ) . ' >>' ).PHP_EOL ?> </ul> <p><?= $this->Paginator->counter( [ 'format' => __( 'Page {{page}} of {{pages}}, showing {{current}} record(s) out of {{count}} total' ) ] ) ?></p> </nav> </section>
plugins/Admin/src/Template/Users/view.ctp
<section class="container"> <header class="form-signin-heading"> <h1><?= $title_for_layout ?></h1> </header> <?= $this->Flash->render() ?> <table class="table table-hover table-responsive"> <tbody> <tr> <th><?= __( 'ID' ) ?></td> <td><?= $this->Number->format( $admin->id ) ?></td> </tr> <tr> <th><?= __( 'Username' ) ?></th> <td><?= h( $admin->username ) ?></td> </tr> <tr> <th><?= __( 'E-mail' ) ?></th> <td><?= h( $admin->email ) ?></td> </tr> <tr> <th><?= __( 'Password' ) ?></th> <td> = Secret = </td> </tr> </tbody> </table> </section>
plugins/Admin/src/Template/Users/add.ctp
<section class="container"> <div class="form-signin"> <header class="form-signin-heading"> <h1><?= $title_for_layout ?></h1> <h2><?= __( 'Please enter user name and password.' ) ?></h2> </header> <?= $this->Flash->render() ?> <div class="users form"> <?= $this->Form->create( $admin ).PHP_EOL ?> <?= $this->Form->input( 'username', [ 'class' => 'form-control', 'placeholder' => 'username' ] ).PHP_EOL; ?> <?= $this->Form->input( 'email', [ 'class' => 'form-control', 'placeholder' => 'email' ] ).PHP_EOL; ?> <?= $this->Form->input( 'password', [ 'class' => 'form-control', 'placeholder' => 'password' ] ).PHP_EOL; ?> <?= $this->Form->button( __( 'Add' ), [ 'class' => 'btn btn-lg btn-primary btn-block' ] ).PHP_EOL; ?> <?= $this->Form->end().PHP_EOL; ?> </div> </div> </section>
plugins/Admin/src/Template/Users/edit.ctp
<section class="container"> <div class="form-signin"> <header class="form-signin-heading"> <h1><?= $title_for_layout ?></h1> <h2><?= __( 'Please enter user name and password.' ) ?></h2> </header> <?= $this->Flash->render() ?> <div class="users form"> <?= $this->Form->create( $admin ).PHP_EOL ?> <?= $this->Form->input( 'username', [ 'class' => 'form-control', 'placeholder' => 'username' ] ).PHP_EOL; ?> <?= $this->Form->input( 'email', [ 'class' => 'form-control', 'placeholder' => 'email' ] ).PHP_EOL; ?> <?= $this->Form->input( 'password', [ 'class' => 'form-control', 'placeholder' => 'password' ] ).PHP_EOL; ?> <?= $this->Form->button( __( 'Submit' ), [ 'class' => 'btn btn-lg btn-primary btn-block' ] ).PHP_EOL; ?> <?= $this->Form->end().PHP_EOL; ?> </div> </div> </section>
plugins/Admin/src/Template/Users/login.ctp
<section class="container"> <div class="form-signin"> <header class="form-signin-heading"> <h1><?= $title_for_layout ?></h1> <h2><?= __( 'Please enter user name and password.' ) ?></h2> </header> <?= $this->Flash->render() ?> <div class="users form"> <?= $this->Form->create().PHP_EOL ?> <?= $this->Form->input( 'username', [ 'class' => 'form-control', 'placeholder' => 'username' ] ).PHP_EOL ?> <?= $this->Form->input( 'password', [ 'class' => 'form-control', 'placeholder' => 'password' ] ).PHP_EOL ?> <?= $this->Form->button( __( 'Login' ), [ 'class' => 'btn btn-lg btn-primary btn-block' ] ).PHP_EOL ?> <?= $this->Form->end().PHP_EOL ?> </div> </div> </section>
ユーザ登録
http://localhost:8888/my_app_name/admin/users/add から 最初のユーザを 登録します
これで ハッシュ化された パスワードが 生成されます
ログイン認証
/plugins/Admin/src/Controller/AppController.php
class AppController extends BaseController { public function initialize() { parent::initialize(); $this->loadComponent( 'Auth', [ 'loginRedirect' => [ 'controller' => 'Pages', 'action' => 'display', 'front-page', ], 'logoutRedirect' => [ 'controller' => 'Users', 'action' => 'login', ], 'loginAction' => [ 'controller' => 'Users', 'action' => 'login', ], 'authenticate' => [ 'Form' => [ 'userModel' => 'Admin.Admins', ] ], 'sessionKey' => 'Auth.Admin', ] ); } ~ bake された 記述 ~ }
これで ログイン ログアウト が できるようになりました
UsersController で $this->loadModel( ‘Admin.Admins’ ); の Admin. が わからなくて パスワードが ハッシュ化されなくて ずっと悩んでました…
プラグイン - CakePHP Cookbook 3.X ドキュメントを参考にさせていただきました。
https://book.cakephp.org/3.0/ja/plugins.html
手探りで 勉強しているので 間違っていたら ご指摘いただけると助かります!!