こんにちは。GeNERACEのみきあらいです。
今回はPHP初心者の方に向けて、コードの最適化について書かせて頂きました。初めにコードを書くにあたって意識したことをまとめ、その後に実際にどのようなコードを書いたのか、サンプルコードをご紹介します。
今回紹介するコードを書いた開発環境は以下のとおりです。
- 言語: PHP
- フレームワーク: CakePHP
コードを書くときは、DRY原則の考えを参考にしました。
DRY原則とは、すでにご存知の方もいると思いますが、“Don’t Repeat Yourself”の略です。
この原則の概要は「同じコードを繰り返し書かない」です。
「同じコードを繰り返し書かない」というものの、実際にどのような点を意識していけばいいのか、他の人が書いた記事やコードを見たり、自分なりにコードを書いたりして気付いた点を下記にまとめました。
ポイント
- publicファンクションとprivateファンクションの役割を徹底的にわける
- エラーを弾く順番は「DBアクセスしなくてもエラーを判定できるもの→DBアクセスが必要なもの」
- 処理を書くときは他のコントローラーでも使う可能性があるか確認する
publicファンクションでは、データの登録や表示を行う処理だけを書いて、データの判定処理やチェック処理はprivateメソッドにまとめて、publicファンクションでは必要に応じてprivateメソッドを呼び出します。
DBアクセスを極力少なくするために、順番を守る必要があります。
もしDBアクセスをして、その後にDBアクセスをしなくてもエラーを判定できるメソッドを実行してエラーが出た場合は、先にDBアクセスした分がムダになり、DBに余計な負荷を与えるだけです。
任意のコントローラーでのみ使うのならprivateメソッドに、他のコントローラーでも使うのなら親コントローラーに共通メソッドを作ります。
では早速、サンプルコードをご紹介します。ご紹介するのは、DRY原則を意識しないで書いたコードと、DRY原則を意識して改善したコードの二種類です。
(※コードはあくまでもサンプルです。)
<サンプルコード1>
// TestsController.php public function register() { // 1. パラメータを取得。 if ($this->request->is('post')) { $testId = $this->request->data['Test']['testId']; } else { $this->redirect(array('controller' => 'test', 'action' => 'index')); } // 2. 1で受け取ったIDをもとにデータを取得。 $sampleList = $this->Sample->getList($testId); // 3. 2で取得したデータのチェック。 if (empty($sampleList)) { return $this->render('error'); } // 4. 1で受け取ったIDのチェック。 if (!is_int($testId) || $testId < 0) { $this->redirect(array('controller' => 'test', 'action' => 'index')); } // 5-1. 2で取得したデータにカテゴリーと名前をつけて登録。 while (list(, $data) = each($sampleList)) { // 5-2. 名前を取得。 $dataName = $this->Name->getData($data['Sample']['name_id']); $data['Sample']['name'] = $dataName; $data['Sample']['category'] = self::CATEGORY_TEST; $this->Sample->save($data); } // 6. 完了ページを表示。 return $this->render('complete'); }
色々と直さなくてはいけない箇所がたくさんあるコードですね。
まず、1〜3まではパラメータからIDを取得し、そのIDをもとにデータを取得して、さらにそのデータをチェックするという流れで特に問題はないのですが、問題はそのあとです。
4の段階で、1で取得したIDをチェックしています。もし4でエラーになった場合はリダイレクトしregister()が終了するため、2でDBアクセスをする意味がなくなってしまいます。
受け取ったパラメータのチェックの内容に関しても、必要に応じて共通メソッドもしくはprivateファンクションに作ってそれを使う方がわかりやすいものがサンプルコード1の中にあります。
また、DBにデータを登録する処理を行なっている5-1、5-2のコードに注目してください。もし$sampleListが10,000件以上あったら、その件数と同じ回数で5-2の処理を行うことになります。
サンプルコード1は、上記のポイントからは大きくずれていることがわかります。
そこで、サンプルコード1を改善したサンプルコード2をご紹介します。
(※コードはあくまでもサンプルです。)
<サンプルコード2>
// TestsController.php public function register() { // 1. パラメータを取得。 // →リクエストパラメータの取得とそのチェックをする共通メソッド // (例:getPostRequestParam($paramName, $controllerName))を使うように改善。 $testId = $this->getPostRequestParam('testId', 'Test'); // 2. 1で受け取ったIDのチェック。 // →DBアクセスする前に、DBアクセスしなくてもできるチェックを行うように改善。 // パラメータの整合性チェックを行うprivateファンクション(例:isValidId($param))を使う。 if (!$this->isValidId($testId)) { $this->redirect(array('controller' => 'test', 'action' => 'index')); } // 3. 1で受け取ったIDをもとにデータを取得。共通メソッド(例:getData($id))を使う。 $sampleList = $this->Sample->getList($testId); // 4. 3で取得したデータのチェック。 if (empty($sampleList)) { return $this->render('error'); } // 5-1. 3で取得したデータにカテゴリーと名前をつけて登録。初めに登録する名前のデータを一括で取得するため、 // 3で取得したデータから名前のIDを取り出して、IDリストを作る。 while (list(, $data) = each($sampleList)) { $dataNameIdList[] = $data['Sample']['name_id']; } reset($sampleList); // 5-2. 登録する用の名前のデータを、5-1で取得したIDリストをもとに一括取得する。 // 共通メソッドgetList($idList)を使う。 $nameList = $this->Name->getList($dataNameIdList); // 5-3. もう一回$sampleListを回して、データを登録。 while (list(, $data) = each($sampleList)) { while (list(, $nameData) = each($nameList)) { // ※すでに名前のデータは一括で取得しているため、 // サンプルコード1のようにwhileの中で毎回DBアクセスしないで済みます。 if ($nameData['Name']['name_id'] == $data['Sample']['name_id']) { $data['Sample']['name'] = $nameData['Name']['name']; $data['Sample']['category'] = self::CATEGORY_TEST; $this->Sample->save($data); break; } } reset($nameList); } // 6. 完了ページを表示。 return $this->render('complete'); } private function isValidId($id) { if (!is_int($id) || $id < 0) { return false; } return true; }
このように、サンプルコード1からサンプルコード2に書き変えることにより、以下のポイントを抑えたコードに変わりました。
- DB負荷を避けるため、DBアクセス不要のチェックメソッドをDBアクセス必須のチェックメソッドの前に行う。
- DB負荷を避けるため、DBアクセスの数を減らせるように処理の流れを直す。
- 同じような処理を他のコントローラーで書かなくても済むように、共通メソッドを必要に応じて作る。
今回ご紹介したコードはあくまでサンプルコードですが、このようにDRY原則を常に意識してコードを書けば、可読性も高く、同じ処理を複数のコントローラーで定義するといったムダも防ぐことができます。
また、先週のプログの記事にもあったように、チェックメソッドの順番に気をつけて、DB負荷をかけないことを意識したコードを書くことが可能になることがわかりました。
今回は以上です。最後までお読みいただきましてありがとうございました!
Pingback: PHP初心者向けのコードの最適化 | GeNERACE labo : ちゅどん道中記