简体   繁体   中英

Laravel 5.2 Eloquent - Model through Many-To-Many Relationship

I have 3 models: Environments, Sites and Incidents. Each have their respective tables.

My model relationships are defined as follows:

Environments have many Sites ( environment_id in sites table)

public function sites()
{ 
   return $this->hasMany('App\Site');
}

Sites belong to an Environment ( environment_id in sites table) and belong to many Incidents (incident_site relationship table with incident_id and site_id )

public function environment()
{
    return $this->belongsTo('App\Environment');
}

public function incidents()
{
    return $this->belongsToMany('App\Incident');
}

Incidents belong to many Sites (incident_site relationship table with incident_id and site_id )

public function sites()
{
    return $this->belongsToMany('App\Site');
}

Problem: I am trying to retrieve a collection of all Site Incidents through the Environment model like this:

$environment->incidents()->count();

The only way I've been able to get it to work in a controller so far is like this:

    $environment->load(['sites.incidents' => function ($q) use ( &$incidents ) {
       $incidents = $q->orderBy('id','desc')->get();
    }]);

But it's not ideal to work with in other areas of the App.

Question: How do I go about making the above relationship work through a method in the Environment model? Is there an easier way?

There isn't any provision for using hasManyThrough() in many-to-many relation. But you can achieve this by using either DB::raw() or you can add following function to your BaseModel as given in this forum .

public function manyThroughMany($related, $through, $firstKey, $secondKey, $pivotKey)
   {
       $model = new $related;
       $table = $model->getTable();
       $throughModel = new $through;
       $pivot = $throughModel->getTable();

       return $model
           ->join($pivot, $pivot . '.' . $pivotKey, '=', $table . '.' . $secondKey)
           ->select($table . '.*')
           ->where($pivot . '.' . $firstKey, '=', $this->id);
   }

Update: Use

first you would need to create a Model for incident_site

class incident_site extends Model{
    public $table = 'incident_site';
    //your other code
}

In your Enviorment model add the Incidents() method:

public function Incidents()
    {
        return $this->manyThroughMany('App\Incident', 'App\incident_site', 'site_id', 'id', 'incident_id');
}

Update:

Have modified the function according to your needs.
Change your function to following:

public function manyThroughMany($related, $through ,$middle , $firstKey, $secondKey, $pivotKey)
    {
        $model = new $related;
        $table = $model->getTable();
        $throughModel = new $through;
        $pivot = $throughModel->getTable();
        $middleModel = new $middle;
        $middleModelIds = $middleModel->where($this->getForeignKey(),$this->getKey())->get()->lists('id')->toArray();
        //$middleModelIds = $this->with($middleModel)->where()->get()->lists('id')->toArray();
        //$middleModelIds = $this->sites()->get()->lists('id')->toArray();
        return $model
            ->join($pivot, $pivot . '.' . $pivotKey, '=', $table . '.' . $secondKey)
            ->select($table . '.*')
            ->whereIn($pivot . '.' . $firstKey,$middleModelIds);// '=', $this->id);
    }

Use:
Extra argument of middle table needs to passed.

public function Incidents()
    {
        return $this->manyThroughMany('App\Incident', 'App\incident_site','App\Site','site_id', 'id', 'incident_id');
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM