アプリ版:「スタンプのみでお礼する」機能のリリースについて

まず、foreachと連想配列が苦手で、なかなか理解できずにいる者ですが、
先日の私の質問https://oshiete.goo.ne.jp/qa/12629824.htmlで、
No2&3さんのご指摘もなるほどと思いながら格闘して自分で実装がやりきれず、
No1さんの以下のサンプルを本番に適用してみているところです。

<?PHP
$kyugi=filter_input(INPUT_GET,"kyugi",FILTER_VALIDATE_INT,FILTER_REQUIRE_ARRAY);
$checked["kyugi"]=[0=>"",1=>"",2=>""];
if($kyugi){
foreach($kyugi as $val){
$checked["kyugi"][$val]=" checked";
}
}
?>
<form>
<label><input type="checkbox" value="0" name="kyugi[]"<?=$checked["kyugi"][0]?>>野球</label>
<label><input type="checkbox" value="1" name="kyugi[]"<?=$checked["kyugi"][1]?>>蹴球</label>
<label><input type="checkbox" value="2" name="kyugi[]"<?=$checked["kyugi"][2]?>>庭球</label><br>
<input type="submit" value="send">
</form>


上記URLと同じ趣旨のしつこい質問ですみませんが、ラジオボタンであれば、MySQLの1カラムに対して0、1、2、3...などと異なる数値が格納されていくのですが、チェックボックスだと、選択肢の数だけカラムをつくって0か1を格納するしかないのでは?という疑問が晴れません。

そして、上記No2&3さんによれば、そのようなやり方は悪手だということでした。

確かにチェックボックスが野球 蹴球 庭球とあるからといって
MySQL側も野球 蹴球 庭球と複数の列(column)を
設けるなんて、場当たり的でスマートではない、と理解はしてます。

そして、No3さんのご教示も理屈では理解できますが、自分には高度過ぎて実装ハードルが高そうです・・・。

