Laravel-adminでユーザーのパスワードを暗号化して登録する

# 【追記】正しい方法

コメント欄でご指摘頂いている通り、form()メソッド内に `saving` を仕込むことで対応できます。
（2021/06/07時点では公式ドキュメントの記載箇所が見つかりませんでした）

```php
    protected function form()
    {
        $form = new Form(new User());

        $form->text('name', __('Name'));
        $form->email('email', __('Email'));
        $form->datetime('email_verified_at', __('Email verified at'))->default(date('Y-m-d H:i:s'));
        $form->password('password', __('Password'))->rules('nullable');
        $form->text('remember_token', __('Remember token'));

        // ここで保存時の処理を追記する
        $form->saving(function (Form $form) {
            $form->password = bcrypt($form->password);
        });

        return $form;
    }

```

# はじめに

LaravelでAdminをさくっと作るのに便利なLaravel-adminですが、細かいことをしようとすると裏側を理解しないと苦労することが多いのかなと思います。

その中でも、ほとんどのケースで必要になる `入力したパスワードを暗号化して保存する` という処理のサンプルがあまりなかったので書いてみました。

なお、[Laravel-admin実装時メモ](https://qiita.com/TsukasaGR/items/384bbc5fd493086c7a1f)を作業ベースとしていますので、こちらで書かれたものへの追記という理解でお読み頂ければと思います。

# 暗号化せずにパスワードの項目を追加する

まずは暗号化せずにFormにパスワードを追加するだけの処理です。

```diff
protected function form()
{
    return Admin::form(User::class, function (Form $form) {

        $form->display('id', 'ID');

        $form->text('name', '氏名');
        $form->text('email', 'メールアドレス');
+        $form->password('password', 'パスワード');

        $form->select('department_id', '所属部署')->options(Department::pluck('name', 'id'));

        $form->display('created_at', 'Created At');
        $form->display('updated_at', 'Updated At');
    });
}

```

この1文だけ入れれば完了です。
ただし、このままだと入力したパスワードが暗号化されずに保存されてしまいますので、次項でこのパスワードを暗号化して保存する処理を行います。

# パスワードを暗号化して保存する

まず、既存の登録、更新処理がどのように実行されているかルーティングを確認してみます。

```shell
> php artisan route:list | grep admin/user
tsukasas-pc:api sekiguchi_t$ php artisan route:list | grep admin/user
|        | GET|HEAD  | admin/users                                | users.index          | App\Admin\Controllers\UserController@index                      | web,admin                |
|        | POST      | admin/users                                | users.store          | App\Admin\Controllers\UserController@store                      | web,admin                |
|        | GET|HEAD  | admin/users/create                         | users.create         | App\Admin\Controllers\UserController@create                     | web,admin                |
|        | PUT|PATCH | admin/users/{user}                         | users.update         | App\Admin\Controllers\UserController@update                     | web,admin                |
|        | GET|HEAD  | admin/users/{user}                         | users.show           | App\Admin\Controllers\UserController@show                       | web,admin                |
|        | DELETE    | admin/users/{user}                         | users.destroy        | App\Admin\Controllers\UserController@destroy                    | web,admin                |
|        | GET|HEAD  | admin/users/{user}/edit                    | users.edit           | App\Admin\Controllers\UserController@edit                       | web,admin                |
t
```

UserController内のstore/updateメソッドを指定していることがわかります。
が、UserController内を見てもメソッドが見当たりません。

そこで、クラス上部を見てみると、ModelFormを利用していることがわかります。

```php:app/Admin/UserController.php
<?php

namespace App\Admin\Controllers;

use App\Models\Department;
use App\Models\User;

use Encore\Admin\Form;
use Encore\Admin\Grid;
use Encore\Admin\Facades\Admin;
use Encore\Admin\Layout\Content;
use App\Http\Controllers\Controller;
use Encore\Admin\Controllers\ModelForm;

class UserController extends Controller
{
    // ModelFormのTraitを利用している
    use ModelForm;
```

このModelFormを見てみると、

```php:vendor/encore/laravel-admin/src/Controllers/ModelForm.php
<?php

namespace Encore\Admin\Controllers;

trait ModelForm
{
    // ...省略
    public function update($id)
    {
        return $this->form()->update($id);
    }

    public function store()
    {
        return $this->form()->store();
    }
```

ここで定義されていることがわかります。

今回はこのstore/update処理前にパスワードをbcyptで暗号化したいので、

1. もとの処理をオーバーライドして新たなstore/updateを作成
2. パスワードを暗号化
3. 親(ModelForm)のstore/updateに処理を渡す

をします。

ということで、変更内容はこちらになります。

```diff:app/Admin/UserController.php

class UserController extends Controller
{
-   use ModelForm;
    // ModelFormのstore/updateにエイリアスを設定する
+   use ModelForm {
+       store as _store;
+       update as _update;
+   }

    // パスワードをbcryptで暗号化したうえで、もとのModelFormのstore/updateを呼び出す
+   public function store()
+   {
+       Input::merge(['password' => bcrypt(Input::get('password'))]);
+       $this->_store();
+   }

+   public function update($id)
+   {
+       Input::merge(['password' => bcrypt(Input::get('password'))]);
+       $this->_update($id);
+   }
```

# さいごに

サンプルなさそうだなと思ったので自作しましたが、 `いや、これ使ったらもっとシンプルに出来るから！` みたいな情報がありましたらぜひコメント頂けますと幸いです🙇