Laravel合并关联关系

28

有没有办法在 Laravel 中合并两个关系?

目前是这样设置的,但我能否返回两个合并的关系?

  public function CompetitionsHome() {
    return $this->HasMany( 'Competition', 'home_team_id' );
  }
  public function CompetitionsGuest() {
    return $this->HasMany( 'Competition', 'guest_team_id' );
  }
  public function Competitions() {
    // return both CompetitionsHome & CompetitionsGuest
  }
6个回答

36
尝试使用获取器方法来获取属性,该属性返回从关系中返回的合并集合。
public function getCompetitionsAttribute($value)
{
    // There two calls return collections
    // as defined in relations.
    $competitionsHome = $this->competitionsHome;
    $competitionsGuest = $this->competitionsGuest;

    // Merge collections and return single collection.
    return $competitionsHome->merge($competitionsGuest);
}

或者,在返回集合之前,您可以调用其他方法以获取不同的结果集。

public function getCompetitionsAttribute($value)
{
    // There two calls return collections
    // as defined in relations.
    // `latest()` method is shorthand for `orderBy('created_at', 'desc')`
    // method call.
    $competitionsHome = $this->competitionsHome()->latest()->get();
    $competitionsGuest = $this->competitionsGuest()->latest()->get();

    // Merge collections and return single collection.
    return $competitionsHome->merge($competitionsGuest);
}

你可以在这里阅读更多关于可用的集合方法的内容:http://laravel.com/api/class-Illuminate.Support.Collection.html#methods - Andreyco
9
尽管这样可以运行,但需要注意的是,它并不返回关系实例。因此,您不能像这样执行操作 App\User::find(1)->with('competitions'),但您可以执行 App\User::find(1)->with('competitions_guest')(除非有语法错误)。 - daraul
2
@daraul,在查询构建器中,有没有可能合并关系并在其中使用它们? - Adam
这是否还需要在模型类中添加一个protected $appends = ['competitions'];属性呢? - Potherca

9

如果您希望使用merge()方法来合并两个集合(关系),它将覆盖具有相同索引键的元素,因此您将丢失从一个关系中获得的某些数据。

相反,您应该选择push()方法,它通过将一个集合推送到另一个集合的末尾来创建新的数组键。

以下是示例:

public function getCompetitionsAttribute($value) {
    $competitionsHome = $this->competitionsHome;
    $competitionsGuest = $this->competitionsGuest;

    // PUSH ONE TO OTHER!
    return $competitionsHome->push($competitionsGuest);
}

这是正确的答案,以防止主/索引键被覆盖。 - Mike Barwick
我不同意,合并也可以防止重复实例。 - JoeriShoeby
1
这在很大程度上取决于这两个关系是否返回相同模型的实例还是不同模型的实例。 - AI0867

5
我创建了一个Laravel包,允许您创建一个合并多个hasMany关系的关系,支持贪婪加载(->with(...)),而且您无需创建视图。
GitHub上的包:https://github.com/korridor/laravel-has-many-merged 您的问题可以通过这样的方式解决:
use Korridor\LaravelHasManyMerged\HasManyMergedRelation;

class Example extends Model
{

  use HasManyMergedRelation;

  // ...
  
  public function Competitions() {
    return $this->hasManyMerged('Competition', ['home_team_id', 'guest_team_id']);
  }

}

5

我为使用视图合并关系创建了一个包裹:

https://github.com/staudenmeir/laravel-merged-relations

首先,在迁移中创建合并视图:

use Staudenmeir\LaravelMergedRelations\Facades\Schema;

Schema::createMergeView(
    'competitions',
    [(new YourModel)->CompetitionsHome(), (new YourModel)->CompetitionsGuest()]
);

然后定义关系:

class YourModel extends Model
{
    use \Staudenmeir\LaravelMergedRelations\Eloquent\HasMergedRelationships;

    public function competitions()
    {
        return $this->mergedRelationWithModel(Competition::class, 'competitions');
    }
}

像使用其他关系一样使用它:

$model->competitions;

$model->competitions()->paginate();

YourModel::with('competitions')->get();

2
在 Laravel 8 中,您可以这样做:

  public function Competitions() {
    $home= $this->CompetitionsHome();
    $guest => $this->CompetitionsGuest();

    $all = $home->union($guest)
                ->orderBy('created_at')
                ->get();
  }


这个工作了,但是当我使用 where 语句时似乎只影响到联结的一侧而不是两侧。 - basel juma

0

如果有人像我一样因为谷歌而来到这里:既然merge()和push()都不允许急切加载(以及其他很好的关系特性),讨论并没有在这个帖子中结束,而是在这里继续进行:Laravel Eloquent自引用表上的内部连接

我在这里提出了一个解决方案here,欢迎对此问题提出更多想法和贡献。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接