今天談的是Laravel中一個非要有用,但一開始可能有點難理解的功能。Pivot 表是兩個“主表”之間關系的中間表。
Pivot 表實例
在官方文檔中,他們用 用戶-角色(User-Role) 關系來做例子,用戶可能會屬於多個角色,反之亦然。為了使大家理解的更清楚,我們這里舉另一個例子:商店(Shops )與商品( Products )。
我們假設一個商家有多個商店遍布全國各地,並且有各種各樣的商品,他們需要存儲哪個商品是由哪個商店賣出的信息。這是一個多對多關系非常好的例子:一個商品可以屬於多個商店,而一個商店又會有很多的商品。
所以這里就有一個潛在的數據庫結構:
shops
– id
– name
products
– id
– name
product_shop
– product_id
– shop_id
列表中最后一個表 product_shop 就是標題中被稱作“pivot”的表。這里有幾點需要注意的:
Pivot表的名字應該包含兩個表名的單數形式,由下划線分開,並按字母順序排列,所以我們最終得到的是 product_shop ,而不是 shop_product 。 你可以使用 artisan make:migration 命令來創建 pivot 表,也可以通過 Jeffrey Way 的擴展包 Laravel 5 Generators Extended 中的 artisan make:migration:pivot 命令。 Pivot 表字段 :默認情況下,只需包含兩個字段–分別對應兩個表的外鍵,我們這里是 product_id 和 shop_id 。如果需要的話,你也可以添加跟多字段,后面我們會進行討論。 多對多關系模型:BelongsToMany
我們已經有了數據庫表和遷移,現在讓我們為它們創建模型。這里主要是分配一個多對多的關系,它可以在任何一個主表模型中定義。
選擇1:app/Shop.php class Shop extends Model
{
/**
* The products that belong to the shop.
*/
public function products()
{
return $this->belongsToMany('App\Products');
}
} 選擇2:app/Product.php class Product extends Model
{
/**
* The shops that belong to the product.
*/
public function shops()
{
return $this->belongsToMany('App\Shop');
}
}
實際上,你也可以都做,主要是看你在代碼中如何使用這個關系–你需要 $shop->products 還是 $product->shops ,或者兩者都需要。
現在的這個聲明,Laravel會假設 pivot 表命名遵守上面提到的規則,也就是 product_shop 。假如不是的話(比如是復數形式),你可以提供第二個參數:
public function products()
{
return $this->belongsToMany('App\Products', 'products_shops');
}
此外,你還可以指定 pivot 表中字段的名稱,如果它們不用於 product_id 和 shop_id 。只需要添加兩個參數:第一個是當前模型的字段,另一個是需要關聯的模型的字段:
public function products()
{
return $this->belongsToMany('App\Products', 'products_shops',
'shops_id', 'products_id');
}
這里的一個主要好處是,你不需要創建一個單獨的ProductShop 模型。
多對多關系管理:attach-detach-sync
現在已經准備好了數據表和模型,那么,現在的問題是我們如果只用兩個模型而不用第三個中間模型來保存數據呢?看看下面幾點。
例如,我們需要給當前 商店(shop>) 實例添加一個 商品(product) ,我們可以使用關系函數中的 attach() 方法:
$shop = Shop::find($shop_id);
$shop->products()->attach($product_id);
結果就是我們會給 product_shop 表中添加新的一列,值分別為 $product_id 和 $shop_id 的值。
相似的,我們也可以解除( detach )一個關系,假設我們把一個商品從商店中移除:
$shop->products()->detach($product_id);
或者更進一步,刪除一個特定商店中所有的商品,只需要調用該方法,不傳遞參數:
$shop->products()->detach();
你可以用過傳遞數組參數來附加和分離關系:
$shop->products()->attach([123, 456, 789]);
$shop->products()->detach([321, 654, 987]);
另一個非要有用的函數是更新整個 pivot 表。一個常見的例子是:管理面板中,一個商品下面有多個商店的多選框,在執行更新操作的時候,需要檢查所有的商店,刪除新的多選數組中不存在的,並添加或更新有的。一件頭疼的事情。
但現在不是了,有一個叫做 sync() 的方法,它接受一個數組參數,然后會自動執行完所有的同步工作。
$product->shops()->sync([1, 2, 3]);
結果就是,不管之前 product_shop 表中的值是怎么樣的,調用該方法之后,只會有 shop_id 是1、2、3的三行。
給Pivot 表添加額外列
我上面提到了,你很可能想給 pivot 表添加額外的字段。在我們的例子中,保存特定商店中商品的數量和時間戳是有必要的。我們可以跟通常一樣使用 migration 來添加字段,但我們還需要對模型進行一些修改:
public function products()
{
return $this->belongsToMany('App\Products')
->withPivot('products_amount', 'price')
->withTimestamps();
}
你可以看到,我們能通過一個簡單的 withTimestamps() 方法來添加時間戳,以及通過給 withPivot() 方法添加參數來添加額外的字段。
現在我們就可以在代碼的循環中使用這些值了,有一個叫做 pivot 的屬性:
foreach ($shop->products as $product)
{
echo $product->pivot->price;
}
基本上, ->pivot 表示 pivot 中間表,並以此來訪問我們提到的字段,如 created_at 。
現在,如何在調用 attach() 的方法是添加這些值呢?該方法接受另一個數組參數,你可以把所有需要的附加字段添加到其中。
$shop->products()->attach(1, ['products_amount' => 100, 'price' => 49.99]); 總結
因此,pivot 表可以非常方便的處理Eloquent 中的多對多關系,它不需要為這個中間表單獨的創建一個模型。希望本文對你有所幫助。