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

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

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

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

Q&A

解決済

3回答

3758閲覧

PHPのセッションの使い方について

gsuisk

総合スコア72

PHP

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

0グッド

1クリップ

投稿2017/09/13 16:51

編集2017/09/13 16:59

セッションが機能しなくて困っています。

PHPで商品のデータを保持するデータベースを作成しました。

id、goods、price、makerの4つのカラムを持つテーブルstockです。

fetch.phpというファイルで、このテーブルの全レコードを抽出して出力するというプログラムを作成しました。また、出力した商品名をURLとし、クリックすると詳細ページに飛ぶようにしました。

fetch.phpは以下です。

<?php session_start(); header('Expires:-1'); header('Cache-Control:'); header('Pragma:'); $user = 'testuser'; $password = 'pw'; $dbName = 'shop'; $host = 'localhost'; $dsn = "mysql:host={$host};dbname={$dbName};charset=utf8"; ?> <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>全商品一覧</title> </head> <body> <?php try { $pdo = new PDO($dsn, $user, $password); $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $sql = "SELECT * FROM stock"; $stm = $pdo->prepare($sql); $stm->execute(); $result = $stm->fetchAll(PDO::FETCH_ASSOC); if(count($result)>0){ echo "<table>"; echo "<tr>"; echo "<th>", "ID", "</th>"; echo "<th>", "商品名", "</th>"; echo "<th>", "価格", "</th>"; echo "<th>", "メーカー", "</th>"; echo "</tr>"; foreach ($result as $row){ $_SESSION['id']=$row['id']; $_SESSION['goods']=$row['goods']; $_SESSION['price']=$row['price']; $_SESSION['maker']=$row['maker']; echo "<tr>"; echo "<td>", $row['id'], "</td>"; echo "<td>", '<a href="detail.php">', $row['goods'],"</a>", "</td>"; echo "<td>" ,$row['price'], "</td>"; echo "<td>", $row['maker'], "</td>"; echo "</tr>"; } echo "</table>"; } else { echo "見つかりませんでした。"; } } catch (Exception $e) { echo $e->getMessage(); } ?> </body> </html>

商品名をクリックすると飛ぶ詳細ページはdetail.phpで以下になります。
詳細ページと言っても、その商品のid、商品名、価格、メーカーを出力するだけです。

<?php session_start(); if(empty($_SESSION['id'])){ $id=""; }else { $id=$_SESSION['id']; } if(empty($_SESSION['goods'])){ $goods=""; }else { $goods=$_SESSION['goods']; } if(empty($_SESSION['price'])){ $price=""; }else { $price=$_SESSION['price']; } if(empty($_SESSION['maker'])){ $maker=""; }else { $maker=$_SESSION['maker']; } ?> <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>商品詳細</title> </head> <body> <p> <?php echo $id; ?> </p> <p> <?php echo $goods; ?> </p> <p> <?php echo $price; ?> </p> <p> <?php echo $maker; ?> </p> <br> <form> <input type="button" onClick='history.back()' value="戻る"> </form> </body> </html>

商品一覧(fetch.php)から詳細ページ(detail.php)にデータを送信するのにセッションを使ったのですが、機能しません。

上のコードだと、どの商品をクリックしても最後の商品の詳細が出力されてしまいます。

foreach内でセッションが設定されているので、最後のループでの値が設定されてしまっているのだと思いますが、どこでセッションを設定すれば全商品のデータをdetail.phpに送る方法が思いつきません。

このような場合はどのように実装すべきでしょうか?

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

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

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

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

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

guest

回答3

0

foreach内でセッションが設定されているので、最後のループでの値が設定されてしまっているのだと思いますが、

この認識は正しいです。

どこでセッションを設定すれば全商品のデータをdetail.phpに送る方法が思いつきません。

どうも、
セッションがセットされるタイミング(PHPが動作するタイミング)に誤解があるように思います。
PHPは基本的にサーバにアクセスしたタイミングでだけ動作します。

今回の場合だと、fetch.phpにアクセスしたタイミングと、detail.phpにアクセスしたタイミングです。

PHPが「どの商品を選択したか」わかるのは、detail.phpにアクセスした後なので、
detail.phpにアクセスするタイミングで、何らかの方法で「どの商品を選択したか」をdetail.phpに伝える必要があります。

こういったケースだと、GETを利用してリンクに情報を埋め込むケースが多いです。
例えば、

PHP

1//fetch.php 2 echo "<td>", '<a href="detail.php?id=',$row['id'],'">', $row['goods'],"</a>", "</td>";

みたいな感じですね。

次に、GETで埋め込んだ情報を使って、detail.phpで、$_SESSIONから情報を取り出す必要があります。
これには先に、fetch.phpで$_SESSIONに情報を入れるタイミングで取り出しやすい形を意識して連想配列に情報を格納する必要があります。

