質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

PDO

PDO(PHP Data Objects)はPHPのデータベース抽象化レイヤーです。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

6回答

3654閲覧

PHPでのSQL文の書き方について

erika.m

総合スコア46

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

PDO

PDO(PHP Data Objects)はPHPのデータベース抽象化レイヤーです。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

1クリップ

投稿2016/06/15 11:42

編集2016/06/15 16:10

PHP、MySQLでWebシステムを作成中の独学初心者です。

自分の中で、出来るだけHTMLとSQL文を混ぜたくないという思いがあります。
この考えが合っているのかは分かりませんが、ソースの可読性を高める為になると信じているからです。

そのため、SQL用のファイル/クラスを作り、そこにSQL単位で関数を書きSQL文を書いて行っています。
(パーミッションの設定もしやすい)
今まで短いSQL文だけだったので、自分の中では分かりやすい!いい感じだ!と喜んでいたのですが、カラムの多いテーブルのINSERT/UPDATE文でつまづきました。

引数が大量になり何とも不恰好なソースになってしまったのです。

そこで皆さんはどのような書き方をしているのか教えてもらいたいです。
どうぞよろしくお願いします。

ちなみに今まではこんな感じで書いてました。


php

1// 元クラス 2class DATABASE{ 3 protected $pdo; 4 function __construct($dsn, $user, $pass, $options){ 5 try { 6 $this->pdo = new PDO($dsn, $user, $pass, $options); 7 $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 8 $this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); 9 } catch (Exception $ex) { 10 die($ex->getMessage()); 11 } 12 } 13} 14// ログインページで使用 15class LOGIN extends DATABASE{ 16 function login($id, $pass){ 17 try { 18 $sql = "-------ログインチェック用SQL文------"; 19 $stmh = $this->pdo->prepare($sql); 20 $stmh->bindValue(":id", $id, PDO::PARAM_STR); 21 $stmh->bindValue(":pass", $pass, PDO::PARAM_STR); 22 $stmh->execute(); 23 $result = $stmh->fetch(PDO::FETCH_ASSOC); 24 return $result; 25 } catch (Exception $ex) { 26 die($ex->getMessage()); 27 } 28 } 29 function aaaa(){...} 30 function bbbb(){...} 31} 32// 表出力ページで使用 33class LIST extends DATABASE{ 34 function getList($case){ 35 try { 36 $sql = "-------表出力用SQL文------"; 37 $stmh = $this->pdo->prepare($sql); 38 $stmh->bindValue(":case", $case, PDO::PARAM_INT); 39 $stmh->execute(); 40 $result = $stmh->fetch(PDO::FETCH_ASSOC); 41 return $result; 42 } catch (Exception $ex) { 43 die($ex->getMessage()); 44 } 45 } 46 function cccc(){...} 47 function dddd(){...} 48}

補足

↑は上手くいっている部分しか書いてませんでした。
今までの書き方だとINSERT/UPDATEしようとすると↓こうなってしまって、他にスマートな書き方はないかと質問しました。
あくまで例なのでDB設計はスルーで

PHP

1// データ更新用phpで使用 2class UPSERT extends DATABASE{ 3 function insert($name_sei, $name_mei, $name_seikana, $name_meikana, $age, $sex, $hight, $weight, $company, $tel1, $tel2, $address){ 4 try { 5 $sql = "-------レコード挿入SQL文------"; 6 $stmh = $this->pdo->prepare($sql); 7 $stmh->bindValue(":...", $..., PDO::PARAM_INT); 8 ... 9 ... 10 $stmh->execute(); 11 } catch (Exception $ex) { 12 die($ex->getMessage()); 13 } 14 } 15}

例えば、連想配列に格納してそれを受け渡しする(回答を参考にしました)
例えば、受け取る変数分クラスにプロパティを用意しておいて、渡す側で直接そっちに格納する(これがいいかな?)
例えば、そもそもSQL文は関数に入れずに直接書くべき
などなど

すみません、流行りが分からなかったので...
一般的にはこうします、こうするのがよりベターです、というご回答があればぜひお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答6

0

ベストアンサー

方向性としてはとても的を射ていると思います。
既存の優れたO/Rマッパーや、
フレームワークのモデルの考え方を参考にされるととても勉強になると思います。

その上で、以下は私の個人的な感想というか考えになります。

