見出し画像

Laravelでユーザー情報を編集してみた(email,password)

そこまで難しくないだろーとか言ってやってみたら意外とあれ?ってなってたので備忘録として書いておきますw

passwordの方が簡単なのでpasswordからいきます〜。

大まかな流れ

ルーティング設定

コントローラー設定

ビュー設定

emailの編集に必要なマイグレーション ファイルを作成

ユーザーの新規email入力部分

確認メールの送信

確認メールをクリックした時の処理

上記が正しければ、emailを更新する

というような流れで実装していきます。

ルーティング設定

Route::get('/user/{user}/passwordchange_show', 'UserController@passwordchange_show')->name('passwordchange_before');
Route::post('/user/{user}/passwordchange', 'UserController@passwordchange')->name('passwordchange_after');

ビュー設定

現在のパスワード、新しいパスワード、新しいパスワードの確認を入力する部分を作ります。

             <form method="POST" action="{{ route('passwordchange_after', ['user' => $user->id]) }}">
                       @csrf

                      <input id="password" type="password" class="form-control @error('currentpassword') is-invalid @enderror" name="currentpassword" autocomplete="new-password">

                      <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" autocomplete="new-password">

                      <input id="password-confirm" type="password" class="form-control" name="password_confirmation" autocomplete="new-password">

                       <div class="form-group row mb-0">
                           <div class="col-md-6 offset-md-4">
                               <button type="submit" class="btn btn-primary">
                                   {{ __('登録する') }}
                               </button>
                               <a class="btn btn-link" href="{{ route('password.request') }}">
                                   {{ __('Forgot Your Password?') }}
                               </a>
                           </div>
                       </div>
                   </form>

コントローラー設定

   use App\Http\Requests\ChangePassword;   
   
   public function passwordchange_show(User $user)
   {
       $check_user = Auth::user();
   #1  if($check_user->id == $user->id) {
           return view('users.passwordchange')->with('user', $user);
       } else {
           return back();
       }
   }

   public function passwordchange(ChangePassword $request, User $user)
   {
       $new_password = $request->password;
       $old_password = $request->currentpassword;
   #2  if(!(Hash::check($old_password, $user->password))) {
           return redirect()->back()->with('say', '現在のパスワードが間違っています。');
       } else {
   #3      if(Hash::check($new_password, $user->password)) {
               return redirect()->back()->with('say', '新しいパスワードが、現在のパスワードと同じです。違うパスワードを設定してください。');
           } else {
               $user->password = Hash::make($request['password']);
               $user->save();
               return redirect()->route('my_page', ['user' => $user->id])->with(['user' => $user, 'say' => 'パスワードが正しく変更されました']);
           }
       }
   }

#1, if文がtrueであればビューページに飛んで行けるようにします。

#2, 現在のパスワードが入力されたものと一致しているかをチェック。

#3, 新しいパスワードと確認パスワードが一致するかチェック。

以上がうまくいけばhashを使って新しいパスワードを作成し、更新していきます。

フォームリクエストも適宜用意した方が良いかなと思います。

次はemailです。

emailの変更

emailもpasswordと同じように変更できれば良いんですけど、正しいemail(確認メール)として判定してから変更しないと不正なemailが登録されてしまう可能性があります。

そこに対応するためにテーブルやtokenを使っていきます。

Route::get('/user/{user}/emailchange_show', 'UserController@emailchange_show')->name('emailchange_before');
Route::post('/user/{user}/emailchange', 'UserController@emailchange')->name('emailchange_after');
Route::get('/reset/{token}', 'UserController@emailchange_reset')->name('emailchange_reset');

マイグレーション ファイルを作成

php artisan make:migration email_resets_table
  public function up()
   {
       Schema::create('email_resets', function (Blueprint $table) {
           $table->id();
           $table->foreignId('user_id')->constrained()->onDelete('cascade');
           $table->string('new_email')->unique();
           $table->string('token');
           $table->timestamps();
       });
   }

保存する情報はuser_id,新しいemail, そしてtokenです。このtokenは確認メールを実装する時に使用します。

モデルファイルも作ります。2つメソッドを用意します。確認メール関連です。

php artisan make:model EmailReset
use Notifiable;

   protected $fillabel = ['user_id', 'new_email', 'token'];

   public function sendEmailResetNotification($token)
   {
       $this->notify(new ChangeEmail($token));
   }

   public function routeNotificationForMail($notification)
   {
       return $this->new_email;
   }

routeNotificationForMailメソッドはメールの送信先を指定しています。

新規email入力部分

現在のメールアドレスを表示し、その下に入力画面があります。

             <form method="POST" action="{{ route('emailchange_after', ['user' => $user->id]) }}">
                       @csrf
                       
                       <h5>現在のメールアドレス</h5>
    
                       <p style="margin-top: .4rem;">{{$user->email}}</p>
                          

                       <div class="form-group row">
                         <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('新しいメールアドレス') }}</label>
                         <div class="col-md-6">
                           <input id="email" type="email" class="form-control @error('new_email') is-invalid @enderror" name="new_email" value="{{ old('new_email') }}" autocomplete="email">
                         </div>
                       </div>

                       <div class="form-group row mb-0">
                           <div class="col-md-6 offset-md-4">
                               <button type="submit" class="btn btn-primary">
                                   {{ __('登録する') }}
                               </button>
                           </div>
                       </div>
                   </form>