例えば、こんな感じでIDをキーとした二次元配列に全データを格納しておくと、IDさえわかればdetail.php側でセッションから取得できます。

PHP

1//fetch.php 2$_SESSION[$row['id']] = $row; 3

という感じで、頑張ってみてください。
*もし、多次元配列についてよくわからないという事であれば、SESSIONを使わずに多次元配列の概念の理解と操作を先に学習されることをお勧めします。

投稿2017/09/13 17:57

tanat

総合スコア18713

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

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

0

session_start()を使ってデータを送るよりも、商品詳細ページにおいてもDBからデータを取得するようにした方がやりやすいように感じます。

PHP

1//fetch.php 2echo "<td>", '<a href="detail.php?goods=',$row['id'],'">', $row['goods'],"</a>", "</td>";

このようにして、商品IDを'id'というキーに入れて商品詳細ページに送ります。

PHP

1//datail.php 2$pdo = new PDO($dsn, $user, $password); 3//送った'id'を取得する 4$id = filter_input(INPUT_GET, 'goods', FILTER_SANITIZE_NUMBER_INT); 5//WHERE句で詳細を閲覧する商品のデータを取得 6$sql = "SELECT * FROM stock WHERE id = $id"; 7$statement = $pdo->query($sql); 8$records = $statement->fetchAll(); 9$statement = null; 10$pdo = null;

商品の一覧ページから取得した'id'から詳細を閲覧する商品のデータを取得しています。
あとは$recordsの内容を表示すれば表示すればオッケーだと思います。

参考になればいいなと思います。

投稿2017/09/14 05:20

編集2017/09/14 13:55
alone.mk2

総合スコア58

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

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

gsuisk

2017/09/14 09:51

ありがとうございます。 fetch.phpにある$databaseとは$dbNameのことでしょうか? またfetch.phpでPDOオブジェクトを作るときにtry{}catch{}は必要ないのでしょうか?
gsuisk

2017/09/14 09:52

ちなみに$databaseを$dbNameにしてじっこうしたのですが、 $statement = $database->query($sql); ここの部分でエラーが出てしまいます...
gsuisk

2017/09/14 10:53

$id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT); このようにしたらエラーは消えたのですが、$recordsが以下のようになっていました。 Array ( [0] => Array ( [id] => 2 [0] => 2 [goods] => バッグ [1] => バッグ [price] => 3000 [2] => 3000 [maker] => AS [3] => AS ) )
gsuisk

2017/09/14 10:58

fetchAll(PDO::FETCH_ASSOC)にしたら解決しました!
alone.mk2

2017/09/14 13:11

コメントが遅れてしまいすみません。 解決できて、何よりです。
alone.mk2

2017/09/14 13:56

ちなみにですけど、try{}catch{}は使わなくても大丈夫ですよ
gsuisk

2017/09/14 14:53

ありがとうございます。 ちなみに ?goods=',$row['goods'],' としてGETリクエストすることも可能でしょうか? idだとURLの末尾の数字を変えただけで他のページに飛んでしまうので、、
alone.mk2

2017/09/14 15:07

WHERE句でsqlを取得する際に、goodsに変更さえすれば可能だと思います。 一度、試してみるといいかもしれませんね!
gsuisk

2017/09/14 15:38

ありがとうございます! 試してみたのですが、goodsは日本語なのでエンコードが必要だと思うので、 ?goods=',rawurlencode($row['goods']),' このようにして、 detail.phpで $goods = filter_input(INPUT_GET, 'goods', FILTER_SANITIZE_FULL_SPECIAL_CHARS); $goods=rawurldecode($goods); $sql = "SELECT * FROM stock WHERE goods = $goods"; このようにしたのですが、うまく動作しません・・・ どこが原因かわかりますか?
alone.mk2

2017/09/14 16:27

私もやってみましたが、できませんでした。 お力になれず、申し訳ないです。
guest

0

ベストアンサー

まず、商品一覧の連想配列の構造を考えてみるとわかりやすいかもしれません。
商品は下記のようなデーター構造だと思います。

php

1$goods = [ 2 '商品ID' => ['id' => '商品ID', 'goods' => '商品名', 'price' => '値段', 'maker' => 'メーカー'], 3 ... 4]; 5//または 6$goods = [ 7 ['id' => '商品ID', 'goods' => '商品名', 'price' => '値段', 'maker' => 'メーカー'], 8 ... 9];

したがって、商品一覧ページでセッションに商品情報を保存しておいて、商品詳細ページで個別の商品を取り出したい場合はfetch.phpforeach()部分を下記のようにします。また、リンクは、どの商品の詳細なのかを明確にするためにdetail.php?goods=商品IDとします。

