CakePHP2から5に乗り換えた話 〜ログインまでの長い道のり〜

このはてしなく長いCake坂

この記事を三行にまとめると

本体のインストール
authenticationプラグインのインストール
やっとログインができる……
今回はcomposerでCakePHP5をインストールしてログインとログアウトができるところまでをやりたいと思います。



本体のインストール

まずは本体のインストールですね。インストールにはcomposerが必要です。composerって何やねんって方は、昔CakePHP3をインストールしたときにも書いたので、ちょっと長い記事ではありますけど良かったらこっちを参考にしてください。

CakePHP3を触ってみました 〜composerって何やねん〜

上記の記事ではcomposer.pharというコマンドでインストールをしていますが、今回はcomposerというコマンドが使えるようになっているものとします。

composerが使える状態になったら以下のコマンドで本体のインストール+新規プロジェクトの作成を行います。

composer self-update && composer create-project --prefer-dist cakephp/app:"5.*" akatsukinn

最後のakatsukinnはプロジェクト名です。好きな名前をつけてください。この名前でフォルダが作成されます。またここはパスを指定する箇所でもあるので例えば「akatsukinn/app」みたいに書けばakatsukinnフォルダの下にappという名前でフォルダが作成されます。

ちなみにCakePHP5はPHPのバージョンが8.1以上でないと動かないので、サーバーやローカル環境のPHPが8.0以下ならまずPHPのバージョンアップをしろって言わるので注意してください。

インストールした本体がウェブでアクセスできるところにあればトップページにアクセスすると以下のような画面が出ます。



何か微妙にエラーが出ているみたいですが今は無視で良いです。他にもたぶん初期状態だとデータベースの接続がエラーになっていたりすると思うので、自分の環境に合わせて書き換えてください。

データベースの設定はconfigフォルダの中にあるapp_local.phpで書きます。