確認メールの送信

   use App\Http\Requests\ChangeEmail; 
   
   public function emailchange_show(User $user)
   {
       $check_user = Auth::user();
       if($check_user->id == $user->id) {
           return view('users.emailchange')->with('user', $user);
       } else {
           return back();
       }
   }

   public function emailchange(ChangeEmail $request, User $user) 
   {
       $check_user = Auth::user();
       if($check_user->id == $user->id) {
           // トークン生成
           $new_email = $request->new_email;
      #1   $token = hash_hmac(
               'sha256',
               Str::random(40) . $new_email,
               config('app.key')
           );
      #2   $email_reset = new EmailReset();
           $email_reset->user_id = $user->id;
           $email_reset->new_email = $new_email;
           $email_reset->token = $token;

           $email_reset->save();

      #3   $email_reset->sendEmailResetNotification($token);

           return redirect()->route('my_page', ['user' => $user->id])->with(['user' => $user, 'say' => '確認メールが送信されました']);
       } else {
           return redirect()->route('my_page', ['user' => $user->id])->with(['user' => $user, 'say' => 'メールの送信に失敗しました']);
       }
   }

#1, new->emailを元にtokenを作成しています。

#2, テーブルに保存するための値を代入していきます。

#3, 確認メールを送信します。モデルにあるメソッドを呼び出しています。最終的にはapp/notifications/ChangeEmailというところを動かすようになっています。

app/notifications/ChangeEmail

ここはemailが変更される時に送る確認メールを送信している場所になります。

php artisan make:notification ChangeEmail
<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Lang;

class ChangeEmail extends Notification
{
   use Queueable; #1 public $token;
   /**
    * Create a new notification instance.
    *
    * @return void
    */
   public function __construct($token)
   { #2    $this->token = $token;
   }

   /**
    * Get the notification's delivery channels.
    *
    * @param  mixed  $notifiable
    * @return array
    */
   public function via($notifiable)
   {
       return ['mail'];
   }

   /**
    * Get the mail representation of the notification.
    *
    * @param  mixed  $notifiable
    * @return \Illuminate\Notifications\Messages\MailMessage
    */
   public function toMail($notifiable)
   {
       return (new MailMessage)
           ->subject(Lang::get('変更認証メールです'))
           ->line(Lang::get('下記リンクをクリックしてメールアドレスの変更を完了してください'))
   #3      ->action(Lang::get('変更認証メール'), url('reset', $this->token))
           ->line(Lang::get('この変更に身に覚えがない場合は、無視してください'));
   }

   /**
    * Get the array representation of the notification.
    *
    * @param  mixed  $notifiable
    * @return array
    */
   public function toArray($notifiable)
   {
       return [
           //
       ];
   }
}

#1, この処理で$tokenをいうものを取得できるようにしている。

#2, $this->tokenとする事でtokenを使用できるように設定している。

#3, urlにtoken情報を入れて送信している。

確認メールをクリックした時の処理(emailの更新)

public function emailchange_reset(Request $request, $token)
   {
       // token情報を取得
       $email_reset = EmailReset::where('token', $token)->first();

       // レコードが存在しているかつ、有効期限内でアクセスしているかを判断
   #1  if($email_reset && !$this->tokenStatus($email_reset->created_at)) {

           // ユーザーのemailを更新して保存
           $user = User::find($email_reset->user_id);
           $user->email = $email_reset->new_email;
           $user->save();

           // token履歴は使わないので削除
           $email_reset->delete();

           return redirect()->route('my_page', ['user' => $user->id])->with(['user' => $user, 'say' => 'メールアドレスが正常に変更されました']);
       } else {
           if($email_reset) {
               $user = User::find($email_reset->user_id);
               // 新規作成が出来なくなるので削除
               $email_reset->delete();
               return redirect()->route('my_page', ['user' => $user->id])->with(['user' => $user, 'say' => 'メールアドレスの変更に失敗しました']);
           }
       }
   }

   protected function tokenStatus($created_at)
   {
       // $expiresはurlは1時間有効なので3600秒で追加
       // Carbon::parse($created_at)はtokenが作成された日時を取得
       // addSeconds($expires)で上記で取得した日時に$expiresつまり、1時間分の時間を追加
       // isPast()は上記で1時間追加した時間を現在過ぎているかどうかを判断。
       $expires = 3600;
       return Carbon::parse($created_at)->addSeconds($expires)->isPast();
   }

少し量が多かったのでコメントアウトでも書いてます。

確認メールをクリックした後にこのアクションに飛んできます。

#1, tokenStatusは確認メールをクリックしたのが発行されてから1時間以内かをチェックしています。trueであれば変更するようにしています。

trueであれば変更した後、falseであれば最初の処理でemail_resetのレコードを削除しています。不要だし、再度変更リクエストが送れなくなってしまったりするので。

処理が終わった後はredirectでviewに飛ばします。

ここまでで処理は終了です!お疲れ様でした!

データベースなどで正しく変更されているか確認してみましょう!

学んだ事

routeNotificationForMailメソッドがメールの送信先を指定してくれている。

emailの方が手順が多い。->正しいか確認するため。

まとめ

emailはパスワードに比べれば確かに処理は多いですが、1つ1つ見ていくと流れが見えてきます。

やる前はemailもpasswordみたいに出来るのかと思ってましたw

参考にしたサイト

今回はそんな感じで

以上!


この記事が気に入ったら、サポートをしてみませんか?
気軽にクリエイターの支援と、記事のオススメができます!
https://tenlife123.com/ LaravelとかVue.jsで学んだ事を書いてます。 フリーライドスキーヤー。国内外の大会に出場、戦績は入賞、優勝など。 Sponsor: Salomon,Niseko Grand Hirafu, Mt.石井スポーツ. 19歳

こちらでもピックアップされています

ちっちゃな実装編
ちっちゃな実装編
  • 25本

個人開発までいかない、単発の実装やエラーなど。

コメントを投稿するには、 ログイン または 会員登録 をする必要があります。