手冊上是需要這樣子寫:
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 協議》,轉載必須註明作者和本文連結