【jQuery】パスワードの表示切り替え・フォームのsubmit

# はじめに
今回は、LaravelとjQueryを使用し、パスワードの表示切り替えと、フォームのsubmitの２点についてまとめました。

※おことわり※
基本的に学習内容のアウトプットです。
初学者であるため、間違い等あればご指摘いただけますと嬉しいです。

# この記事の目的
jQueryのアウトプットがメインです。
以下内容のアウトプット
- パスワードの表示と非表示を切り替える
- パスワードと同時に、表示切替ボタンの文言を切り替える（ `表示する` ⇄ `非表示にする` ）
- 確認ダイアログを表示後、フォームの送信を行う
- submitボタン押印時に、前のエラーメッセージを非表示にする

# この記事の内容
1. 実装内容
2. 前提 
3. ルーティングの設定
4. ビューの作成・リンク設置
5. バリデーションの作成
6. コントローラの定義
7. jsの作成

# 1. 実装内容
パスワードの更新フォームを実装します。

**実装内容：**
- パスワード再設定用のフォームを用意する
- 送信前に確認ダイアログを表示し、OKが押された場合、フォームを送信しパスワードの更新処理を行う

# 2. 前提
**テーブルについて**
- userテーブルが存在している
- userテーブルには下記カラムが存在している
    - user_id（ユーザーID）
    - password（パスワード）

**機能について**
- ログイン機能実装済み（UsersController）
<br/>

:::note
ここから、実装に入ります。
:::

# 3. ルーティングの設定
パスワード更新機能に必要な、ルーティングの設定を行います。
>コントローラの関数は後ほど定義します。
```php:web.php
// 省略

Route::prefix('password')->group(function () {
  // パスワード再設定フォームを表示
  Route::get('/edit', 'UsersController@passwordEdit')->name('password.edit');
  // パスワード更新処理
  Route::post('/update', 'UsersController@passwordUpdate')->name('password.update');
});
```

# 4. ビューの作成・リンク設置
パスワード再設定フォームと、アクセスするためのリンクを設置します。
>ビューファイルは resources/views/users 配下に作成しています。
>最低限のコードになりますので、アレンジして使ってください。

### パスワード再設定用フォーム

**フォームの内容：**
- 現在のパスワード
    - `readonly` で読み取り専用
    - 表示/非表示ボタンで表示切り替え　・・・[7. jsの作成]() で実装
- 新パスワード　, 新パスワード確認用　
    - バリデーションで入力値を検証　・・・[5. バリデーションの作成]() で実装
- 更新ボタン
    - 押印時に確認ダイアログを表示　・・・[7. jsの作成]() で実装
    - フォームの送信　・・・[7. jsの作成]() で実装

```php:password_edit.blade.php
@extends('xxxx')
@section('xxxx')
<main>
    <form id="send_form" method="POST" enctype="multipart/form-data" action="{{ route('password.update') }}">
        @csrf
        <h2>パスワード変更</h2>
        <dl>
            <dt>
                現在のパスワード
                <span><a id="show_btn">表示する</a></span>
            </dt>
            <dd>
                <div class="">
                    <input type="password" name="current_password" id="current_password" readonly value="{{ $current_password }}" >
                </div>
            </dd>
        </dl>
        <dl>
            <dt>新パスワード</dt>
            <dd>
                <div class="">
                    <input name="password" type="password" value="">
                </div>
                <span class="err-msg" id="err-msg">{{$errors->first('password')}}</span>
            </dd>
        </dl>
        <dl>
            <dt>
                新パスワード
                <span>確認</span>
            </dt>
            <dd>
                <div class="">
                    <input name="password_confirmation" type="password" value="">
                </div>
            </dd>
        </dl>
        <dl>
          <div>
              <a href="{{ route('admin.menu.index') }}">戻る</a>
              <a href="#" id="submit_btn">更新する</a>
           </div>
        </dl>
    </form>
</main>
@endsection

@section('js')
<script src="{{ url('js/password.js') }}"></script>
@endsection
```

### リンクの設置
パスワード再設定フォームへのリンクを設置します。

```php
<a href="{{ route('password.edit') }}">
```

# 5. バリデーションの作成
パスワード再設定フォーム用のバリデーションを作成します。

**検証内容：**
- 新パスワードと、新パスワード確認用いずれも空ではないか
- 新パスワードと、新パスワード確認用が一致しているか

