sllhsmile / wherehasin
Hyperf ORM whereHasIn
Requires
- php: >=8.1
- hyperf/config: ^3.0@dev
- hyperf/database: ^3.0@dev
- hyperf/stringable: ^3.0@dev
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.68
- phpunit/phpunit: ^10.5 || ^11.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-08-28 08:31:57 UTC
README
Hyperf WHERE HAS IN
Hyperf wherehasin是一个使用 IN 子查询优化关联过滤的 Hyperf ORM 扩展包,适用于 Hyperf 3.0+ 和 PHP 8.1+。
whereHasIn与原生whereHas的执行计划不同。仅在已评估索引与数据分布、确实需要IN子查询时使用;关联回调可以添加筛选条件,但其select()不会改变扩展为关联键生成的单列子查询。
环境
- PHP >= 8.1
- Hyperf 3.0+
安装
composer require sllhsmile/wherehasin
简介
Hyperf的关联关系查询whereHas在日常开发中给我们带来了极大的便利,但是在主表数据量比较多的时候会有比较严重的性能问题,主要是因为whereHas用了where exists (select * ...)这种方式去查询关联数据。
通过这个扩展包提供的whereHasIn方法,可以把语句转化为where id in (select xxx.id ...)的形式,从而提高查询性能,下面我们来做一个简单的对比:
当主表数据量较多的情况下,
where id in会有明显的性能提升;当主表数据量较少的时候,两者性能相差无几。
主表test_users写入130002条数据,关联表test_user_profiles写入1002条数据,查询代码如下
<?php /** * SQL: * * select * from `test_users` where exists * ( * select * from `test_user_profiles` * where `test_users`.`id` = `test_user_profiles`.`user_id` * ) * limit 10 */ $users1 = User::whereHas('profile')->limit(10)->get(); /** * SQL: * * select * from `test_users` where `test_users`.`id` in * ( * select `test_user_profiles`.`user_id` from `test_user_profiles` * where `test_users`.`id` = `test_user_profiles`.`user_id` * ) * limit 10 */ $users1 = User::whereHasIn('profile')->limit(10)->get();
最终耗时如下,可以看出性能相差还是不小的,如果数据量更多一些,这个差距还会更大
whereHas 0.50499701499939 秒 whereHasIn 0.027166843414307 秒
使用
whereHasIn
此方法已支持Hyperf ORM中的所有关联关系,可以替代whereHas
User::whereHasIn('profile')->get(); User::whereHasIn('profile', function ($q) { $q->where('id', '>', 10); })->get();
orWhereHasIn
User::where('name', 'like', '%Hyperf%')->orWhereHasIn('profile')->get();
多级关联关系
User::whereHasIn('painters.paintings', function ($q) { $q->whereIn('id', [600, 601]); })->orderBy('id')->get()->toArray();
需要注意的是,如果是BelongsTo类型的关联关系,使用whereHasIn时使用的不是主键,而是外键
<?php /** * 这里用的是"user_id in",而不是"id in" * * select * from `test_user_profiles` where `test_user_profiles`.`user_id` in * ( * select `test_users`.`id` from `test_users` where `test_user_profiles`.`user_id` = `test_users`.`id` * ) */ $profiles = Profile::whereHasIn('user')->get();
whereHasMorphIn
此方法已支持Hyperf ORM中的所有关联关系,可以替代whereHasMorph
Image::whereHasMorphIn('imageable', Post::class, function ($q) { $q->where('id', '>', 10); })->get();
测试
安装开发依赖后执行:
composer test
集成测试默认跳过真实数据库。要验证跨连接关系、回调 select() 和通配 Morph 的 NULL 类型处理,可在本地 MySQL 上显式运行:
WHEREHASIN_TEST_MYSQL=1 DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=hxh DB_USERNAME=root DB_PASSWORD=root composer test
测试仅创建并在结束时删除 wherehasin_test_* 临时表。