多态关联自定义的类型字段的处理
laravel5.5
手册上是需要这样子写:
use Illuminate\Database\Eloquent\Relations\Relation;
Relation::morphMap([
'posts' => 'App\Post',
'videos' => 'App\Video',
]);
但是,一般设计数据库的时候 我们type字段都用tinyint类型的。
查找解决办法
type:0-video(视频) 1-article(文章) 2-dynamic(动态)
select count(*) as aggregate
from `tvccf_comments`
where
`tvccf_comments`.`article_id` = 6
and
`tvccf_comments`.`article_id` is not null
and
`tvccf_comments`.`type` = App\Model\Article
and
`is_ban` = 1
and
`parent_id` = 0
首先看了一个sql,type=App\Model\Article,我们想要的是type=1。
然后查看源码
public function comments()
{
return $this->morphMany('App\Model\Comment', '', 'type', 'id', '');
}
然后可以看到实例化了\Illuminate\Database\Eloquent\Relations\MorphMany对象
public function morphMany($related, $name, $type = null, $id = null, $localKey = null)
{
$instance = $this->newRelatedInstance($related);
list($type, $id) = $this->getMorphs($name, $type, $id);
$table = $instance->getTable();
$localKey = $localKey ?: $this->getKeyName();
return $this->newMorphMany($instance->newQuery(), $this, $table.'.'.$type, $table.'.'.$id, $localKey);
}
protected function newMorphMany(Builder $query, Model $parent, $type, $id, $localKey)
{
return new MorphMany($query, $parent, $type, $id, $localKey);
}
然后跟着MorphMany类往上看,找到了Illuminate\Database\Eloquent\Relations\MorphOneOrMany类
public function __construct(Builder $query, Model $parent, $type, $id, $localKey)
{
$this->morphType = $type;
$this->morphClass = $parent->getMorphClass();
parent::__construct($query, $parent, $id, $localKey);
}
看一下MorphOneOrMany类的顶层类Illuminate\Database\Eloquent\Relations\Relation
public function __construct(Builder $query, Model $parent)
{
$this->query = $query;
$this->parent = $parent;
$this->related = $query->getModel();
$this->addConstraints();
}
可以看到调用了addConstraints()方法
那么就找到调用处Illuminate\Database\Eloquent\Relations\MorphOneOrMany类
public function addConstraints()
{
if (static::$constraints) {
parent::addConstraints();
$this->query->where($this->morphType, $this->morphClass);
}
}
这里自己调用toSql()方法,看一下sql语句
$this->query->where($this->morphType, $this->morphClass)->toSql();
select * from `tvccf_comments` where `tvccf_comments`.`content_id` = ? and `tvccf_comments`.`content_id` is not null and `tvccf_comments`.`type` = ?
可以看到type就在这里将数据绑定的。我们只需要找到$this->morphClass这个在哪里有设置就好了。
其实在上面已经看到了,在构造函数中就设置了。
public function __construct(Builder $query, Model $parent, $type, $id, $localKey)
{
$this->morphType = $type;
$this->morphClass = $parent->getMorphClass();
parent::__construct($query, $parent, $id, $localKey);
}
而且可以看到getMorphClass()方法是一个public的。我们在model中重写就好。
下面是具体的解决办法
// video
public function getMorphClass()
{
reutn 0;
}
但是在每一个类似内容的model中都写,很累的。
就做了一个trait。
namespace App\Traits;
trait CommentType
{
public function getMorphClass()
{
$map = [
'videos' => 0,
'articles' => 1,
'dynamics' => 2,
];
return $map[$this->getTable()];
}
}
然后model里面直接use就好。
class Dynamic extends Model
{
use CommentType;
}
调用
Video::comments()->paginate();
希望对大家有帮助。
完结!!!!撒花!!!
发现后续问题
当添加评论后,需要dynamic表的评论数量+1,下面就直接不支持了。
$comment->content()->increment('comments');
// 这个是评论model里面的关联
public function content()
{
$res = $this->morphTo('', 'type', 'content_id');
return $res;
}
怎么办呢?看了一下源码
public function morphTo($name = null, $type = null, $id = null)
{
list($type, $id) = $this->getMorphs(
Str::snake($name), $type, $id
);
// 这里关键
return empty($class = $this->{$type})
? $this->morphEagerTo($name, $type, $id)
: $this->morphInstanceTo($class, $name, $type, $id);
}
可以看到$class = $this->{$type},这里的$type就是评论表里面区分内容的字段
type:0-video(视频) 1-article(文章) 2-dynamic(动态) 这样子的。
从这里可以看出来为啥数据库存app\model\article这种的啦。
怎么解决呢?非常简单,直接上代码
// 评论模型
public function content($type)
{
$this->setAttribute('type', $this->modelToType($type));
$res = $this->morphTo('', 'type', 'content_id');
$this->setAttribute('type', $type);
return $res;
}
public function modelToType($type)
{
$map = [
'App\Model\Video',
'App\Model\Article',
'App\Model\Dynamic',
];
return $map[$type];
}
调用
$comment->content($comment->type)->increment('comments');
搞定,工作去了,剩下的你们自己优化吧。
本作品采用《CC 协议》,转载必须注明作者和本文链接
如果按手册上写的这样改下行不行呢?随手写的,没有试哈 :warning:
可以,很实用!!!
用获取器!!!