用多对多关联的前提
如果模型 A 多对多关联模型 C,必须存在一张中间表 B 记录着双方的外键,因为就是靠着这张中间表 B 记录着模型 A 与模型 C 的关系。
举例,数据表结构如下
// 商品表
goods
goods_id - integer // 商品主键
goods_name - varchar // 商品名称
// 分类表
category
category_id - integer // 分类主键
name - varchar // 分类名称
// 商品分类关系表
goods_category_rel
id - integer // 主键
goods_id - integer // 商品表主键的外键
category_id - integer // 分类表主键的外键
如果按以前的数据表设计,商品表与分类表不是这样设计的,而是 goods 表有一个 category_id 字段,该字段保存 category 表的 category_id,这样每个商品都对应着一个分类,比如荔枝的分类是水果,但这样的设计缺点也是显而易见的,一个商品只能有一个分类,比如荔枝的分类只能是水果,但如果把数据库设计成上面这样的结构,荔枝的分类就可以有多个,比如除了水果,还能是特价产品,或者是其它分类等等。
多对多关联定义
商品表模型:
<?php
namespace app\api\model;
class Goods extends Base
{
protected $pk = 'goods_id';
protected $hidden = ['create_time', 'update_time'];
// 获取商品的分类
public function category()
{
// Goods 模型多对多关联 Category 模型
return $this->belongsToMany('Category', 'GoodsCategoryRel');
// 完整写法
// return $this->belongsToMany('Category', 'GoodsCategoryRel', 'category_id', 'goods_id');
}
}
belongsToMany
方法的参数如下:
belongsToMany('关联模型', '中间模型', '外键1', '外键2');
- 关联模型(必须):模型名或者模型类名
- 中间模型:默认规则是当前模型名+
_
+关联模型名 (可以指定模型名) - 外键1:关联模型在中间表的外键,默认的外键名规则是关联模型名+
_id
- 外键2:当前模型在中间表的外键,默认规则是当前模型名+
_id
中间表模型
<?php
namespace app\api\model;
class GoodsCategoryRel extends Base
{
protected $hidden = ['create_time', 'update_time'];
}
分类表模型
<?php
namespace app\api\model;
class Category extends Base
{
// 因为不是默认主键所以要重新定义
protected $pk = 'category_id';
protected $hidden = ['create_time', 'update_time'];
}
实例演示
演示1
获取 goods_id 为 10001 的商品详情以及它的分类
Goods 控制器
<?php
namespace app\api\controller;
use app\api\model\Goods as GoodsModel;
class Goods extends Base
{
// 获取 goods_id 等于 10001 的商品详情
public function detail()
{
$goods_id = 10001;
$detail = GoodsModel::getDetail($goods_id);
echo '<pre>';
var_dump($detail);
echo '</pre>';
}
}
Goods 模型
<?php
namespace app\api\model;
class Goods extends Base
{
// 因为不是默认主键名称,所以要重新定义
protected $pk = 'goods_id';
protected $hidden = ['create_time', 'update_time'];
// 获取商品详情
public static function getDetail($goods_id)
{
// 关联预载入查询
// 第二个参数是所用到的关联方法的方法名
return self::get($goods_id, 'category');
}
// 多对多关联获取商品的所有分类
public function category()
{
return $this->belongsToMany('Category', 'GoodsCategoryRel');
// 完整写法
// return $this->belongsToMany('Category', 'GoodsCategoryRel', 'category_id', 'goods_id');
}
}
Goods 控制器 detail 方法输出
object(app\api\model\Goods)#42 (2) {
["data"]=>
array(8) {
["goods_id"]=>
int(10001)
["goods_name"]=>
string(12) "桂味荔枝"
["stock_total"]=>
int(0)
["status"]=>
int(10)
["sort"]=>
int(0)
["is_delete"]=>
int(0)
["create_time"]=>
int(1688696518)
["update_time"]=>
int(1688696518)
}
["relation"]=>
array(1) {
["category"]=>
object(think\model\Collection)#50 (1) {
["items":protected]=>
array(2) {
[0]=>
object(app\api\model\Category)#41 (2) {
["data"]=>
array(8) {
["category_id"]=>
int(6)
["name"]=>
string(12) "新鲜水果"
["parent_id"]=>
int(1)
["image_id"]=>
int(0)
["status"]=>
int(1)
["sort"]=>
int(0)
["create_time"]=>
int(1688454738)
["update_time"]=>
int(1688454738)
}
["relation"]=>
array(1) {
["pivot"]=>
object(think\model\Pivot)#51 (2) {
["data"]=>
array(5) {
["id"]=>
int(1001)
["goods_id"]=>
int(10001)
["category_id"]=>
int(6)
["create_time"]=>
int(1688696518)
["update_time"]=>
int(1688696518)
}
["relation"]=>
array(0) {
}
}
}
}
[1]=>
object(app\api\model\Category)#47 (2) {
["data"]=>
array(8) {
["category_id"]=>
int(10)
["name"]=>
string(12) "特价产品"
["parent_id"]=>
int(1)
["image_id"]=>
int(0)
["status"]=>
int(1)
["sort"]=>
int(0)
["create_time"]=>
int(1688454870)
["update_time"]=>
int(1688454870)
}
["relation"]=>
array(1) {
["pivot"]=>
object(think\model\Pivot)#52 (2) {
["data"]=>
array(5) {
["id"]=>
int(1002)
["goods_id"]=>
int(10001)
["category_id"]=>
int(10)
["create_time"]=>
int(1688795635)
["update_time"]=>
int(1688795635)
}
["relation"]=>
array(0) {
}
}
}
}
}
}
}
}
单条查询返回的是当前模型(商品模型)实例,商品模型实例的 relation 属性(关联数组)保存着关联模型实例,在该关联数组中,关联方法名 category 作为键名,结果集对象作为键值,结果集对象的 items 属性(索引数组)保存着关联查询结果。
所用的 SQL 如下
SELECT * FROM `shop_goods` WHERE `goods_id` = 10001 LIMIT 1
SELECT `shop_category`.*,`pivot`.`id` AS `pivot__id`,`pivot`.`goods_id` AS `pivot__goods_id`,`pivot`.`category_id` AS `pivot__category_id`,`pivot`.`create_time` AS `pivot__create_time`,`pivot`.`update_time` AS `pivot__update_time` FROM `shop_category` INNER JOIN `shop_goods_category_rel` `pivot` ON `pivot`.`category_id`=`shop_category`.`category_id` WHERE `pivot`.`goods_id` = 10001
获取当前商品的分类,访问 category(关联方法名) 属性即可,如下
<?php
namespace app\api\controller;
use app\api\model\Goods as GoodsModel;
class Goods extends Base
{
public function detail()
{
$goods_id = 10001;
$detail = GoodsModel::getDetail($goods_id);
echo '<pre>';
var_dump($detail->category);
echo '</pre>';
}
}
输出如下
object(think\model\Collection)#50 (1) {
["items":protected]=>
array(2) {
[0]=>
object(app\api\model\Category)#41 (2) {
["data"]=>
array(8) {
["category_id"]=>
int(6)
["name"]=>
string(12) "新鲜水果"
["parent_id"]=>
int(1)
["image_id"]=>
int(0)
["status"]=>
int(1)
["sort"]=>
int(0)
["create_time"]=>
int(1688454738)
["update_time"]=>
int(1688454738)
}
["relation"]=>
array(1) {
["pivot"]=>
object(think\model\Pivot)#51 (2) {
["data"]=>
array(5) {
["id"]=>
int(1001)
["goods_id"]=>
int(10001)
["category_id"]=>
int(6)
["create_time"]=>
int(1688696518)
["update_time"]=>
int(1688696518)
}
["relation"]=>
array(0) {
}
}
}
}
[1]=>
object(app\api\model\Category)#47 (2) {
["data"]=>
array(8) {
["category_id"]=>
int(10)
["name"]=>
string(12) "特价产品"
["parent_id"]=>
int(1)
["image_id"]=>
int(0)
["status"]=>
int(1)
["sort"]=>
int(0)
["create_time"]=>
int(1688454870)
["update_time"]=>
int(1688454870)
}
["relation"]=>
array(1) {
["pivot"]=>
object(think\model\Pivot)#52 (2) {
["data"]=>
array(5) {
["id"]=>
int(1002)
["goods_id"]=>
int(10001)
["category_id"]=>
int(10)
["create_time"]=>
int(1688795635)
["update_time"]=>
int(1688795635)
}
["relation"]=>
array(0) {
}
}
}
}
}
}
演示2
获取所有的商品详情以及它的分类
Goods 控制器
<?php
namespace app\api\controller;
use app\api\model\Goods as GoodsModel;
class Goods extends Base
{
// 获取所有商品
public function list()
{
$goodsList = GoodsModel::getList();
echo '<pre>';
var_dump($goodsList);
echo '</pre>';
}
}
Goods 模型
<?php
namespace app\api\model;
class Goods extends Base
{
protected $pk = 'goods_id';
protected $hidden = ['create_time', 'update_time'];
// 获取所有商品列表
public static function getList()
{
// 关联预载入
// 参数为所用到的关联方法
return self::with(['category'])
->select();
}
// 多对多获取商品分类
public function category()
{
return $this->belongsToMany('Category', 'GoodsCategoryRel');
// 完整写法
// return $this->belongsToMany('Category', 'GoodsCategoryRel', 'category_id', 'goods_id');
}
}
Goos 控制器 list 方法输出
object(think\model\Collection)#43 (1) {
["items":protected]=>
array(2) {
[0]=>
object(app\api\model\Goods)#42 (2) {
["data"]=>
array(8) {
["goods_id"]=>
int(10001)
["goods_name"]=>
string(12) "桂味荔枝"
["stock_total"]=>
int(0)
["status"]=>
int(10)
["sort"]=>
int(0)
["is_delete"]=>
int(0)
["create_time"]=>
int(1688696518)
["update_time"]=>
int(1688696518)
}
["relation"]=>
array(1) {
["category"]=>
object(think\model\Collection)#52 (1) {
["items":protected]=>
array(2) {
[0]=>
object(app\api\model\Category)#41 (2) {
["data"]=>
array(8) {
["category_id"]=>
int(6)
["name"]=>
string(12) "新鲜水果"
["parent_id"]=>
int(1)
["image_id"]=>
int(0)
["status"]=>
int(1)
["sort"]=>
int(0)
["create_time"]=>
int(1688454738)
["update_time"]=>
int(1688454738)
}
["relation"]=>
array(1) {
["pivot"]=>
object(think\model\Pivot)#53 (2) {
["data"]=>
array(5) {
["id"]=>
int(1001)
["goods_id"]=>
int(10001)
["category_id"]=>
int(6)
["create_time"]=>
int(1688696518)
["update_time"]=>
int(1688696518)
}
["relation"]=>
array(0) {
}
}
}
}
[1]=>
object(app\api\model\Category)#48 (2) {
["data"]=>
array(8) {
["category_id"]=>
int(10)
["name"]=>
string(12) "特价产品"
["parent_id"]=>
int(1)
["image_id"]=>
int(0)
["status"]=>
int(1)
["sort"]=>
int(0)
["create_time"]=>
int(1688454870)
["update_time"]=>
int(1688454870)
}
["relation"]=>
array(1) {
["pivot"]=>
object(think\model\Pivot)#54 (2) {
["data"]=>
array(5) {
["id"]=>
int(1002)
["goods_id"]=>
int(10001)
["category_id"]=>
int(10)
["create_time"]=>
int(1688795635)
["update_time"]=>
int(1688795635)
}
["relation"]=>
array(0) {
}
}
}
}
}
}
}
}
[1]=>
object(app\api\model\Goods)#40 (2) {
["data"]=>
array(8) {
["goods_id"]=>
int(10002)
["goods_name"]=>
string(15) "妃子笑荔枝"
["stock_total"]=>
int(0)
["status"]=>
int(10)
["sort"]=>
int(0)
["is_delete"]=>
int(0)
["create_time"]=>
int(1690166322)
["update_time"]=>
int(1690166322)
}
["relation"]=>
array(1) {
["category"]=>
object(think\model\Collection)#51 (1) {
["items":protected]=>
array(1) {
[0]=>
object(app\api\model\Category)#49 (2) {
["data"]=>
array(8) {
["category_id"]=>
int(6)
["name"]=>
string(12) "新鲜水果"
["parent_id"]=>
int(1)
["image_id"]=>
int(0)
["status"]=>
int(1)
["sort"]=>
int(0)
["create_time"]=>
int(1688454738)
["update_time"]=>
int(1688454738)
}
["relation"]=>
array(1) {
["pivot"]=>
object(think\model\Pivot)#55 (2) {
["data"]=>
array(5) {
["id"]=>
int(1003)
["goods_id"]=>
int(10002)
["category_id"]=>
int(6)
["create_time"]=>
int(1690167553)
["update_time"]=>
int(1690167553)
}
["relation"]=>
array(0) {
}
}
}
}
}
}
}
}
}
}
多条查询返回的是 Collection(结果集)对象,Collection 对象的 item 属性保存着所有商品模型实例,每个商品模型实例的 relation 属性 (关联数组)属性保存着关联模型实例,关联方法名 category 作为键名,结果集对象作为键值,结果集对象的 items 属性(索引数组)保存着关联查询结果。
所用的 SQL 如下
SELECT * FROM `shop_goods`
SELECT `shop_category`.*,`pivot`.`id` AS `pivot__id`,`pivot`.`goods_id` AS `pivot__goods_id`,`pivot`.`category_id` AS `pivot__category_id`,`pivot`.`create_time` AS `pivot__create_time`,`pivot`.`update_time` AS `pivot__update_time` FROM `shop_category` INNER JOIN `shop_goods_category_rel` `pivot` ON `pivot`.`category_id`=`shop_category`.`category_id` WHERE `pivot`.`goods_id` IN (10001,10002)
获取每个商品的分类,访问 category(关联方法名) 属性即可,如下
<?php
namespace app\api\controller;
use app\api\model\Goods as GoodsModel;
class Goods extends Base
{
public function list()
{
$goodsList = GoodsModel::getList();
foreach($goodsList as $goods){
echo '<pre>';
var_dump($goods->category);
echo '</pre>';
}
}
}
Goods 控制器 list 方法输出如下
object(think\model\Collection)#52 (1) {
["items":protected]=>
array(2) {
[0]=>
object(app\api\model\Category)#41 (2) {
["data"]=>
array(8) {
["category_id"]=>
int(6)
["name"]=>
string(12) "新鲜水果"
["parent_id"]=>
int(1)
["image_id"]=>
int(0)
["status"]=>
int(1)
["sort"]=>
int(0)
["create_time"]=>
int(1688454738)
["update_time"]=>
int(1688454738)
}
["relation"]=>
array(1) {
["pivot"]=>
object(think\model\Pivot)#53 (2) {
["data"]=>
array(5) {
["id"]=>
int(1001)
["goods_id"]=>
int(10001)
["category_id"]=>
int(6)
["create_time"]=>
int(1688696518)
["update_time"]=>
int(1688696518)
}
["relation"]=>
array(0) {
}
}
}
}
[1]=>
object(app\api\model\Category)#48 (2) {
["data"]=>
array(8) {
["category_id"]=>
int(10)
["name"]=>
string(12) "特价产品"
["parent_id"]=>
int(1)
["image_id"]=>
int(0)
["status"]=>
int(1)
["sort"]=>
int(0)
["create_time"]=>
int(1688454870)
["update_time"]=>
int(1688454870)
}
["relation"]=>
array(1) {
["pivot"]=>
object(think\model\Pivot)#54 (2) {
["data"]=>
array(5) {
["id"]=>
int(1002)
["goods_id"]=>
int(10001)
["category_id"]=>
int(10)
["create_time"]=>
int(1688795635)
["update_time"]=>
int(1688795635)
}
["relation"]=>
array(0) {
}
}
}
}
}
}
object(think\model\Collection)#51 (1) {
["items":protected]=>
array(1) {
[0]=>
object(app\api\model\Category)#49 (2) {
["data"]=>
array(8) {
["category_id"]=>
int(6)
["name"]=>
string(12) "新鲜水果"
["parent_id"]=>
int(1)
["image_id"]=>
int(0)
["status"]=>
int(1)
["sort"]=>
int(0)
["create_time"]=>
int(1688454738)
["update_time"]=>
int(1688454738)
}
["relation"]=>
array(1) {
["pivot"]=>
object(think\model\Pivot)#55 (2) {
["data"]=>
array(5) {
["id"]=>
int(1003)
["goods_id"]=>
int(10002)
["category_id"]=>
int(6)
["create_time"]=>
int(1690167553)
["update_time"]=>
int(1690167553)
}
["relation"]=>
array(0) {
}
}
}
}
}
}
如果觉得作者写得好,请帮我点个赞,谢谢。
在ThinkPHP框架中,多对多关联可以通过模型关联来实现。ThinkPHP提供了一种名为`belongsToMany`的方法来定义多对多关联。
要实现多对多关联,首先需要定义三个模型,分别代表多对多关联中的两个表和关联表。然后,在模型中使用`belongsToMany`方法进行关联定义。
举个例子,假设有三个表:`user`、`role`和`user_role`。`user`表和`role`表之间是多对多的关系,通过中间表`user_role`来进行关联。
首先,在`User`模型中定义与`Role`模型的多对多关联:
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
public function roles()
{
return $this->belongsToMany('Role', 'user_role');
}
}
然后,在`Role`模型中也定义与`User`模型的多对多关联:
<?php
namespace app\index\model;
use think\Model;
class Role extends Model
{
public function users()
{
return $this->belongsToMany('User', 'user_role');
}
}
最后,在控制器中可以通过以下方式获取指定用户的所有角色,或者获取指定角色的所有用户:
<?php
namespace app\index\controller;
use app\index\model\User;
class UserController
{
public function roles($user_id)
{
$user = User::get($user_id);
$roles = $user->roles;
return view('roles', ['roles' => $roles]);
}
}
通过`$user->roles`可以获取指定用户的所有角色,或者通过`$role->users`可以获取指定角色的所有用户。ThinkPHP会自动根据关联关系查询相关的数据并返回结果。
通过以上示例,你可以在ThinkPHP框架中实现多对多关联。根据具体的数据库表结构和需求,你可以相应地调整模型和关联的定义。