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

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

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

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

Q&A

解決済

2回答

1228閲覧

Uncaught PDOException: SQLSTATE[HY000]: General error: 2031を解決したい

退会済みユーザー

退会済みユーザー

総合スコア0

PHP

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

0グッド

0クリップ

投稿2019/06/23 17:39

編集2019/06/23 17:46

お世話になっています。

独自フレームワークを作成しており、PDOのinsert、update、delete文を簡易化したいなと思って
ConnectPdoクラスを作成していますが、update()のところでGeneral errorエラーが返ってきます。

err

1Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 2031 in /var/www/html/myapp/app/core/lib/ConnectPdo.php:209 Stack trace: #0

流れは、以下の形でcontrollerから引数を渡してあげれば、
pdoのupdate文を実行させるようにしています。
(現在は、テストのためcontrollerで実行していますが、行く行くはmodelからConnectPdoクラスを使って実装します)

php

1function test() 2{ 3 $pdo = new ConnectPdo; 4 $pdo->connect_db(); 5 $table = 'test'; 6 $hash = array( 7 'name' => 'testtest', 8 'title' => 'テストです' 9 ); 10 $where = array('id' => 2, 'title' => 'fff'); 11 echo $pdo->update($table,$hash,$where); 12}

このエラーから下記のコードより「return $prepare->execute();」の部分でエラーが発生しています。

php