まず、引数が多いことは特に問題にはならないと考えます
問題になるのは、

  • 引数のうち、必然性の無いオプション的な項目が多すぎる(メソッドになんでもやらせようとした結果、責任範囲があいまいになる)
  • 引数によって振る舞いが変わりすぎる

ようなケースかと思います。
特定のテーブルに対して挿入や更新を行うメソッドにおいて、
フィールドの数だけ引数が存在するのは引数に必然性があるので全く問題無いと思います。

また、下手に連想配列で渡してしまうと、
その引数のチェックをメソッド内で行う必要が発生し、
見通しの悪いソースになってしまうことがあります。

これをどうクリアするかは色々な方法がありますが、
例えばタイプヒンティング
参考URL
を使い、
引数はオブジェクトで渡す。
引数のオブジェクトとしての正当性はそのクラスのコンストラクタで保証する。

というような方法があります。
例えばこんな感じでしょうか。
*実際には抽象クラスを使って、テーブル単位での共通の振る舞いを記述するケースが多いかと思います。
*既存のO/Rマッパーやフレームワークの方が確実に参考になります。

クラス定義

PHP

1class member{ 2 private $first_name = ""; 3 private $family_name = ""; 4 5 public function __construct($first_name,$family_name){ 6 //引数の正当性はここでチェックする。問題があれば例外を吐いて死ぬ 7 if(//menberレコードとして必須条件を記入){ 8 $this->first_name = $first_name; 9 $this->family_name = $family_name; 10 return true; 11 } 12 // 13 throw new Exception('memberのインスタンス化に失敗'); 14 } 15 //フィールドにあたる部分を連想配列で取得 16 public function getFieldsAsASSOC(){ 17 return array("first_name" => $this->first_name, "family_name" => $this->first_name); 18 19 } 20 21 22} 23class menberUpdater extends updater{ 24 public function update(member $menber){ 25 //memberオブジェクトの正当性は自身に保証させるので、ここでチェックする必要は無し 26 try { 27 $sql = "-------レコード更新SQL文------"; 28 $stmh = $this->prepare($sql); 29 $stmh->execute($member->getFieldsAsASSOC()); 30 //update出来たかチェックして、成功してたらtrueを返すような処理を書く 31 } catch (Exception $ex) { 32 die($ex->getMessage()); 33 } 34 } 35 36} 37

実行

PHP

1 2// 3 4try{ 5 6//入力値がおかしかったらここで例外 7$menber = new member("tanaka","taro"); 8$updater = new memberUpdater(); 9$updater->update($menber); 10}catch($e){ 11//エラー処理 12} 13

投稿2016/06/15 18:52

編集2016/06/15 18:53
tanat

総合スコア18713

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

erika.m

2016/06/17 04:20

ありがとうございます。 実際のソースがとても参考になりました。
guest

0

ある程度以上大掛かりになるのであれば、直接PHPで書くより、何かしらのフレームワークを使ったほうがいいのではないかと思います。

フレームワークと言っても、「設定より規約」型のCakePHPから、MVCの枠組み程度のCodeIgniterまで、いろいろあります。比較検討した上で、自分にあったものを使ってみるのがいいでしょう。

投稿2016/06/15 13:23

maisumakun

総合スコア145184

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

erika.m

2016/06/15 16:12

PHPのフレームワーク、使ったことがないので調べてみます!
guest

0

もっと単純にプリぺアド処理分を?とそれに適合する配列になるよう調整すればよいでしょう
例えば

INSERT INTO テーブル VALUES(?,?,?);

に対して配列を

$data=(1,2,"test");

的に用意すれば

$stmt = $pdo->prepare( $query); $stmt->execute($data);

のような処理で行けます
このほうがバルクインサート処理をするときにも楽です

投稿2016/06/16 06:06

yambejp

総合スコア114843

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

型別に別途クラスを作っておいて、カラムをプロパティに持たせて、型クラスのインスタンスを代入するのはどうでしょう?型チェックや必須項目等、型クラス側にプロパティを持たせてチェック。
データは皆さんおっしゃるように配列でデータを渡します。
こんな雰囲気で。(あちこちエラーあるかも)

php

1private $elms = new array(); 2 3public insert($arr){ 4 try{ 5 $this->setElms($arr); 6 $this->sqlExec(); //elmsを使ってSQLを作成し実行 7 }catch (Exception $ex) { 8 die($ex->getMessage()); 9 } 10} 11private function init(){ // コンストラクタで実行 12 $elms['name'] = new varcharElm('notnull'); 13 $elms['age'] = new intElm(); 14} 15private setElms($arr){ 16 foreach ( $elms as $key => $val ) { 17 if(array_key_exists($arr,$key)) 18 $elms[$key]->setVal($val); //型エラー等問題があればsetval側でエラー 19 }else{ //keyに対応するデータがなければ初期化 20 $elms[$key]->clear(); 21 $elms[$key]->req(); //notnull等問題があればreq側でエラー 22 } 23 } 24} 25private sqlExec(){}

投稿2016/06/15 17:28

編集2016/06/15 17:46
hirohiro

総合スコア2068

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

引数が大量……ですか?
見たところ別段多くないと思いますよ。

参考までに引数をまとめたいなら引数を一つだけにして、連想配列で渡すという方法はあります。

マニュアルのですが具体例です。

php

1<?php 2/* Execute a prepared statement by passing an array of insert values */ 3$calories = 150; 4$colour = 'red'; 5$sth = $dbh->prepare('SELECT name, colour, calories 6 FROM fruit 7 WHERE calories < :calories AND colour = :colour'); 8$sth->execute(array(':calories' => $calories, ':colour' => $colour)); 9?>

引用元 http://php.net/manual/en/pdostatement.execute.php

追記2

SQLの周りのコードを綺麗にするならこうです

php

1 //テストデータ 2 $example_name_sei = 'a'; 3 $example_name_mei = 'b'; 4 $example_name_seikana = 'a'; 5 $example_name_meikana = 'b'; 6 $example_age = 5; 7 $example_sex = 'm'; 8 $example_hight = 60; 9 $example_weight = 70; 10 $example_company = 'hoge'; 11 $example_tel1 = 111; 12 $example_tel2 = 111; 13 $example_address = 'hogehoge'; 14 15//これを別のところで作る 16$hogedatas = array( 17 ':name_sei' => $example_name_sei, 18 ':name_mei' => $example_name_mei, 19 ':name_seikana' => $example_name_seikana, 20 ':name_meikana' => $example_name_meikana, 21 ':age' => $example_age, 22 ':sex' => $example_sex, 23 ':hight' => $example_hight, 24 ':weight' => $example_weight, 25 ':company' => $example_company, 26 ':tel1' => $example_tel1, 27 ':tel2' => $example_tel2, 28 ':address' => $example_address 29); 30 31function InsertRecord($array){ 32 try{ 33 $sql = 'INSERT INTO hogetable SET 34 name_sei = :name_sei, 35 name_mei = :name_mei, 36 name_seikana = :name_seikana, 37 name_meikana = :name_meikana, 38 age = :age , 39 sex = :sex , 40 hight = :hight, 41 weight = :weight, 42 company = :company, 43 tel1 = :tel1 , 44 tel2 = :tel2 , 45 address = :address '; 46 $stmh = $this->prepare($sql); 47 $stmh->execute($array); 48 }catch (Exception $ex) { 49 die($ex->getMessage()); 50 } 51} 52 53 54//どっかで発火 55InsertRecord($hogedatas); 56

投稿2016/06/15 13:35

編集2016/06/15 16:18
oskbt

総合スコア1895

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

erika.m

2016/06/15 16:16 編集

すみません、多くなった関数は途中で変だと感じて書くのを止めたので載せてないです。 -----補足で追記しました。----- executeに連想配列で渡すというのも試してみます。
guest

0

項目が多いのがあれだったら

PHP

1function ins($table, $data) { 2 $key = array_keys($data); 3 $val = array_values($data); 4 $sql = 'insert into ' . $table 5 . ' (' . implode(',', $key) . ')' 6 . ' VALUES(' . implode(',', $val) . ')'; 7}

こんな感じで。
ただし、文字はシングルクォーテーションでくくるとか
エスケープどうするかとか考えなきゃいけないことは
ありますけどね。

投稿2016/06/15 11:57

takasima20

総合スコア7458

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

erika.m

2016/06/15 16:14

イレギュラーへの対応やエスケープが難しそうですね。 参考にします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問