ThinkPHP 多对多关联

用多对多关联的前提

如果模型 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'];
   
}

实例演示

shop_goods 表

 shop_category 表

shop_goods_category_rel 表

演示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框架中实现多对多关联。根据具体的数据库表结构和需求,你可以相应地调整模型和关联的定义。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值