源码分析 - PHPHub 的 Vote 功能与 Laravel 多态数据关系 (Polymorphic Relationship)

用例 Use Case

  • 会员可以对某个主题投票;
  • 会员可以对某个回复进行投票;

Laravel 的 ORM Eloquent 对这一类型的业务逻辑有很好的支持, 如下面几个例子

一般 CMS 通用评论功能

  • 用户可以对文章进行评论;
  • 用户可以对用户进行评论;
  • 用户可以对专题进行评论;

标签功能

在某知名系统里面, 有 post 表和 page 表, 分别对应文章和页面.

  • 管理员可以给 post 打标签;
  • 管理员可以对 page 打标签;

这中类型的业务逻辑使用 Laravel 的 多态数据关系 (Polymorphic Relationship) 来解决那是最好不过了.

开始代码

1. 添加 Route

# ------------------ Votes ------------------------

Route::get('/topics/{id}/upvote', [
    'as' => 'topics.upvote',
    'uses' => 'TopicsController@upvote',
    'before' => 'auth' // 需要登录用户才能访问.
]);

Route::get('/topics/{id}/downvote', [
    'as' => 'topics.downvote',
    'uses' => 'TopicsController@downvote',
    'before' => 'auth'
]);

Route::get('/replies/{id}/vote', [
    'as' => 'replies.vote',
    'uses' => 'RepliesController@vote',
    'before' => 'auth'
]);

我们使用了 name route, 这样在页面里面, 使用帮助函数

route('topics.upvote', $topic->id)

就可以生成类似以下的链接了.

http://phphub.org/topics/20/upvote

2. 添加页面入口

此代码省略..., 下面是效果图

3. 创建 Migration

php artisan generate:migration create_votes_table

上面命令会在 app/database/migrations 文件夹下生成类似于 2014_08_20_130447_create_votes_table.php 的文件.

up 方法里面填入以下

Schema::create('votes', function(Blueprint $table)
{
    $table->increments('id');
    $table->integer('user_id')->unsigned()->index();
    $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');

    $table->integer('votable_id')->index();
    $table->string('votable_type')->index();
    $table->enum('is', ['upvote', 'downvote']);

    $table->timestamps();
});

可读性很好的代码, 这里不解释.

4. 创建 Model 文件

php artisan generate:model Vote

上面命令会在 app/model/ 文件夹下生成 Vote.php 文件, 在里面添加以下几行方法

public function votable()
{
   return $this->morphTo();
}

5. 开始链接 Topic 和 Reply

app/model/Topic.phpapp/model/Reply 文件中加入相同的方法

public function votes()
{
    return $this->morphMany('Vote', 'votable');
}

6. 开始调用

Topic 话题

以下是给 Topic 话题 投票的时候的调用, TopicsController 里面

public function upvote($id)
{
    $topic = Topic::find($id);
    $topic->votes()->create(['user_id' => Auth::user()->id, 'is' => 'upvote']);
    return Redirect::back();
}

通过上面的 create 方法, 创建了一个 vote, 看下数据库里面的内容, 注意 votable 字段:

Reply 评论

以下是给 Reply 评论 投票的时候的调用, RepliesController 里面

public function vote($id)
{
   $reply = Reply::find($id);
   $reply->votes()->create(['user_id' => Auth::user()->id, 'is' => 'upvote']);
   return Redirect::back();
}

通过上面的 create 方法, 创建了一个 vote, 看下数据库里面的内容, 同样注意 votable 字段:

总结

介绍完了, 很神奇吧, 最少的代码, 兼顾可读性, 把一个复杂的逻辑轻轻松松的解决. 这就是 Laravel4. :+1:

PHPHub 的 Vote 功能具体的代码请参照 源码 .

本帖已被设为精华帖!
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 4
Summer

@lovecn 今天修改.

9年前 评论

更新删除怎么做

6年前 评论

@Summer 多态的删除怎么做, 如点赞和取消点赞

6年前 评论

votable 字段存的是model 路径。怎么只存table 名

1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!