laravel with联表查询增加子表筛选条件

本文介绍了一个简单的管理员与角色关联表的数据库设计,并展示了如何使用PHP的Eloquent ORM进行角色信息的关联查询,包括根据角色名称筛选管理员的具体实现。

1、例如管理员和角色表:

管理员表:

CREATE TABLE `admins` (
  `adminid` int(10) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `name` varchar(90) NOT NULL COMMENT '姓名',
  `role_id` tinyint(2) NOT NULL COMMENT '角色ID',
  PRIMARY KEY (`adminid`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8

角色表:

CREATE TABLE `roles` (
  `roleid` smallint(6) NOT NULL AUTO_INCREMENT COMMENT '角色ID',
  `name` varchar(100) NOT NULL COMMENT '角色名称'
  PRIMARY KEY (`roleid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

2、管理员表模型:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class AdminsModel extends Model {
    /**
     * @var string
     */
    protected $connection = 'mysql';

    /**
     * @var string
     */
    protected $table = 'admins';

    /**
     * @var string
     */
    protected $primaryKey = 'adminid';

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasOne
     */
    public function roleInfo() {
        return $this->hasOne(RolesModel::class, 'roleid', 'role_id')->select(['roleid', 'name']);
    }
}

3、根据角色名称筛选:

AdminsModel::where($condition)->with(['roleInfo'])->whereHas('roleInfo', function ($roleQuery) use ($roleName) {
    if ($roleName) {
        $roleQuery->where('name', '%' . $roleName . '%');
    }
});

<think>嗯,用户现在的问题是关于Laravel ORM中的with方法,是否可以通过添加筛选条件来限制主查询的记录。用户之前的问题是关于PHP字符串处理,现在转向了Laravel ORM,可能是在开发过程中遇到了关模型查询的需求。 首先,我需要回忆一下Laravel的Eloquent ORM中with和whereHas的用法。根据用户提供的引用内容,引用3和引用5提到了with和whereHas的区别。with用于预加载关数据,而whereHas用于基于关的存在性筛选主模型。但用户现在的问题是,在使用with方法时,能否通过条件来限制主查询的记录。 根据引用3中的例子,User::with(['posts' => function ($query) { ... }])->get(); 这里的with方法中的闭包是用于对关模型posts添加条件,比如筛选标题包含'first'的文章。但是这种情况下,主查询User仍然会返回所有用户,只是每个用户的posts关数据会被过滤。用户想知道的是,能否在with中添加条件,使得主查询只返回满足某些条件的用户,比如那些有特定posts的用户。 这时候,需要区分with和whereHas的用途。whereHas是用来在主查询中添加基于关条件,从而限制主模型的结果。而with只是预加载关数据,不影响主查询的结果数量,除非结合has或whereHas使用。 用户可能混淆了with和whereHas的功能。例如,在引用1中,User::with('orders')->has('orders')会筛选出有orders的用户,而with只是预加载orders数据,has才是限制主查询条件。 因此,回答应该明确指出:with方法中的条件仅影响关数据的加载,不影响主查询的记录。要限制主查询的记录,需要结合whereHas或has方法。例如,User::whereHas('posts', function ($query) { ... })->with(['posts' => function ($query) { ... }])->get(); 这样既筛选了主模型的用户,又预加载了符合条件的关数据。 同时,引用5提到user->whereHas('post')会得到发过文章的用户,而with('post')只是加载关数据,但主查询仍包括所有用户,包括没有文章的用户。所以用户需要同时使用whereHas来限制主查询,用with来加载过滤后的关数据。 总结回答结构:首先明确with的作用,然后解释如何结合whereHas来限制主查询,再举例说明,最后讨论性能优化和常见误区。</think>在 Laravel ORM 中,使用 `with` 方法时**可以直接为关模型添加筛选条件**,但需要特别注意:**这些条件仅作用于关模型的加载过程,不会直接限制主查询的记录**。若要通过关条件限制主查询结果,需结合 `whereHas` 或 `has` 方法。以下是具体实现方式及场景分析: --- ### 一、`with` 方法的筛选条件作用域 #### 1. **仅筛选数据** ```php User::with(['posts' => function ($query) { $query->where('status', 'published'); // 仅预加载已发布的文章 }])->get(); ``` - **结果**:所有用户都会被返回,但每个用户的 `posts` 关数据仅包含 `status='published'` 的记录 - **SQL 现**:主查询选择所有用户,子查询筛选的 `posts` [^3] #### 2. **限制主查询记录的需求** 若需要**仅返回拥有特定关记录的主模型**(例如:仅查询有已发布文章的用户),必须结合 `whereHas`: ```php User::whereHas('posts', function ($query) { $query->where('status', 'published'); // 主查询限制条件 })->with(['posts' => function ($query) { $query->where('status', 'published'); // 关数据筛选(可选) }])->get(); ``` - **效果**:主查询仅返回有已发布文章的用户,同时预加载这些用户的已发布文章 - **SQL 逻辑**:主查询通过 `EXISTS` 子句过滤用户,子查询加载关数据[^1] --- ### 二、筛选条件的组合策略 #### 1. **基础场景:筛选主记录并加载关数据** ```php // 查询有订单的用户,并加载订单金额大于100的订单 User::whereHas('orders', function ($query) { $query->where('amount', '>', 100); })->with(['orders' => function ($query) { $query->where('amount', '>', 100); }])->get(); ``` - **注意**:`whereHas` 和 `with` 中的条件需保持一致,避免逻辑冲突 #### 2. **性能优化:避免重复条件** 若主查询和关查询条件相同,可通过作用域复用: ```php // 在 Order 模型中定义作用域 public function scopeHighValue($query) { return $query->where('amount', '>', 100); } // 查询时调用 User::whereHas('orders', fn($q) => $q->highValue()) ->with(['orders' => fn($q) => $q->highValue()]) ->get(); ``` --- ### 三、`with` 与 `whereHas` 的对比 | 方法 | 作用范围 | 性能影响 | 典型场景 | |---------------------|------------------|----------------|-----------------------------| | `with` + 条件 | 关数据加载阶段 | 子查询优化加载 | 预加载过滤后的关数据 | | `whereHas` + 条件 | 主查询阶段 | 影响主查询性能 | 根据关存在性筛选主模型记录 | | 二者组合 | 双重过滤 | 需权衡执行计划 | 精确控制主记录和关数据 | --- ### 四、常见误区与解决方案 #### 1. **错误:仅用 `with` 条件限制主查询** ```php // 错误示例:仍然返回所有用户 User::with(['posts' => function ($query) { $query->where('status', 'published'); }])->get(); ``` - **修复**:必须添加 `whereHas` 或 `has` #### 2. **条件不一致导致数据缺失** ```php // 错误:主查询筛选有订单的用户,但关加载未完成订单 User::whereHas('orders') ->with(['orders' => function ($query) { $query->where('status', 'pending'); }])->get(); ``` - **结果**:用户可能有其他状态的订单,但关数据仅显示 `pending` 订单 - **建议**:明确是否需要关数据的子集或全集 --- ### 五、高级用法:嵌套关筛选 ```php // 查询有评论的文章,且评论包含关键词,同时加载这些评论 Article::whereHas('comments', function ($query) { $query->where('content', 'like', '%Laravel%'); })->with(['comments' => function ($query) { $query->where('content', 'like', '%Laravel%'); }])->get(); ``` --- ### 六、性能建议 1. **避免 N+1 问题**:始终优先使用 `with` 预加载关数据 2. **索引优化**:确保 `whereHas` 中使用的关字段已建立索引 3. **选择性加载字段**: ```php User::with(['posts:id,user_id,title']) ->whereHas('posts', fn($q) => $q->where('views', '>', 100)) ->get(['id', 'name']); ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值