1<?php 2 /** 3 * update文 4 * hashはmust 5 * 6 * @param string $table table name 7 * @param array $hash setするカラムと値 8 * @param array $where where 9 * @param string $custom_cond カスタムwhere 10 * @return [type] [description] 11 */ 12 function update($table,$hash,$where=false,$custom_cond=false) 13 { 14 if(empty($hash)) return false; 15 16 /*---------- 17 * set組み立て 18 -----------*/ 19 $countHash = count($hash); 20 $array_keys = array_keys($hash); 21 $array_values = array_values($hash); 22 23 $implode_hash = ':'; 24 $implode_hash .= implode(' :',$array_keys); 25 26 $explode_hash = explode(' ',$implode_hash); 27 28 $i = 0; 29 $values = array(); 30 $sets_value = ''; 31 foreach($hash as $key => $value) { 32 $values[$i] = $value; 33 $sets_value .= $array_keys[$i] .' = ' .$explode_hash[$i]; 34 35 $i++; 36 37 if($i == $countHash) break; 38 39 $sets_value .= ','; 40 } 41 42 $sql = 'update ' .$table .' set ' .$sets_value; 43 44 45 46 /*---------- 47 * where組み立て 48 -----------*/ 49 if($where){ 50 $countWhere = count($where); 51 52 $keys_where = array_keys($where); 53 $values_where = array_values($where); 54 55 $keys_implode = ':'; 56 $keys_implode .= implode(' :',$keys_where); 57 58 $keys_explode = explode(' ',$keys_implode); 59 60 $wheres = ''; 61 $i = 0; 62 63 foreach($where as $key => $value) { 64 $wheres .= $keys_where[$i] .' = ' .$keys_explode[$i]; 65 $i++; 66 67 if($i == $countWhere) break; 68 69 $wheres .= ' and '; 70 } 71 72 $sql .= ' where ' .$wheres; 73 } 74 75 /*---------- 76 * PDO実行 77 -----------*/ 78 //custom_condがあった場合 79 if($custom_cond){ 80 $sql .= ' ' .$custom_cond; 81 return $this->_pdo->query($sql); 82 } 83 84 //custom_condがなかった場合は、prepareでexecute 85 $prepare = $this->_pdo->prepare($sql); 86 $array_values = array_values($hash); 87 88 // hash 89 for($i=0; $i<$countHash; $i++) { 90 if(is_numeric($array_values[$i])){ 91 $prepare->bindParam($explode_hash[$i],$array_values[$i],PDO::PARAM_INT); 92 }else{ 93 $prepare->bindParam($explode_hash[$i],$array_values[$i],PDO::PARAM_STR); 94 } 95 } 96 97 // where 98 if($where) { 99 for($j=0; $j<$countWhere; $j++) { 100 if(is_numeric($values_where[$j])){ 101 $prepare->bindValue($keys_explode[$j],$values_where[$j],PDO::PARAM_INT); 102 }else{ 103 $prepare->bindValue($keys_explode[$j],$values_where[$j],PDO::PARAM_STR); 104 } 105 } 106 } 107 108 return $prepare->execute(); 109 }

(エラーチェックなどは、後ほど実装していくつもりです。)

$whereのところで以下のようにすると、update文が実行できますが、
なぜ他のwhereの条件が入ってくると、実行されないのか。
なかなか解決できずにいます。

php

1$where = array('id' => 2);

ちなみに、 **echo $sql ** を仕込むとsql文は以下の形で生成されます。

php

1echo $sql; 2//update test set name = :name,title = :title where id = :id and title = :title 3return $prepare->execute();

コードが見辛くて申し訳ございません。
お力を貸していただければ幸いでございます。

よろしくお願いいたします。

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

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

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

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

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

m.ts10806

2019/06/23 20:47

bindParamとbindValueが混在しているのはどういった理由でしょうか?
guest

回答2

0

おなじtitleで二箇所していしているからでは?

ラベル付きではなく、疑問符でprepare処理をすると楽だと思います

投稿2019/06/24 00:51

yambejp

総合スコア114585

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

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

退会済みユーザー

退会済みユーザー

2019/06/25 13:23

mts10806さんにもご指摘いただきましたが、 prepareは複数のパラメーターマークを使用できないんですね.... そして、疑問符でもprepareを処理できるのですね! これまで、パラメーターマークを使用していたので、疑問符でも試してみます! ありがとうございます!
guest

0

ベストアンサー

理由としては、下記と思います。

PDOStatement::execute() をコールする際には、 文に渡すパラメータにはそれぞれ固有のパラメータマークを設定する必要があります。 エミュレーションモードが有効になっていない限り、 ひとつのプリペアドステートメントの中で、同じ名前のパラメータマークを 複数使用することはできません。

つまり「:title」が複数回出てきているため、PDO側のルールに引っかかっているということですね。
カラム名と合わせる必要はないので別名にするか、疑問符 (?) パラメータマークを利用してはいかがでしょうか。

それか、名前のパラメータマークを利用したいのであれば、特にupdateですし、Whereには「更新されないカラム」を指定されるのが通例ではないかなと思います。※一気に複数更新したいとかならまだ別ですが

あまり汎用的にしすぎるのも今回のような問題が起きることにもなりますので、なるべく通例に沿った作りのみを許容しておくようにしてもいいかもしれません。
どのように作ったとしてもイレギュラーというのは発生するものなので、イレギュラーをなるべく吸収しようと作った結果、あまり緩い作りになってしまってもフレームワークとしては良くありません。
イレギュラーはSQLを自分で作らせて直実行にしてもいいかもしれませんね。

個人的な意見ですが、私であればUPDATEとDELETEはWhereにキーのみ許容する作りを軸にします。

蛇足:
コメントで書きましたがbindParamとbindValueが混在する作りはあまり良くないと思います。
できればbindValueのみで統一しましょう。

投稿2019/06/24 00:48

編集2019/06/24 00:50
m.ts10806

総合スコア80765

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

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

退会済みユーザー

退会済みユーザー

2019/06/25 13:22

返答が遅くなってしまい、申し訳ございません。 前回に引き続き、ご回答ありがとうございます。 まず、bindParamとbindValueに関しましてはエラーがなぜ発生したのか分からず、 試しに実行してみたものを、こちらに転記してしまいました... やはり、bindParam or bindValueは統一した方が良いのですね。 修正いたします。 >つまり「:title」が複数回出てきているため.... こちらに関しましても、リファレンスまで載せていただきありがとうございます! :titleが複数使われることによるエラーだとは思ってもみませんでした。 先ほど試したところ、おっしゃる通りwhereでも指定していた「:title」が悪さをしていたようです! ありがとうございます! >Whereには「更新されないカラム」を指定されるのが通例ではないかなと思います こちら、存じ上げておりませんでした。 まだまだ知らないことだらけで、お恥ずかしいです。 いただいたアドバイスを参考に、修正を加えていきます! ありがとうございます!
m.ts10806

2019/06/25 21:50

解決されたようで何よりです >やはり、bindParam or bindValueは統一した方が良いのですね。 統一された方が良いのはもちろん、各所で言われているようにbindValueを採用してください。(違いとかで調べるとでてきます) update,deleteについてはデータベースの特性、「プライマリーキー」「主キー」が関係してきます。 利用者が自分以外にもいると変更される可能性のあるカラムが指定されると画面上はあるように見えて更新時に変わってた(つまり作業してる間に他の人が更新された)ということがあります。そうするとtitleのように更新されるカラムがあると「更新できない」現象が発生します。 なので、変更されないプライマリーキーになっているカラムを指定することで担保します。もちろん削除されれば更新はできないわけですが、それとこれとは別問題で、更新時に「全く同じtitleのデータがあったら?」と考えると自ずと答えは出ますね。キーになってないカラムは同じ内容が存在できますので。
退会済みユーザー

退会済みユーザー

2019/06/26 03:33

ご返答ありがとうございます。 bindValueとbindParamの違いを確認しました。 bindParamでは、変数の参照値がバインドされるので、bindParam()を実行後も値を変更できるということ、それに対しbindValueは変数の「値」が入るので、実行後の変更は利かない。ということがわかりました。 ということは、mts10806さんのおっしゃる通り、bindValueを使用したほうが良いですね。 ありがとうございます。 >update,deleteについてはデータベースの特性.... こちら、すごく勉強になります!!! DB周りは、これから勉強していこうと思って、とりあえずPDOを汎用的に使えるようにすることだけに注意が集中してましたが、データの更新は、特に気を遣わないといけないところでした。 仰る通り、同じタイトルのデータがあったとき、他のユーザーの値まで、更新されるという可能性を考慮するべきでした。 ということで、先日頂きました以下のご回答より、Whereに一意となるキーのみ許容するように修正しました。 >個人的な意見ですが、私であればUPDATEとDELETEはWhereにキーのみ許容する作りを軸にします。 mts10806さんのご回答は、経験の少ない私にも理解しやすく、どう実装すべきかまで教えてくださるので、とても参考になります! 本当にありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問