私が考えていたのは、その「悪手」を地でいってるのですが(涙

●DB存在するチェックボックスの値(value=1)を受け取るmytable
create table
`kyugi1` char(1) default NULL COMMENT '好きな球技(野球)',
`kyugi2` char(1) default NULL COMMENT '好きな球技(蹴球)',
`kyugi3` char(1) default NULL COMMENT '好きな球技(庭球)',

●データが蓄積されていくイメ―ジ
   kyugi1 kyugi2 kyugi3
    野球 蹴球 庭球
回答者1  1  0  1
回答者2  1  0  0
回答者3  1  1  1
回答者4  1  1  0
回答者5  0  0  1
 :

●レコード挿入分
insert into mytable (`kyugi1`,`kyugi2`,`kyugi3`) values (1,1,0);


【質問です】
(1) 上記のNo1さんのサンプルをこのまま貼り付けても動きそうな気がしたのですが、実行すると以下の3行目で止まっております

$checked["kyugi"]=[0=>"",1=>"",2=>""];

↑これは、0=>"野球",1=>"蹴球",2=>"庭球" ・・・などと書くべきところではないですよね?

(2) 上記のNo1さんの検索フォームですが、value="0"、value="1"、value="2"ではなく、全てvalue="1"であるべきではないでしょうか。

(3) 上記のNo1さんの検索フォームですが、nameは、kyugi[]という配列ではなく、テーブル(DBではmytable)に合わせて、name="kyugi1"、name="kyugi2"・・・でなくてはならないのではないでしょうか?

(4) あと、上記のNo1さんのフォームを使わせていただく場合、SQL側は(MySQL5です)は、kyugiテーブルに、ラジオボタン的に、0か1か2・・・と格納されているパターンであれば

if(is_array($_GET["kyugi"])){
$sql = "select * from mytable where kyugi in ('" . implode("','", $_GET["kyugi"]) . "')";
}

で照会できますが、0か1の場合は、どういうスマートな書き方がありえるなりますでしょうか。

where kyugi1 = 1 or kyugi2 = 1 or kyugi2 = 1 でもシンプルかと思いきや、問題は全然そんな簡単ではなく、「チェックされたものだけをORで結ぶSQL」って
野球1 蹴球1 庭球1
野球1 蹴球1 庭球0
野球1 蹴球0 庭球0
野球0 蹴球0 庭球0
野球0 蹴球0 庭球1
野球0 蹴球1 庭球1
こんないろんなパターンSQL文を構築しなければならないですよね。。。野球 蹴球 庭球の3つならこの6パターンだけですみますが、本番環境はチェックボックスが7つもあって、
網羅的なSQLをつくるとスゴイことになってしまうのです。7×7=49ではなく、もっとパターンありますよね??

ラジオボタンの扱いはいろいろ前例を持っていますが、チェックボックスの扱いが本当にわけわかってなくてすみません。。。

A 回答 (4件)

悪手であっても動けば正義でしょう。



1.
php 5.4 で導入された配列の短縮構文が使えない環境でしょうか?
以下のように修正してください。

修正前: [ k=>v, ... ]
修正後: array( k=>v, ... )

2-4.
配列にしたほうが処理が綺麗ですが、
個別の名前にして愚直に処理する考えもありです

<input type=checkbox name=k1 value=y>野球
<input type=checkbox name=k2 value=y>卓球
<input type=checkbox name=k3 value=y>水球
<button type=submit name=z>send</button>

-- GET 引数を判定して変数に入れる
$k1 = isset($_GET['k1']) && $_GET['k1'] == 'y';
$k2 = isset($_GET['k2']) && $_GET['k2'] == 'y';
$k3 = isset($_GET['k3']) && $_GET['k3'] == 'y';
$first = !isset($_GET['z']);

-- 変数からチェック済みか判定
$ch = array( 'k1' => '', 'k2' => '', 'k3' => '' );
if ($k1 || $first) $ch['k1'] = ' checked';
if ($k2 || $first) $ch['k2'] = ' checked';
if ($k3 || $first) $ch['k3'] = ' checked';

-- 変数から検索
$w = array();
if ($k1) $w[] = '野球 = 1';
if ($k2) $w[] = '卓球 = 1';
if ($k3) $w[] = '水球 = 1';
if (count($w) > 0) {
_ $wh = implode(' OR ', $w);
_ $sql = "select ... where ... and ($wh)";
} else {
_ $sql = チェックなしの場合();
}
    • good
    • 1
この回答へのお礼

Ogre7077さん、お礼が遅くなってすみません。
回答いただいて2日間、早くご回答を組み込んでみたくて、ようやく本件に戻ってこれました。

>動けば正義

はい、中長期期にはスマートにしたい目標はありますが、
足下は、たとえ愚直でも、拡張性がなくても、場当たり的なものでも、
思ったように動くのであれば、とりあえず、これほどありがたいことはありません。

そして、結果的にもうまくいきました!!

見事に「チェックしたものだけがorで結ばれた」、つまり
and (野球=1 or 蹴球=1 or 庭球=1) ・・・のようなSQL文が出力されたときには「そうか、こうやるのか!」と鳥肌たちました。


動作報告といたしましては、

(1) この部分は無くても思うような動作ができました。
(変数からチェック済みか判定、というのが何を判定しているのか
私の知識ではわからなかったですが・・・)

> $first = !isset($_GET['z']);
> -- 変数からチェック済みか判定
> $ch = array( 'k1' => '', 'k2' => '', 'k3' => '' );
> if ($k1 || $first) $ch['k1'] = ' checked';
> if ($k2 || $first) $ch['k2'] = ' checked';
> if ($k3 || $first) $ch['k3'] = ' checked';

(2) 以下の3変数の前のアンダーバー「_」は、不要かと削除しました。
チェックなしの場合は、WHERE条件つけずに全部SELECT(抽出)するという
仕様にして、else以下はなしにしました。

> if (count($w) > 0) {
> _ $wh = implode(' OR ', $w);
> _ $sql = "select ... where ... and ($wh)";
> } else {
> _ $sql = チェックなしの場合();
> }

とにかく、ここで1週間つまずいて、気になって他の部分に手を付けるモチベーションが上がらなかったので、精神衛生上も本当に助かりました。ありがとうございました。

お礼日時:2021/10/27 01:47

No2です。



1点修正します。
野球のチェックした人数を数えるセレクト文ですが、
or null
が必要でした。

select count((kyugi & power(2,0)) > 0 or null) from mytable;
になるようです。


>HTML検索フォームの方が少しイメージわかなくて

HTMLのform要素は、おそらく既存のもので良いと思います。
type が checkbox の input がいくつかあって、
submit する input が1つある、ということですよね。

valueは、0,1,2で、nameがkyugi[]となっていますから、
POSTで受け取るなら、
$kyugi = $_POST['kyugi'];
などで受け取れると思います。
(エスケープ処理は別途必要とは思います)

$kyugi には、もしチェックされていれば
[0,1,2] とか [0,2] などの配列が入るはずですから、

$counter = [];
if($kyugi){
foreach($kyugi as $k){
array_push($counter, ' count((kyugi & '.(2 ** $k).') > 0 or null) ');
}
$sql = 'select '.implode(',',$counter).' from mytable;';
}

みたいな感じで、$sql文ができるのではないかと思います。


とはいえ、ビット演算はあとからメンテナンスするときに
思い出すのが大変かもしれません。

それに、選択肢が増えるとき、
たとえば卓球を増やすとき、
野球
蹴球
庭球
卓球
と最後に増やしていくぶんには大丈夫なのですが、

野球
卓球
蹴球
庭球
のように、既存の間に入れ込もうとすると、
数字が合わなくなりますから、注意が必要になります。

なので、この回答は参考程度にしていただければと思います。
    • good
    • 0
この回答へのお礼

sukitaroさん、詳しい補足ありがとうございます、思いもよらない解法もあるものだとうなりながら、また理解が難しいところも噛みしめながら読ませていただきました。

>既存の間に入れ込もうとすると、数字が合わなくなりますから、注意

こういう落とし穴までは到底考えが及びませんでしたが、
ご提示の理屈というかアイデアは本当にはっとさせられました。

重ねて御礼申し上げます。

お礼日時:2021/10/27 01:34

0か1で、チェックのあるなしを把握するなら、


ビット演算でできるような気がします。


選択肢1(野球)が2のゼロ乗、
選択肢2(蹴球)が2の1乗、
選択肢3(庭球)が2の2乗、
みたいにして、全部を足せばint型で管理できます。
32ビットなら、選択肢は32個まで増やせるはずです。

列も1つで済むので、
データが蓄積されていくイメ―ジは、
   kyugi
回答者1  5(2進数の101)
回答者2  1(2進数の001)
回答者3  7(2進数の111)
回答者4  3(2進数の011)
回答者5  4(2進数の100)

レコード挿入は、
insert into mytable (`kyugi`) values (1*power(2,0) + 1*power(2,1) + 0*power(2,2));

野球にチェックした人数をとり出すなら、
select count((kyugi & power(2,0)) > 0) from mytable;

みたいな感じになるのではないかと思います。
    • good
    • 0
この回答へのお礼

sukitaroさん、ご回答&コードまでお示しいただきありがとうございました。
まったく新しいアプローチでにときめきました。

仰せのビット演算をネットでも調べてみましたが、高速化にもなる結構な高等テクニックだと見受けました。

HTML検索フォームの方が少しイメージわかなくて組み込めるか自信ないですが、研究してみます。

お礼日時:2021/10/24 13:59

見た感じ、回答No.1(yambejpさん)は、HTML出力部についてなので、(1)~(3)は、回答No.1どおりで良いと思います。


データベースアクセスのコードを考えるところは、質問者(litton101さん)が頑張るということで。

(4)については、手っ取り早いのは、質問と選択肢をExcelで管理して、そこから必要なSQL文を計算式・マクロ使って組み立てるとよいです。
もっと良い方法はいろいろありますが。
    • good
    • 0
この回答へのお礼

osamuyさんあちこちでお世話になり、ありがとうございます。越えられそうで越えられない壁でとん挫しそうでした。。。

余談ですが、
https://teratail.com/questions/171166
このQ&Aなんかも私の悩みと瓜二つですが、2つに割ったわけわからないTableから、RIGHT JOINで元質問の表になるというのがどうしても解せないです。

お礼日時:2021/10/24 13:56

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!