Laravel的远程一对多关联通过hasManyThrough实现,允许模型A经由模型B访问模型C。其底层基于JOIN查询,需注意键名自定义、预加载避免N+1问题及仅支持两跳关联的限制。

Laravel中的“远程关联”或“远程一对多”(Remote Has Many)通常指的是
hasManyThrough
实现Laravel的远程一对多关联,最常用的就是
hasManyThrough
Country
Post
User
我们来看一个具体的例子:假设我们有国家(Country)、用户(User)和文章(Post)三个模型。一个国家有多个用户,一个用户有多篇文章。现在,我们想直接获取某个国家下的所有文章。
数据库结构示例:
立即进入“豆包AI人工智官网入口”;
立即学习“豆包AI人工智能在线问答入口”;
countries
id
name
users
id
name
country_id
posts
id
title
user_id
模型定义:
首先,确保你的模型之间已经建立了直接的关联:
// app/Models/Country.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Country extends Model
{
// 一个国家有多个用户
public function users()
{
return $this->hasMany(User::class);
}
// 接下来我们要添加远程一对多关联
public function posts()
{
// 第一个参数是最终要关联的模型 (Post)
// 第二个参数是中间模型 (User)
return $this->hasManyThrough(Post::class, User::class);
}
}// app/Models/User.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
use HasFactory;
// 一个用户属于一个国家
public function country()
{
return $this->belongsTo(Country::class);
}
// 一个用户有多篇文章
public function posts()
{
return $this->hasMany(Post::class);
}
}// app/Models/Post.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
// 一篇文章属于一个用户
public function user()
{
return $this->belongsTo(User::class);
}
}如何使用:
现在,你就可以像访问普通关联一样,获取一个国家下的所有文章了:
$country = Country::find(1); $posts = $country->posts; // 获取该国家所有用户的文章集合
Laravel在底层会执行一个JOIN查询,将
countries
users
posts
hasManyThrough
JOIN
hasManyThrough
INNER JOIN
以我们上面的
Country
User
Post
SELECT
posts.*
FROM
posts
INNER JOIN
users ON users.id = posts.user_id
INNER JOIN
countries ON countries.id = users.country_id
WHERE
countries.id = ?; -- 这里的问号就是你查询的Country的ID它首先将
posts
users
posts.user_id = users.id
countries
users.country_id = countries.id
posts
与传统关联的不同之处:
传统的
hasMany
belongsTo
hasMany
User
Post
User
id
Post
user_id
WHERE
belongsTo
Post
User
Post
user_id
User
id
而
hasManyThrough
hasManyThrough
hasManyThrough
hasManyThrough
方法签名大致是这样的:
hasManyThrough(
string $related,
string $through,
string $firstForeignKey = null, // 中间模型(through)在当前模型(this)上的外键
string $secondForeignKey = null, // 最终模型(related)在中间模型(through)上的外键
string $firstLocalKey = null, // 当前模型(this)的本地键
string $secondLocalKey = null // 中间模型(through)的本地键
)我们继续使用
Country
User
Post
users
countries
country_id
country_ref
posts
users
user_id
author_id
countries
id
country_uuid
users
id
user_uuid
那么,
Country
posts
// app/Models/Country.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Country extends Model
{
protected $primaryKey = 'country_uuid'; // 假设主键是 country_uuid
public function posts()
{
return $this->hasManyThrough(
Post::class,
User::class,
'country_ref', // 'users' 表中的外键,指向 'countries' 表的键 (country_uuid)
'author_id', // 'posts' 表中的外键,指向 'users' 表的键 (user_uuid)
'country_uuid', // 'countries' 表的本地键
'user_uuid' // 'users' 表的本地键
);
}
}// app/Models/User.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
use HasFactory;
protected $primaryKey = 'user_uuid'; // 假设主键是 user_uuid
protected $foreignKey = 'country_ref'; // 假设关联 country 的外键是 country_ref
public function country()
{
return $this->belongsTo(Country::class, 'country_ref', 'country_uuid');
}
public function posts()
{
return $this->hasMany(Post::class, 'author_id', 'user_uuid');
}
}// app/Models/Post.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
protected $foreignKey = 'author_id'; // 假设关联 user 的外键是 author_id
public function user()
{
return $this->belongsTo(User::class, 'author_id', 'user_uuid');
}
}这里需要注意的是参数的顺序和它们各自代表的意义:
Post::class
User::class
'country_ref'
User
Country
users.country_ref
'author_id'
Post
User
posts.author_id
'country_uuid'
Country
countries.country_uuid
'user_uuid'
User
users.user_uuid
通过这种方式,无论你的数据库命名有多么“非主流”,你都可以灵活地配置
hasManyThrough
hasManyThrough
一个比较明显的限制是,
hasManyThrough
hasManyThrough
性能考量方面:
N+1 查询问题: 尽管
hasManyThrough
hasManyThrough
$countries = Country::all();
foreach ($countries as $country) {
// 这里每次循环都会触发一个 hasManyThrough 查询
// 如果有N个国家,就会有N+1次查询(1次获取所有国家,N次获取文章)
echo $country->posts->count();
}解决办法是使用预加载(Eager Loading),通过
with()
$countries = Country::with('posts')->get();
foreach ($countries as $country) {
// posts 已经被预加载,不会再触发额外查询
echo $country->posts->count();
}预加载
hasManyThrough
LEFT JOIN
UNION
复杂的JOIN操作:
hasManyThrough
INNER JOIN
误用场景: 有时候,开发者可能会将
hasManyThrough
belongsToMany
belongsToMany
hasManyThrough
Role
Permission
RoleUser
User
Role
belongsToMany
hasManyThrough
总的来说,
hasManyThrough
以上就是Laravel远程关联?远程一对多如何实现?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号