= 開発効率向上のための Tips = == 概要 == この Tips は, EC-CUBE標準規約には含まれないが, 開発や保守の効率を向上するための秘訣を記載します. なお, この Tips に沿っていない既存のソースコードが多々ありますが, すべてを修正するのは現実的ではありません. 新規にカスタマイズを加える箇所のみ適用すると良いでしょう. === 要件 === 主に, PHP5.3.2 以降の環境で稼働するものを対象とします. == PHP5 の構文を使用する == EC-CUBE 2.12.0 以降, PHP4 はサポートしていないため, PHP5 の構文を使用します. これらを適切に使用することで, 開発・保守効率の向上が望めます. === Property Visibility (アクセス権) === クラスのメンバーには, public, protected, private のアクセス権を付与します. これにより, 外部クラスからの意図せぬメンバ変数アクセスを防ぐことができ, 堅牢かつ保守しやすいコードになります. {{{ #!php sendOrderMail($this->order_id); } } /** * OK 引数やアクセサメソッドを経由することにより, 意図せぬ振舞い変更を防ぐ */ class MyGoodClass { private $order_id; public function setOrderId($order_id) { $this->order_id = $order_id; } public function getOrderId() { return $this->order_id; } public function doSendMail() { $objPurchase = new SC_Purchase_Ex(); $objPurchase->sendOrderMail($this->getOrderId()); } } }}} private または protected 修飾子により, 外部からのアクセスを防止することは重要ですが, EC-CUBEは継承によるカスタマイズを多用するため, 特に理由の無い限りは protected 修飾子を利用します. {{{ #!php sendOrderMail($order_id); } } /** * OK 内部用クラスは, 継承によるカスタマイズの余地を残すため, protected を使用する */ class MyGoodClass { protected function localFunction($order_id) { $objPurchase = new SC_Purchase_Ex(); $objPurchase->sendOrderMail($order_id); } } }}} === Exception (例外) === 適切な Exception を使用することにより, エラーハンドリングを簡潔に記述できます. {{{ #!php 10) { return false; } return $number + 10; } } /** * OK Exception を使用すると, 返り値が簡潔になり, エラーハンドリングもしやすくなります. * Exception クラスを拡張し, 独自の Exception を定義しています. * customFunction() は, 必ず数値が返り, エラーが発生した場合は MyException をスローします. */ class MyGoodClass { /** * @return integer 計算後の値を返す. * @throw MyException 処理が失敗した場合 */ public function customFunction($number) { if ($number > 10) { throw new MyException('error message!'); } return $number + 10; } } class MyException extends Exception { public function __construct($message, $code, $previous) { parent::__construct($message, $code, $previous); } } }}} === Enum (列挙型) === PHP の標準構文ではありませんが, interface を使用することにより Enum(列挙型)を擬似的に表現できます. 定数をたくさん定義するより, 直感的に記述できます. {{{ #!php setPropertiesFromArray($arrOrderDetail); } } /** * 税込単価を返します. * * @return integer 税込単価 */ public function getTotalInTax() { return SC_Helper_DB_Ex::sfTax($this->getPrice()) * $this->getQuantity(); } /** * 該当商品のカテゴリIDを配列で返します. * * @return array カテゴリ一覧の配列 */ public function getCategoryIds() { // エンティティクラスの内部での DBアクセスは極力避けるべきですが, // 内部でDBアクセスした方が効率が良い場合は, その旨のコメントを入れて使用します. // この場合は, product_id に紐づいた category_id を取得します. // 以下のロジックを外出しするより簡潔なコードになります. $objQuery = SC_Query_Ex::getSingletonInstance(); $col = 'category_id'; $from = 'dtb_product_categories'; $where = 'product_id = ?'; $arrResults = $objQuery->select($col, $from, $where, array($this->getProductId())); $arrCategoryIds = array(); foreach ($arrResults as $val) { $arrCategoryIds[] = $val; } return $arrCategoryIds; } /* * 読み取り専用にしたいプロパティは, getter のみ作成します. * 更新可能なプロパティは setter を作成します. */ public function getOrderId() { return $this->order_id; } public function getProductId() { return $this->product_id; } public function getProductClassId() { return $this->product_class_id; } public function getProductCode() { return $this->product_code; } public function getClassCategoryName1() { return $this->classcategory_name1; } public function getClassCategoryName2() { return $this->classcategory_name2; } public function getPrice() { return $this->price; } public function setQuantity($quantity) { $this->quantity = $quantity; } public function getQuantity() { return $this->quantity; } protected function getPointRate() { return $this->point_rate; } /** * 引数の連想配列を元にプロパティを設定します. * DBから取り出した連想配列を, プロパティへ設定する際に使用します. * * @param array プロパティの情報を格納した連想配列 * @param ReflectionClass $parentClass 親のクラス. 本メソッドの内部的に使用します. */ public function setPropertiesFromArray($arrProps, ReflectionClass $parentClass = null) { $objReflect = null; if (is_object($parentClass)) { $objReflect = $parentClass; } else { $objReflect = new ReflectionClass($this); } $arrProperties = $objReflect->getProperties(); foreach ($arrProperties as $objProperty) { $objProperty->setAccessible(true); $name = $objProperty->getName(); $objProperty->setValue($this, $arrProps[$name]); } // 親クラスがある場合は再帰的にプロパティを取得 $parentClass = $objReflect->getParentClass(); if (is_object($parentClass)) { self::setPropertiesFromArray($arrProps, $parentClass); } } /** * プロパティの値を連想配列で返します. * DBを更新する場合などで, 連想配列の値を取得したい場合に使用します. * * @return array 連想配列のプロパティの値 */ public function toArray() { $arrResults = array(); $objReflect = new ReflectionClass($this); $arrProperties = $objReflect->getProperties(); foreach ($arrProperties as $objProperty) { $objProperty->setAccessible(true); $name = $objProperty->getName(); $arrResults[$name] = $objProperty->getValue($this); } // 親クラスがある場合は再帰的にプロパティを取得 $parentClass = $objReflect->getParentClass(); if (is_object($parentClass)) { $arr = self::toArray($parentClass); $arrResults = array_merge(self::toArray($parentClass), $arrResults); } return $arrResults; } } }}} PHP で, 以下のようにデータベースから取得できます {{{ #!php getOrderDetail($order_id); $this->arrObjOrderDetail = array(); foreach ($arrOrderDetail as $orderDetail) { $this->arrObjOrderDetail[] = new SC_Entity_OrderDetail($orderDetail); } }}} Smarty で記述した場合に, 連想配列より簡潔にコーディングできます {{{

}}} データベースを更新する場合も簡潔にコーディングできます {{{ #!php setQuantity(2); $objQuery = SC_Query_Ex::getSingletonInstance(); $objQuery->update('dtb_order_detail', $objOrderDetail->toArray(), 'order_detail_id = ?', $objOrderDetail->getOrderDetailId()); }}} == ユニットテストを活用する == [wiki:EC-CUBE標準規約/単体テストガイドライン]