Menu

18.对回复点赞

本节说明

  • 对应视频第 18 小节:A User Can Favorite Any Reply

本节内容

本节我们来实现对回复的 点赞 功能。首先新建测试文件:
forum\tests\Feature\FavoritiesTest.php

<?php

namespace Tests\Feature;

use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;

class FavoritiesTest extends TestCase
{
    use DatabaseMigrations;

    /** @test */
    public function au_authenticated_user_can_favorite_any_reply()
    {
        // If I post a "favorite" endpoint
        // It Should be recored in the database
    }
}

根据测试逻辑填充代码:

<?php

namespace Tests\Feature;

use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;

class FavoritiesTest extends TestCase
{
    use DatabaseMigrations;

    /** @test */
    public function au_authenticated_user_can_favorite_any_reply()
    {
        $reply = create('App\Reply');

        // If I post a "favorite" endpoint
        $this->post('replies/' . $reply->id . '/favorites');

        // It Should be recored in the database
        $this->assertCount(1,$reply->favorites);
    }
}

运行测试自然会失败:
file
这是我们下一步的工作,我们继续。首先新增路由:
Code\forum\routes\web.php

.
.
Route::post('/replies/{reply}/favorites','FavoritesController@store');

接着新建控制器:

$ php artisan make:controller FavoritesController

forum\app\Http\Controllers\FavoritesController.php

<?php

namespace App\Http\Controllers;

use App\Reply;
use Illuminate\Http\Request;

class FavoritesController extends Controller
{
    public function store(Reply $reply)
    {
        return \DB::table('favorites')->insert([
           'user_id' => auth()->id(),
           'favorited_id' => $reply->id,
           'favorited_type' => get_class($reply),
        ]);
    }
}

再次运行测试:
file
接着新建迁移文件:

$ php artisan make:migration create_favorites_table --create=favorites

forum\database\migrations{timestamp}_create_favorites_table.php

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateFavoritesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('favorites', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('user_id');
            $table->unsignedInteger('favorited_id');
            $table->string('favorited_type',50);
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('favorites');
    }
}

再次运行测试:
file
这是因为此时没有登录用户,所以user_id的值为null,所以我们应该增加一个测试,用来测试未登录用户的情形:
forum\tests\Feature\FavoritiesTest.php

.
.
use DatabaseMigrations;

/** @test */
public function guests_can_not_favorite_anything()
{
    $this->withExceptionHandling()
        ->post('/replies/1/favorites')
        ->assertRedirect('/login');
}
.
.

运行测试:
file
让我们来修复它:
forum\app\Http\Controllers\FavoritesController.php

<?php

namespace App\Http\Controllers;

use App\Reply;
use Illuminate\Http\Request;

class FavoritesController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth');
    }

    public function store(Reply $reply)
    {
        return \DB::table('favorites')->insert([
           'user_id' => auth()->id(),
           'favorited_id' => $reply->id,
           'favorited_type' => get_class($reply),
        ]);
    }
}

再次测试:
file
成功修复。让我们接着完成未完成的测试:

.
.
/** @test */
public function au_authenticated_user_can_favorite_any_reply()
{
    $this->signIn();  -->先登录

    $reply = create('App\Reply');

    // If I post a "favorite" endpoint
    $this->post('replies/' . $reply->id . '/favorites');

    // It Should be recored in the database
    $this->assertCount(1,$reply->favorites);
}
.

再次运行测试:
file
给出的信息比较模糊,我们修改一下Favorites.php

.
.
public function store(Reply $reply)
{
     \DB::table('favorites')->insert([
       'user_id' => auth()->id(),
       'favorited_id' => $reply->id,
       'favorited_type' => get_class($reply),
    ]);
}
.

再次测试:
file
在我们的测试中,我们使用了$this->assertCount(1,$reply->favorites),上面的问题就是$reply->favorites返回了false所致:因为我们还未进行模型关联。在我们的项目中,点赞 的动作不仅可以对reply进行,还能对thread等进行。所以我们将使用 Laravel 模型的 多态关联 :多态关联允许一个模型在单个关联上属于多个其他模型 。
forum\app\Reply.php

.
.
public function favorites()
{
    return $this->morphMany(Favorite::class,'favorited');
}
.

再次测试:
file
这是我们下一步的工作:

$ php artisan make:model Favorite

forum\app\Favorite.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Favorite extends Model
{
    protected $guarded = [];
}

再次测试:
file
我们重构store()方法:

<?php

namespace App\Http\Controllers;

use App\Reply;
use Illuminate\Http\Request;

class FavoritesController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth');
    }

    public function store(Reply $reply)
    {
         return $reply->favorite();
    }
}

我们把保存favorite的代码放在favorite()中:
forum\app\Reply.php

.
.
public function favorite()
{
    $this->favorites()->create(['user_id'=>auth()->id()]);
}
.

运行测试:
file
点赞 这个动作,每个用户对同一个回复只能进行一次。所以我们需要增加相关测试:
forum\tests\Feature\FavoritiesTest.php

.
.
/** @test */
public function au_authenticated_user_may_only_favorite_a_reply_once()
{
    $this->signIn();

    $reply = create('App\Reply');

    try{
        $this->post('replies/' . $reply->id . '/favorites');
        $this->post('replies/' . $reply->id . '/favorites');
    }catch (\Exception $e){
        $this->fail('Did not expect to insert the same record set twice.');
    }

    $this->assertCount(1,$reply->favorites);
}
.

修改迁移文件,在数据库层面进行控制:
forum\database\migrations{timestamp}_create_favorites_table.php

.
.
public function up()
{
    Schema::create('favorites', function (Blueprint $table) {
        $table->increments('id');
        $table->unsignedInteger('user_id');
        $table->unsignedInteger('favorited_id');
        $table->string('favorited_type',50);
        $table->timestamps();

        $table->unique(['user_id','favorited_id','favorited_type']);
    });
}
.
.

运行测试:
file
现在我们来修复:
forum\app\Reply.php

.
.
public function favorite()
{
    $attributes = ['user_id' => auth()->id()];

    if( ! $this->favorites()->where($attributes)->exists()){
        return $this->favorites()->create($attributes);
    }

}
.

运行测试:
file
最后运行一下全部测试:
file
我们将在下一章节完成功能代码的编写。

本文章首发在 Laravel China 社区
上一篇 下一篇
讨论数量: 0
发起讨论


暂无话题~