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

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

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

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

Q&A

解決済

3回答

1612閲覧

functionでプリペアードステートメントを実行して値を返したい

qopllqopllqop

総合スコア36

PHP

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

0グッド

0クリップ

投稿2016/05/06 21:19

###前提・実現したいこと
PDOでDB接続し、phpでコンバージョンの分析ツールを作成しています。

画面上に「検索ワードトップ10」「症状トップ10」「年齢別トップ10」と、同じ処理を3つ表示させるため、functionを使って処理を行おうとしています。

functionを使用せずに記述した場合は問題なく表示されるのですが、functionを使用すると以下のようなエラーメッセージが表示されてしまいます。

function内でプリペアードステートメントを使って処理する際に、欠けている知識についてご教授願えれば幸いです。

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

###発生している問題・エラーメッセージ

Fatal error: Call to a member function fetchall() on a non-object in /home/********/www/conversion/index.php on line 34

###該当のソースコード

php

1function topten($str){ 2 global $db; 3 require_once('connect_by_pdo.php'); 4 $template = "SELECT $str,count(*) AS count_num FROM t_conversion WHERE user_id=$user_id GROUP BY $str ORDER BY count_num DESC LIMIT 0,10;"; 5 $stmt = $db->query($template); 6 $row = $stmt->fetchall(PDO::FETCH_ASSOC); 7 foreach($row as $key => $value){ 8 ${$str} = $value['{$str}']; 9 $count_num = $value['count_num']; 10 $all_row .= "<tr><td>${$str}</td><td>$count_num</td></tr>"; 11 } 12 return $all_row; 13} 14

###試したこと
関数内にデータベース接続に関する内容を記述した(connect_by_pdo.php)
接続に使われている$dbを関数内でグローバル宣言した

###補足情報(言語/FW/ツール等のバージョンなど)

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

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

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

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

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

takasima20

2016/05/06 21:42

connect_by_pdo.php も提示できますか。
qopllqopllqop

2016/05/06 21:55

はい!追記させていただきます。
guest

回答3

0

ベストアンサー

プリペアードステートメントになってませんが・・・

予測として、

PHP

