Kiwi
Kiwi

Reputation: 2773

Laravel merge relationships

Is there a way to merge 2 relationships in laravel?

this is the way it's setup now, but Is there a way I could return both merged?

  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
  }

Upvotes: 31

Views: 29434

Answers (5)

Arsii Rasheed
Arsii Rasheed

Reputation: 351

In Laravel 8 you can do it like this:


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

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

Upvotes: 2

korridor
korridor

Reputation: 313

I created a package for Laravel that allows you to create a relation that merges multiple hasMany relations, supports eager loading (->with(...)) and you do not need to create a view.

Package on GitHub: https://github.com/korridor/laravel-has-many-merged

Your question would be solved with something like this:

use Korridor\LaravelHasManyMerged\HasManyMergedRelation;

class Example extends Model
{

  use HasManyMergedRelation;

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

}

Upvotes: 6

Jonas Staudenmeir
Jonas Staudenmeir

Reputation: 25936

I've created a package for merging relationships using views:
https://github.com/staudenmeir/laravel-merged-relations

First, create the merge view in a migration:

use Staudenmeir\LaravelMergedRelations\Facades\Schema;

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

Then define the relationship:

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

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

Use it like any other relationship:

$model->competitions;

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

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

Upvotes: 5

spetsnaz
spetsnaz

Reputation: 1291

In case you prefer merge() method to combine two collections (relationships), it will override elements with the same index keys so you will loose some of your data gained from one relationship.

You should choose push() method instead, which creates new array keys by pushing one collection to the end of other collection

Here is a sample :

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

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

Upvotes: 9

Andreyco
Andreyco

Reputation: 22872

Try out getter method for property which returns merged collections returned from relations.

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);
}

Or you can call additional methods before collection is returned to get different result sets.

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);
}

Upvotes: 37

Related Questions