1.前提・実現したこと
Laravelに標準装備されている認証機能「Auth」でユーザーの登録・ログイン・ログアウトは可能な状態です。ユーザーの削除機能はソフトデリート(論理削除)したいと思い、下記のReaDoubleというサイトを参考にしながらコードを書きました。
2.発生している問題・エラーメッセージ
deleted_at列に削除した日時が保存されているのに、ソフトデリートしたはずのユーザーアカウントで普通にログインやツイートが出来てしまいます。
Eloquentは、データベースから実際にレコードを削除するだけでなく、モデルを「ソフトデリート」することもできます。モデルがソフト削除されても、実際にはデータベースから削除されません。代わりに、モデルに「deleted_at」属性がセットされ、モデルを「削除」した日時が保存されます。(引用元:ReaDouble)
3.該当のソースコード
//database/migrations/2021_01_19_213508_add_column_soft_deletes_users_table.php <?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class AddColumnSoftDeletesUsersTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('users', function (Blueprint $table) { $table->softDeletes(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('users', function (Blueprint $table) { $table->dropSoftDeletes(); }); } }
//app/Models/Users.php <?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; class Users extends Model { use HasFactory; use SoftDeletes; protected $table = 'users'; }
//app/Http/Controllers/AccountController.php <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Models\Users; use \App\Models\Tweet; use Illuminate\Support\Facades\Auth; class AccountController extends Controller { public function deleteData(Request $request) { $user = Users::find($request->user_id); $user->delete(); $tweets = Tweet::latest()->get(); return view('timeline',[ 'tweets' => $tweets, ]); } }
//resources/views/timeline.blade.php //関係ある箇所のみ <form action="{{route('delete')}}" method="post"> @csrf <input type="hidden" name="user_id" value="{{Auth::user()->id}}"> <button type="submit">退会する</button> </form>
//routes/web.php //関係ある箇所のみ Route::post('/timeline/softdelete', [App\Http\Controllers\AccountController::class, 'deleteData'])->name('delete');
4.自分で調べたことや試したこと
他にも下記のサイト等いくつか調べましたが、これらの内容だと特に問題なさそうなので、何故ソフトデリートされたはずのデータでログインやツイートが出来てしまうのか原因が分かりません。
5.使っているツールのバージョンなど補足情報
OS Mac
PHP 7.3.11
Laravel 8.18.1
追記(m.ts10806様ご確認お願いします)
//app/Http/Controllers/Auth/LoginController.php(認証時) //(変更前)関係ある箇所のみ protected $redirectTo = 'timeline'; //(変更後)関係ある箇所のみ protected function redirectTo() { if (Auth::user()->deleted_at == null) { return 'timeline'; }else{ Auth::logout(); return 'timeline'; } }
//TimelineController(投稿時) //今回のWebアプリはログインユーザーのみがツイートできる前提なので、実際は上記の認証時の変更のみで大丈夫であり、この投稿時の変更は必要ないと考えています。 //(変更前)関係ある箇所のみ public function postTweet(Request $request) { $request->validate([ 'tweet' => 'required|max:140', ]); Tweet::create([ 'user_id' => Auth::user()->id, 'name' => Auth::user()->name, 'tweet' => $request->tweet, ]); return back(); } //(変更後)関係ある箇所のみ public function postTweet(Request $request) { $request->validate([ 'tweet' => 'required|max:140', ]); if(Auth::user()->deleted_at == null) { Tweet::create([ 'user_id' => Auth::user()->id, 'name' => Auth::user()->name, 'tweet' => $request->tweet, ]); } return back(); }
認証時、投稿時に「deleted_atがnullである」というのはどこに含みましたか?
お返事ありがとうございます!
認証時、投稿時に「deleted_atがnullである」というのは全く考慮していませんでした。
それに対して素人なりに考えたコードを質問内容の最後に追記させていただきました。
ソフトデリートとはdeleted_at列に削除した日時が保存されるだけでは意味がなく、追記のように認証時や投稿時にdeleted_atの値で処理を判定する必要があるということで合っていますでしょうか?
一応、追記のコードならdeleted_at列に削除した日時が保存されているユーザーはログインしようとしてもゲストとしてtimeline.blade.phpが表示されるだけになりました。
> deleted_atの値で処理を判定する必要がある
SQL直書きしてるんでなければ自前で判定処理を書く必要ナッシング
お返事ありがとうございます!
timeline.blade.phpからuser_idをPOST送信し、AccountController.phpにてレコード検索した結果をdeleteすることで、deleted_at列に削除した日時が保存されていると思っているのですが、これがSQL直書きということで上手くいかない原因なのでしょうか?
SQL直書き個所は見当たりませんね。
モデル/Eloquentの機能を使ってるように見えます。
普通なら追記のコードは必要なく、私の場合は何故かソフトデリートが上手くいかないので追記のコードが必要ということでしょうか?
「deleted_at列に削除した日時が保存されている」とあるので
ソフトデリートは「できている」ように見えますが、問題はソフトデリートの結果可否ではないと思います。
そもそもソフトデリートでユーザー削除というのが間違いなのでしょうか。。。実際はどのような方法でユーザー削除をされることが多いのでしょうか?
それはポリシー次第です。
ソフトデリートでもDELETEでもどちらでも構いません。
コード見るのが怠い(読んでない)ので回答は出来ません、ソフトデリート自体は出来ているので取得するSQLが正しくないと思われるので、まず発行されているSQLを確認しましょう
で、おかしいSQLを発行していると推定出来た個所を修正するのです。
自分なりに調べてみても「SQLが正しくないと思われるので、まず発行されているSQLを確認しましょう」の意味がよく分かりませんでした。お手数おかけして申し訳ございませんが、どのようにしたらいいのかもう少し教えていただけないでしょうか?本当はソフトデリートが出来ているならそれだけでログインやツイートはできないはずという意味でしょうか?
そのまま「Laravel 発行SQL 確認」で検索してみればいいのでは・・・
> 本当はソフトデリートが出来ているならそれだけでログインやツイートはできないはずという意味でしょうか?
ちゃいます。実際に実行されているSQLがSoftDeletesされているか(deleted_at)が埋まっているかどうか関係無しにSELECTしているという事です。
で、SQLの確認についてはmtsさんのコメントの通りlaravelの機能を調べてデバッグログとして出しても良いし、DB側のログ出力機能を使っても良いです。
SQLのselect文にて、既に論理削除されたレコードを除く条件が必要ということで合っていますでしょうか?
SQL直書きを言っているなら違うけど・・・試しもしない段階で何度も聞かれるのも面倒くさいからとりあえずログ見たら?
何度もすみません。TimelineControllerに、$sql = Users::where('id',1)->toSql(); var_dump($sql);と追記したところ「string(69) "select * from `users` where `id` = ? and `users`.`deleted_at` is null"」と確認できたのですが、IS NULL演算子でdeleted_atがnullを探しているように見えます。こういうことではないのでしょうか?
それならソフトデリートをちゃんと見てますね
それで結果がおかしいのなら別の箇所で意図しない動作をしてるんじゃないでしょうか?
Authのログイン認証がどこでどのようにされているのかを調べていて、勘違いかもしれませんがvendor\laravel\ui\auth-backend\AuthenticatesUsers.phpなのかな?と思っていても初学者には解読が難しく。。。他に何がおかしい可能性が考えられますでしょうか。
回答1件
あなたの回答
tips
プレビュー