1$template = "SELECT $str,count(*) AS count_num FROM t_conversion WHERE user_id=$user_id GROUP BY $str ORDER BY count_num DESC LIMIT 0,10;"; 2$stmt = $db->query($template); 3$row = $stmt->fetchall(PDO::FETCH_ASSOC); 4```この`$template`の中の変数が設定されていない、または間違っているんじゃないでしょうか? 5それで、`query`したときに失敗して`PDOStatement`オブジェクトが返ってきてないので、`fetchAll`が無い、と言われているわけです。 6 7どちらにしても、`connect_by_pdo.php`を見てみないことには何とも言えません。 8 9<ぼそ> 本当にプリペアードステートメントにしなくていいんですか? </ぼそ>

投稿2016/05/07 03:55

shi_ue

総合スコア4437

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

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

qopllqopllqop

2016/05/07 07:23

おっしゃる通り、プリペアードステートメントになっていませんでした(汗)。何を思い込んでいたのか、お恥ずかしい話です。$templateの中の$user_idが関数内で定義されていなかったことが原因でした。 迅速なご回答ありがとうございました。
guest

0

以下のように修正してください。

Before

php

1$template = "SELECT $str,count(*) AS count_num FROM t_conversion WHERE user_id=$user_id GROUP BY $str ORDER BY count_num DESC LIMIT 0,10;"; 2$stmt = $db->query($template);
After

php

1$template = "SELECT $str,count(*) AS count_num FROM t_conversion WHERE user_id=? GROUP BY $str ORDER BY count_num DESC LIMIT 0,10"; 2$stmt = $db->prepare($template); 3$stmt->bindValue(1, $user_id); 4$stmt->execute();

shi_ue様が指摘されているように、現状のコードではプリペアードステートメントを使用したことになっていません。
SQL文に埋め込んでいる$user_idをプレースホルダ(?のこと)に置き換え、bindValueメソッドでプレースホルダに値をバインドしてやる必要があります。

http://php.net/manual/ja/pdo.prepare.php
http://php.net/manual/ja/pdostatement.bindvalue.php
http://php.net/manual/ja/pdostatement.execute.php

あと、PDOでSQLを実行する場合、SQL文の末尾の;は不要です。
付けても付けなくても動作しますが、付けていると条件によっては複文を実行できてしまい、バグやSQLインジェクションの原因となり得ますので、付けないことをお勧めします。
http://blog.tokumaru.org/2013/12/pdo-and-mysql-allow-multiple-statements.html

投稿2016/05/07 06:46

KiyoshiMotoki

総合スコア4791

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

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

qopllqopllqop

2016/05/07 07:25

ご教授いただきありがとうございました。 ご指摘の通り、何を勘違いしたのかプリペアードステートメントになっていませんでした。 関数内で$user_idを定義できていなかったことが原因でした。 まったくの凡ミスでお恥ずかしい限りです。 また、ご丁寧にサンプルコードやSQLの;につきましても、ありがとうございます。 とても助かりました!
KiyoshiMotoki

2016/05/07 07:45 編集

失礼しました、以下は全くの間違いですw ----------------------------------------------------------------------------------------- > 関数内で$user_idを定義できていなかったことが原因でした。 私も説明不足でしたが、エラーの原因はそこではありません。 私の回答の Before - After を見比べていただくと分かりますが、 それぞれの2行目で呼び出すメソッドが  $stmt = $db->query($template);   ↓  $stmt = $db->prepare($template); という具合に変更されていますね? "query"メソッドはSQL文を直接 実行するもので、その戻り値はboolean型です。 そのため、次の行で $stmt->fetchall を実行したときに  Call to a member function fetchall() on a non-object  オブジェクトではないものに対してメンバー関数fetchall() を呼び出した と、怒られたわけです。
qopllqopllqop

2016/05/07 07:53

再度コメントありがとうございます! データ型がブーリアンで返っているとのことで $stmt = $db->query($template); のまま$user_idを定義して実行し、$stmtをvar_dumpしてみました。 trueが返ってくるのかな?と思ったのですが、 object(PDOStatement)#3 (1) { ["queryString"]=> string(145) "SELECT conversion_symptom,count(*) AS count_num FROM t_conversion WHERE user_id=1 GROUP BY conversion_symptom ORDER BY count_num DESC LIMIT 0,10;" } が返ってきていました。fetchallしてもエラーを起こさなかったので、オブジェクトが返っているのかな?とおもいましたが、解釈が間違っているのかな?と新たな疑問が湧いてしまいました。(こちらは自身で勉強し、それでもわからなければ再度スレッドを立てさせていただきます) この辺りの勉強がまだまだ足りていないようですので、もう少し勉強したいと思います。 ご丁寧な回答をいただき、感謝しております。ありがとうございました!
KiyoshiMotoki

2016/05/07 07:57

申し訳ありません、私の認識の方が間違いで、queryメソッドの戻り値はPDOStatement型でした。 http://php.net/manual/ja/pdo.query.php コメントでご報告いただいている通り、 変数 $user_id が宣言されていないことがエラーの原因ですね。
qopllqopllqop

2016/05/07 08:17

おぉぉぉ。アドバイスに指摘したようになってすみません! 安全性は別として、queryでもfetchallできるということですね。 ありがとうございました。すっきりしました!
guest

0

パッと見た感じだと

PHP

1 $template = "SELECT $str,count(*) AS count_num FROM t_conversion WHERE user_id=$user_id GROUP BY $str ORDER BY count_num DESC LIMIT 0,10;"; 2

の$user_idが関数内で定義されていないので、結果としてSQLが間違っているんだと思います。
$user_idも$strと同様に引数として関数に渡して上げてみて下さい。

また、このエラーはエラー表示を設定してればエラーが出るはずなので
ソースの先頭に

PHP

1ini_set('display_errors', 1); 2error_reporting(E_ALL);

としてエラーを表示させてみて下さい。
エラー表示はデバッグの最も基本的な情報となるので開発中は常に表示されるようにしないと辛いです。

  • 既に指摘されていますが、プリペアードステートメントになっていないので、それは別途対応されることを強くお勧めします。

投稿2016/05/07 04:28

tanat

総合スコア18709

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

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

shi_ue

2016/05/07 04:39 編集

PDOはSQLエラーの時、標準ではエラーを吐かないと思うのですが・・・違いますか? $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); とすると、エラーを表示しますが。
tanat

2016/05/07 05:12

あ、ご指摘ありがとうございます。 そうですね。SQLエラーはデフォルトだとエラーを吐かないですね。 勘違いしていました。 ただ、 今回のケースだと(多分) それ以前の$user_idの部分で未定義のエラーを捕捉出来ると思われるので、エラー表示が必要というのは変わらないので、後ほど誤解が無いように修正しますね。
qopllqopllqop

2016/05/07 07:29

ご教授いただきして、ありがとうございます! ご指摘の通り、何を勘違いしたのか、プリペアードステートメントになっておらず、$user_idを関数内で定義することで解決いたしました。 プリペアードステートメントに書き直したいと思います。 また、エラー表示の設定も行っておりませんでしたので、設定して原因わかりやすくしたいと思います。的確なアドバイスをありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問