TP5关于一对多关联查询的搜索问题

jungle_drums
  • 274

在开发商城的时候,需要对订单进行搜索,输入框的内容用来查询订单号或商品名称。
目前订单表和订单商品表是分开的,查询订单列表使用关联查询,如下:

// $this是订单表模型,goods是订单商品表模型
public function goods()
{
    return $this->hasMany('goods' , 'order_id' , 'id', 'g');
}

public function getList() {
    $map = [
        'o.order_no|g.goods_name' => ['like','%A%']
    ];
    $result =  $this->alias('o')
        ->with(['goods'])
        ->where('user_id' , '=' , $userId)
        ->where($map)
        ->order(['createtime' => 'desc'])
        ->page($page , 10)
        ->fetchSql(true)
        ->select();
}

返回结果:

SELECT * FROM `order` `o` WHERE `user_id` = 5 AND ( `o`.`order_no` 
 LIKE '%A%' OR `g`.`goods_name` LIKE '%A%' ) ORDER BY `createtime` DESC LIMIT 0,10

去掉fetchsql的错误提示(goods_name字段是正确的):

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'g.goods_name' in 'where clause'

更新:目前使用的了LEFT JOIN的写法实现模糊查询的效果,但是实际上是要查询订单列表,LEFT JOIN之后将产品数据组合成订单会发现这里的分页效果会有问题,搜出10条产品有可能是3个订单 也有可能是5个订单。

$search = $keyword ? ['o.order_no|g.goods_name' => ['like', "%{$keyword}%"]] : [];
$list = $this->alias('o')
        ->field('o.*, g.goods_id, g.images, g.goods_name, g.goods_attr, g.goods_no, g.goods_price')
        ->join('goods g', 'o.id = g.order_id', 'left')
        ->where('o.user_id', $userId)
        ->where($search)
        ->order('o.createtime', 'DESC')
        ->page($page, 10)
        ->select();

请问要怎么编写条件来实现订单号和商品名称的模糊查询呢?

补充:

谢谢解答,目前想到的解决方案是查询的时候订单JOIN产品查出订单,之后再查出产品列表组合结果集。
回复
阅读 3.3k
3 个回答
moonsola
  • 813

返回的sql里明显没有商品表。
看文档 https://www.kancloud.cn/manua...

//如果使用关联预查询功能,就可以变成2次查询(对于一对一关联来说,如果使用JOIN方式只有一次查询),有效提高性能。
$list = User::with('profile')->select([1,2,3]);
foreach($list as $user){
    // 获取用户关联的profile模型数据
    dump($user->profile);
}

说明with后的关联模型是另起一条查询了,没有使用join。所以你fetchsql得到的sql里没有商品表,g.goods_name这个字段自然就会提示找不到。
要不尝试按照本页文档上join的方式修改代码试一试。

另外注意文档里有这样一句
https://www.kancloud.cn/manua...

避免在模型内部使用复杂的join查询和视图查询。

推荐使用数据库类完成较复杂的联表查询的操作。

题目是TP5,不过这部分内容差别不大。

宋鹏飞
  • 18

那就直接使用Db类操作数据库吧

$search = $keyword ? ['o.order_no|g.goods_name' => ['like', "%{$keyword}%"]] : [];
$list = Db::name('goods g')
        ->field('o.*, g.goods_id, g.images, g.goods_name, g.goods_attr, g.goods_no, g.goods_price')
        ->join('order o', 'o.id = g.order_id')
        ->where('o.user_id', $userId)
        ->where($search)
        ->group('o.order_id')
        ->order('o.createtime', 'DESC')
        ->page($page, 10)
        ->select();
李昊天
  • 1.8k
public function getList() {
    $map = [
        'o.order_no' => ['like','%A%']
    ];
    $result =  $this->alias('o')
        ->with(['goods'=>function($query){
        $query->whereLike('goods_name','%A%');
        }])
        ->where('user_id' , $userId)
        ->where($map)
        ->order('createtime','desc')
        ->page($page , 10)
        ->fetchSql(true)
        ->select();
}

hasMany('关联模型','外键','主键');
不明白你的$this->hasMany('goods' , 'order_id' , 'id', 'g');第四个参数是干嘛的!;

一对多用with后是先查询主数据 然后根据关联条件查询到所有的关联数据 有两个sql
比如: 先查询所有的用户: select id,username from user;
然后去查询关联数据 比如用户文章吧:
select * from user_article where user_id in(1,2,3);
这里in的id是将前面的数据做一个迭代然后保存关联键的数据 然后去查询
然后将查询到的数据和主数据再一个迭代合并到对应的数据下 就形成了

[
'id'=>1,
'username'=>'test',
'article'=>
    [
        [
            'user_id'=>1,
            'title'=>'xxx'
        ],
        [
            'user_id'=>2,
            'title'=>'xxx'
        ]
    ]
]
宣传栏