目录

Thinkphp的belongsToMany多对多-和-hasManyThrough远程一对多的区别是什么

Thinkphp的belongsToMany(多对多) 和 hasManyThrough(远程一对多)的区别是什么?

虽然 belongsToMany (多对多) 和 hasManyThrough (远程一对多) 都会使用 JOIN 查询,但它们的 核心区别 在于 关联关系的本质不同 ,具体如下:


1️⃣ belongsToMany (多对多)

📌 适用场景

两个表之间是多对多关系 ,并且需要 通过中间表 来关联数据。例如:

  • 用户 users角色 roles 之间是多对多关系。
  • 中间表 access 记录用户与角色的对应关系。

🛠 数据库结构

CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255) NOT NULL
);

CREATE TABLE roles (
    id INT PRIMARY KEY AUTO_INCREMENT,
    role_name VARCHAR(255) NOT NULL
);

CREATE TABLE access (
    user_id INT NOT NULL,
    role_id INT NOT NULL,
    PRIMARY KEY (user_id, role_id),
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
    FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE
);

🔎 ThinkPHP 代码

class User extends Model 
{
    public function roles()
    {
        return $this->belongsToMany(Role::class, 'access');
    }
}

查询 用户 ID=1 的所有角色:

$user = User::find(1);
$roles = $user->roles;

最终生成的 SQL:

SELECT r.*
FROM roles r
JOIN access a ON r.id = a.role_id
WHERE a.user_id = 1;

特点

  • 需要 中间表( access 存储关系。
  • 查询时 通过 JOIN 中间表 ,找到对应的 多个 角色(多对多)。
  • 数据模型: 一个用户有多个角色,一个角色也可能属于多个用户。

2️⃣ hasManyThrough (远程一对多)

📌 适用场景

两个表之间没有直接关联 ,但可以 通过中间表建立关系 ,例如:

  • 每个城市( cities )有多个用户( users
  • 每个用户( users )有多个话题( topics
  • citiestopics 之间没有直接的关系 (需要通过 users 进行关联)

🛠 数据库结构

CREATE TABLE cities (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255) NOT NULL
);

CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255) NOT NULL,
    city_id INT NOT NULL,
    FOREIGN KEY (city_id) REFERENCES cities(id) ON DELETE CASCADE
);

CREATE TABLE topics (
    id INT PRIMARY KEY AUTO_INCREMENT,
    title VARCHAR(255) NOT NULL,
    user_id INT NOT NULL,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

🔎 ThinkPHP 代码

class City extends Model 
{
    public function topics()
    {
        return $this->hasManyThrough(Topic::class, User::class);
    }
}

查询 城市 ID=1 的所有话题:

$city = City::find(1);
$topics = $city->topics;

最终生成的 SQL:

SELECT t.*
FROM topics t
JOIN users u ON t.user_id = u.id
WHERE u.city_id = 1;

特点

  • 没有中间表 (不像 belongsToMany )。
  • 需要通过 另一张表( users 进行 跨表关联查询
  • 数据模型: 一个 城市 有多个 话题 ,但 城市表和话题表之间无直接关系 ,只能通过 users 关联。

🔎 主要区别总结

关联方式适用场景需要中间表?关联方式MySQL 查询关联模型关系类型
belongsToMany (多对多)用户 - 角色✅ 需要( accessJOIN 中间表JOIN accessUser -> roles()多对多
hasManyThrough (远程一对多)城市 - 话题❌ 不需要通过 另一张表 远程关联JOIN usersCity -> topics()远程一对多

🛠 用类比解释

belongsToMany (多对多)

像是 学生(User)和课程(Role) 之间的关系:

  • 一个学生可以选多门课
  • 一门课可以有多个学生
  • 但学生和课程 不是直接关联的 ,而是通过 选课表(access) 连接的。

hasManyThrough (远程一对多)

像是 学校(City)和课表(Topic) 之间的关系:

  • 每个 学校 有多个 学生
  • 每个 学生 有多个 课表
  • 学校和课表 之间 没有直接关系 ,只能通过 学生表 关联。

✅ 结论

虽然 belongsToManyhasManyThrough 都使用 JOIN 查询 ,但:

  1. belongsToMany 是多对多关系,必须有中间表access )。
  2. hasManyThrough 远程一对多,不需要中间表,但需要一个中间关联模型 (如 users )。
  3. SQL 查询方式不同
    • belongsToMany 直接 JOIN 中间表
    • hasManyThrough 远程查询, JOIN 另一张表