以下基于 tp news table 来分析.

1-1 1-many many-many

关系数据库最重要的几个关系,看如何运用:

表:

news_t:
    id, title,content. cate_id, user_id
comments_t:
    id, body, commentable_type, commentable_id...
cates_t:
    id, name...
users_t:
    id, email, name...
pined_t:
    id, news_id, comments_id,status,user_id(冗余),reply_user(冗余)

结合 laravel-query-bulider 的关系描述:

1 news hasmany(morphmany) comments;
1 news belongsto 1 cate;
1 news belongsto 1 user;
1 news belongstomany pined;

关系的本质都是 sqld 的 join 查询,并没有什么特殊的.

最后 1 个可能不好理解.

描述多对多的示例是posts-tags,这个是符合的.但上面的news-pined_comments关系实质不是多对多,1 个 news 可能有多条置顶评论,但 1 条置顶评论不应该出现在多个 news 里.

为啥又可以的?记住:多对多的实质是中间表,pined 就是中间表. 多对多更多是sql语法上的抽象,不能完全理解为关系上的抽象.

news--pined---comments

查找某个新闻(id=2)下的置顶(status=1)评论

select c.* from comments_t as cl inner join pined_t as p on (p.comment_id= c.id)
inner join news_t as n on (p.status=1 and n.id = p.news_id and n.id=2);

其实最简单的做法,就是直接在 comments table 增加字段 status 表示是否置顶.

但 tp 这里的 pined 不但有评论置顶,还是 news 置顶,feed 置顶..就是是个中间表无误了.

如果还要设计啥精选评论,思路都是一样的.

除了这个多对多,个人觉得 tp 里的这个关系设计不够laravel,分类没有采用morph,而是用的 channel 字段自己判断 type:

public function pinnedComments() {
    return $this->belongsToMany(Comment::class, 'news_pinneds', 'target', 'raw')->where('channel', 'news:comment');
}

实际这个置顶表耦合了太多内容,不规范的地方比如,还是用 join 试图查找某个分类下的所有的置顶文章.

->join('news_pinneds', function ($join) use ($datetime) {
    return $join->on('news_pinneds.target', '=', 'news.id')->where('channel', 'news')->where('expires_at', '>', $datetime);
})

有没有足够laravel-eloquentnic的方法?

///in cate model
public funtion pined_news() {
    return $this->belongstoMany(News::class,'pinned',
    'target','cate_id');
}

其他的

还有有几个 table 和关系是通过 trait 封装的,包括:

# 被收藏
news_collections:
     id, news_id, user_id
# 被点赞
likes:
    id,user_id,likeable_type, likable_id

关系描述是:

# 被多人收藏(1篇文章允许被多人收藏,1人允许点赞多篇文章)
1 news belongstomany  many user_id
# 被多人点赞(点赞可分类型,所以是morph)
1 news morphmany many user_id