どうしてもリンクにクエリストリング(?goods=商品ID)を書きたくない場合は、フォームでPOSTするかJavaScriptでPOSTするか、Cookieに保存します。

php

1//fetch.php 2foreach ($result as $row) { 3 $_SESSION[$row['id']] = [ 4 'id' => $row['id'], 'goods' => $row['goods'], 'price' => $row['price'], 'maker' => $row['maker', 5 ]; 6 //または 7 //$_SESSION[] = [ 8 // 'id' => $row['id'], 'goods' => $row['goods'], 'price' => $row['price'], 'maker' => $row['maker', 9 //]; 10 echo "<tr>"; 11 echo "<td>". $row['id'] ."</td>"; 12 echo "<td>". '<a href="detail.php?goods='. $row['id'] .'">'. $row['goods'] ."</a></td>"; 13 echo "<td>". $row['price'] ."</td>"; 14 echo "<td>". $row['maker'] ."</td>"; 15 echo "</tr>"; 16}

そして、detail.phpでは下記のようにします。

php

1//detail.php 2session_start(); 3 4//商品ID$_GET['goods']取得 5$goodsId = filter_input(INPUT_GET, 'goods', FILTER_SANITIZE_NUMBER_INT); 6$id = $goods = $price = $maker = ''; 7 8if (! empty($goodsId) && ! empty($_SESSION[$goodsId])) { 9 if (! empty($_SESSION[$goodsId]['id'])) { 10 $id = $_SESSION[$goodsId]['id']; 11 } 12 if (! empty($_SESSION[$goodsId]['goods'])) { 13 $goods = $_SESSION[$goodsId]['goods']; 14 } 15 //ゼロ円考慮 16 if (isset($_SESSION[$goodsId]['price']) && $_SESSION[$goodsId]['price'] !== '') { 17 $price = $_SESSION[$goodsId]['price']; 18 } 19 if (! empty($_SESSION[$goodsId]['maker'])) { 20 $maker = $_SESSION[$goodsId]['maker']; 21 } 22}

商品数が少ない場合は問題ありませんが、すべての商品情報をセッションに入れているため、商品が多くなるとセッションの連想配列が膨大になりパフォーマンスが悪くなります。

したがって、商品詳細ページでもDBアクセスして商品情報をDBから取得するのがよいです。

投稿2017/09/13 18:02

Tomak

総合スコア1652

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

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

gsuisk

2017/09/14 09:56

ありがとうございます。 詳細ページでもDBにアクセスするほうが良いのですね。 やり方としてはalone.mk2さんが回答されているように、 <a href="detail.php ?id = ' , $row[' id '] , ' "> として、 詳細ページで再びPDOオブジェクトを生成して $id = filter_input(INPUT_GET, 'goods', FILTER_SANITIZE_NUMBER_INT); として取得するのが良いでしょうか?
Tomak

2017/09/14 12:46

はい、そうですね。 セキュリティを考慮する場合は、DBのSQLクエリはプレースホルダを使ってバインドしたほうが良いです。下記の「:id」をプレースホルダといいます。 プリペアドステートメント ------------------------- $st = $conn->prepare('SELECT ... WHERE id = :id'); $st->bindValue(':id', $id, PDO::PARAM_INT); $st->execute(); $result = $st->fetch(); //1レコードのみなら //DB結果ダンプ var_dump($result);
gsuisk

2017/09/14 13:05

ありがとうございます。 $id = filter_input(INPUT_GET, 'goods', FILTER_SANITIZE_NUMBER_INT); は $id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT); の間違いでしょうか? fetch.phpで ?id=',$row['id'],' としているので送信したているのはidですよね?
gsuisk

2017/09/14 13:08

また、fetch()としてしまうと Array ( [id] => 2 [0] => 2 [goods] => バッグ [1] => バッグ [price] => 3000 [2] => 3000 [maker] => AS [3] => AS ) $resultにこのような配列が入っていしまいます。 fetchAll(PDO::FETCH_ASSOC)の方が良いですよね?
gsuisk

2017/09/14 13:13

$pdo = new PDO($dsn, $user, $password); $id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT); $sql = "SELECT * FROM stock WHERE id = :id"; $stm = $pdo->prepare($sql); $stm->bindValue(':id', $id, PDO::PARAM_INT); $stm->execute(); $result = $stm->fetchAll(PDO::FETCH_ASSOC); $stm = null; $dbName = null; このあと$resultをforeachで回せばうまくできました。 これでよいのですかね..?
Tomak

2017/09/14 13:26 編集

