LaravelでAjax非同期通信を使う方法をユーザ削除する処理を例に紹介します。
流れとしては以下のようになります。
- JavaScriptで削除対象のユーザIDを取得
- Ajaxでサーバ(コントローラ)にユーザIDを送信
- コントローラからサービスを呼び出し、サービスでユーザ削除処理実行
- サービスで削除処理した実行結果をコントローラに渡す
- 受け取った実行結果をコントローラからjson形式でviewに返す
実行環境
- PHP: 7.2
- Laravel: 5.7
- jQuery: 3.3.1
CSRFの対策処理
POSTする場合は、CSRF対策をする必要があります。ヘッダーにCSRFトークンを追加しましょう。
1 | <meta name="csrf-token" content="{{ csrf_token() }}"> |
JavaScript(jQuery)の処理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // 削除ボタン押下 $(function() { $('#delete_button').on('click', function() { $.ajax({ headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') }, url: "{{ action('UsersController@destroy', ['user_id' => $user->id]) }}", type: 'POST', data: {'user_id': {{ $user->id }}, '_method': 'DELETE'} }) // Ajaxリクエストが成功した場合 .done(function(data) { $('.delete_message').text(data.responseJSON); }) // Ajaxリクエストが失敗した場合 .fail(function(data) { alert(data.responseJSON); }); }); }); |
削除ボタンの連続クリックを防ぐには、以下のように2重送信防止処理をを削除ボタン押下後に書き、削除処理実行後にボタン解除の処理を書くと良いでしょう。
1 2 3 4 | // 2重送信防止 $('#delete_button').find('button').each(function(i, elm) { $(elm).prop('disabled', true).addClass('disabled'); }); |
コントローラの処理
削除処理自体はサービスで行っているため、コンストラクタでサービスのインスタンスを生成します。
削除処理の実行結果を受け取り、削除が成功したか失敗したかでHTTPステータスを変えています。
・UsersController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | .... private $deleteService; /** * コンストラクタ * * @param App\Services\Users\DeleteService $delete_service * */ public function __construct(DeleteService $delete_service) { $this->deleteService = $delete_service; } /** * ユーザ削除 * * @param App\User $user_id ユーザID * * @return \Illuminate\Http\Response */ public function destroy($user_id) { // ユーザ削除処理実行 $user_delete = $this->deleteService>deleteUser($user_id); if (!$user_delete) { // HTTPステータス:500 エラー return response()->json($this->deleteService>getDeleteMessage(), \Illuminate\Http\Response::HTTP_INTERNAL_SERVER_ERROR); } // HTTPステータス:200 成功 return response()->json($this->deleteService>getDeleteMessage(), \Illuminate\Http\Response::HTTP_OK); } ... } |
サービスの処理
getDeleteMessage()メソッドはコントローラで、削除処理実行結果のメッセージを受け取れるようにしています。
一度削除対象のユーザIDから行ロック(削除済みの場合でも)を行い、ユーザが存在しない場合または削除されていた場合、例外を投げています。softDeleteを使用していない場合は、withTrashed()メソッドは必要ないです。
手っ取り早く削除対象あるかどうか判断する場合はfindOrFail()メソッドを使うと良いでしょう。
・DeleteService.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | ... private $delete_message = null; /** * エラーメッセージ取得 * * @return string $this->delete_message */ public function getDeleteMessage() { return $this->delete_message; } /** * ユーザ削除処理 * * @param array $user_id ユーザID * * @return \Illuminate\Http\Response */ public function deleteUser($user_id) { DB::beginTransaction(); try { // 削除対象のユーザを行ロック $user = User::lockForUpdate()->withTrashed()->find($user_id); // 削除対象ユーザが存在しない又は削除済みの場合エラー if ($user === null || !empty($user>deleted_at)) { throw new NotFoundException(); } // 削除処理実行 $user>delete(); DB::commit(); $this->delete_message = 'ユーザの削除に成功しました。'; return true; } catch (NotFoundException $e) { DB::rollBack(); \Log::error($e->getMessage()); $this->delete_message = 'ユーザが存在しないかすでに削除されています。'; return false; } catch (Exception $e) { DB::rollBack(); \Log::error($e->getMessage()); $this->delete_message = '予期せぬエラーが発生しました。'; return false; } } ... } |
ルーティング
・web.php
1 | Route::resource(users, 'UsersController'); |
ルーティングはresourceルートを使用していますが、削除のみのルーティングの場合は以下でも使えます。
1 | Route::post('users/{id}', 'UsersController'@destroy); |
以上、 LaravelでAjax非同期通信を使う方法でした。削除以外にも色々使えますね。