FuelPHPでormのhas_many機能を用いたAND条件の検索を行いたいです。
解決済
回答 1
投稿
- 評価
- クリップ 1
- VIEW 2,442
発生している問題・エラーメッセージ
ormモデルでhas_manyの機能を使用し検索ページを作っています。
or_whereを用いた複数条件の OR検索は実装出来たのですが
複数条件全てを満たす AND検索がうまく実装出来ません。
...わかりづらい質問になってしまい申し訳ありませんが、宜しくお願いいたします。
該当のソースコード
下記のような構造のテーブルがあります
class Model_Hoge extends \Model_Orm
{
protected static $_properties = array(
"id",
"contents"
);
protected static $_has_many = array(
'fuga_tables' => array(
'key_from' => 'id',
'model_to' => 'Model_Fuga',
'key_to' => 'hoge_id',
'cascade_save' => false,
'cascade_delete' => false,
)
);
}
class Model_Fuga extends \Model_Orm
{
protected static $_properties = array(
"fuga_id",
"hoge_id",
"type"
);
protected static $_primary_key = array('fuga_id','hoge_id');
}
サンプルデータ
Model_Hoge
id | Contents |
---|---|
1 | AAA |
2 | BBB |
3 | CCC |
Model_Fuga
fuga_id | hoge_id | type |
---|---|---|
1 | 1 | search_type_A |
2 | 1 | search_type_B |
3 | 2 | search_type_A |
4 | 3 | search_type_B |
試したこと
// OR検索
//関連するFugaモデルのtypeがsearch_type_Aまたはsearch_type_BのHogeモデルのデータを取得する
// [成功] Model_Hogeの id => 1,2,3に該当するデータが取得できる
Model_Hoge::query()
->related("fuga")
->where("type","search_type_A")
->or_where("type","search_type_B")
->get();
// AND検索
//関連するFugaモデルのtypeがsearch_type_Aとsearch_type_Bの両方のデータを持っているHogeモデルのデータを取得する
// [失敗] Model_Hogeの id => 1に該当するデータを取得したかったが、取得件数は0件だった
Model_Hoge::query()
->related("fuga")
->where("type","search_type_A")
->where("type","search_type_B")
->get();
AND検索の失敗例のコードは検索結果が0件なのは当然なのですが、他にいい方法がわかりませんでした。
補足情報
PHP 5.6.19
DATABASE psql (9.3.11)
↑MYSQLでも構いません
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
0
実装したことのない機能だったので学習がてら挑戦してみました。
手をつける前に想像してたよりは一手間必要な感じでした。
構成はFuelPHP1.8 + MySQL5.6.21です。
re:試したこと
先に、試したことへの回答を。実際に発行されたクエリを確認するには
CodeProfilerが便利ですが、最後に発行されたクエリだけなら
DB::last_query()でも取得できます。まずは失敗するというand検索のほうから。
->where("type","search_type_A")
->where("type","search_type_B")
このあたりのメソッドで生成されるクエリが以下の内容です。
WHERE `type` = 'search_type_A' AND `type` = 'search_type_B'
「typeがAかつB」のものとなっています。条件の照合対象はあくまでテーブル上の1レコードなので
この条件では絶対にヒットしないということでした。続いて一見うまくいっているor検索の
ほうですがこちらも少し問題があるようでした。
WHERE `type` = 'search_type_A' OR `type` = 'search_type_B'
抽出されるfugaレコードが「typeがAもしくはBのどちらか」に限定されてしまうため、
もし実際はA、B、C、Dを持つhogeがあったとしても、A、Bだけがリレーションされた状態で
取得されてしまいます。(表示や・計算にfugaを使わないならセーフかも)
目標とするクエリ
一旦fugaテーブルから条件に適合するhoge_idを集計して、そのhoge_idを元に
hogeテーブルを検索するとよさげです。目標とするクエリはこんな感じ。
SELECT * FROM `hoge` LEFT JOIN `fuga` ON (`hoge`.`id` = `fuga`.`hoge_id`)
WHERE `hoge`.`id` IN (
SELECT `hoge_id` FROM `fuga`
WHERE `type` IN ('search_type_A', 'search_type_B')
GROUP BY `hoge_id` HAVING count(`hoge_id`) = 2
)
サブクエリ部分の説明
- 検索typeと一致するfugaレコードのhoge_idをすべて抽出
- hoge_idでグループ分け
- count(hoge_id) = 検索type数なら一致とみなす
FuelPHPで組み立てる
サブクエリ部分はDBクラスのクエリビルダで組み立てて、
メインはORMモデルに任せるのが楽そうです。
$input = ['search_type_A', 'search_type_B'];
$count = count($input);
//compileメソッドでサブクエリを文字列として取得
//or検索ならhaving句はいらない
$sub_query = DB::select('hoge_id')
->from('fugas')
->where('type', 'in', $input)
->group_by('hoge_id')
->having(DB::expr('count(hoge_id) = ' . $count))
->compile();
//DB::exprメソッドでサブクエリをエスケープ無しで生のまま埋め込む
$data['hoges'] = Model_Hoge::query()
->related('fugas')
->where('id', 'in', DB::expr("({$sub_query})"))
->get();
試行環境ではテーブル名だけ変えていましたが
上記コードでリレーションを保持したまま検索が行えました。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.35%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2016/04/26 13:04