>$id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT); の間違いでしょうか? >fetch.phpで ?id=',$row['id'],' としているので送信したているのはidですよね? そうですね。私の回答のコードでは「'...?goods='.$row['id']」としていたので見落としてしまいました。 >$resultにこのような配列が入っていしまいます。 >fetchAll(PDO::FETCH_ASSOC)の方が良いですよね? fetch()では結果配列をどのように受け取るか、引数を渡して設定することができます。どのように結果を受け取りたいか、好みで決めると良いと思います。 PDO::FETCH_ASSOC ---- カラム名をキーとした連想配列で結果を受け取ることができます。 PDO::FETCH_BOTH(初期値) ---- カラム名をキーとした連想配列と、0,1,2... の数字をキーとした配列を受け取ることができます。 PDO::FETCH_ASSOCと、PDO::FETCH_NUM両方ということです。
gsuisk

2017/09/14 13:38

わかりました!丁寧にありがとうございます。 ちなみになのですが、idではなく ?goods=' ,$row['goods'], ' のように 商品名を送信してGETリクエストすることも可能でしょうか?
gsuisk

2017/09/14 13:40

?goods=',rawurlencode($row['goods']),' goodsは日本語なのでエンコードが必要だと思って上のように書いたのですが機能しませんでした(汗)
Tomak

2017/09/14 16:41

商品名を渡した場合は、$_GET['goods']を受け取ったらURLデコードしなければいけません。 ただし、商品名で検索するより通常プラマリーキーであるレコードID(商品ID)でSELECTしたほうがパフォーマンスが良いです。 何かしら、DBからSELECTする前や、DBとは関係ない部分で「商品名」を使いたい場合は下記のようにします。 -- //AリンクのURL(商品ID、商品名) echo '<a href="detail.php?id='. $row['id'] .'&goods='. urlencode($row['goods']) .'">...</a>' -- //商品ID$_GET['id'] $id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT); //商品名$_GET['goods'] $goods = filter_input(INPUT_GET, 'goods', FILTER_SANITIZE_URL); $goods = urldecode($goods);
gsuisk

2017/09/14 18:11

idのみでGETリクエストするとURLの末尾の「id=数字」の数字を変えるだけで他のページに移動できるのでよくないのかなと思っていました。 Tomakさんに上に書いていただいたのはidとgoodsの両方をセットでGETリクエストで送るということですね。 detail.phpで以下のように書いたのですが、レコードが見つからなかったです... $id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT); $goods = filter_input(INPUT_GET, 'goods', FILTER_SANITIZE_URL); $goods = urldecode($goods); $sql = "SELECT * FROM stock WHERE id = :id AND goods = :goods"; $stm = $pdo->prepare($sql); $stm->bindValue(':id', $id, PDO::PARAM_INT); $stm->bindValue(':goods', $goods); $stm->execute(); $records = $stm->fetchAll(PDO::FETCH_ASSOC); $stm = null; $dbName = null;
gsuisk

2017/09/14 18:15

何度もすみません... あと、例えば「idが2でgoodsがバッグ」をクリックするとURLが detail.php?id=2&goods=バッグ と日本語がエンコードされていませんでした。ページのソースではエンコードされているのですが、、、
Tomak

2017/09/14 20:45

> idのみでGETリクエストするとURLの末尾の「id=数字」の数字を変えるだけで他のページに移動できるのでよくないのかなと思っていました。 その仕様で特に問題ないと思います。。。 例えば、本件の質問ページは「https://teratail.com/questions/92366」で、今表示しているページは「質問詳細ページ」ですよね? 質問IDは「92366」です。 POSTしなくても、このURLに移動するときちんとこのページが表示されます。 >detail.phpで以下のように書いたのですが、レコードが見つからなかったです すみません、よくよく考えたら$_GETは urldecode() 済みなので、普通に文字列のサニタイズすればいいだけでした。多分下記なら大丈夫なハズですが、このSQL文の「AND goods = xxx」必要ないです。 -- $goods = filter_input(INPUT_GET, 'goods', FILTER_SANITIZE_FULL_SPECIAL_CHARS); $sql = "SELECT * FROM stock WHERE id = :id AND goods = :goods"; >detail.php?id=2&goods=バッグ >と日本語がエンコードされていませんでした。ページのソースではエンコードされているのですが、、、 最近のブラウザは、自動でURLデコードして表示してくれます。ページのソースではデコードすると実際のコードとは違うことになるので、生のHTMLテキストが表示されます。 ためしに、ブラウザのURLロケーションバーに下記を入力してエンターしてみてください。「?id=2&goods=バッグ」と表示されるはずです。 ---- http://ドメイン/detail.php?id=2&goods=%E3%83%90%E3%83%83%E3%82%B0
gsuisk

2017/09/15 04:19

ありがとうございます。できました! idだけにしても問題ないのですね。ブラウザのURLロケーションバーに日本語が表示されるのも何か変なのでidだけにしようと思います。 親切にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問