解決済み

PDOで複数のテーブルを扱いたい

  • 困ってます
  • 質問No.8326234
  • 閲覧数1015
  • ありがとう数2
  • 気になる数0
  • 回答数6
  • コメント数0

お礼率 48% (25/52)

PDOで複数のテーブルを扱いたい

現在ドットインストールというサイトで勉強しております。
http://dotinstall.com/lessons/sns_php_v2

このユーザー管理のレッスンで自分なりに応用してみようと戦っております。
これでは、ログインを行い登録したユーザーのプロフィールを表示するという内容なのですが、管理人とユーザーという風に分けてadminのテーブルに管理者を作成し登録し、登録された会員は自分のプロフィールを表示できるという流れにしたいと思っております。

レッスン通りでは問題なく出来ました。

そこで次にテーブルを分けて表示することにチャレンジしております。
users(会員テーブル)とは別にaddress(住所テーブル)を作成し会員テーブルと同じようにidカラムと住所を入れるtext項目をデータベースに作成しチャレンジしました。

サイト通りプロフィールを表示させる方法でやればこちらもテーブルを読み込ませることはできるのですが、URLの数値を変えれば当然他の人の情報も見えてしまいます。

$meというセッションで取得したいのですがusersの情報はログイン時にセッションとして受け取っているので#meで受け取りができるのですがaddressのテーブル情報にもこのセッション($me)で表示させるにはどのようにすればよいのでしょうか?

やりたいことはログインした会員の情報のみ(他人のデータをみれない)表示させたい。
データベースのほかのテーブルもusersのセッションで共有したいということです。

わかりにくい説明で申し訳ございませんが、お助けいただけませんでしょうか。

質問者が選んだベストアンサー

  • 回答No.6

ベストアンサー率 77% (407/525)

(私の意見押し付けて申し訳ないですが)ドットインストール流の「手続き型」の書き方だとコードが汚くなるので、「オブジェクト指向」をふんだんに採用して書き直してみました。元のコードをこれ以上添削する気にはならなかったのでこれで勘弁してください(汗)
https://github.com/Certainist/sns_php
お礼コメント
milkkokoa94856

お礼率 48% (25/52)

本当にありがとうございました。

先ほどダウンロードさせて頂き中を拝見させていただきました。
オブジェクト指向の方はまだ勉強を全然行っていないのですが、全てに説明を付けていただいており生徒になった気分で見ておりました。
これを機にオブジェクト指向も勉強し答える側になれればと思います。
このような初心者のご質問に先生のように最後まで付き合っていただき大変感謝致しております。

まだまだわからないことが多くこちらでご質問させていただくこともあると思いますが、お時間がありましたらお相手していただけると助かります。

何度も聞き返すような失礼な対応にも真摯にご回答頂きありがとうございました。
投稿日時 - 2013-11-02 18:09:16

その他の回答 (全5件)

  • 回答No.5

ベストアンサー率 77% (407/525)

エラーメッセージを見ると、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);

のミスでしょうか。
補足コメント
milkkokoa94856

お礼率 48% (25/52)

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>

です。
色々調べて試しているためグチャグチャになってしまっているかもしれません。
何度も申し訳ございませんがチェックの方をしていただけませんでしょうか。
投稿日時 - 2013-11-02 05:17:07
  • 回答No.4

ベストアンサー率 77% (407/525)

php.iniの設定で、

error_reporting = E_ALL & ~E_NOTICE

になっていませんかね?もしそうであれば、その設定が原因で表示されていないエラーがある疑いがあります。

error_reporting = E_ALL | E_STRICT

にしてすべてのエラーを表示してください。
おそらく

session_start();

を忘れていたりとかそういうのでは・・・?
補足コメント
milkkokoa94856

お礼率 48% (25/52)

初歩的な質問に長くお付き合い頂き感謝致します。
エラーの方は無事に表示出来ました。

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();
部分です。


やはりセッションの方がうまくいってないということでしょうか?
投稿日時 - 2013-11-01 19:02:43
  • 回答No.3

ベストアンサー率 77% (407/525)

$_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();

でも誤りではありません。
補足コメント
milkkokoa94856

お礼率 48% (25/52)

>$_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句の条件を消した場合は全然違う人のですが取得できます。
これは条件を指定して取得できないということはセッションが上手くわたっていないということなのでしょうか?
投稿日時 - 2013-11-01 12:49:06
  • 回答No.2

ベストアンサー率 77% (407/525)

> カラム名が同じになっていれば紐づけできているということでいいのでしょうか?

カラム名自体は違っても構いません。
要は異なるテーブルをリンクするために、全く同じ値を構成するカラムがあればいいだけです。
下記のように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テーブルから必要なものだけ残りの情報を取り出す。これはセッションに保存したりせずに毎回データベースに接続して行うべき。
補足コメント
milkkokoa94856

お礼率 48% (25/52)

丁寧なご回答ありがとうございます。

セッションには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();

記述の方が間違っているのでしょうか?
何度も申し訳ございませんがよろしくお願いいたします。
投稿日時 - 2013-11-01 08:20:07
  • 回答No.1

ベストアンサー率 77% (407/525)

・あらゆる情報全てを「ユーザー固有のID」と紐つけて保存
・セッションに保存するのは「ユーザー固有のID」のみ
・必要なときにSQLでWHERE句を使ってユーザーIDで絞り込んで情報取得

「え?毎回データベースつなぐの重くない?」と思うかもしれませんが、
リモートのデータベースでない場合はよほどのことが無い限りこれでも
十分なパフォーマンスを期待できます。

あんまりPDOの質問と関係ないかな・・・?
一応参考リンク貼っておきますね。
http://qiita.com/mpyw/items/b00b72c5c95aac573b71
補足コメント
milkkokoa94856

お礼率 48% (25/52)

素早い回答と指摘ありがとうございます。
>・あらゆる情報全てを「ユーザー固有のID」と紐つけて保存

usersテーブルにidのカラムがあり主キーです。
bankテーブルに同じidカラムを作って同じ整数が振り分けられています。

カラム名が同じになっていれば紐づけできているということでいいのでしょうか?

>・セッションに保存するのは「ユーザー固有のID」のみ

このドットインストールの例でいくと$meにuserのデータを保存してあると思うのですが、どのタイミングでセッションをセットしておくのでしょうか?login.phpにIDを別途セットするべきなのか、今ログインのためにセットされている記述を変更するべきなのでしょうか?


2点質問を返すような失礼な書き方で申し訳ございませんがご回答頂けると光栄です。
投稿日時 - 2013-10-30 10:56:32
結果を報告する
このQ&Aにはまだコメントがありません。
あなたの思ったこと、知っていることをここにコメントしてみましょう。
関連するQ&A
AIエージェント「あい」

こんにちは。AIエージェントの「あい」です。
あなたの悩みに、OKWAVE 3,600万件のQ&Aを分析して最適な回答をご提案します。

その他の関連するQ&A、テーマをキーワードで探す

キーワードでQ&A、テーマを検索する

ピックアップ

ページ先頭へ