簡體   English   中英

Laravel 嵌套關系渴望加載尊重祖先 ID

[英]Laravel nested relations eager load respect ancestor id

假設我有一個名為Research的 model。 每個研究都屬於多個Location模型。 並且每個Location model BelongsToMany Contact模型。 但是,每個Contact也與Research相關。

class Research extends Model {
   protected $table = 'researches';

   public function locations()
   {
      return BelongsToMany( Location::class, 'research_locations_list', 'research_id', 'location_id' );
   }
}

class Location extends Model {
   protected $table = 'locations';

   public function researches()
   {
      return BelongsToMany( Research::class, 'research_locations_list', 'research_id', 'location_id' );
   }

   public function contacts()
   {
      return BelongsToMany( Contact::class, 'location_contacts_list', 'location_id', 'contact_id' );
   }
}

class Contact extends Model {
   protected $table = 'contacts';

   public function locations()
   {
      return BelongsToMany( Location::class, 'location_contacts_list', 'location_id', 'contact_id' );
   }
}

researches表:

+----+------------+
| id |  research  |
+----+------------+
|  1 | Research 1 |
|  2 | Research 2 |
+----+------------+

locations表:

+----+---------------+
| id |   location    |
+----+---------------+
|  1 | United States |
|  2 | Great Britain |
|  3 | Germany       |
+----+---------------+

contacts表:

+----+---------+
| id | contact |
+----+---------+
|  1 | Jack    |
|  2 | John    |
|  3 | Hanz    |
+----+---------+

research_locations_list表:

+----+-------------+-------------+
| id | research_id | location_id |
+----+-------------+-------------+
|  1 |           1 |           1 |
|  2 |           1 |           2 |
|  3 |           2 |           2 |
|  4 |           2 |           3 |
+----+-------------+-------------+

所以研究 1 在美國和英國進行,研究 2 在英國和德國進行

location_contacts_list表:

+----+-------------+------------+-------------+
| id | location_id | contact_id | research_id |
+----+-------------+------------+-------------+
|  1 |           1 |          1 |           1 |
|  2 |           1 |          2 |           1 |
|  3 |           2 |          1 |           2 |
|  4 |           3 |          3 |           2 |
+----+-------------+------------+-------------+

研究 1 應該有 Jack 和 John 在美國的聯系人,在其他地方沒有聯系人;

研究 2 應該有英國的 John 和德國的 Hanz 聯系人;

現在,通過延遲加載我可以實現:

$researches = Research::all();

foreach( $researches as $research )
{
    foreach( $research->locations as $location )
    {
        $contacts = $location->contacts()->wherePivot( 'research_id', $research->id )->get();
        // Will return John and Jack in United States for Research 1 and John in Great Britain and Hanz in Germany for Research 2
    }
}

現在,問題是:如何通過預加載來實現這一點?

$researches = Research::with( 'locations.contacts' )->all();

foreach( $researches as $research )
{
    foreach( $research->locations as $location )
    {
        $contacts = $location->contacts;
        // Will return John and Jack in United States, John in Great Britain ( which is not supposed to happen ) for Research 1 and John in Great Britain and Hanz in Germany for Research 2
    }
}

也許我可以以某種方式指示聯系人尊重祖先身份? 喜歡:

$research = Research::with( 'locations.contacts' )->where( 'researches.id = location_contacts_list.research_id' )->all();

更新

我最接近解決這個問題的方法是像這樣修改Location model:

class Location extends Model {
   protected $table = 'locations';

   public function researches()
   {
      return BelongsToMany( Research::class, 'research_locations_list', 'research_id', 'location_id' );
   }

   public function contacts()
   {
      return BelongsToMany( Contact::class, 'location_contacts_list', 'location_id', 'contact_id' );
   }

   // Modify contacts attribute getter
   public function getContactsAttribute()
   {
      $contacts = $this->contacts();
      
      if( !empty( $this->pivot->research_id ) )
      {
         $contacts = $contacts->wherePivot( 'research_id', $this->pivot->research_id );
      }
      
      return $contacts->get();
   }
}

但是看起來有點臟。。。

在您的解決方案中,您會遇到 N+1 查詢問題。 我可以建議以下解決方案:

class Research extends Model
{
    protected $table = 'researches';

    public function locations(): BelongsToMany
    {
        return $this->belongsToMany(Location::class, 'research_locations_list');
    }

    public function contacts(): BelongsToMany
    {
        return $this->belongsToMany(Contact::class, 'location_contacts_list')
            ->withPivot('location_id');
    }

    public function contactsByLocationAttribute(int $locationId): Collection
    {
        return $this->contacts
            ->filter(static function ($contact) use ($locationId) {
                return $contact->pivot->location_id === $locationId;
            });
    }
}


$researches = Research::with(['locations', 'contacts'])->get();
foreach ($researches as $research) {
    foreach ($research->locations as $location) {
        $contacts = $research->contactsByLocation($location->id);
    }
}

這里總是只有 3 個對數據庫的查詢。 並且只會加載必要的模型

如果我做對了,您想在with語句中添加一些條件。 如果你想使用 eloquent 語法,你可以這樣做:

$research = Research::with(['YOUR RELATION' => function ($query) {
    $query->where('YOUR COLUMN', 'EQUALS TO SOMETHING');
}])->get();

請記住,由於內部with您使用嵌套關系,例如locations.contacts ,查詢中的where function 將僅過濾最后一個 model (在本例中為contacts )。 如果你想根據某些條件過濾位置和聯系人,你必須寫類似這樣的東西(只是一個例子):

$research = Research::with(['locations' => function ($query) {
    $query->where('id', 1)->with(['contacts' => function ($query) {
        $query->where('name', 'Tim');
    }]);
})->get();

但是,為了做到這一點,您還需要與 pivot 表建立關系(如果您還想在條件內使用它)。 否則,您必須使用不同的語法,使用連接。 從文檔中查看此頁面以獲取查詢構建器https://laravel.com/docs/9.x/queries#main-content

也許,這個https://laravel.com/docs/9.x/eloquent-relationships#has-many-through可以幫助你。 你應該試試這個

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM