2014-04-14 105 views
0

我有急切的加載/ n + 1問題,研究和閱讀和觀看關於這個問題的教程整天排除故障,但還沒有解決它。我已經建立了模型的關係,但是當我用輔助函數傳遞數據時,我得到了這個n + 1問題。 我想從url site.com/Artist/songs獲取藝術家名稱,並獲取所有歌曲並顯示這樣的網址。Laravel 4 Eager Loading n + 1期

site.com/$Artist/songs/$id

我的藝術家/ index.blade.php視圖看起來像這樣http://i61.tinypic.com/2nqzatk.jpg 我不知道我在這裏失蹤。

在此先感謝!

我的表

歌曲 ID,標題,正文,蛞蝓,命中,artist_id,created_at,的updated_at

藝術家 ID,姓名,身體,created_at,的updated_at

routes.php文件

Event::listen('illuminate.query', function($query) 
{ 
    var_dump($query); 
}); 
... 
Route::get('{artist}/songs', '[email protected]'); 
Route::get('{artist}/songs/{id}', ['as' => 'artist.songs.show', 'uses' => '[email protected]']); 

型號:Song.php

class Song extends Eloquent { 
    protected $guarded = ['id']; 

    /** 
    * Setting up relationship for the artist model for easier usage 
    * 
    */ 
    public function artist() 
    { 
     return $this->belongsTo('Artist'); 
    } 

    // Override find method 
    public static function find($id, $name = null) 
    { 
     $song = static::with('artist')->find($id); 

     // If the song is found 
     if ($song) 
     { 
      // If the song doesn't belong to that artist, throw an exception which will redirect to home, defined in global.php 
      if ($name and $song->artist->name !== $name) 
      { 
       throw new Illuminate\Database\Eloquent\ModelNotFoundException; 
      } 

      return $song; 
     } 
     // If the song is not found, throw an exception which will redirect to home, defined in global.php 
     else 
     { 
      throw new Illuminate\Database\Eloquent\ModelNotFoundException; 
     } 

    } 

    // Get songs from artist 
    public static function byArtist($name) 
    { 
     return Artist::byName($name)->songs; 
    } 

} 

模型Artist.php

class Artist extends Eloquent { 
    protected $fillable = []; 

    /** 
    * Setting up relationship with the song model for easier usage 
    * $artist->songs; 
    */ 
    public function songs() 
    { 
     return $this->hasMany('Song'); 
    } 

    // Get artist by name 
    public static function byName($name) 
    { 
     return static::whereName($name)->first(); 
    } 

} 

控制器:ArtistsController.php

class ArtistsController extends BaseController { 

    // Set default layout for this controller 
    protected $layout = 'layouts.master'; 

    /** 
    * Display a listing of the resource. 
    * GET /artists 
    * 
    * @return Response 
    */ 
    public function index($name) 
    { 
     $this->data['songs'] = Song::byArtist($name); 

     $this->layout->content = View::make('artists.index', $this->data); 
    } 

helpers.php

function link_to_artist_song(Song $song) 
{ 
    return link_to_route('artist.songs.show', $song->title, [$song->artist->name, $song->id]); 
} 

爲藝術家 藝術家/ index.blade索引圖。 php http://i61.tinypic.com/2nqzatk.jpg

@extends('layouts.master') 

@section('content') 

    @if(isset($songs)) 
     <h1>All Songs</h1> 

     <ul class="list-group"> 
     @foreach($songs as $song) 
      <li class="list-group-item">{{ link_to_artist_song($song) }}</li> 
     @endforeach 
     </ul> 
    @endif 
@stop 

回答

2

你從不急於加載任何東西,這就是爲什麼你可能面臨n + 1問題。 如果我明白你的意思,這裏的代碼有點困難,你想要給所有藝術家的歌曲加上$ name的歌曲,對吧?

因此,這裏是你需要做它的工作一切:

// controller 
public function index($name) 
{ 
    // with('songs') is eager loading related songs for you 
    $this->data['artist'] = Artist::with('songs')->whereName($name)->first(); 

    $this->layout->content = View::make('artists.index', $this->data); 
} 

// the problem of your queries is in the helper: 
function link_to_artist_song(Song $song) 
{ 
    return link_to_route('artist.songs.show', $song->title, [ 
     $song->artist->name, // this is calling db query for each song to retrieve its artist (despite it is always the same) 
     $song->id]); 
} 

// so instead use this in your view 
@foreach($artist->songs as $song) 
    <li class="list-group-item"> 
    {{ link_to_route('artist.songs.show', $song->title, [$artist->name, $song->id]) }} 
    </li> 
@endforeach 
+0

是的,這是正確的我想從一個給定的藝術家的所有歌曲與$名稱從URL。 我用您的代碼行取代了 '$ this-> data ['songs'] = Artist :: with('songs') - > where'($ name) - > first()這個錯誤與我的幫手。 「傳遞給link_to_artist_song()的參數1必須是Song的一個實例,布爾給定,在...中調用」 因此,這些方法根本無法用於提取歌曲? 歌模型 \t'公共靜態函數byArtist($名) \t { \t \t回報藝術家::綽號($名) - >歌曲; \t}' 藝術家模型 \t'公共靜態函數綽號($名) \t { \t \t回靜態:: whereName($名) - >第一(); \t}' – Vartox

+0

我希望能夠抓住藝術家的名字並顯示這樣的鏈接 'domain.com/ArtistName/songs/1' 這就是爲什麼我想要加載的渴望。 – Vartox

+0

您想要列出名稱爲$ name的給定藝術家的所有歌曲,所以您只需要一位藝術家和所有相關歌曲。除非你想要別的東西。檢查我的編輯在一秒鐘內 –

0

當你有大量的歌手與許多藝術家的歌曲存在n + 1問題。它必須獲得所有歌曲(1個查詢),然後爲每首歌曲獲取藝術家(n個查詢)。

在這種情況下,您已經知道該歌曲,所以這是一個查詢,那麼您需要該歌曲的所有藝術家,這只是一個額外的查詢。

只有當你試圖找到某種流派的歌曲時,n + 1問題纔會起作用,例如,它可能會返回很多歌曲。然後,對於每首歌曲,您必須進行額外的查詢以獲取該歌曲的藝術家。這將是急切加載最有用的地方。

$song = Song::find($id); 
$artist = $song->artist; 
+0

你的答案的第一部分是真實的,第二部分是不是很清楚。 – hannesvdvreken

+0

我修改了一下,希望它變得更清晰。 – user3158900

0

你搶一首歌曲,並做$song->artist它會做一個查詢每次。您可以使用Query scopes

class Song extends Eloquent { 

    public function scopeByArtist($query, $name) { 
     return Artist::whereName($name)->first()->songs(); //->with('artist'); 
    } 
} 

像這個藝術家已經被加載。

查詢有:

$songs = Song::byArtist($name)->get(); 
+0

您在作用域方法中忘記了第一個參數,它不會加載藝術家 –