下面的函数允许我检索给定模型类型的相关条目,这些条目具有例如与其相关联的重叠标签.
<?php
public function getRelated($entity, $fields = [], $relations = []) {
$related = new EloquentCollection();
// get models of same entity type that have relations in common
if (count($relations)) {
foreach ($relations as $relation) {
// check if the current model in use has any of the currently iterated relationship at all
if ($entity->{$relation}()->exists()) {
// if yes, get all the ids of the related models, for instance tags
$relationIds = $entity->{$relation}()->get()->pluck('id')->toArray();
// now, get all same type models except the current one
$relationEntities = $this->entity::whereNot('id', $entity->id)
// also except the ones we already found
->whereNotIn('id', $related->modelKeys())
// and only get those which also have current relationship (e.g. tags)
->whereHas($relation, function (Builder $query) use($relationIds, $relation) {
$query->whereIn($relation . '.id', $relationIds);
})
->get()
->sortKeysDesc()
->take(10);
$related = $related->concat($relationEntities->except($related->modelKeys()));
}
}
}
}
这样做效果很好.例如,我可以加载一篇博客文章,并通过调用以下命令来显示具有一些相同标签的相关博客文章:
$post = Post::find(1);
$related = $this->getRelated($post, [], ['tags']);
现在,我还需要尊重除标记之外的其他类型,因此$relations
是一个数组:"向我展示具有任何这些标记、类别等共同之处的相关博客文章."
我的第一个问题是在子查询中.I had to hardcode the id in the relation table:
$query->whereIn('id', $relationIds);
had to be
$query->whereIn($relation . '.id', $relationIds);
所以现在我的问题是,$relation . '.id'
并不是适用于所有情况.关系fooBars()
期望foo_bar.id
,但看起来是fooBar.id
.
没有自动的方法让Laravel根据我当前查询的关系自动知道列名吗?如果不是,我必须为我要查询的每个关系定义关系表的列名(Id).我是不是漏掉了什么?
或者我可以以某种方式加载关系并查看数据库方案?这里的最佳实践是什么?
一种方法是使用蛇形shell ($query->whereIn(Str::of($relation)->snake() . '.id', $relationIds);
),但这感觉既模糊又肮脏.
所有这些都是多态的MoryToMany/MorphedByMany.
编辑:为了更好地理解,我添加了一个我的模型看起来像什么的例子.它们都使用默认命名约定.同样,我想获得相关的帖子,要么有一些共同的标签,要么有共同的FooBars.
<?php
class Post extends Entity
{
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
public function fooBars()
{
return $this->morphToMany(FooBar::class, 'foo_barable');
}
}
class Tag extends Entity
{
public function posts()
{
return $this->morphedByMany(Post::class, 'taggable');
}
}
class FooBar extends Entity
{
public function posts()
{
return $this->morphedByMany(Post::class, 'foo_barable');
}
}
感谢@KGG的回答,我想出了以下几点:
<?php
if (count($relations)) {
foreach ($relations as $relation) {
if ($entity->{$relation}()->exists()) {
$relationIds = $entity->{$relation}()->get()->modelKeys();
$relatedClass = get_class($entity->$relation()->getRelated());
$relationPrimaryKey = ($instance = new $relatedClass)->getQualifiedKeyName();
$relationEntities = $this->entity::whereNot('id', $entity->id)
->whereNotIn('id', $related->modelKeys())
->whereHas($relation, function (Builder $query) use($relationIds, $relationPrimaryKey) {
$query->whereIn($relationPrimaryKey, $relationIds);
})
->get()
->sortKeysDesc()
->take(10)
;
$related = $related->concat($relationEntities->except($related->modelKeys()));
}
}
}