web-dev-qa-db-ja.com

CakePHP:findメソッドでクエリを作成するときに「HAVING」操作を使用するにはどうすればよいですか?

CakePHPのpaginate()メソッドを使用してSQLクエリで「HAVING」句を使用しようとしています。

いくつか検索したところ、Cakeのpaginate()/ find()メソッドではこれを達成できないようです。

私が持っているコードは次のようになります。

_$this->paginate = array(
        'fields' => $fields,
        'conditions' => $conditions,
        'recursive' => 1,
        'limit' => 10, 
        'order' => $order,
        'group' => 'Venue.id');
_

$ fieldsの1つは、エイリアス「distance」です。距離が25未満の場合のクエリを追加したい(例:距離が25未満の場合)。

これまでに2つの回避策を見てきましたが、残念ながらどちらも私のニーズに合いません。私が見た2つは次のとおりです。

1)「グループ」オプションにHAVING句を追加します。例えば_'group' => 'Venue.id HAVING distance < 25'_。これは、実行される初期カウントクエリを台無しにするため、ページネーションと組み合わせて使用​​すると機能しないようです。 (つまり、明らかに無効な構文であるSELECT distinct(Venue.id HAVING distance < 25)を試みます。

2)WHERE条件の後にHAVING句を追加する(例:_WHERE 1 = 1 HAVING field > 25_)HAVING句が必要であると思われるため、これは機能しませんafter CakeがWHERE条件の後に配置するグループステートメント生成するクエリで。

CakePHPのfind()メソッドでこれを行う方法を知っている人はいますか? query()は多くの手直しが必要であり、独自のページ付けロジックを実装する必要があるため、使用したくありません。

前もって感謝します

20
cowls

あなたはそれをグループ条件と一緒に置く必要があります。このような

$this->find('all', array(
    'conditions' => array(
        'Post.length >=' => 100
    ),
    'fields' => array(
        'Author.id', 'COUNT(*) as Total'
    ),
    'group' => array(
        'Total HAVING Total > 10'
    )
));

それがあなたを助けることを願っています

36
api55

マニュアル によると、CakePHP/2はついにhavingをサポートします。バージョン2.10.0では配列検索パラメータとして追加されました。22では releasednd 2017年7月。

2.10移行ガイド から:

Model::find()havingおよびlockオプションをサポートするようになり、検索操作にHAVINGおよびFOR UPDATEロック句を追加できるようになりました。

1

次のトリックを使用して、WHERE句の最後に独自のHAVING句を追加しました。 「dbo-> expression()」メソッドはケーキに記載されています サブクエリドキュメント

function addHaving(array $existingConditions, $havingClause) {
  $model = 'User';
  $db = $this->$model->getDataSource();

  // Two fun things at play here,
  // 1 - mysql doesn't allow you to use aliases in WHERE clause
  // 2 - Cake doesn't allow a HAVING clause separate from a GROUP BY
  // This expression should go last in the WHERE clause (following the last AND)
  $taut = count($existingConditions) > 0 ? '1 = 1' : '';
  $having = $db->expression("$taut HAVING $havingClause");

  $existingConditions[] = $having;

  return $existingConditions;
}
1
Ben

これはページネーションの問題を解決しない別のアイデアですが、AppModelのfindコマンドをオーバーライドするだけなのでクリーンです。クエリにグループと要素を追加するだけで、HAVING句に変換されます。

public function find($type = 'first', $query = array()) {
    if (!empty($query['having']) && is_array($query['having']) && !empty($query['group'])) {
      if ($type == 'all') {
        if (!is_array($query['group'])) {
          $query['group'] = array($query['group']);
        }

        $ds = $this->getDataSource();
        $having = $ds->conditions($query['having'], true, false);
        $query['group'][count($query['group']) - 1] .= " HAVING $having";

        CakeLog::write('debug', 'Model->find: out query=' . print_r($query, true));
      } else {
        unset($query['having']);
      }
    }

    return parent::find($type, $query);
  }

ここで見つけました

https://groups.google.com/forum/?fromgroups=#!topic/tickets-cakephp/EYFxihwb55I

0
Matt

同じ問題がありました。内部コードを変更することは想定されていませんが、PaginatorComponentを開いて188行目を変更すると、次のようになります。

$count = $object->find('count', array_merge($parameters, $extra));

これに:

$count = $object->find(
             'count', 
             array_merge(array("fields" => $fields),$parameters, $extra)
         );

すべてが修正されます。 HAVING句を「グループ」に追加でき、COUNT(*)は問題になりません。

または、行を作成します。

$count = $object->paginateCount($conditions, $recursive, $extra);

$fieldsを含めるには:

$count = $object->paginateCount($fields,$conditions, $recursive, $extra);

その後、モデルのメソッドを「オーバーライド」して、find()に$ fieldsを含めるようにしてください。これで完了です!、= P

0
Sokar