```terminal:terminal
php artisan make:request UpdatePasswordRequest
```

```php:UpdatePasswordRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class UpdatePasswordRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, mixed>
     */
    public function rules()
    {
        $rules = [
            'password' => ['required', 'confirmed'],
            'password_confirmation' => ['required', 'same:password'],
        ];

        return $rules;
    }

    /**
     * エラーメッセージ
     * @return array
     */
    public function messages()
    {
        return [
            'password.required' => 'パスワードを入力してください',
            'password.confirmed' => 'パスワードが再入力欄と一致していません',
        ];
    }
}
```

# 6. コントローラの定義
パスワード再設定フォームを表示する処理と、パスワードの更新処理を実装します。

**Tips：**
- ユーザー情報を取得①
セッションのログインユーザーIDから、ユーザー情報を取得しています。

- 現在のパスワードを復号化②
`decrypt(暗号化されたパスワード);` で暗号化されたパスワードを復号化します。
 
- パスワードを暗号化③
`encrypt(暗号化したいパスワード);` でパスワードを暗号化します。

```php:UsersController.php
<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Http\Requests\UpdatePasswordRequest;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\Request;
use Exception;

class UsersController extends Controller
{
    // パスワード再設定フォームを表示
    public function passwordEdit() 
    {
        // ユーザー情報を取得① 
        $user_id = $request->session()->get('login_user_id');
        $user = \App\Models\User::find($user_id);

        // 現在のパスワードを復号化②
        try {
            $current_password = decrypt($user->password);
            return view('password_edit', compact($current_password));
        } catch (Exception $e) {
            $e->getMessage();
            Log::error(__METHOD__ . '...パスワードの復号化に失敗しました。...error_message = ' . $e);
            return redirect()->route('password.edit');
        }
    }

    // パスワード更新処理
    public function passwordUpdate(UpdatepasswordRequest $request)
    {
        try {
            // パスワードを暗号化③
            $password = encrypt($request->password);
            // パスワード更新
            \App\Models\User::where('user_id', 1)
                ->first()
                ->update(['password' => $password]);
            return redirect()->route('TOPページなど指定');
        } catch (Exception $e) {
            $e->getMessage();
            Log::error(__METHOD__ . '...パスワードの更新に失敗しました。...error_message = ' . $e);
            return redirect()->route('password.edit');
        }
    }
}
```

# 7. jsの作成
`public/js` ディレクトリ配下に、`password.js` ファイルを作成します。

**Tips：**
-  ボタンの切替①
以下押印時に `表示する` を `非常時にする` に切り替えています。
`<span><a id="show_btn">表示する</a></span>` 

- パスワードの表示切替②
表示切替ボタン押印時に、 以下コードの `type="password"` を `type="text"` に切り替えパスワードを表示します。
`<input type="password" name="current_password" id="current_password" readonly value="{{ $current_password }}" >`

- パスワード更新③
以下押印時に、「この内容で更新してもよろしいですか？」と確認ダイアログを表示し、OKが押されたらフォームを送信します。
`<a href="#" id="submit_btn">更新する</a>`

- 前のエラーメッセージが存在する場合④
フォームを送信し、バリデーションに引っかかった場合、エラーメッセージが出力されます。
再度フォームを送信する際、エラーメッセージを非表示にします。
`<span class="err-msg" id="err-msg">{{$errors->first('password')}}</span>`

```javascript:password.js
$(function () {
  $(document).on('click', '#show_btn', function () {
    // ボタンの切替①
    if($('#show_btn').text() == '表示する') {
      $('#show_btn').text('非表示にする');
    } else {
      $('#show_btn').text('表示する')
    }
    // パスワードの表示切替②
    var password_input = $('#current_password');
    if(password_input.attr('type') == 'password') {
      password_input.attr('type', 'text');
    } else {
      password_input.attr("type", "password");
    }
  });

  // パスワード更新③
  $(document).on('click', '#submit_btn', function () {
    // 前のエラーメッセージが存在する場合④
    if($('#err-msg').text()) {
      $('#err-msg').attr('hidden', true);
    }
    var result = confirm('この内容で更新してもよろしいですか？');
    if(result){
        $('#send_form').submit();
    }else{
        return false;
    }
  });
});
```