82.删除最佳回复

未匹配的标注

本节说明

  • 对应视频教程第 82 小节:Confusing Errors and Solutions

本节内容

按照目前的逻辑,当我们删除了最佳回复时,对应话题的best_reply_id仍然是被删除回复的 id。我们在最佳回复被删除时,让best_reply_idnull。我们依旧从测试开始:
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');
        });
    }
    .
    .

运行测试:
file
如果我们选择后一种,我们就需要添加外键约束。首先我们新建数据库迁移类:

$ 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();
    }
    .
    .

}

注:不幸地是,我选用第二种方法时,测试环境下外键约束无效(在应用中试验已生效),所以我选择了第一种。若碰到类似问题,欢迎在评论区贴出探讨。

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 0
发起讨论 只看当前版本


暂无话题~