はもちくわ

コードについて自分なりの解釈を書いてます。

【PHP】SQLでプレースホルダにバインドするときのエラーを回避した話

バイト先で自分のミスを楽して確実に減らすため、ExcelVBAで業務効率化システム作って運用してみたら、想像以上に業務改善が達成されてしまい、、、、、そのせいで、一時手当をもらってしまい、、、、、もう飽きたからと引くにも引けず、、、、結局そのまま維持管理、ブラッシュアップすることになり、、、、やっと、誰でも使えるレベルまで完成しまして、、、、一段落ついたので、PHPの勉強に戻ってまいりました。

 

いやー、改めてExcelでの処理は遅いと思いました。
シート上で計算せずに、2次元配列で計算すれば多少早いですが、それでも結果をシートに出力すると、動いているか不安になるぐらい遅くなりますね。特にページ、印刷設定を入れるとファイル飛んだ?!って心配になるぐらい遅い。。。。PHPSQLのほうが断然早いと思いました。PDFでもExcel形式でもCSVでも出力できるし便利ですよね。

 

さて、私はPHPからMySQL使っていますが、データを追加(INSERT)、更新(UPDATE)する時、

という流れで動かしています。
でも、、、カラムが多くなると、必ずと言っていいほどエラーが出ます(笑)
原因の大多数は、カラム名、変数名のミスやコロンの抜けやら、、、単純なコーディングミスです。
結果、SQL文のプレースホルダの数とバインドする数がずれてしまったり、SQL文自体がおかしくなってエラーが出てしまうわけです。
スキルが高くない私からすれば、シンタックスエラーとかゴニョゴニョ言われれば、コード自体が悪いのか?と疑って、原因を調べるコードを差し込み、何度も実行させて、、、、、、やっと発見すると、ただの単純ミスかぁ。。。。となる。

ミスを修正して動いてしまえば何も問題はないですが、やはり、この入力ミスは、なるべく減らしたいと思いまして、こんな方法を使いはじめました。
それは、、、、、なるべく自分で入力しない(笑)ということです。



まず、phpMyAdminからカラム名をエクスポートします。別にどんな方法でもいいですが、私はSQL形式で画面出力させて、エディター上にコピペしています。使うのはカラム名だけですが、ドバっと全部ペーストしたあとに不要な部分を消し、コメントアウトさせておきます。ここでの注意点ですが、SQL経由でエクスポートしたカラム名は、バッククウォートでくくられています。エディターの置換機能を使ってクォーテーションに変えておかないと、見た目が異常に似てるので、そのまま使ってしまうと、あとあと原因を探るのがキツくなります(笑)

文だけでは寂しいので、MySQLの画面を使って補足します。

フォーマットは結構あり、シングルクウォートでくくられたPHP配列形式もあります。ですが、情報量が多いので私は使ってません。おそらく、上手に使えばそのままコードに組み込めるんだと思います。

このSQLで出力するのは、テーブルを別のサーバーに移す時によく使います。私は、ローカル環境で試しながらコード書いて、エラーが無くなったらサーバーに上げるようにしているので、テーブルを大きく作り変えたり、新規で追加したときは、一気に本番サーバーに移動できるので重宝しています。まあ、たまにバージョン差でエラーになりますが、全部作り直すより断然効率的ですからね。

話がそれますが、私は、コードを書くエディターに、Atomを使っています。単独の開発なのでGitHubを使うわけでは無いのですが、コードの色分けがわかりやすく、使った変数は入力支援で出てきたりしてくれるので、日本語アドオンを組み込んで便利に使ってます。でも、、、、残念ながら2022年の年末で開発終了ですって。。。

 

次は、コメントアウトさせておいたカラム名を一次元配列にぶち込みます。特に変わった技術は使いません。
array( )のカッコ内にカンマで区切って並べるだけです。このとき、更新させたいカラム名だけにするなど上手に応用すれば良いですが、MySQLでNullを許可してないカラムを省略してしまうと、エラーになりますのでご注意ください。

ここまでが前準備です。



ここからが本題。まずSQL文を作って変数に入れます。SQLの作成は、先程作ったカラム項目の配列を使い、foreachとimplodeを使って作成します。極力、カラム名は自分でタイプしないようにするのです!
この作成のときに注意するのは、命令句とテーブル名、カラム名が繋がらないようにしましょう。分割して変数に入れ込むので気づきにくいです。これに気づかずにSQLでエラーが頻発して、いやになってあきらめてしまいがちです(笑)

具体的に例を上げてSQLを書いてみます。
では、テーブル名「name_list」として、カラム名に「id」「name」「tel」「address」「mail」の5つがあるとします。このテープルに追加(INSERT)するSQLを書くとしましょう。コロンで作ったプレースホルダにバインドする形とします。

$co = array('id','name','tel','address','mail');

//配列をカンマ区切りでつなげる
$co_list = implode(",",$co);

//ホルダ用にコロンつきでリストを作る
$holder_list = ":";
$holder_list .= implode(",:",$co);

//SQL文(クエリ)を作る
$sql = 'INSERT INTO name_list ('.$co_list.') VALUES('.$holder_list.')';

/*
変数$sqlに入っているSQL文は
 INSERT INTO name_list(id, name, tel, address, mail) VALUES(:id, :name, :tel, :address, :mail)
と同じ。
*/

こんな感じになります。まだSQL文としては簡単なINSERTで、カラム名が5つですから、直接入力しても間違えないでしょうけど、これがカラム名20オーバーになると、、、あまり考えたくないですよね(笑)実際、お店の予算編成とか経営成績とか考えてください。やばいっすよね。
この例では、foreachを使わずにSQL文を作成できていますが、ちょっと工夫が必要な文にするときは使います。UPDATEなんかは、カラム名=:カラム名で作りますから使ったほうがよさそうです。

 

次に、bindValueでプレースホルダにバインドさせるのですが、ここも自分でカラム名を書きません!foreachでループさせます。
SQLのデータを追加したり、更新したりするときって、元データを変更した後になりますから、ほとんどがSELECTでデータ抽出して、それを連想配列に入れたあとになりますよね。
そうしますと、連想配列のキーには、下のような感じで、すでにカラム名がキーになっている状態なわけですよ。
 変数名(配列)[データ数][カラム名]ということは、一次元配列に入れたカラム名がそのまま配列の2次元目のキーとして使えるというわけです!
ここで気をつけるのは、カラム名の配列キーとデータテーブルのキーは当然別物なので混同しないようすることですかね。ごちゃごちゃすると同じキーの変数を使っておかしなことになりますのでご注意ください。
もし、連想配列からでなく、変数で代入する値を持っていたら、カラム名配列をうまく使った変数名をつけておいて、可変変数(${})で対応してもいいです。

そして、最後に実行させます。

 

私はこれでエラー頻度が減りました(笑)
自分で入力しない!これ大事(笑)



PHPのマニュアルをさらっと見てみたのですが、どうもプリペアドステートメント使うなら、もっと簡単にバインドする方法があるみたい。。。私はまだカラム数だけbindValue使ってますわ。。。。スキルしょぼっ!!(笑)