Yii2 - 在多个条件下左连接

时间:2015-12-29 13:50:22

标签: php mysql yii2

我有三个表格,其中包含以下关系,

  ------- 1        0..* ------------
 |Product|-------------|Availability|
  -------               ------------
    1 |
      |
    1 |
  --------
 |MetaData|
  --------

我的原始sql看起来像这样

SELECT p.ID FROM product p 
LEFT JOIN availability a ON a.productID=p.ID 
          AND a.start>=DATE_ADD(DATE(now()), INTERVAL 7 DAY)
LEFT JOIN meta_data m ON m.ID=p.meta_dataID
WHERE a.ID IS NULL
AND m.published_state=1;

也就是说,找到每个Product MetaData.published_state等于1且没有AvailabilityAvailability.start超过7天now() }}。

我正在尝试使用ActiveRecord方法完成相同的操作,使用类似下面的内容

$products = Product::find()
            ->joinWith('metaData')
            ->joinWith('availability')
            ->onCondition(['>=', 'availability.start', strtotime('+7 days')])
            ->where(['is', 'availability.ID', NULL])
            ->andWhere(['=', 'meta_data.published_state', 1])
            ->all();

但是,这没有结果。使用Connection::createCommand()运行原始sql会返回我期望的行,因此数据没有问题。

我怀疑这个问题是由join条件引起的,where条件“互相流血”;加入和应用于加入或在哪里而不是分开。

如何输出正在运行的实际sql查询?这是从控制台控制器调用的动作。

如何更改代码以返回所需的Products

3 个答案:

答案 0 :(得分:7)

我相信这是一个更好的解决方案。与其使用leftJoin之类的Raw查询,不如使用joinWith来补充andOnCondition关系(这将需要的条件添加到join语句中)。

$products = Product::find()
    ->joinWith(['metaData' => function (ActiveQuery $query) {
        return $query
            ->andWhere(['=', 'meta_data.published_state', 1]);
    }])
    ->joinWith(['availability' => function (ActiveQuery $query) {
        return $query
            ->andOnCondition(['>=', 'availability.start', strtotime('+7 days')])
            ->andWhere(['IS', 'availability.ID', NULL]);
    }])
    ->all();

此外,当您在关系内编写where子句时,它看起来更干净。它的工作原理与在外部编写相同(如果我没看错),但是在重构查询时,您可以轻松删除整个关系,而不会忘记外部的关系条件。

答案 1 :(得分:5)

只需使用以下条件即可。

$query = Product::find()
 -> leftJoin('availability', 'availability.productID=product.ID  AND a.start>=DATE_ADD(DATE(now()), INTERVAL 7 DAY)')
 ->leftJoin('meta_data', 'meta_data.ID=product.meta_dataID')
 ->where(['is', 'availability.ID', NULL])
 ->andWhere(['=', 'meta_data.published_state', 1])
 ->all();

答案 2 :(得分:0)

使用此:

$sql = 'SELECT p.ID FROM product p 
LEFT JOIN availability a ON a.productID=p.ID 
          AND a.start>=DATE_ADD(DATE(now()), INTERVAL 7 DAY)
LEFT JOIN meta_data m ON m.ID=p.meta_dataID
WHERE a.ID IS NULL
AND m.published_state=1';

$products = Product::findBySql($sql);

Yii Active Record具有findBySql($sql)方法,该方法允许您使用原始SQL查询运行并从数据库获取数据。当您对Yii的查询方法感到困惑时,或者当您想让查询与Yii一起运行变得更加复杂时,这很有用。

因此,基本上,在上面的代码块中,您只是将原始SQL查询放入了一个名为$sql的变量中,并将其用作了findBySql()方法的参数值。