82.删除最佳回复
- 本系列文章为
laracasts.com
的系列视频教程——Let's Build A Forum with Laravel and TDD 的学习笔记。若喜欢该系列视频,可去该网站订阅后下载该系列视频, 支持正版 ;- 视频源码地址:github.com/laracasts/Lets-Build-a-...;
- 本项目为一个 forum(论坛)项目,与本站的第二本实战教程 《Laravel 教程 - Web 开发实战进阶》 类似,可互相参照。
本节说明
- 对应视频教程第 82 小节:Confusing Errors and Solutions
本节内容
按照目前的逻辑,当我们删除了最佳回复时,对应话题的best_reply_id
仍然是被删除回复的 id
。我们在最佳回复被删除时,让best_reply_id
为null
。我们依旧从测试开始:
forum\tests\Feature\BestReplyTest.php
.
.
/** @test */
public function if_a_best_reply_is_deleted_then_the_thread_is_properly_updated_to_reflect_that()
{
$this->signIn();
$reply = create('App\Reply',['user_id' => auth()->id()]);
$reply->thread->markBestReply($reply);
$this->deleteJson(route('replies.destroy',$reply));
$this->assertNull($reply->thread->fresh()->best_reply_id);
}
}
注:需要添加路由命名:
Route::delete('/replies/{reply}','RepliesController@destroy')->name('replies.destroy');
从实现的机制来看,可以分为以下两种类型:
- 代码层面 —— 利用 Eloquent 监控器 的
deleted
事件连带删除,好处是灵活、扩展性强,不受底层数据库约束,坏处当删除时不添加监听器,就会出现漏删; - 数据库层面 —— 利用 MySQL 自带的外键约束功能,好处是数据一致性强,基本上不会出现漏删,坏处是有些数据库不支持,如 SQLite。
如果我们选择第一种,那么只需增加少量代码即可:
forum\app\Reply.php
.
.
protected static function boot()
{
parent::boot(); //
static::created(function ($reply){
$reply->thread->increment('replies_count');
});
static::deleted(function ($reply){
if($reply->id == $reply->thread->best_reply_id){
$reply->thread->update(['best_reply_id' => null]);
}
$reply->thread->decrement('replies_count');
});
}
.
.
运行测试:
如果我们选择后一种,我们就需要添加外键约束。首先我们新建数据库迁移类:
$ php artisan make:migration add_references
使用以下代码替换:
database/migrations/{timestamp}_add_references.php
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddReferences extends Migration
{
public function up()
{
Schema::table('threads', function (Blueprint $table) {
// 当 best_reply_id 对应的 replies 表数据被删除时,best_reply_id 变为 null
$table->foreign('best_reply_id')
->references('id')
->on('replies')
->onDelete('set null');
});
}
public function down()
{
Schema::table('threads', function (Blueprint $table) {
// 移除外键约束
$table->dropForeign(['best_reply_id']);
});
}
}
正如上面所说,SQLite 不支持外键约束,所以我们需要额外配置:
forum\tests\TestCase.php
.
.
use Illuminate\Support\Facades\DB;
.
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
protected function setUp()
{
parent::setUp();
DB::statement('PRAGMA foreign_keys = ON');
$this->disableExceptionHandling();
}
.
.
}
注:不幸地是,我选用第二种方法时,测试环境下外键约束无效(在应用中试验已生效),所以我选择了第一种。若碰到类似问题,欢迎在评论区贴出探讨。