[Laravel] ユーザ登録関連
Laravelでwebアプリを作成していると認証機能と言うものが目に入るのではないでしょうか?
今回はその認証機能について解説していきます。
まずは認証関連のルーティング追加
Laravelでは認証関連の雛形を用意してくれています。
以下のように記載
<?php
Auth::routes(); //-- この行を追加
Route::get('/', 'PostController@index');
上記のAuth::routes()によって3行目以降がルーティングとして設定されたことになります。
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
| | GET|HEAD | / | | App\Http\Controllers\PostController@index | web |
| | GET|HEAD | api/user | | Closure | api,auth:api |
| | GET|HEAD | login | login | App\Http\Controllers\Auth\LoginController@showLoginForm | web,guest |
| | POST | login | | App\Http\Controllers\Auth\LoginController@login | web,guest |
| | POST | logout | logout | App\Http\Controllers\Auth\LoginController@logout | web |
| | GET|HEAD | password/confirm | password.confirm | App\Http\Controllers\Auth\ConfirmPasswordController@showConfirmForm | web,auth |
| | POST | password/confirm | | App\Http\Controllers\Auth\ConfirmPasswordController@confirm | web,auth |
| | POST | password/email | password.email | App\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail | web |
| | GET|HEAD | password/reset | password.request | App\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm | web |
| | POST | password/reset | password.update | App\Http\Controllers\Auth\ResetPasswordController@reset | web |
| | GET|HEAD | password/reset/{token} | password.reset | App\Http\Controllers\Auth\ResetPasswordController@showResetForm | web |
| | GET|HEAD | register | register | App\Http\Controllers\Auth\RegisterController@showRegistrationForm | web,guest |
| | POST | register | | App\Http\Controllers\Auth\RegisterController@register | web,guest |
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
HTTPとは?
HTTPは、インターネット通信のプロトコル(手順)のひとつです。
ルーティングを確認するとGETやPOSTなどのmethodが出てきます。
HTTPの中には様々なメソッドがありますが、その中にはGETやPOSTなどのメソッドがあります。
GETメソッド
GETメソッドはクライアント(PCやスマホ)からサーバーに対して、サーバー側が持っているリソース(例:投稿データやユーザーデータなどのこと)を参照する目的で使います。
GETメソッドはURLの後ろに?を付けて、それ以降の文字列にパラメーターを記述します
POSTメソッド
POSTメソッドは、サーバー側にリソースを登録する時に使います(サーバー側が持っているリソースを「更新」する時に使用されることもあります)。
POSTメソッドはURLではなく、ボディ部と呼ばれる部分にパラメーターを含めます。
ユーザー登録に関してのルーティングを確認
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+------------+
// 略
| | GET|HEAD | register | register | App\Http\Controllers\Auth\RegisterController@showRegistrationForm | web,guest |
| | POST | register | | App\Http\Controllers\Auth\RegisterController@register | web,guest |
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+------------+
抜粋した最後の1行のMethodの列には、POSTと書かれています。
また、Action列を見ると、「登録」を意味するregisterアクションメソッドが処理されることがわかります。
HTTPのPOSTメソッドは、サーバー側にリソースを登録する時に使う、と先ほど説明しましたが、これらのことからRegisterControllerのregisterアクションメソッドではユーザー登録の処理を行うということが想像できます。
RegisterControllerクラスの確認
ユーザー登録画面表示およびユーザー登録処理を行うコントローラーは、App\Http\Controllers\Auth\RegisterControllerです。
Laravelをインストールした時点で存在しています。
.
└──作成したファイル名
└── app
└── Http
└── Controllers
└── Auth
└── RegisterController.php
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
class RegisterController extends Controller
{
/*
|--------------------------------------------------------------------------
| Register Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users as well as their
| validation and creation. By default this controller uses a trait to
| provide this functionality without requiring any additional code.
|
*/
//RegistersUsersトレイトの中にshowRegistrationFormアクションメソッドとregisterアクションメソッドが定義
use RegistersUsers;
//-- 略
}
RegistersUsersトレイトの確認
トレイトとは単にいくつかの機能をまとめるためだけのものです。
use トレイト名と記述することでトレイト内で定義している機能を他の複数のクラスで共通して使うことができる。
└── vendor
└── laravel
└── framework
└── src
└── Illuminate
└──Foundation
└──Auth
└──RegistersUsers.php
<?php
namespace Illuminate\Foundation\Auth;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
trait RegistersUsers
{
use RedirectsUsers;
/**
* Show the application registration form.
*
* @return \Illuminate\Http\Response
*/
public function showRegistrationForm()
{
//registerという名前のビューを表示する処理
return view('auth.register');
}
/**
* Handle a registration request for the application.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function register(Request $request)
{
//ユーザー登録画面からリクエストされたユーザー情報の妥当性チェック(バリデーションと言います)を行なう。$thisは、トレイトをuseしているクラス、つまりRegisterControllerクラス(のインスタンス)のことを指す。
$this->validator($request->all())->validate();
//ーザー情報をデータベースのusersテーブルに登録し、それをイベントとして発生させている。
event(new Registered($user = $this->create($request->all())));
//先ほど登録したユーザー情報で、ログイン済みの状態にしている。
$this->guard()->login($user);
//registeredメソッド内で何か処理が定義してあれば、その結果を返す
//registeredメソッド内で何も処理が定義していなければ、redirectPathメソッドで定義されたパス(URL)へリダイレクトする
return $this->registered($request, $user)
?: redirect($this->redirectPath());
}
//-- 略
}
Laravelではユーザー登録に関する機能をRegistersUsersトレイトとしてまとめておいてくれているので、もしRegisterControllerクラス以外にもユーザー登録に関するクラスを作りたい場合にも、このトレイトを使って効率的に開発できるようになっています。
さらにRegistersUsersトレイトは、RedirectsUsersトレイトというものをuse(使用)しています。
vendor
└── laravel
└── framework
└── src
└── Illuminate
└──Foundation
└──Auth
└──RegistersUsers.php
<?php
namespace Illuminate\Foundation\Auth;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
trait RegistersUsers
{
use RedirectsUsers;
// 略
}
RedirectsUsersトレイトのコードの中にredirectPathメソッドがあります。
RedirectsUsersトレイトは、以下にあります。
vendor
└── laravel
└── framework
└── src
└── Illuminate
└──Foundation
└──Auth
└──RedirectsUsers.php
<?php
namespace Illuminate\Foundation\Auth;
trait RedirectsUsers
{
/**
* Get the post register / login redirect path.
*
* @return string
*/
public function redirectPath()
{
//$thisも、一連のトレイトをuseしているクラス、つまりRegisterControllerクラス(のインスタンス)のことを指す。
//method_exists関数は、第一引数にクラス、第二引数にメソッド名を受け取り、第一引数のクラスに第二引数のメソッドが存在するかどうかをtrueかfalseで返す
if (method_exists($this, 'redirectTo')) {
return $this->redirectTo();
}
//if文の判定で、redirectToメソッドが存在しないとなった場合には以下のコードが処理されています。
return property_exists($this, 'redirectTo') ? $this->redirectTo : '/home';
}
}
if文全体の処理としてはmethod_exists関数でredirectToメソッドの有無を判定し、メソッド有りであれば、redirectToメソッドの結果を返しています。
└── app
└── Http
└── Controllers
└── Auth
└── RegisterController.php
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
class RegisterController extends Controller
{
/*
|--------------------------------------------------------------------------
| Register Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users as well as their
| validation and creation. By default this controller uses a trait to
| provide this functionality without requiring any additional code.
|
*/
use RegistersUsers;
/**
* Where to redirect users after registration.
*
* @var string
*/
//初期設定ではHOMEという定数が定義されている。
protected $redirectTo = RouteServiceProvider::HOME;
// 略
}
└── app
└── Providers
└── RouteServiceProvider
<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider
{
// 略
/**
* The path to the "home" route for your application.
*
* @var string
*/
//初期設定では/homeになっているが'/'に変更する
public const HOME = '/';
// 略
}
vendorディレクトリはcomposer installコマンドによってインストールされたPHPの各種ライブラリのコードが配置される箇所ですが、通常はここにあるコードを直接修正することはしません。
これはComposerなどでのパッケージ管理や、gitでのソースコード管理について慣れてくると自然にわかってくることなのですが、いまはvendorディレクトリ配下のコードは特別な事情が無ければ修正するものではない、と覚えておいてください。
バリデーションの確認
RegisterControllerクラスのvalidatorメソッドを見てみましょう。
└── vendor
└── laravel
└── framework
└── src
└── Illuminate
└──Foundation
└──Auth
└──RegistersUsers.php
<?php
// 略
trait RegistersUsers
{
// 略
public function register(Request $request)
{
//ユーザー登録画面からリクエストされたユーザー情報のバリデーションを行なっている。
$this->validator($request->all())->validate();
// 略
}
//-- 略
}
└── app
└── Http
└── Controllers
└── Auth
└── RegisterController.php
<?php
// 略
class RegisterController extends Controller
{
// 略
protected function validator(array $data)
{
return Validator::make($data, [
//nameの場合、必須(required)、文字列(string)、最大255文字まで(max:255)といった定義。
'name' => ['required', 'string', 'max:255'],
//emailの場合、メールアドレスの形式であること(email)、usersテーブルの他のメールアドレスと被らないこと(unique:users)といった定義。
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
//最低8文字以上(min:8)、自分の項目名_confirmedという別の項目(つまり、password_confirmedという項目)と同じ値であること(confirmed)といった定義
'password' => ['required', 'string', 'min:8', 'confirmed'],
]);
}
// 略
}
バリデーションの変更
もし、バリデーションを変更する場合は、app/Http/Controllers/Auth/RegisterController.phpの中身を変更する。
例えば、英数字であるかどうかのチェックや字数制限を設定する場合
//以下のような記述で最低1文字、最大16文字、英数字であるかどうかのチェックの項目を増やしたことになる。
'name' => ['required', 'string', 'alpha_num', 'min:1', 'max:16', 'unique:users'],
ユーザー登録画面の作成
//authディレクトリを作成、register.blade.phpファイルを追加。
└── resources
└── views
└── auth
└── register.blade.php
仮のビューを記載
@extends('app')
@section('title', 'ユーザー登録')
@section('content')
<div class="container">
<div class="row">
<div class="mx-auto col col-12 col-sm-11 col-md-9 col-lg-7 col-xl-6">
<h1 class="text-center"><a class="text-dark" href="/">memo</a></h1>
<div class="card mt-3">
<div class="card-body text-center">
<h2 class="h3 card-title text-center mt-2">ユーザー登録</h2>
<div class="card-text">
<!-- formタグで囲まれた部分に、ユーザー情報の入力フォームと送信ボタンが存在 -->
<form method="POST" action="{{ route('register') }}">
<!-- @csrfは脆弱性からWebサービスを守るためのトークン情報 -->
@csrf
<div class="md-form">
<label for="name">ユーザー名</label>
<!-- old関数を使うことで、入力した内容が保持された状態でユーザー登録画面が表示されるようになり、ユーザーはエラーになった箇所だけを修正すれば良くなる。passwordとpassword_confirmationはold関数を使ってもnullが返る。 -->
<input class="form-control" type="text" id="name" name="name" required value="{{ old('name') }}">
<small>英数字1〜16文字(登録後の変更はできません)</small>
</div>
<div class="md-form">
<label for="email">メールアドレス</label>
<input class="form-control" type="text" id="email" name="email" required value="{{ old('email') }}" >
</div>
<div class="md-form">
<label for="password">パスワード</label>
<input class="form-control" type="password" id="password" name="password" required>
</div>
<div class="md-form">
<label for="password_confirmation">パスワード(確認)</label>
<input class="form-control" type="password" id="password_confirmation" name="password_confirmation" required>
</div>
<button class="btn btn-block blue-gradient mt-2 mb-2" type="submit">ユーザー登録</button>
</form>
<div class="mt-0">
<a href="{{ route('login') }}" class="card-text">ログインはこちら</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
ログアウトを可能にする
ログアウトはLoginController.phpの中で処理を記載します。
└── app
└── Http
└── Controllers
└── Auth
└── LoginController.php
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
//AuthenticatesUsersトレイトの中にlogoutアクションメソッドが定義されている。
use AuthenticatesUsers;
// 略
}
AuthenticatesUsersトレイトの確認
└── vendor
└── laravel
└── framework
└── src
└── Illuminate
└──Foundation
└──Auth
└──AuthenticatesUsers.php
<?php
namespace Illuminate\Foundation\Auth;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;
trait AuthenticatesUsers
{
// 略
/**
* Log the user out of the application.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function logout(Request $request)
{
//ユーザーをログアウトさせる処理を行う。
$this->guard()->logout();
//セッションを再作成。
$request->session()->invalidate();
//CSRFトークンを再作成
$request->session()->regenerateToken();
//loggedoutメソッドから何か戻り値があれば、それをlogoutアクションメソッドの呼び出し元に返す。
//loggedoutアクションメソッドから何も戻り値が無ければ、redirect関数により'/'へリダイレクトされるが、loggedoutメソッドは何もしていないのでログアウトすると/へリダイレクトされる。
return $this->loggedOut($request) ?: redirect('/');
}
/**
* The user has logged out of the application.
*
* @param \Illuminate\Http\Request $request
* @return mixed
*/
protected function loggedOut(Request $request)
{
//
}
// 略
}
ログアウト後のリダイレクト先を変えたい場合
LoginControllerにloggedoutメソッドを追加する。
└── app
└── Http
└── Controllers
└── Auth
└── LoginController.php
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
class LoginController extends Controller
{
// 略
protected function loggedOut(Request $request)
{
// ここにやりたい処理内容を書く
}
}
そうすると、AuthenticatesUsersトレイトのloggedoutメソッドの処理内容よりも、LoginControllerに書かれたloggedoutメソッドの内容が優先して処理されます。
これをオーバーライド(上書き)と言います。
登録用のナビバーを作成
└── resources
└── views
└── nav.blade.php
<nav class="navbar navbar-expand navbar-dark blue-gradient">
<a class="navbar-brand" href="/"><i class="far fa-sticky-note mr-1"></i>Memo</a>
<ul class="navbar-nav ml-auto">
@guest
<li class="nav-item">
<a class="nav-link" href="{{ route('register') }}">ユーザー登録</a>
</li>
@endguest
@guest
<li class="nav-item">
<a class="nav-link" href="">ログイン</a>
</li>
@endguest
@auth
<li class="nav-item">
<a class="nav-link" href=""><i class="fas fa-pen mr-1"></i>投稿する</a>
</li>
@endauth
@auth
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" id="navbarDropdownMenuLink" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<i class="fas fa-user-circle"></i>
</a>
<div class="dropdown-menu dropdown-menu-right dropdown-primary" aria-labelledby="navbarDropdownMenuLink">
<button class="dropdown-item" type="button"
onclick="location.href=''">
マイページ
</button>
<div class="dropdown-divider"></div>
<button form="logout-button" class="dropdown-item" type="submit">
ログアウト
</button>
</div>
</li>
<!-- ログアウトを可能にする -->
<form id="logout-button" method="POST" action="{{ route('logout') }}">
@csrf
</form>
@endauth
</ul>
</nav>
@guestから@endguestに囲まれた部分は、ユーザーがまだログインしていない状態の時のみ処理。
逆に@authから@endauthに囲まれた部分は、ユーザーがログイン済みの状態の時のみ処理
ユーザー登録時のエラーメッセージを表示する
正常に登録できたらトップページに遷移する。
バリデーションエラーメッセージの表示
resources/views/authディレクトリのregister.blade.phpを編集。
└── resources
└── views
└── auth
└── register.blade.php
@section('content')
<div class="container">
<div class="row">
<div class="mx-auto col col-12 col-sm-11 col-md-9 col-lg-7 col-xl-6">
<h1 class="text-center"><a class="text-dark" href="/">memo</a></h1>
<div class="card mt-3">
<div class="card-body text-center">
<h2 class="h3 card-title text-center mt-2">ユーザー登録</h2>
@include('error_card_list') {{--この行を追加--}}
<div class="card-text">
<form method="POST" action="{{ route('register') }}">
@csrf
<div class="md-form">
<label for="name">ユーザー名</label>
<input class="form-control" type="text" id="name" name="name" required value="{{ old('name') }}">
<small>英数字1〜16文字(登録後の変更はできません)</small>
</div>
<div class="md-form">
<label for="email">メールアドレス</label>
<input class="form-control" type="text" id="email" name="email" required value="{{ old('email') }}" >
</div>
<div class="md-form">
<label for="password">パスワード</label>
<input class="form-control" type="password" id="password" name="password" required>
</div>
<div class="md-form">
<label for="password_confirmation">パスワード(確認)</label>
<input class="form-control" type="password" id="password_confirmation" name="password_confirmation" required>
</div>
<button class="btn btn-block blue-gradient mt-2 mb-2" type="submit">ユーザー登録</button>
</form>
<div class="mt-0">
<a href="{{ route('login') }}" class="card-text">ログインはこちら</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
そしてresources/viewsディレクトリにerror_card_list.blade.phpを作成
└── resources
└── views
└── error_card_list.blade.php
//MessageBagクラスのanyメソッドを使っていますが、これはエラーメッセージの有無を返す。
@if ($errors->any())
<div class="card-text text-left alert alert-danger">
<ul class="mb-0">
//@foreachで繰り返し表示を行なう。
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
$errors変数は、Illuminate\Support\MessageBagクラスのインスタンスであり、バリデーションエラーメッセージを配列で持っている。
バリデーションエラーメッセージの日本語化
resources/langディレクトリにjaディレクトリを作成し、そこにvalidation.phpを作成。
└── resources
└── lang
└── ja
└── validation.php
config/app.phpを以下の通り編集
<?php
return [
// 略
/*
|--------------------------------------------------------------------------
| Application Locale Configuration
|--------------------------------------------------------------------------
|
| The application locale determines the default locale that will be used
| by the translation service provider. You are free to set this value
| to any of the locales which will be supported by the application.
|
*/
'locale' => 'ja', //-- この行を編集
/*
|--------------------------------------------------------------------------
| Application Fallback Locale
|--------------------------------------------------------------------------
|
| The fallback locale determines the locale to use when the current one
| is not available. You may change the value to correspond to any of
| the language folders that are provided through your application.
|
*/
//fallback_localeは、localeで指定した言語のファイルが見つからなかった場合にどの言語ファイルを使用するかを定義。今回作成したvalidation.php以外に関しては、laravel/resources/lang/enディレクトリの言語ファイルが使用される。
'fallback_locale' => 'en',
//略
];
項目名の日本語化
今のままだと項目名は英語のままなので日本語にするためにlaravel/resources/lang/ja/validation.phpを以下のように編集。
<?php
return [
// 略
/*
|--------------------------------------------------------------------------
| カスタムバリデーション属性名
|--------------------------------------------------------------------------
|
| 以下の言語行は、例えば"email"の代わりに「メールアドレス」のように、
| 読み手にフレンドリーな表現でプレースホルダーを置き換えるために指定する
| 言語行です。これはメッセージをよりきれいに表示するために役に立ちます。
|
*/
//==========ここから追加==========
'attributes' => [
'name' => 'ユーザー名',
'email' => 'メールアドレス',
'password' => 'パスワード',
],
//==========ここまで追加==========
];