2017-12-28 54 views
3

私は3つの関連モデルから情報を引き出す必要がある方法があります。私は動作する解決策がありますが、N + 1のクエリの問題に悩まされています(また、正しくロードすることができるかどうかを確認する方法についてのソリューションも探しています)。正しくロードしていますか? (Eloquent)

3つのモデルは、チャレンジ、エントランス、ユーザーです。

チャレンジモデルが含まれています

/** 
* Retrieves the Entrants object associated to the Challenge 
* @return \Illuminate\Database\Eloquent\Relations\HasMany 
*/ 
public function entrants() 
{ 
    return $this->hasMany('App\Entrant'); 
} 

応募モデルは含まれています

 /** 
    * Retrieves the Challenge object associated to the Entrant 
    * @return \Illuminate\Database\Eloquent\Relations\BelongsTo 
    */ 
    public function challenge() 
    { 
     return $this->belongsTo('App\Challenge', 'challenge_id'); 
    } 

    /** 
    * Retrieves the User object associated to the Entrant 
    * @return \Illuminate\Database\Eloquent\Relations\BelongsTo 
    */ 
    public function user() 
    { 
     return $this->belongsTo('App\User', 'user_id'); 
    } 

とユーザーモデルが含まれています

/** 
* Retrieves the Entrants object associated to the User 
* @return \Illuminate\Database\Eloquent\Relations\HasMany 
*/ 
public function entrants() 
{ 
    return $this->hasMany('App\Entrant'); 
} 

私は積極的なロードを使用しようとしています方法は次のようになります:

/** 
* Returns an array of currently running challenges 
* with associated entrants and associated users 
* @return array 
*/ 
public function liveChallenges() 
{ 
    $currentDate = Carbon::now(); 
    $challenges = Challenge::where('end_date', '>', $currentDate) 
     ->with('entrants.user') 
     ->where('start_date', '<', $currentDate) 
     ->where('active', '1') 
     ->get(); 

    $challengesObject = []; 
    foreach ($challenges as $challenge) { 
     $entrants = $challenge->entrants->load('user')->sortByDesc('current_total_amount')->all(); 
     $entrantsObject = []; 
     foreach ($entrants as $entrant) { 
      $user = $entrant->user; 
      $entrantsObject[] = [ 
       'entrant' => $entrant, 
       'user' => $user 
      ]; 
     } 

     $challengesObject[] = [ 
      'challenge' => $challenge, 
      'entrants' => $entrantsObject 
     ]; 
    } 

    return $challengesObject; 
} 

は私が感じる私は、ドキュメントが推奨するものに続くように:https://laravel.com/docs/5.5/eloquent-relationships#eager-loading

をしかし、私は反対しN + 1つのクエリを作っていないよ確認するためにチェックする方法がわからないように、コードにだけ2.任意のヒントや提案へ熱心な読み込みが正しく機能していることを確認する方法とともに、歓迎します。

+0

もしあなたが熱心な読み込みの動作を知りたいのであれば、( 'entrants.user')で ' - > 'を使った後に' $ challeng'変数をダンプし、メソッドを使わないでください。結果を調べる。'dd($ challenge)' –

答えて

2

一度参入とユーザーの両方をロードしますあなたは->get()になります。 ->load('user')を実行すると、別のクエリを実行してユーザーを取得します。 ->with('entrants.user')を実行したときに既にそれを取り込んだので、これを行う必要はありません。

->load('user')の代わりに->loadMissing('user')を使用すると、冗長な呼び出しを防止する必要があります。

しかし、Collection methodsあなたのレバレッジ場合は、ちょうどあなたが$challengesを宣言先頭に1つのクエリを実行していると離れて取得することができます。

foreach ($challenges as $challenge) { 
    // at this point, $challenge->entrants is a Collection because you already eager-loaded it 
    $entrants = $challenge->entrants->sortByDesc('current_total_amount'); 
    // etc... 

$challenge->entrantsがすでに参入が移入されているため、あなたは->load('user')を使用する必要はありません関連ユーザーコレクションメソッド->sortByDesc()を利用するだけで、PHPでリストをソートすることができます。

また、->all()を実行する必要がないため、モデルの配列に変換されます(モデルのコレクションとしてそのまま残しておくことができます)。

+0

そのloadステートメントは、私のクエリを大幅に減らしましたが、loadMissingメソッドでエラーが発生しました。私は5.5を使用しています。 –

+0

私はloadMissingを試したことがないことを正直に思います。しかし、私は負荷の横のドキュメントでそれを見ました。私はその価値を理解するためにさらに実験し、それをどのように使用する必要があります – JonTroncoso

3

Laravel Debugbarを使用して、各リクエストに対してLaravelアプリケーションが作成しているクエリを確認します。

あなたの雄弁問合せは、わずか3生のSQLクエリを生成する必要がありますし、この行がN追加のクエリを生成しないことを確認する必要があります:あなたが->with('entrants.user')を行う際

$entrants = $challenge->entrants->load('user')->sortByDesc('current_total_amount')->all() 
+1

うわー、デバッガは悪いですし、余分なロードステートメントがたくさんのクエリを追加してくれて、ありがとうございました(私はデバッガを取り除くために必要な多くの余分なクエリを見つけました) –

関連する問題