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

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

解決済

1回答

413閲覧

phpとmysqlの練習サイトを作っています。データベースからの結果をindex.phpの任意の位置に表示したいです。

meka

総合スコア23

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クリップ

投稿2019/05/01 03:36

編集2019/05/02 09:56

前提・実現したいこと

phpとmysqlの練習サイトを作っています。

内容としてはindex.phpから日付選択をしてサーバーに送信、データベースからマッチする日付のデータを引っ張って来てindex.phpの任意の位置に結果を表示するというものです。

問題点として <label> 日付: <input type="date" name="date"></label> で選択した日付がPOSTすると保持されず日付がundefindになることです。

こちらに掲載したコードでエラーは出ませんが、実現したいことは(index.php)の<?phpif($_SERVER['REQUEST_METHOD'] === 'POST') {$search->post();を(index.php)の上の方で書いて、(search.php)の処理にてreturnで結果を返す。その結果を(index.php)の任意の場所で表示するということです。

POSTしてretutn処理すると消えるのでreturn処理でデータを返す方法をご教授お願いできますでしょうか?

補足情報:

  • ローカル開発環境で練習しています。
  • POSTの関係ないメソッドのrerun処理は問題なくできています。

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

Notice: Undefined variable …

該当のソースコード

//ファイル名 index.php

php

1<?php 2 3require_once(__DIR__ . '/config.php'); 4require_once(__DIR__ . '/search.php'); 5 6try { 7 $search = new \SearchDate\Price(); 8} catch (Exception $e) { 9 echo $e->getMessage(); 10 exit; 11} 12 13?> 14<!DOCTYPE html> 15<html lang="ja"> 16<head> 17 <meta charset="utf-8"> 18 <title>練習サイト</title> 19 <link rel="stylesheet" href="styles.css"> 20</head> 21<body> 22 23 <form action="" method="post"> 24 <label> 日付: <input type="date" name="date"></label> 25 <input type="submit" value="検索"> 26 27 <div>サンプル1</div> 28 <div>サンプル2</div> 29 <div><?php if ($_SERVER['REQUEST_METHOD'] === 'POST') { 30 $search->post(); 31 } ?></div> 32  //本来ここではなく、このファイルの上の方でこちらのコードを書きたいです。 33 34 </form> 35 36</body> 37</html> 38

//ファイル名 search.php

php

1<?php 2 3namespace SearchDate; 4 5class Price { 6 private $_db; 7 8 public function __construct() { 9 $this->_connectDB(); 10 $this->_createToken(); 11 } 12 13 private function _connectDB() { 14 try { 15 $this->_db = new \PDO(PDO_DSN, DB_USERNAME, DB_PASSWORD); 16 $this->_db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); 17 } catch (\PDOException $e){ 18 throw new \Exception('Faild to connect DB'); 19 } 20 } 21 22 private function _createToken() { 23 24 if (!isset($_SESSION['token'])) { 25 $_SESSION['token'] = 26 bin2hex(openssl_random_pseudo_bytes(16)); 27 } 28 } 29 30 private function _validateSelect() { 31 32 $date = filter_input(INPUT_POST,'date',FILTER_SANITIZE_NUMBER_INT); 33 if ($date == NULL || FALSE) { 34 echo '日付を指定してください'; 35 exit; 36 } 37 } 38 39 private function _validateToken() { 40 41 if (!isset($_SESSION['token'])) { 42 throw new \Exception('invalid token!'); 43 } 44 } 45 46 public function _searchDate() { 47 48 $date = filter_input(INPUT_POST,'date',FILTER_SANITIZE_NUMBER_INT); 49 $res = $this->_db->prepare('select カラム名 from テーブル名 where カラム名 = :date'); 50 $res->bindValue(':date', $date, \PDO::PARAM_STR); 51 $res->execute(); 52 53 $price = $res->fetch(\PDO::FETCH_ASSOC); 54 55 if ($price == false) { 56 echo '該当なし'; 57 } else { 58 echo $price["カラム名"]; 59  //こちらの結果を return で返して(index.php)の任意の位置で表示したいです。 60 } 61 } 62 63 public function post() { 64 try { 65 66 $this->_validateSelect(); 67 $this->_validateToken(); 68 $this->_searchDate(); 69 70 } catch (\Exception $e) { 71 header('Location: http://' . $_SERVER['HTTP_HOST']); 72 } 73 exit; 74 } 75 76} 77

試したこと

__construct内での初期化や、index.phpに変数を書いて結果を引っ張ってくるなど試してみましたが、そもそも問題が初期化で合っているのかも不明。search.phpの_searchDate() のechoやvar_dump()では結果が取れているのであともう一歩な気がしているのですが・・・。要は、ページにアクセスしたユーザーが日付指定をしてsubumitしないと日付が選択されず空のままになってしまっていて、index.phpで変数書いてもエラーになっているのかとか色々悩んでいます。

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

ここにより詳細な情報を記載してください。

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

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

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

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

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

m.ts10806

2019/05/01 05:06

PHPもタグに追加しておいてください
m.ts10806

2019/05/01 05:09

またお手元のコードのインデントをきちんとつけた上で質問本文にコードをご提示ください エラーが中略されていますとどこででたかわかりませんのでなるべく全文そのままご提示ください
m.ts10806

2019/05/01 07:57

PHPもタグに追加しておいてください
meka

2019/05/01 08:11

すみません、このサイトを使うのが初めてなのとphpも初心者のようなものなのでどのように質問していいのか分かりませんが、phpのタグも追加するというのは今まで試したダメだった変数のタグを書けばいいのでしょうか? 単純にindex.phpの「ここに結果を表示させたいです」の箇所にsearch.phpのfunction _searchDate()で出ている結果を表示させるだけなのですが、、、 質問の仕方が悪いようでしたらスミマセン。
m.ts10806

2019/05/01 08:16

いいえ。タグとは質問につける関連カテゴリのようなものです。「検索タグ」ですね。 いまは「MySQL」 しかつけられてないですが、質問内容としてはMySQLは関連的にはサブ的で、メインはPHPであるため、「PHP」のタグは必要ということでの追加依頼です。適切なタグをつける件については「質問するときのヒント」を、使えるタグについては「タグ一覧」を参考にしてください。
meka

2019/05/01 08:21

納得です!修正させて頂きました!最初に質問する時に他のタグが消えていたようです。
guest

回答1

0

ベストアンサー

気になったところを箇条書きにします(全部拾えているわけではないと思います)

  • $_POSTではなくfilter_input()で取得すべき
  • 「検索条件が入ってなかったら」どうすべきか決めること。全件出すのか、何も出さないのか
  • 入力値そのままSQLに突っ込むとSQLインジェクションの脆弱性があるので、bindValue()でバインドしてSQLエスケープすること(PHPマニュアル確認してもらえればわかりますが使うメソッドはquery()ではなくなるので注意)
  • fetchだと最初の1件しか取得できないけど、それでいいのでしょうか。fetchAllで全部取ってきたほうがいいような
  • データがとれてること確認できてるならそのデータをreturnして呼び先で受け取って出力処理書けばいいだけでは
  • _validateSelect()とか_validateToken()でException投げてるのはいいけど受け取ってもリダイレクトしているだけなので意味ないのでは。それならException投げるのではなくてエラー受け取って画面にエラーメッセージ出力したほうがいいと思う(そこは決めてください。エラー捕捉しているのに使ってないことが問題なので)

ポイントだけ調整コード
(既にアドバイスした内容ですが)


php

1 2class Price { 3 private $_db;

php

1class Price { 2 private $_db; 3 public $_result = [];

php

1 public function _searchDate() { 2 3 $date = filter_input(INPUT_POST,'date',FILTER_SANITIZE_NUMBER_INT); 4 $res = $this->_db->prepare('select カラム名 from テーブル名 where カラム名 = :date'); 5 $res->bindValue(':date', $date, \PDO::PARAM_STR); 6 $res->execute();


※「メソッドはどこからどう呼び出されるか知らない」のが実際の前提なので「前のメソッドでチェックしている前提」は捨ててきちんと丁寧に書くこと。PDOException も同理由。

php

1 public function _searchDate() { 2 try { 3 $date = filter_input(INPUT_POST,'date',FILTER_SANITIZE_NUMBER_INT); 4 $where = ''; 5 if(!is_null($date)){ 6 $where = ' where カラム名 = :date'; 7 } 8 $stmt = $this->_db->prepare('select カラム名 from テーブル名'.$where ); 9 if(!is_null($date)){ 10 $stmt->bindValue(':date', $date, \PDO::PARAM_STR); 11 } 12 $stmt->execute(); 13 $this->_result = $stmt->fetchAll(); 14 } catch (\PDOException $e){ 15 die(var_dump($e)); 16 }

php

1 public function post() { 2 try { 3 4 $this->_validateSelect(); 5 $this->_validateToken(); 6 $this->_searchDate(); 7 8 } catch (\Exception $e) { 9 header('Location: http://' . $_SERVER['HTTP_HOST']); 10 } 11 exit; 12 }


exit入れるならheaderの直後であるべき、だけど、元の記述だとせっかく捕捉したExceptionを握りつぶしているので、練習とか学習とか開発途中のものだったらExceptionを全てvar_dump()などで出力しておくべき。

php

1 public function post() { 2 try { 3 4 $this->_validateSelect(); 5 $this->_validateToken(); 6 $this->_searchDate(); 7 8 } catch (\Exception $e) { 9 die(var_dump($e)); 10 } 11 }

index.php

php

1<?php 2require_once(__DIR__ . '/config.php'); 3require_once(__DIR__ . '/search.php'); 4 5$search = new \SearchDate\Price(); 6if ($_SERVER['REQUEST_METHOD'] === 'POST') { 7 $search->post(); 8} 9?> 10<!DOCTYPE html> 11<html lang="ja"> 12<head> 13 <meta charset="utf-8"> 14 <title>練習サイト</title> 15 <link rel="stylesheet" href="styles.css"> 16</head> 17<body> 18 19 <form action="" method="post"> 20 <label> 日付: <input type="date" name="date"></label> 21 <input type="submit" value="検索"> 22 23 <div>サンプル1</div> 24 <div>サンプル2</div> 25 <div> 26<?php 27 foreach($search->_result as $rownum=>$rowdata){ 28 echo $rowdata["カラム名"]; 29 } 30?> 31 </div> 32 33 </form> 34 35</body> 36</html>

投稿2019/05/01 08:39

編集2019/05/02 13:20
m.ts10806

総合スコア80850

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

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

meka

2019/05/01 09:19

ご回答と他のポイントへのアドバイスありがとうございます。 $_POSTではなくfilter_input()で取得すべき →了解致しました。この辺これから勉強させていただきます。 「検索条件が入ってなかったら」どうすべきか決めること。全件出すのか、何も出さないのか 入力値そのままSQLに突っ込むとSQLインジェクションの脆弱性があるので、bindValue()でバインドしてSQLエスケープすること(PHPマニュアル確認してもらえればわかりますが使うメソッドはquery()ではなくなるので注意) →これもprepare()があったのでどっちを使おうか迷ったのですがアドバイス頂いた感じで行くと、こちらの方がセキュリティが強いという認識で大丈夫でしょうか? fetchだと最初の1件しか取得できないけど、それでいいのでしょうか。fetchAllで全部取ってきたほうがいいような →検索結果は一件で大丈夫なのでこのように書きました。仮にsqlの命令がある範囲の合計や平均だったとして、これも答えは1つになるのですが命令の答えが一つでもFechAllを使っていった方がプログラミング的には良いでしょうか? データがとれてること確認できてるならそのデータをreturnして呼び先で受け取って出力処理書けばいいだけでは →ここが今回の質問の内容です。この呼び出し方が分からなくて困っていて質問させていただきました。どのようにコードを書いて呼び出せばいいのかご教授お願いできますでしょうか。ここを教えて頂きたいです。 _validateSelect()とか_validateToken()でException投げてるのはいいけど受け取ってもリダイレクトしているだけなので意味ないのでは。それならException投げるのではなくてエラー受け取って画面にエラーメッセージ出力したほうがいいと思う(そこは決めてください。エラー捕捉しているのに使ってないことが問題なので) →私の勉強不足のようです、、これから勉強させていただきます。
m.ts10806

2019/05/01 09:23

> どのようにコードを書いて呼び出せばいいのかご教授お願いできますでしょうか。ここを教えて頂きたいです。 関数からのreturnを知らずにこのコードを書いたとは思えないのですが、いかがでしょうか。 function test($a){ return $a." test"; } $str = test("hoge"); echo $str; // hoge test
m.ts10806

2019/05/01 09:26 編集

まあreturnでなくてもpublicでプロパティを持っておいてそれに対してアクセスでも良いですけど。 class Price { private $_db; public $_result = []; //中略 public function post() { // $this->_result = $this->_searchDate();
m.ts10806

2019/05/01 09:28

> →これもprepare()があったのでどっちを使おうか迷ったのですがアドバイス頂いた感じで行くと、こちらの方がセキュリティが強いという認識で大丈夫でしょうか? 回答に書いた「SQLインジェクション」で検索。 あと下記の記事は必読 https://qiita.com/mpyw/items/b00b72c5c95aac573b71 > →検索結果は一件で大丈夫なのでこのように書きました。仮にsqlの命令がある範囲の合計や平均だったとして、これも答えは1つになるのですが命令の答えが一つでもFechAllを使っていった方がプログラミング的には良いでしょうか? そこは仕様なので決めてください。ただ、実際のクラス側は本来、どのような入力値が来るのを知らない前提で構築するものなので、「何も渡されなかった時にどうするか」は考慮して組む必要があります。
meka

2019/05/01 09:45

プログラミンサイトで独学していて、分からないポイントはネットの情報かき集めて作っています。なので知識が断片的だなと自分でも思っています。プログラミング無知状態でhtmlから初めて.css.javascript.php.mysql根詰めて三カ月経ちました(汗 ここにきて詰まってしまいました、、、 一応書いてみましたがエラーになっちゃいました(汗 大分お時間取らせてしまっていますし、アドバイス頂いたポイントもすごく良いヒントになったのでもう少し頑張ってみます! ちょっと色々試してみて、また解決できないようであれば質問させていただきます! ありがとうございます!
m.ts10806

2019/05/01 10:29

必ずPHPマニュアルは確認してください。まずは辞書がわりに使えるようになること。それだけで一気にのびます。(逆に辞書がわりにも使えないと初心者から脱することはできません)
m.ts10806

2019/05/01 20:37

コメント欄ではマークダウン使えませんのでコードは質問本文に追記してください
m.ts10806

2019/05/02 06:57

えっと、結局どのように解決されたのでしょうか。
meka

2019/05/02 07:48

お気にかけて下さってありがとうございます(汗 まだ解決していなくて今も悩みながらご指摘いただいた箇所を直しておりました。後ほど書き直したコードで再質問しようかと思っておりました。
meka

2019/05/02 07:52

ちなみにすみません、この状態でこの質問を終了するとまずい感じになってしまうのでしょうか? 逆にご迷惑がかかるようでしたら、また質問文が長くなって見にくくなってしまうかもしれませんが追記致します!
m.ts10806

2019/05/02 08:51

解決したかどうかの判断は質問者さんに委ねられるものですが、「この質問とおなじ流れ」であれば、続けて良いかと思います。質問文は最大一万文字まで入りますし、中にはコードが長いのでコードは別サービスにあげられる方もいらっしゃいます。 どこまでを「問題」として取り扱うかだと思います。 ベストアンサーは解除できますので一度解除して検討されては
meka

2019/05/02 09:50

ご指摘いただいた箇所をいくつか直し、質問とコードを掲載しなおしました。 ただsessionに関してはどのようにエラーを確認しながら直せば良いのかわからなくまだ手がついておりません。また、returnの問題も解決はしておりません。どのように書き換えて行けばよろしいでしょうか?
m.ts10806

2019/05/02 10:24

ひとまずfilter_inputに変えられたのですかね?取得できないことも加味して結果がnullかどうかでwhere句をつける、つけないの分岐は入れておいてください。 あとは私のほうでも動作確認してみます。(夜遅くなるかもしれませんが)
m.ts10806

2019/05/02 12:16

type=dateのテキストボックスってYYYY-MM-DD形式なので filter_inputのオプションFILTER_SANITIZE_NUMBER_INTだとチェック弾かれてnullが返って来そうに思いますが、それも含めてこのコードをどこまで理解して組まれていますか? if ($date == NULL || FALSE) { というチェックの仕方もちょっと意味不明です。 is_null()とかで良いのでは。 ツッコミどころが多すぎて「ちょっと見てみる」の範疇を超えてしまってますね・・・。デバッグ・リファクタリング依頼に応えるわけにはいけないですし、 エラー内容も変わったようですし、解決したいところだけを絞ったほうが良さそうです。
meka

2019/05/02 12:44 編集

phpの公式を確認したところ、FILTER_SANITIZE_NUMBER_INTだと+-数字以外は除去するとのことでした。入力される文字列は日付だったので日付関係のフィルタリングが無かったもので,おそらくFILTER_SANITIZE_FULL_SPECIAL_CHARSもありなんだろうなと思いましたが、dateの表現が数字と-で表されいるようなのでこちらが良いかと思いました。ちなみに問題なく動いております。
meka

2019/05/02 13:11 編集

また仰る通り、if ($date == NULL || FALSE) に関しては is_null($date)の意味になるのでちょっと考えはしたのですが、if ($date == FALSE)にした方が結果的には良かったです。すみません、青色初心者なのもので・・・・
m.ts10806

2019/05/02 13:24

「必ず設定される前提」はまずいので(機能はどこからどう呼び出されるか知っていてはおかしい) フィルタリング失敗でfalse 、未設定でnullならきちんとチェックすべきです。 if(is_null($date) || !$date )
meka

2019/05/02 13:53 編集

何もなければこの辺は何か設定されるんじゃない?的に考えていました(汗 コードを確認していますが、ここまでしっかりと指定してやらないとダメなんですね。すごく勉強になります!ご教授頂いたコードを今日明日じっくり研究させていただきます!ありがとうございます!
m.ts10806

2019/05/02 13:54 編集

PHPはスクールでならったことがないですが、あくまで「役割分担」の観点からロジックを考えているかどうかの問題と思います。 三項演算子のように短く書けるときはきちんと書きますし、「どこまで想定して書けているか」というところになってくると思います。あとは「部品化しすぎて全体の流れの整合性はとれているか」とかですね。 1個1個を見れば問題ないように見えて全体の流れから見るとおかしくなっている人はよくいます。ネットからコピペしてきたツギハギコード書いている人とかはそうですね。 書き方に一貫性がないとか、実務をそれなりに長くやっているエンジニアが回答者となったときに質問を見ていてすごく気にするところです(私も結構長くPHPやってます)。 でもそういう小さい綻びがあとで思わぬ大事故につながるので、コピペしてくるにしてもきちんと理解して、全体の流れ・整合性を損なわないように入れ込まないと手に負えないコードになるわけです。 PHPマニュアルは確認されているようですが、そこはきちんと「INPUT/OUTPUT」を確認して実装に反映できているかとか、関連機能をおさえているかとか、「的確な使い方ができているか」は大事だと思います。 ※こっそりなおしましたが変数名のつけ方とか、汎用的すぎるのも良くなくて、返り値の型にあわせたほうが変数にもきちんと役割が振れますし「何か月か先に自分が読んで理解できるか」という観点でコードを書くと良いです。
meka

2019/05/04 07:41 編集

先日はありがとうございました!おかげさまでphpの理解と使い方に少しだけ光がさして来ました。ご教授頂いた内容に少し質問があったのと、見直してここおかしいかな?で直したコードを見て頂きたいなと思ったのですが、一度こちらの質問は終わらせた方がよろしいでしょうか? ここまで丁寧に教えて下さる方がいらっしゃらなかったもので・・・「必ず設定される前提」は無いとの言葉が心にしみました。
m.ts10806

2019/05/04 08:21

>「必ず設定される前提」は無い というよりPHP自体が「どこからどう呼び出されるか知らない」ので、 個々の機能も「どこからどう使われるのか知っている前提で組まれるのはおかしい」ということですね。 例えprivateを付与していても同クラス内から使うことはできるわけですし、「どこからどう呼び出されてもちゃんと動くように作ることが大事」と覚えておいてください。 >一度こちらの質問は終わらせた方がよろしいでしょうか? そこはお任せします。本質問の要件で解決につながったと思うのでしたら締めて良いと思いますし(ヒントをもとに自分で次の段階に進めそうなら、とかそのあたりの基準は質問者さんで決めるものと思います)
meka

2019/05/05 03:01

一番最初に質問させていた内容は解決しましたので一旦こちら終了したいと思います。再度質問をアップさせて頂きました。本当にありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問