'Datasources' => [
  'default' => [
    'className' => 'Cake\Database\Connection',
    'driver' => 'Cake\Database\Driver\Mysql',
    'persistent' => false,
    'host' => 'ホスト名',
    'username' => 'ユーザー名',
    'password' => 'パスワード',
    'database' => 'データベース名',
    'encoding' => 'utf8',
    'timezone' => 'UTC',
  ],
 
  // 以下略



データベースのtimezoneをAsia/Tokyoにする

上記のデータベース設定を見るとtimezoneのところがUTCになってますね。これをAsia/Tokyoに書き換えるとデータベース接続エラーになるかもしれません。

SQLSTATE[HY000]: General error: 1298 Unknown or incorrect time zone: 'Asia/Tokyo'

Asia/Tokyoなんてタイムゾーンねーよ的なことを言ってますが、そんなはずはないのでAsia/Tokyoのタイムゾーンを使えるようにしてみましょう。

修正箇所は二ヶ所です。まずはconfigフォルダの中にあるbootstrap.phpでデフォルトのタイムゾーンをAsia/Tokyoに変更します。

date_default_timezone_set(Configure::read('App.defaultTimezone'));
→ date_default_timezone_set('Asia/Tokyo');

あとはapp_local.phpのtimezoneをコメントアウトするか削除すればOKです。

'Datasources' => [
  'default' => [
    'className' => 'Cake\Database\Connection',
    'driver' => 'Cake\Database\Driver\Mysql',
    'persistent' => false,
    'host' => 'ホスト名',
    'username' => 'ユーザー名',
    'password' => 'パスワード',
    'database' => 'データベース名',
    'encoding' => 'utf8',
    // 'timezone' => '+9:00',
  ],

  // 以下略

これでタイムゾーンがAsia/Tokyoになります。



authenticationプラグインのインストール

それでは今日の本題とも言うべきログイン画面を作っていきましょう。

以前のCakePHPではAuthComponentというコンポーネントを使ってログイン処理を実装しましたが、CakePHP5ではAuthComponentは非推奨になっており、デフォルトの本体にもコンポーネントが入っていません。CakePHP5ではauthenticationというプラグインを使ってログイン処理を実装します。

このauthenticationプラグインもデフォルトでは搭載されてないのでcomposerを使ってインストールする必要があります。

composer require cakephp/authentication

公式のドキュメントを見るとバージョン2を指定してインストールするように書いてあるんですが、CakePHP5ではバージョン3以上じゃないと動かないらしいので、公式通りにやるとエラーになります。ドキュメントの方もそのうち修正されると思いますが、バージョンを指定するなら3.0を指定してください。

composer require "cakephp/authentication:^3.0"

これでプラグインが使える状態になります。



AppController.phpとApplication.php

次にログインを必要とするコントローラーでコンポーネントをロードします。たぶん特定のコントローラーではなくAppController.phpとかに書くことになりますかね。

// AppController.php
namespace App\Controller;

use App\Controller\AppController;

class AppController extends Controller {
  public function initialize(): void {
    parent::initialize();
    $this->loadComponent('Authentication.Authentication');    
  }
}

それからApplication.phpで以下のクラスをインポートしてクラスのBaseApplicationの後ろにAuthenticationServiceProviderInterfaceを追加します。

// Application.php
use Authentication\AuthenticationService;
use Authentication\AuthenticationServiceInterface;
use Authentication\AuthenticationServiceProviderInterface;
use Authentication\Middleware\AuthenticationMiddleware;
use Cake\Routing\Router;
use Psr\Http\Message\ServerRequestInterface;

class Application extends BaseApplication implements AuthenticationServiceProviderInterface {

・・・中略・・・

}

さらにApplication.phpの中を見ていくとmiddlewareという関数があり、その中で$middlewareQueueという変数にaddでいろいろ何かを追加してるっぽいコードがあるんですが、ここにAuthenticationMiddlewareっていうのを追加します。

$middlewareQueue->

・・・中略・・・

->add(new AuthenticationMiddleware($this))

あとgetAuthenticationServiceという関数も追加します。

public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface {
  $authenticationService = new AuthenticationService([
    'unauthenticatedRedirect' => Router::url('/users/login'),
    'queryParam' => 'redirect',
  ]);

  $authenticationService->loadIdentifier('Authentication.Password', [
    'fields' => [
      'username' => 'email',
      'password' => 'password',
    ],
  ]);

  $authenticationService->loadAuthenticator('Authentication.Session');
  $authenticationService->loadAuthenticator('Authentication.Form', [
    'fields' => [
      'username' => 'email',
      'password' => 'password',
    ],
    'loginUrl' => Router::url('/users/login'),
  ]);

  return $authenticationService;
}

URLは自分の環境に合わせて書き換えてください。ここでは「/users/login」をログインURLとしています。

ログインの認証に使うフィールドも適宜書き換えてください。ここではemailとpasswordというフィールドで認証するという設定になっています。



やっとログインができる……

今回は/users/loginをログインURLとしたので、UsersController.phpでログイン処理を実装します。

最初にbeforeFilterで/users/loginのページだけログインしていなくてもアクセスできる設定にします。

namespace App\Controller;

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

class UsersController extends AppController {
  public function beforeFilter(EventInterface $event) {
    parent::beforeFilter($event);
    $this->Authentication->addUnauthenticatedActions(['login']);
  }
}

続いてloginアクションに認証処理を書きます。

public function login() {
  $this->request->allowMethod(['get', 'post']);
  $result = $this->Authentication->getResult();

  // すでにログイン済みならリダイレクト
  if($result && $result->isValid()) {
    return $this->redirect('/users/index');
  }

  // 認証に失敗したらエラーメッセージを出す
  if($this->request->is('post') && !$result->isValid()) {
    $this->Flash->error('メールアドレスかパスワードが違います');
  }
}

この処理は公式のドキュメントと同じことやってるのでそっち見た方が早いかもしれませんが、こんな感じで認証を行うことができます。isValid関数で認証が通っているかどうか判定できるので、trueなら適当なページに飛ばしてfalseならフォームにエラーメッセージを出すようにしています。

テンプレート側はこんな感じですかね。

// templates/Users/login.php
<?= $this->Flash->render() ?>
<?= $this->Form->create() ?>
  <?= $this->Form->text('email', ['required' => true]) ?>
  <?= $this->Form->password('password', ['required' => true]) ?>
  <?= $this->Form->submit('ログイン'); ?>
<?= $this->Form->end() ?>

必要最低限しか書いてませんが、emailとpasswordのテキストフィールドを用意してformデータをpostできるようにしています。エラーメッセージは$this->Flash->render()で出力されます。このエラーメッセージのHTMLをカスタマイズしたい場合はtemplatesのelementsフォルダの中にあるerror.phpを編集すればOKです。



ログアウト

/users/logoutでログアウトする場合はこんな感じです。

public function logout() {  
  $result = $this->Authentication->getResult();
  
  if($result && $result->isValid()) {
    $this->Authentication->logout();
    return $this->redirect('/users/login');
  }
}






ここまで来れば何とかログイン画面は実装完了です。お疲れ様でした。

やり方を知っていれば数十分で終わるような作業なんですけど、僕は上記の処理を全部完了させるのに丸一日使ってしまいましたよ。これだからプログラミングは一生勉強が必要とか言われるんだよね。どんどん知らないことが増えていくからな。

ちなみにCakePHP2で作ったサイトをCakePHP5で作り直す場合、パスワードのハッシュ方式などが変わっているのでそのままではログインができません。なのでその場合はパスワードのハッシュ化をやり直すか、CakePHP2のパスワードをそのまま使える設定を行う必要があります。
2のパスワードを5でそのまま使う場合

その他のCakePHP5に乗り換えた話はこちら
ようやくのぼりはじめたはてしなく遠い開発坂
 もしかしたら何か関連しているかも? 
 質問や感想などお気軽にコメントしてください