1. 程式人生 > >laravel之多對多的關係模型

laravel之多對多的關係模型

今天談的是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 中的多對多關係,它不需要為這個中間表單獨的建立一個模型。希望本文對你有所幫助。

譯自: laraveldaily.com