- ベストアンサー
オブジェクト指向について
- オブジェクト指向についての質問
- データベース接続関連のクラスに記述された処理が正しいか
- オブジェクト指向の呼び出し方を指導してほしい
- みんなの回答 (7)
- 専門家の回答
質問者が選んだベストアンサー
- ベストアンサー
ページングリンクを表示したければ // 総ページ数を取得 $max_page = ceil($whole_count / COMMENTS_PER_PAGE); // ページラベルを格納する配列 $labels = array(); // 1ページずつラベルを生成 for ($i = 1; $i <= $max_page; $i++) { $labels[] = $i !== $page ? "<a href=\"?page={$i}\">{$i}</a>" : // 現在以外のページ (string)$i // 現在のページ ; } // ページラベルを「 | 」区切りで結合してまとめる $labels = implode(' | ', $labels); として変数$labelsを用意し、それをメソッドが返す連想配列に入れてみてはどうでしょうか? bankのテーブルに登録されなかったのは何故でしょうね・・・提示していただいたコードにはこれといってミスは無いように思えます。 try { // トークンをチェック Token::check($token); // 登録 DB::connect()->signup($name, $email, $password); DB::connect()->bankup($no, $bank, $bankshi, $bankba, $bankme); // ログインページに遷移 redirect('/login.php'); } catch (Exception $e) { // 例外スタックを配列に変換 $errors = exception_to_array($e); } という流れで何も例外がキャッチされずにそのままログインページに遷移出来ているならば、SQL実行には成功しているはずです。「既にログイン済みなのでTryブロックに入る前にindex.phpに飛ばされている」というケースが考えられますが、新規登録フォームはちゃんとブラウザで表示でき、それで送信したあとにsignupメソッドは正常に稼働しているんですよね?そうであればちょっと思い当たる節はないですね・・・すみません・・・ なお、「signup」と「bankup」という2つの連続する処理で、signupだけ成功してbankupは失敗すると普通はsignupも無かったことにしたい、と考えますよね?そういった処理を実現するのが「トランザクション処理」です。 トランザクション(transaction) [e-Words] http://e-words.jp/w/E38388E383A9E383B3E382B6E382AFE382B7E383A7E383B3.html PDOは簡単にトランザクションを行うことが出来ます。 PDO::beginTransaction http://php.net/manual/ja/pdo.begintransaction.php PDO::commit http://www.php.net/manual/ja/pdo.commit.php PDO::rollback http://www.php.net/manual/ja/pdo.rollback.php PDO::lastInsertId (最後に生成されたAUTO_INCREMENTの値を返す。マニュアルには直接記載されていないが、コメント欄にある通りMySQLではトランザクション処理中だけ使える。) http://php.net/manual/ja/pdo.lastinsertid.php ひとつ気になったのですが、$noってsignupで自動生成された会員番号のことですよね?でしたらPDO::lastInsertIdも用いて下記のようにしてみてはいかがですか。signupとbankupは必ず同時に起こるアクションなので、signup_and_bankupというラッパーメソッドをpublicで作っておき、signupとbankupはprivateにして外部から呼び出さないものとしましょう。 【DB.php】 public function signup_and_bankup($name, $email, $password, $bank, $bankshi, $bankba, $bankme) { // トランザクション開始 $this->pdo->beginTransaction(); try { // 会員情報の登録 $this->signup($name, $email, $password); // 生成された会員番号を取得 $no = $this->pdo->lastInsertId(); // 口座情報を登録 $this->bankup($no, $bank, $bankshi, $bankba, $bankme); // 一連の処理が全て成功すればコミット $this->pdo->commit(); } catch (Exception $e) { // 1つでも失敗が起こればロールバック $this->pdo->rollback(); // 例外はここでは処理しないのでもう一度投げる throw $e; } } private function signup(...) { } private function bankup(...) { } 【index.php】 try { // トークンをチェック Token::check($token); // 登録 DB::connect()->signup_and_bankup($name, $email, $password, $bank, $bankshi, $bankba, $bankme); // ログインページに遷移 redirect('/login.php'); } catch (Exception $e) { // 例外スタックを配列に変換 $errors = exception_to_array($e); } filter_struct_utf8に続いてsignup_and_backupでも全く同じ並びの引数なのに2回も渡すのを煩わしく思うならば、filter_struct_utf8の返り値をextractせずに $params = filter_struct_utf8(....); のようにそのまま配列に入れておき、 // 登録 call_user_func_array(array(DB::connect(), 'signup_and_bankup'), $params); とする手もあります。(filter_struct_utf8とsignup_and_backupの引数の順番が一致するように注意してください。キーの値は無視され、単に並んでいる値の順番だけがそのまま引数の順番として扱われます。) call_user_func_array http://php.net/manual/ja/function.call-user-func-array.php call_user_func_arrayの第1引数に渡すcallableという擬似変数型について(今回のケースはこのタイプ3に該当) http://www.php.net/manual/ja/language.types.callable.php 脳内デバッグしかしてないので細かいミス等ありましたらすみません m(_ _)m
その他の回答 (6)
// 情報を連想配列で返す return array( 'comments' => $comments, 'message' => $message, ); としているので <p><?=h($result['comment'])?></p> ではなく <p><?=h($result['message'])?></p> ですね。
補足
ありがとうございました。 $offset = COMMENTS_PER_PAGE * ($page - 1); のような記述あったもので勝手に次のページへなどの機能も表示できるものと勘違いしておりました。 ページを変えるようなものは別途記述が必要ということですね。 messageでは無事に件数を表示できました。 長々とお付き合い頂き誠にありがとうございました>< おまけ程度にご回答頂けると幸いなのですが、ドットインストールで登録する際に、テーブルごとにINSERTでいれていたのですが、オブジェクト指向の場合は public function bankup($no, $bank, $bankshi, $bankba, $bankme) { // 例外初期化 $e = null; // 登録チェック if ($no === '') { $e = e('会員番号が入力されていません。', $e); } if ($bank === '') { $e = e('銀行名が入力されていません。', $e); } if ($bankshi === '') { $e = e('支店名が入力されていません。', $e); } if ($bankba === '') { $e = e('口座番号が入力されていません。', $e); } if ($bankme === '') { $e = e('口座名義が入力されていません。', $e); } // 1つでも例外が発生していればスローする if ($e) { throw $e; } // プリペアドステートメントを生成 $stmt = $this->pdo->prepare(implode(' ', array( 'INSERT', 'INTO `bank`(`no`, `bank`, `bankshi`, `bankba`, `bankme`)', 'VALUES (?, ?, ?, ?, ?)', ))); // 値をバインドして実行 $stmt->execute(array($no, $bank, $bankshi, $bankba, $bankme)); } このような形でクラスにユーザーテーブルとは別に記述して signup.phpで extract( に追加して // POSTされたときのみ実行 if ($_SERVER['REQUEST_METHOD'] === 'POST') { try { // トークンをチェック Token::check($token); // 登録 DB::connect()->signup($name, $email, $password); DB::connect()->bankup($no, $bank, $bankshi, $bankba, $bankme); // ログインページに遷移 redirect('/login.php'); } catch (Exception $e) { // 例外スタックを配列に変換 $errors = exception_to_array($e); } } というふうに追加してやってみたものの会員の方には登録されたのですが、bankのテーブルには登録されませんでした。 会員登録のとこをコピーしたりして自分なりに試してはみたのですがなぜかbankテーブルの方だけ登録がされずアレレとなってしまいました。 追加質問のようで恐縮なのですが、せっかく大先生にお会いできたので解決のヒントになるかと思いご質問させていただきました。
SQLにシンタックスエラーがあると言ってます。 'SELECT CALC_FOUND_ROWS *', ↓ 'SELECT SQL_CALC_FOUND_ROWS *', またミスでした・・・トホホ・・・
補足
ありがとうございました!! ついに一覧を取得し表示することができました。 しかしながら最後の <p><?=h($result['comment'])?></p> ここがエラーになっており Notice: Undefined index: と出ております。 この部分は次のページへなどの部分が表示されるばしょなのでしょうか? 辿っていくとそのようなページかと思っているのですが、このリンクの部分が今だにエラーで夜が明けてしまいましたm(_ _)m 記載させて頂いたエラーでは原因を突き止めるのが難しいとは思いますが、調べて見る箇所などヒントがありましたらアドバイス頂きたくしつこくご質問させていただきました。
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET CHARACTER SET utf8' の件に関してはこちらをご覧になられたのでしょうか? 【PHP, PDOでMySQL接続時の文字コード設定】 http://me.beginsprite.com/archives/889 実はこれ、コンストラクタの初期化クエリで実行したところで通常クエリで実行したのと何も変わらないんですよね。「SET NAMES」と「SET CHARACTER SET」もほとんど変わりません。むしろ後者はお節介なことをしようとするので前者の方が推奨されます。下記で詳しく解説しています。 【PHPでデータベースに接続するときのまとめ】 http://qiita.com/mpyw/items/b00b72c5c95aac573b71 上記のページでもリンクを張っていますが、古いPHPのレンタルサーバーであれば以下のような方法を採ることが出来ます。 【PHP 5.3.6より前のバージョンの PDO MySQL で charset を指定する】 http://qiita.com/ngyuki/items/d88a4df860abb51eb714 cnfファイル設置が面倒であれば、妥協して「SET NAMES utf8」でもいいかもしれません。非常に稀な環境での例外もありますが、基本的にはUTF-8に限り「SET NAMES」の安全性が保証されています。 でもさくらのスタンダードプランってバージョン選べるんじゃないですかね。5.2~5.4で選択出来ると思いますが。5.4以降限定の機能がそれなりに多いので、5.4系の最新バージョンを使えるならそれを使っておくべきだと思います。可能であればジェネレータやfinally構文も使える5.5系の方が望ましいですが。 endforeachのエラーに関しては、 <?php foreach ($result['comments'] as $comment) ?> ↓ <?php foreach ($result['comments'] as $comment): ?> で解決します。私の入力ミスでした、申し訳ないです。 制御構造に関する別の構文 http://php.net/manual/ja/control-structures.alternative-syntax.php オブジェクト指向は誰もがぶち当たる壁なのでめげずに頑張ってください! (実を言うとPDO自体もオブジェクト指向なんですけどね)
補足
素早い回答ありがとうございます。 :がないということに気がつかない私がダメですね>< とりあえずエラー文は消えたのですが・・・ Internal Server Errorが出てしまいました。 このエラーばっかりは自分で解決するしかないかなと思いながら質問を追加してしまいました。 URLが error.php?errors[0]=SQLSTATE[42000]%3A+Syntax+error+or+access+violation%3A+1064+You+have+an+error+in+your+SQL+syntax%3B+check+the+manual+that+corresponds+to+your+MySQL+server+version+for+the+right+syntax+to+use+near+'FROM+`users`+LIMIT+0%2C+15'+at+line+1 となってるがヒントになるのでしょうか? これだけ色々アドバイスを頂き応援してくださっているので何とか今回の件を終了させ頑張って覚えたいと思います><
「1個しかインスタンス作れないならそもそも全部静的メソッド・静的プロパティにすればいいじゃん!」と思うかもしれませんが、そうしちゃうと、connect(新しくPDOインスタンスを生成するメソッド)を呼ぶ前にgetCommentsを呼べてしまいますよね。getCommentsはPDOインスタンス生成が済んでいることが前提のメソッドなので、そうしてしまうといくらか不都合があるわけです。そこでシングルトン形式を採用することで、あらゆるメソッドを呼ぶためには必ずPDOインスタンス生成を通過しなければならないように強制しています。
>> この記述はデータベース接続関連のクラスの中(DB.php)に記述したのですが正解でしょうか? はい、正しいですよ。 extract(filter_struct_utf8(INPUT_GET, array( 'page' => '', ))); try { $result = DB::connect()->getComments($page); } catch (Exception $e) { error_page($e); } のように変数$resultに取得して、あとはHTML部の中で、 <?php if ($result): ?> <ul> <?php foreach ($result['comments'] as $comment) ?> <li><?=h($comment['カラム名'])?></li> <?php endforeach; ?> </ul> <?php endif; ?> <p><?=h($result['comment'])?></p> みたいにすればいいんじゃないですかね。
補足
いつも本当にお世話になります。 図をUPしながら説明していただき頭があがりません。 仕事から帰ってワクワクしながらやってみたのですが、できませんでした。 オブジェクト指向に向いていないのかと凹んできました。 index.phpには <?php // 初期化 require 'init.php'; // ログインされている状態を要求 require_login(); extract(filter_struct_utf8(INPUT_GET, array( 'page' => '', ))); try { // ログイン中のユーザー情報を取得 $me = DB::connect()->getUser($_SESSION['user_id']); // 全てのユーザー情報を取得 $all = DB::connect()->getAllUsers(); $result = DB::connect()->getComments($page); } catch (Exception $e) { // エラーページに遷移 error_page($e); } // 出力を開始 start_output(); ?> <?php include 'adheader.php'; ?> <?php if ($result): ?> <ul> <?php foreach ($result['comments'] as $comment) ?> <li><?=h($comment['name'])?></li> <?php endforeach; ?> </ul> <?php endif; ?> <p><?=h($result['comment'])?></p> </body> </html> DB.phpにはクラスの中にちゃんと public function getComments($page) { // 必ず1以上の整数になるようにする $page = max(1, (int)$page); // 開始オフセット $offset = COMMENTS_PER_PAGE * ($page - 1); // SQL実行 $stmt = $this->pdo->query(sprintf( implode(' ', array( 'SELECT CALC_FOUND_ROWS *', 'FROM `users`', 'LIMIT %d, %d', )), $offset, COMMENTS_PER_PAGE )); // コメントを取得 $comments = $stmt->fetchAll(); // 現在件数を取得 // (PDOのコンストラクタでMYSQL_ATTR_USE_BUFFERED_QUERYを // 有効化しているときだけSELECTに対してrowCountメソッドが使える) $current_count = $stmt->rowCount(); // 総件数を取得 $whole_count = (int)$this->pdo->query('SELECT FOUND_ROWS()')->fetchColumn(); // メッセージ作成 $message = sprintf('全%d件中、%d件から%d件まで表示しています', $whole_count, $offset + 1, $offset + $current_count ); // 情報を連想配列で返す return array( 'comments' => $comments, 'message' => $message, ); } と記述しました。 SQL文のところはusersの登録人数を読ませたいのでテーブルをusersに変えています。 あとレンタルサーバーがさくらサーバーのスタンダードを使用しておりPHPのバージョンが古いためオブジェクト指向を教えていただく前は PDO::MYSQL_ATTR_INIT_COMMAND => 'SET CHARACTER SET utf8')); を記述していました。 オブジェクト指向でもなにかこれの対策が必要なのでしょうか? 今出ているエラーは Parse error: syntax error, unexpected 'endforeach' (T_ENDFOREACH) in /home/アカウント/www/index.php on line 40 40行目には <?php endforeach; ?> が記述されています。 理解力がないので質問するのもご迷惑かと思いながらご質問させていただきました。
お礼
大変丁寧に教えていただきましてありがとうございました!! 本当に勉強になりましたm(_ _)m 登録できなかったのは私の何かのミスだと思うのでテーブル名など記述間違いを探してみます。 ご質問全てに丁寧に教えて頂きやる気も出てきました♪ オブジェクト指向を頑張って覚えたいと思います。 またご質問にこのサイトを利用させていただくとおもいますので、ご縁がございましたら是非よろしくお願いいたします。 この度はありがとうございました。