源码分析 - 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.php
和 app/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 功能具体的代码请参照 源码 .
@lovecn 今天修改.
更新删除怎么做
@Summer 多态的删除怎么做, 如点赞和取消点赞
votable 字段存的是model 路径。怎么只存table 名