親記事
Laravel 5.7で基本的なCRUDを作る - Qiita
User用のフォームリクエストを作る
前回の記事とほぼ同じです。
# User用のフォームリクエストを生成
> php artisan make:request StoreUser
public function authorize()
{
// 認可は別の箇所で行うので、ここでは素通りさせる
return true;
}
public function rules()
{
return [
'name' => 'required|string|max:191',
'email' => 'required|string|email|max:191|unique:users',
'password' => 'required|string|min:6|max:191|confirmed',
];
}
// 忘れずにインポートすること!!!!
+use App\Http\Requests\StoreUser;
class UserController extends Controller
{
/**
* Store a newly created resource in storage.
*
- * @param \Illuminate\Http\Request $request
+ * @param \App\Http\Requests\StoreUser $request
* @return \Illuminate\Http\Response
*/
- public function store(Request $request)
+ public function store(StoreUser $request)
{
(中略)
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\User $user
* @return \Illuminate\Http\Response
*/
public function update(Request $request, User $user)
{
$this->authorize('edit', $user);
+
+ // name欄だけを検査するため、元のStoreUserクラス内のバリデーション・ルールからname欄のルールだけを取り出す。
+ $request->validate([
+ 'name' => (new StoreUser())->rules()['name']
+ ]);
上のコントローラのupdateアクションでは、引数の型をStoreUser
ではなくRequest
のままにしています。
StoreUser
にすると、name欄、email欄、password欄の全てでバリデーション検査が実行されます。
しかし、現在の編集画面ではname欄しか変更できません。
name欄のみの入力値をバリデーションへ渡すと、email欄やpassword欄は空白であるとみなされ、必ず検査に失敗します。
そのため、フォームリクエスト・バリデーションに頼らず、$request->validate
メソッドを使わなければなりません。
しかし、ルールを直に書くと管理が面倒なので、StoreUser
のルールを流用しています。
ビューを修正する
<div class="form-group">
<label for="name">{{ __('Name') }}</label>
- <input id="name" type="text" class="form-control" name="name" required autofocus>
+ <input id="name" type="text" class="form-control @if ($errors->has('name')) is-invalid @endif" name="name" value="{{ old('name') }}" required autofocus>
+ @if ($errors->has('name'))
+ <span class="invalid-feedback" role="alert">
+ {{ $errors->first('name') }}
+ </span>
+ @endif
</div>
<div class="form-group">
<label for="email">{{ __('E-Mail Address') }}</label>
- <input id="email" type="email" class="form-control" name="email" required>
+ <input id="email" type="email" class="form-control @if ($errors->has('email')) is-invalid @endif" name="email" value="{{ old('email') }}" required>
+ @if ($errors->has('email'))
+ <span class="invalid-feedback" role="alert">
+ {{ $errors->first('email') }}
+ </span>
+ @endif
</div>
<div class="form-group">
<label for="password">{{ __('Password') }}</label>
- <input id="password" type="password" class="form-control" name="password" required>
+ <input id="password" type="password" class="form-control @if ($errors->has('password')) is-invalid @endif" name="password" required>
+ @if ($errors->has('password'))
+ <span class="invalid-feedback" role="alert">
+ {{ $errors->first('password') }}
+ </span>
+ @endif
</div>
<div class="form-group">
<label for="name">{{ __('Name') }}</label>
- <input id="name" type="text" class="form-control" name="name" value="{{ $user->name }}" required autofocus>
+ <input id="name" type="text" class="form-control @if ($errors->has('name')) is-invalid @endif" name="name" value="{{ old('name', $user->name) }}" required autofocus>
+ @if ($errors->has('name'))
+ <span class="invalid-feedback" role="alert">
+ {{ $errors->first('name') }}
+ </span>
+ @endif
</div>
カスタム属性名を追加する
'attributes' => [
'title' => __('Title'),
'body' => __('Body'),
+ 'name' => __('Name'),
+ 'email' => __('E-Mail Address'),
+ 'password' => __('Password'),
],
Register用のルールと統合する
以前の記事で自動生成したログイン関連のファイルのうち、ユーザー登録を担当するRegisterコントローラにもUserのバリデーション・ルールが記載されています。
これを削除して、StoreUser内のルールを流用することで統一します。
// 忘れずにインポートすること
use App\Http\Requests\StoreUser;
(中略)
protected function validator(array $data)
{
// return Validator::make($data, [
// 'name' => 'required|string|max:255',
// 'email' => 'required|string|email|max:255|unique:users',
// 'password' => 'required|string|min:6|confirmed',
// ]);
// 統一したバリデーション・ルールを用いる
return Validator::make($data, (new StoreUser())->rules());
}
ResetPassword用のルールと統合する
パスワード再設定でも、独自のバリデーションルールが、下記のトレイトのrules
メソッドで設定されています。
Illuminate\Foundation\Auth\ResetsPasswords
これをResetPasswordControllerで上書きします。
パスワードのルールのみを変更します。
メールアドレスは、対象ユーザーのメールアドレスと一致しなければどのみちエラーとなってしまうので、細かなルールに変更する必要はありません。
// 忘れずにインポートすること
use App\Http\Requests\StoreUser;
(中略)
/**
* パスワード再設定用のバリデーションルール
*
* @return array
*/
protected function rules()
{
return [
'token' => 'required',
'email' => 'required|email',
'password' => (new StoreUser())->rules()['password'],
];
}
(余談) パスワードは6文字以上にすべき
6文字以上なら面倒がありません。
どういうことかと言うと、下記の場所でmb_strlen($password) >= 6
とハードコーディングされていて、6文字未満に設定するのが難しいからです。
Illuminate\Auth\Passwords\PasswordBroker
このハードコーディングされた箇所が実行されないようにするには、先ほどのResetPasswordControllerに下記のような追記が必要です。
// 追加でインポート
use Illuminate\Support\Facades\Password;
use Illuminate\Http\Request;
(中略)
/**
* Reset the given user's password.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/
public function reset(Request $request)
{
$this->validate($request, $this->rules(), $this->validationErrorMessages());
$broker = Password::broker();
$broker->validator(function() {
return true; // 追加のバリデーションは不要
});
$response = $broker->reset(
$this->credentials($request), function ($user, $password) {
$this->resetPassword($user, $password);
}
);
return $response == Password::PASSWORD_RESET
? $this->sendResetResponse($request, $response)
: $this->sendResetFailedResponse($request, $response);
}
上のreset
メソッドは、Illuminate\Foundation\Auth\ResetsPasswords トレイトの同名のメソッドを上書きします。
上のコードでも使っているPassword
ファサードの元が、6
がハードコーディングされているPasswordBroker
クラスなのです。
このようにフレームワークの奥深くにまで手を伸ばすと、メンテナンスが面倒だと思います。
たかがmb_strlen($password) >= 6
を避けるだけのために…。
そういうわけで、パスワードの最小文字数は6以上にすべきだと思います。
一応、ハードコーディングしている箇所を削除してもらえるよう、要望を送りました。
https://github.com/laravel/ideas/issues/1307