從形式上,laravel里每一個model數據(record),在取出的時候都是用的PHP的stdClass來包裹或封裝,一個model數據就是一個stdClass,stdClass是一個沒有屬性和方法的空類,一般用來創建一個匿名對象或將非對象類型轉換成對象,這樣我們就可以很放便的操作它,動態的添加、刪除屬性:

//實例化一個空對象 $obj = new stdClass(); //給對象動態添加屬性或者方法 $obj->name = 'pilishen.com'; $obj->description = '做全球最好的IT實戰教程'; 

那么,當有多條數據取出來的時候,也即有多個stdClass的時候,我們怎么來展現或包裹呢?就是Collection,集合的意思。

file

所以,進一步說,在model數據調取中,laravel first()取到的就是一個stdClass,而get()取到的是多個stdclass,無非是以Collection的形式包裹了起來,下面舉個類子列出所有省份:

file

file

可以看到,因為是取出多條數據,所以返回的是一個Collection{}對象,里面包含一個items[]數組(序列),在這個序列里,裝的就是每一個stdClass{}對象,也即具體的每一個Province數據。

我們再來打印一下first()方法獲取的結果

file

我們可以看到first()方法得到的直接是一個stdClass對象,因為它外層沒有array包裹了,所以就可以直接在其上面獲取各種屬性了,比如說可以直接來調用關系(relationship)了,假設我們創建一個 Province hasMany City 的例子:

file

這樣我們就可以使用 Province::fisrt()->cities()來獲取第一個省所屬的所有城市,那如果需要獲取 id為n 的省的所有城市的話我們可以使用 Province::find(n)->cities(), 這里的find()方法得到的也是一個具體到ID了的stdClass 對象。

這里注意的是,關系(eloquent relationship)的調用只能作用於某個具體的Model對象,也即你只有具體到某個Model,某個ID,或者說某個stdclass對象了,才能進一步去調用其所屬的關系,而不能直接去一堆Model數據上調用關系,或者說不能直接在一個大的collection對象后面直接取關系, 也即這樣Province::get()->cities()是不對的,這相當於Collection{}->cities(),而這個Collection{}本身並沒有cities()這個關系屬性,雖然它里面的每一個Province model item擁有這個關系屬性,但那就隔着一層了。

好吧,不能在get()后面直接調取關系,或者說不能籠統地在一堆數據上直接調取關系,那么,調取關系的正確姿勢有哪些?

  1. 你可以在first() last() find() firstOrFail() findOrFail()這些具體到ID的方法后面直接取關系,比如Province::fisrt()->cities()
  2. 如果你已經get()了,也即已經有一堆數據了,那么可以遍歷以后再取每一個的關系,比如:
$pros = Province::get();   //或者all() foreach($pros as $pro){ $pro->cities(); } 
  1. 當然,如果你是要在Blade視圖里使用遍歷后的關系數據,因為每有一個數據,就要取一次關系,就要執行一次查詢,所以你foreach里有n個數據,就查詢n遍,就有n個query,再加上你之前get()所有數據的那1個query,所以你頁面上總共有n+1個query,當你數據很多的時候,就會導致頁面特別慢,所以你一旦意識到要在視圖里取關系屬性,就要在Controller里提前用with方法來預加載所有的關系,例如這樣:
$pros = Province::with('cities')->get(); //或者all() foreach($pros as $pro){ $pro->cities(); } 

這樣的話,一次性地取得了所有省份以及每個省份下面的城市關系,背后只是執行了2次query,你在視圖里再去遍歷的時候,就不用再執行數據查詢了,性能就會有較大提升。

很多小白抱怨laravel視圖加載慢,不知道他們有沒有查看一下自己頁面的query執行情況呢?一個視圖查詢太多的query,換誰都慢~