- ベストアンサー
PDOで複数のテーブルを扱いたい
- PDOで複数のテーブルを扱いたい
- この質問では、PDOを使用して複数のテーブルを扱いたいという内容です。具体的には、ユーザー管理のレッスンで学んだ内容を応用し、ログインを行い登録したユーザーのプロフィールを表示するという処理を行いたいという要望があります。
- 具体的な実装方法として、usersテーブルとaddressテーブルを分けて表示する方法に挑戦しています。ただし、URLの数値を変えれば他の人の情報も見えてしまう問題があります。セッションを使用してユーザーの情報を取得しているため、addressテーブルの情報もセッションを使用して表示させる方法を知りたいとのことです。主な目的は、ログインした会員の情報のみを表示し、他人のデータは見えないようにすることです。
- みんなの回答 (6)
- 専門家の回答
質問者が選んだベストアンサー
- ベストアンサー
(私の意見押し付けて申し訳ないですが)ドットインストール流の「手続き型」の書き方だとコードが汚くなるので、「オブジェクト指向」をふんだんに採用して書き直してみました。元のコードをこれ以上添削する気にはならなかったのでこれで勘弁してください(汗) https://github.com/Certainist/sns_php
その他の回答 (5)
エラーメッセージを見ると、1行目で既にバッファが送信されているとあります。エディタは何を使用されていますかね?まともなエディタならばエンコーディング指定で UTF-8(BOM有り) と UTF-8(BOM無し) 別称UTF-8N を区別できると思うのですが、どちらになっていますか?もし前者を選択しているならそれが原因のエラーです。後者を選択してください。 $stmt->bindValue(':id', $_SESSION['me'], PDO::PARAM_INT); は $stmt->bindValue(':id', $_SESSION['user_id'], PDO::PARAM_INT); のミスでしょうか。
補足
1行目にスペースが入ってました、お恥ずかしい限りです。 最初のエラーは消えたのですがやはり表示されません。 全て掲載させて頂きますのでご指摘いただければと思います。 login.pho <?php require_once('config.php'); require_once('functions.php'); session_start(); if (!empty($_SESSION['me'])) { header('Location: '.SITE_URL); exit; } function getUser($id, $password, $dbh) { $sql = "select id from susers where id = :id and password = :password limit 1"; $stmt = $dbh->prepare($sql); $stmt->execute(array(":id"=>$id, ":password"=>getSha1Password($password))); $suser = $stmt->fetch(); return $suser; } if ($_SERVER['REQUEST_METHOD'] != 'POST') { // CSRF対策 setToken(); } else { checkToken(); $id = $_POST['id']; $password = $_POST['password']; $dbh = connectDb(); $err = array(); // メールアドレスが登録されていない // アカウントIDが空? if ($id == '') { $err['id'] = 'アカウントIDを入力してください'; } // アカウントとパスワードが正しくない if (!$me = getUser($id, $password, $dbh)) { $err['password'] = 'IDとパスワードが一致しません'; } // パスワードが空? if ($password == '') { $err['password'] = 'パスワードを入力してください'; } if (empty($err)) { // セッションハイジャック対策 session_regenerate_id(true); $_SESSION['me'] = $me; header('Location: '.SITE_URL); exit; } } ?> <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>ログイン画面</title> </head> <body> <h1>ログイン</h1> <form action="" method="POST"> <p>アカウントID:<input type="text" name="id" value="<?php echo h($id); ?>"><?php echo h($err['id']); ?></p> <p>パスワード:<input type="password" name="password" value=""><?php echo h($err['password']); ?></p> <input type="hidden" name="token" value="<?php echo h($_SESSION['token']); ?>"> <p><input type="submit" value="ログイン"> <a href="signup.php">新規登録はこちら!</a></p> </form> </body> </html> 表示したいファイルが、 profile.php <?php require_once('config.php'); require_once('functions.php'); session_start(); if (empty($_SESSION['me'])) { header('Location: '.SITE_URL.'login.php'); exit; } $me = $_SESSION['me']; $dbh = connectDb(); $sql = 'select * from sbank where id = :id limit 1'; $stmt = $dbh->prepare($sql); $stmt->bindValue(':id', $_SESSION['me'], PDO::PARAM_INT); $stmt->execute(); $bank = $stmt->fetch(); ?> <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>ユーザープロフィール</title> </head> <body> <p> Logged in as <?php echo h($me['id']); ?> (<?php echo h($me['email']); ?>) <a href="logout.php">[logout]</a> </p> <h1>ユーザープロフィール</h1> <?php echo $_SESSION['me']; ?><BR> <?php echo $_SESSION['id']; ?><BR> <p>お名前:<?php echo h($me['bankba']); ?></p> <p>メールアドレス:<?php echo h($bank['id']); ?></p> <p><a href="index.php">一覧へ</a></p> <?php if (session_id() === "") { echo "セッションは無効"; } else { echo "セッションは有効"; } ?><BR> <?php echo session_id(), ':', session_name(); ?> </body> </html> です。 色々調べて試しているためグチャグチャになってしまっているかもしれません。 何度も申し訳ございませんがチェックの方をしていただけませんでしょうか。
php.iniの設定で、 error_reporting = E_ALL & ~E_NOTICE になっていませんかね?もしそうであれば、その設定が原因で表示されていないエラーがある疑いがあります。 error_reporting = E_ALL | E_STRICT にしてすべてのエラーを表示してください。 おそらく session_start(); を忘れていたりとかそういうのでは・・・?
補足
初歩的な質問に長くお付き合い頂き感謝致します。 エラーの方は無事に表示出来ました。 Warning: session_start(): Cannot send session cache limiter - headers already sent (output started at /home/appears99/www/sns_php/profile01.php:1) in /home/アカウント/www/profile01.php on line 6 6行目に記述してあるもの session_start(); Notice: Array to string conversion in /home/アカウント/www/profile01.php on line 21 21行目に記述してあるもの $sql = 'select * from sbank where id = :id limit 1'; $stmt = $dbh->prepare($sql); $stmt->bindValue(':id', $_SESSION['me'], PDO::PARAM_INT); $stmt->execute(); $me = $stmt->fetch(); この記述の $stmt->execute(); 部分です。 やはりセッションの方がうまくいってないということでしょうか?
$_GET['id'] って $_SESSION['user_id'] のミスですかね・・・? http://qiita.com/mpyw/items/b00b72c5c95aac573b71 ↑にも記載していますが、executeで引数を渡す方式だと全て「文字列」となりシングルクオートでくくられてしまい、MySQL側にキャストさせることになります。これでも動くのですが、「整数」のままシングルクオートでくくらずにバインドしたいならば下記のようにします。 $dbh = connectDb(); $sql = 'select * from sbank where id = :id limit 1'; $stmt = $dbh->prepare($sql); $stmt->bindValue(':id', $_SESSION['user_id'], PDO::PARAM_INT); $stmt->execute(); $me = $stmt->fetch(); しかしながら、$_SESSIONを編集できるのはサーバー側のみであり、常に整数値が格納されることが保証されているので、簡略化して $me = connectDb()->query("select * from sbank where id = {$_SESSION['user_id']} limit 1")->fetch(); でも誤りではありません。
補足
>$_GET['id'] って $_SESSION['user_id'] のミスですかね・・・? すいませんコピペしてそのまま質問してしまっていました。 ご教授頂きました通りしてみたのですがうまくいきません。 そして $dbh = connectDb(); $sql = 'select * from sbank'; $stmt = $dbh->prepare($sql); $stmt->bindValue(':id', $_SESSION['user_id'], PDO::PARAM_INT); $stmt->execute(); $me = $stmt->fetch(); このようにwhere句の条件を消した場合は全然違う人のですが取得できます。 これは条件を指定して取得できないということはセッションが上手くわたっていないということなのでしょうか?
> カラム名が同じになっていれば紐づけできているということでいいのでしょうか? カラム名自体は違っても構いません。 要は異なるテーブルをリンクするために、全く同じ値を構成するカラムがあればいいだけです。 下記のように2つのテーブルを内部結合させることも出来ます。 SELECT t1.name AS name, t2.address AS address FROM t1 INNER JOIN t2 ON t1.id = t2.id > 今ログインのためにセットされている記述を変更するべきなのでしょうか? 最初に会員に関する情報を全部取ってきてセッションに保存し、データベースでそれらに変更があった場合はセッション側も更新する、という方法でもいいのですが、データベースの「データを1か所に集約する」という理念には反している気がするんですよね。その点では「不変の識別値(ID:Identifier)」という最低限のものだけをセッションに入れるようにした方が分かりやすいし、設計としても好ましいと思います。 よってログインページでは $_SESSION['user_id'] にユーザー固有のIDを保存します。他にユーザーに関する情報のセットは一切行いません。各ページで次の処理を行ってください。 1. if (!isset($_SESSION['user_id'])) { ____header('Location: ログインページURL'); ____exit; } として未ログイン状態であればログインページに飛ばす。 2. ログイン中であることが1のチェックで保証されれば、WHERE句と$_SESSION['user_id']を使って、usersテーブルやbankテーブルから必要なものだけ残りの情報を取り出す。これはセッションに保存したりせずに毎回データベースに接続して行うべき。
補足
丁寧なご回答ありがとうございます。 セッションにはIDのみ保存しログインができました。 そしてセッションを使い呼び出そうとしてみたのですが、またここがうまくいきません。 $me = $_SESSION['user_id']; $dbh = connectDb(); $sql = "select * from sbank where id = :$me limit 1"; $stmt = $dbh->prepare($sql); $stmt->execute(array(":$me" => (int)$_GET['id'])); $id = $stmt->fetch(); 記述の方が間違っているのでしょうか? 何度も申し訳ございませんがよろしくお願いいたします。
・あらゆる情報全てを「ユーザー固有のID」と紐つけて保存 ・セッションに保存するのは「ユーザー固有のID」のみ ・必要なときにSQLでWHERE句を使ってユーザーIDで絞り込んで情報取得 「え?毎回データベースつなぐの重くない?」と思うかもしれませんが、 リモートのデータベースでない場合はよほどのことが無い限りこれでも 十分なパフォーマンスを期待できます。 あんまりPDOの質問と関係ないかな・・・? 一応参考リンク貼っておきますね。 http://qiita.com/mpyw/items/b00b72c5c95aac573b71
補足
素早い回答と指摘ありがとうございます。 >・あらゆる情報全てを「ユーザー固有のID」と紐つけて保存 usersテーブルにidのカラムがあり主キーです。 bankテーブルに同じidカラムを作って同じ整数が振り分けられています。 カラム名が同じになっていれば紐づけできているということでいいのでしょうか? >・セッションに保存するのは「ユーザー固有のID」のみ このドットインストールの例でいくと$meにuserのデータを保存してあると思うのですが、どのタイミングでセッションをセットしておくのでしょうか?login.phpにIDを別途セットするべきなのか、今ログインのためにセットされている記述を変更するべきなのでしょうか? 2点質問を返すような失礼な書き方で申し訳ございませんがご回答頂けると光栄です。
お礼
本当にありがとうございました。 先ほどダウンロードさせて頂き中を拝見させていただきました。 オブジェクト指向の方はまだ勉強を全然行っていないのですが、全てに説明を付けていただいており生徒になった気分で見ておりました。 これを機にオブジェクト指向も勉強し答える側になれればと思います。 このような初心者のご質問に先生のように最後まで付き合っていただき大変感謝致しております。 まだまだわからないことが多くこちらでご質問させていただくこともあると思いますが、お時間がありましたらお相手していただけると助かります。 何度も聞き返すような失礼な対応にも真摯にご回答頂きありがとうございました。