Source for file ActiveRecord.php

Documentation is available at ActiveRecord.php

  1. <?php
  2. /**
  3.  * Teeple2 - PHP5 Web Application Framework inspired by Seasar2
  4.  *
  5.  * PHP versions 5
  6.  *
  7.  * LICENSE: This source file is subject to version 3.0 of the PHP license
  8.  * that is available through the world-wide-web at the following URI:
  9.  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  10.  * the PHP License and are unable to obtain it through the web, please
  11.  * send a note to license@php.net so we can mail you a copy immediately.
  12.  *
  13.  * @package     teeple
  14.  * @author      Mitsutaka Sato <miztaka@gmail.com>
  15.  * @license     http://www.php.net/license/3_0.txt  PHP License 3.0
  16.  */
  17.  
  18. /**
  19.  * exception class for Teeple_ActiveRecord.
  20.  * 
  21.  * @package teeple
  22.  * 
  23.  */
  24. class TeepleActiveRecordException extends Exception
  25. {
  26.     /**
  27.      * コンストラクタです。
  28.      * @param string $message 
  29.      */
  30.     function __construct($message)
  31.     {
  32.         return parent::__construct($message);
  33.     }
  34. }
  35.  
  36. /**
  37.  * ActiveRecordクラスです。
  38.  * 
  39.  * TODO oneToMany -- いったんなしにしとく。
  40.  * TODO oracle等のsequence対応。
  41.  * 
  42.  * <pre>
  43.  * [Entityクラスの作成]
  44.  * このクラスを継承して各テーブルのレコードを表すクラスを作成します。
  45.  * ファイルの配置場所は、ENTITY_DIR で定義されたディレクトリです。
  46.  * ファイル名は、`table名`.class.php, クラス名は Entity_`table名` とします。
  47.  * 
  48.  * 各テーブルの以下のプロパティを定義する必要があります。
  49.  *   // 使用するデータソース名(Teepleフレームワークを使用しない場合は不要。)
  50.  *  public static $_DATASOURCE = "";
  51.  *   // テーブル名称
  52.  *   public static $_TABLENAME = "";
  53.  *   // プライマリキーのカラム名(配列)
  54.  *   public static $_PK = array();
  55.  *   // プライマリキー以外のカラム名(配列)
  56.  *   public static $_COLUMNS = array();
  57.  *   // PKが単一で、AUTOINCREMENT等の場合にTRUEをセット
  58.  *   // ※シーケンスには対応できていません。
  59.  *   public static $_AUTO = TRUE;
  60.  *   // joinするテーブルの定義
  61.  *   public static $_JOINCONFIG = array();
  62.  * </pre>
  63.  * 
  64.  * @package teeple
  65.  * 
  66.  */
  67. {
  68.     /**
  69.      * 使用するデータソース名を指定します。
  70.      * 指定が無い場合は、DEFAULT_DATASOURCE で設定されているDataSource名が使用されます。
  71.      *
  72.      * @var string 
  73.      */
  74.     public static $_DATASOURCE "";
  75.     
  76.     /**
  77.      * このエンティティのテーブル名を設定します。
  78.      * 
  79.      * <pre>
  80.      * スキーマを設定する場合は、"スキーマ.テーブル名"とします。
  81.      * 子クラスにて必ずセットする必要があります。
  82.      * </pre>
  83.      *
  84.      * @var string 
  85.      */
  86.     public static $_TABLENAME "";
  87.     private $_tablename;
  88.     
  89.     /**
  90.      * プライマリキー列を設定します。
  91.      * 
  92.      * <pre>
  93.      * プライマリキーとなるカラム名を配列で指定します。
  94.      * 子クラスにて必ずセットする必要があります。
  95.      * </pre>
  96.      * 
  97.      * @var array 
  98.      */
  99.     public static $_PK array('id');
  100.     private $_pk;
  101.     
  102.     /**
  103.      * プライマリキーが自動セット(auto increment)かどうかを設定します。
  104.      * 
  105.      * <pre>
  106.      * 子クラスにて必ずセットする必要があります。
  107.      * </pre>
  108.      * 
  109.      * @var bool 
  110.      */
  111.     public static $_AUTO TRUE;
  112.     private $_auto;
  113.     
  114.     /**
  115.      * JOINするテーブルを設定します。
  116.      * 
  117.      * ここに設定してある定義は、$this->join('aliasname') を呼ぶことで初めて結合対象となる。<br/>
  118.      * ※ここに設定しただけではJOINされない。
  119.      * 
  120.      * <pre>
  121.      * 指定方法: 'アクセスするための別名' => 設定値の配列
  122.      * 設定値の配列:
  123.      *   'entity' => エンティティのクラス名
  124.      *  'columns' => 取得するカラム文字列(SQLにセットするのと同じ形式)
  125.      *   'type' => JOINのタイプ(SQLに書く形式と同じ)(省略した場合はINNER JOIN)
  126.      *   'relation' => JOINするためのリレーション設定
  127.      *      「本クラスのキー名 => 対象クラスのキー名」となります。
  128.      * 
  129.      * 値の例:
  130.      * 
  131.      * public static $_JOINCONFIG = array(
  132.      *     'fuga' => array(
  133.      *         'entity' => 'Entity_Fuga',
  134.      *         'columns' => 'foo, bar, hoge',
  135.      *         'type' => 'LEFT JOIN',
  136.      *         'relation' => array(
  137.      *             'foo_id' => 'bar_id'
  138.      *         )
  139.      *     )
  140.      * );
  141.      * </pre>
  142.      * 
  143.      * @var array 
  144.      */
  145.     public static $_JOINCONFIG array();
  146.     private $_joinconfig;
  147.     
  148.     /**
  149.      * Loggerを格納します。
  150.      * 
  151.      * @var object 
  152.      */
  153.     protected $_log = null;
  154.     
  155.     protected $_join = array();
  156.     protected $_pdo = null;
  157.     protected $_constraints = array();
  158.     protected $_criteria = array();
  159.     protected $_bindvalue = array();
  160.     protected $_children = array();
  161.     protected $_afterwhere = array();
  162.  
  163.     /**
  164.      * コンストラクタです。
  165.      * 
  166.      * <pre>
  167.      * テーブル名が定義されていないときはクラス名称からEntity_を取り除いたものを
  168.      * テーブル名として使用します。
  169.      * </pre>
  170.      * 
  171.      * @param object $pdo PDOインスタンスを指定します。
  172.      */
  173.     public function __construct($pdo)
  174.     {
  175.         $class_name get_class($this);
  176.         // TODO LoggerMangerに依存している
  177.         $this->_log = LoggerManager::getLogger($class_name);
  178.         $this->_pdo = $pdo;
  179.         
  180.         // 子クラスの設定値を取得
  181.         $ref new ReflectionClass($class_name);
  182.         $this->_tablename $ref->getStaticPropertyValue("_TABLENAME");
  183.         $this->_pk $ref->getStaticPropertyValue("_PK");
  184.         $this->_auto $ref->getStaticPropertyValue("_AUTO");
  185.         $this->_joinconfig $ref->getStaticPropertyValue("_JOINCONFIG");
  186.         
  187.         if ($this->_tablename == ""{
  188.             $this->_tablename preg_replace('/^entity_/'''strtolower($class_name));
  189.         }
  190.         
  191.         return;
  192.     }
  193.  
  194.     /**
  195.      * PDOオブジェクトを取得します。
  196.      * 
  197.      * @return PDO PDOオブジェクト
  198.      */
  199.     public function getPDO()
  200.     {
  201.         return $this->_pdo;
  202.     }
  203.  
  204.     /**
  205.      * このクラスの新しいインスタンスを返します。
  206.      * @return Teeple_ActiveRecord 
  207.      */
  208.     public function newInstance()
  209.     {
  210.         $class_name get_class($this);
  211.         $obj new $class_name($this->_pdo);
  212.         return $obj;
  213.     }
  214.  
  215.     /**
  216.      * 制約を設定します。
  217.      * この機能は必要? -> 必要 楽観的排他制御などに使う
  218.      * 
  219.      * @param string $name カラム名称
  220.      * @param mixed $value カラム値
  221.      */
  222.     public function setConstraint($name$value)
  223.     {
  224.         $this->_constraints[$name$value;
  225.     }
  226.  
  227.     /**
  228.      * 制約を取得します。
  229.      * TODO
  230.      * 
  231.      * @param string $name カラム名称
  232.      * @return mixed カラム値
  233.      */
  234.     public function getConstraint($name)
  235.     {
  236.         return array_key_exists($name$this->_constraints$this->_constraints[$namenull;
  237.     }
  238.  
  239.     //--------------------- ここから流れるインターフェース ----------------------//
  240.     
  241.     /**
  242.      * JOINするテーブルを設定します。
  243.      * 
  244.      * <pre>
  245.      * $aliasnameで指定された _JOINCONFIGの設定で結合します。
  246.      * JOINする条件を追加する場合は第2引数以降に where()と同じ方法で指定します。
  247.      * 
  248.      * 結合をネストする場合は、
  249.      * $this->join('hoge')->join('hoge$fuga')
  250.      * のように、エイリアス名を $ で繋げて指定します。
  251.      * 'hoge'のEntityに定義されている _JOINCONFIG の 'fuga'が適用されます。
  252.      * </pre>
  253.      *
  254.      * @param mixed $aliasname エイリアス名
  255.      * @param string $condition 追加する条件
  256.      * @param string $params 可変長引数($condition)
  257.      * @return Teeple_ActiveRecord 
  258.      */
  259.     public function join({
  260.         
  261.         $args_num func_num_args();
  262.         if ($args_num 1{
  263.             throw new TeepleActiveRecordException("args too short.");
  264.         }
  265.         
  266.         $args_ar func_get_args();
  267.         $aliasname array_shift($args_ar);
  268.         
  269.         // conditionが指定されているか?
  270.         $condition null;
  271.         $cond_params null;
  272.         if (count($args_ar)) {
  273.             $condition array_shift($args_ar);
  274.             if (count($args_ar)) {
  275.                 if (is_array($args_ar[0])) {
  276.                     $cond_params $args_ar[0];
  277.                 else {
  278.                     $cond_params $args_ar;
  279.                 }
  280.             }
  281.         }
  282.         if ($condition != null && $cond_params != null{
  283.             $this->_checkPlaceHolder($condition$cond_params);
  284.         }
  285.         
  286.         $alias_ar explode('$'$aliasname);
  287.         if (count($alias_ar== 1{
  288.             // ネスト無しの場合
  289.             if (isset($this->_joinconfig[$aliasname])) {
  290.                 throw new TeepleActiveRecordException("join config not found: {$aliasname}");
  291.             }
  292.             $this->_join[$aliasname$this->_joinconfig[$aliasname];
  293.         else {
  294.             // ネストありの場合
  295.             $child_name array_pop($alias_ar);
  296.             $base_name implode('$'$alias_ar);
  297.             if (isset($this->_join[$base_name])) {
  298.                 throw new TeepleActiveRecordException("nest join parent not set: {$base_name}");
  299.             }
  300.             $entity $this->_join[$base_name]['entity'];
  301.             $joinconfig $this->_getEntityConfig($entity'_JOINCONFIG');
  302.             if (isset($joinconfig[$child_name])) {
  303.                 throw new TeepleActiveRecordException("nest join alias not found: {$child_name}");
  304.             }
  305.             $this->_join[$aliasname$joinconfig[$child_name];
  306.         }
  307.  
  308.         // condition
  309.         if ($condition != null{
  310.             $this->_join[$aliasname]['condition'array($condition => $cond_params);
  311.         }
  312.         return $this;
  313.     }
  314.     
  315.     /**
  316.      * Where句を追加します。
  317.      * 
  318.      * @param String Where句です。プレースホルダーに?を使用します。カラム名は、エイリアス名.カラム名で指定します。(主テーブルのエイリアスは 'base'を指定します。)
  319.      * @param mixed プレースホルダーにセットする値です。複数ある場合は1つの配列を渡してもよいし、複数の引数として渡してもよいです。
  320.      * @return Teeple_ActiveRecord 
  321.      */
  322.     public function where({
  323.         
  324.         $args_num func_num_args();
  325.         if ($args_num 1{
  326.             throw new TeepleActiveRecordException("args too short.");
  327.         }
  328.         
  329.         $args_ar func_get_args();
  330.         $where_clause array_shift($args_ar);
  331.         if (strlen($where_clause)) {
  332.             $this->_log->info("no where clause.");
  333.             return $this;
  334.         }
  335.         
  336.         // 引数の数とプレースホルダーの数を確認する。
  337.         if (@is_array($args_ar[0])) {
  338.             $args_ar $args_ar[0];
  339.         }
  340.         
  341.         $this->_checkPlaceHolder($where_clause$args_ar);
  342.         //$this->_criteria[$where_clause] = $args_ar;
  343.         $this->_criteria[array(
  344.             'str' => $where_clause,
  345.             'val' => $args_ar
  346.         );
  347.         
  348.         return $this;
  349.     }
  350.     
  351.     /**
  352.      * property = ? の条件を追加します。
  353.      * 
  354.      * <pre>
  355.      * $notnullonly が trueのときは $valueに値がセットされている場合のみ追加されます。
  356.      * falseのときは、 property IS NULL が追加されます。
  357.      * </pre>
  358.      *
  359.      * @param string $property プロパティ名
  360.      * @param mixed $value 
  361.      * @param boolean $notnullonly NULLチェックフラグ
  362.      * @return Teeple_ActiveRecord 
  363.      */
  364.     public function eq($property$value$notnullonly=true{
  365.         
  366.         if ($value === NULL || $value === ""{
  367.             if ($notnullonly{
  368.                 $this->where("{$property} IS NULL");
  369.             }
  370.         else {
  371.             $this->where("{$property} = ?"$value);
  372.         }
  373.         return $this;
  374.     }
  375.     
  376.     /**
  377.      * property <> ? の条件を追加します。
  378.      * 
  379.      * <pre>
  380.      * $notnullonly が trueのときは $valueに値がセットされている場合のみ追加されます。
  381.      * falseのときは、 property IS NOT NULL が追加されます。
  382.      * </pre>
  383.      *
  384.      * @param string $property プロパティ名
  385.      * @param mixed $value 
  386.      * @param boolean $notnullonly NULLチェックフラグ
  387.      * @return Teeple_ActiveRecord 
  388.      */
  389.     public function ne($property$value$notnullonly=true{
  390.         
  391.         if ($value === NULL || $value === ""{
  392.             if ($notnullonly{
  393.                 $this->where("{$property} IS NOT NULL");
  394.             }
  395.         else {
  396.             $this->where("{$property} <> ?"$value);
  397.         }
  398.         return $this;
  399.     }
  400.     
  401.     /**
  402.      * property < ? の条件を追加します。
  403.      * 
  404.      * <pre>
  405.      * $valueの値がセットされているときのみ追加します。
  406.      * </pre>
  407.      * 
  408.      * @param string $property プロパティ名
  409.      * @param mixed $value 
  410.      * @return Teeple_ActiveRecord 
  411.      */
  412.     public function lt($property$value{
  413.         
  414.         if ($value === NULL || $value === ""{
  415.             // do nothing
  416.         else {
  417.             $this->where("{$property} < ?"$value);
  418.         }
  419.         return $this;
  420.     }
  421.     
  422.     /**
  423.      * property > ? の条件を追加します。
  424.      * 
  425.      * <pre>
  426.      * $valueの値がセットされているときのみ追加します。
  427.      * </pre>
  428.      * 
  429.      * @param string $property プロパティ名
  430.      * @param mixed $value 
  431.      * @return Teeple_ActiveRecord 
  432.      */
  433.     public function gt($property$value{
  434.         
  435.         if ($value === NULL || $value === ""{
  436.             // do nothing
  437.         else {
  438.             $this->where("{$property} > ?"$value);
  439.         }
  440.         return $this;
  441.     }
  442.     
  443.     /**
  444.      * property <= ? の条件を追加します。
  445.      * 
  446.      * <pre>
  447.      * $valueの値がセットされているときのみ追加します。
  448.      * </pre>
  449.      * 
  450.      * @param string $property プロパティ名
  451.      * @param mixed $value 
  452.      * @return Teeple_ActiveRecord 
  453.      */
  454.     public function le($property$value{
  455.         
  456.         if ($value === NULL || $value === ""{
  457.             // do nothing
  458.         else {
  459.             $this->where("{$property} <= ?"$value);
  460.         }
  461.         return $this;
  462.     }
  463.     
  464.     /**
  465.      * property >= ? の条件を追加します。
  466.      * 
  467.      * <pre>
  468.      * $valueの値がセットされているときのみ追加します。
  469.      * </pre>
  470.      * 
  471.      * @param string $property プロパティ名
  472.      * @param mixed $value 
  473.      * @return Teeple_ActiveRecord 
  474.      */
  475.     public function ge($property$value{
  476.         
  477.         if ($value === NULL || $value === ""{
  478.             // do nothing
  479.         else {
  480.             $this->where("{$property} >= ?"$value);
  481.         }
  482.         return $this;
  483.     }
  484.     
  485.     /**
  486.      * property in (?,?...) の条件を追加します。
  487.      * 
  488.      * <pre>
  489.      * $valueの値がセットされているときのみ追加します。
  490.      * </pre>
  491.      * 
  492.      * @param string $property プロパティ名
  493.      * @param array $value 
  494.      * @return Teeple_ActiveRecord 
  495.      */
  496.     public function in($property$value{
  497.         
  498.         if (is_array($value|| count($value== 0{
  499.             // do nothing
  500.         else {
  501.             $num count($value);
  502.             $placeholder "";
  503.             for($i=0$i<$num$i++{
  504.                 $placeholder .= "?,";
  505.             }
  506.             $placeholder substr($placeholder0-1);
  507.             
  508.             $this->where("{$property} IN ({$placeholder})"$value);
  509.         }
  510.         return $this;
  511.     }
  512.     
  513.     /**
  514.      * property not in (?,?...) の条件を追加します。
  515.      * 
  516.      * <pre>
  517.      * $valueの値がセットされているときのみ追加します。
  518.      * </pre>
  519.      * 
  520.      * @param string $property プロパティ名
  521.      * @param array $value 
  522.      * @return Teeple_ActiveRecord 
  523.      */
  524.     public function notin($property$value{
  525.         
  526.         if (is_array($value|| count($value== 0{
  527.             // do nothing
  528.         else {
  529.             $num count($value);
  530.             $placeholder "";
  531.             for($i=0$i<$num$i++{
  532.                 $placeholder .= "?,";
  533.             }
  534.             $placeholder substr($placeholder0-1);
  535.             
  536.             $this->where("{$property} NOT IN ({$placeholder})"$value);
  537.         }
  538.         return $this;
  539.     }
  540.     
  541.     /**
  542.      * property like ? の条件を追加します。
  543.      * 
  544.      * <pre>
  545.      * $valueの値がセットされているときのみ追加します。
  546.      * </pre>
  547.      * 
  548.      * @param string $property プロパティ名
  549.      * @param mixed $value 
  550.      * @return Teeple_ActiveRecord 
  551.      */
  552.     public function like($property$value{
  553.         
  554.         if ($value === NULL || $value === ""{
  555.             // do nothing
  556.         else {
  557.             $this->where("{$property} LIKE ?"$value);
  558.         }
  559.         return $this;
  560.     }
  561.     
  562.     /**
  563.      * property like ? の条件を追加します。
  564.      * 
  565.      * <pre>
  566.      * 値の最後に%をつけます。
  567.      * $valueの値がセットされているときのみ追加します。
  568.      * </pre>
  569.      * 
  570.      * @param string $property プロパティ名
  571.      * @param mixed $value 
  572.      * @return Teeple_ActiveRecord 
  573.      */
  574.     public function starts($property$value{
  575.         
  576.         if ($value === NULL || $value === ""{
  577.             // do nothing
  578.         else {
  579.             $this->where("{$property} LIKE ?"addslashes($value.'%');
  580.         }
  581.         return $this;
  582.     }
  583.  
  584.     /**
  585.      * property like ? の条件を追加します。
  586.      * 
  587.      * <pre>
  588.      * 値の最初に%をつけます。
  589.      * $valueの値がセットされているときのみ追加します。
  590.      * </pre>
  591.      * 
  592.      * @param string $property プロパティ名
  593.      * @param mixed $value 
  594.      * @return Teeple_ActiveRecord 
  595.      */
  596.     public function ends($property$value{
  597.         
  598.         if ($value === NULL || $value === ""{
  599.             // do nothing
  600.         else {
  601.             $this->where("{$property} LIKE ?"'%'addslashes($value));
  602.         }
  603.         return $this;
  604.     }
  605.     
  606.     /**
  607.      * property like ? の条件を追加します。
  608.      * 
  609.      * <pre>
  610.      * 値の最初と最後に%をつけます。
  611.      * $valueの値がセットされているときのみ追加します。
  612.      * </pre>
  613.      * 
  614.      * @param string $property プロパティ名
  615.      * @param mixed $value 
  616.      * @return Teeple_ActiveRecord 
  617.      */
  618.     public function contains($property$value{
  619.         
  620.         if ($value === NULL || $value === ""{
  621.             // do nothing
  622.         else {
  623.             $this->where("{$property} LIKE ?"'%'addslashes($value.'%');
  624.         }
  625.         return $this;
  626.     }
  627.     
  628.     /**
  629.      * order by を指定します。
  630.      *
  631.      * @param string $clause order by 句
  632.      * @return Teeple_ActiveRecord 
  633.      */
  634.     public function order($clause{
  635.         
  636.         $this->_afterwhere['order'addslashes($clause);
  637.         return $this;
  638.     }
  639.     
  640.     /**
  641.      * limit を指定します。
  642.      *
  643.      * @param int $num 最大件数
  644.      * @return Teeple_ActiveRecord 
  645.      */
  646.     public function limit($num{
  647.         
  648.         if (is_numeric($num)) {
  649.             $this->_afterwhere['limit'$num;
  650.         }
  651.         return $this;
  652.     }
  653.     
  654.     /**
  655.      * offset を指定します。
  656.      *
  657.      * @param int $num 開始位置
  658.      * @return Teeple_ActiveRecord 
  659.      */
  660.     public function offset($num{
  661.         
  662.         if (is_numeric($num)) {
  663.             $this->_afterwhere['offset'$num;
  664.         }
  665.         return $this;
  666.     }
  667.     
  668.     /**
  669.      * SELECTを実行します。
  670.      * 結果を配列で返します。
  671.      *
  672.      * @return array ResultSetをこのクラスの配列として返します。
  673.      */
  674.     public function select({
  675.         
  676.         $this->_bindvalue = array();
  677.         
  678.         $sql $this->_buildSelectSql();
  679.         $this->_log->debug("exec select: $sql");
  680.         $this->_log->debug("param is: \n".@var_export($this->_bindvalue,TRUE));
  681.         
  682.         $sth $this->_pdo->prepare($sql);
  683.         if($sth{
  684.             $err $this->_pdo->errorInfo();
  685.             throw new TeepleActiveRecordException("pdo prepare failed: {$err[2]}:{$sql}");
  686.         }
  687.         if($sth->execute(@$this->_bindvalue)) {
  688.             $err $sth->errorInfo();
  689.             throw new TeepleActiveRecordException("pdo execute failed: {$err[2]}:{$sql}");
  690.         }
  691.         
  692.         $row_list $sth->fetchAll(PDO::FETCH_ASSOC);
  693.         $this->_log->debug("exec select result: \n".@var_export($row_listTRUE));
  694.         
  695.         $item_list array();
  696.         foreach($row_list as $row{
  697.             $item clone($this);
  698.             $item->_buildResultSet($row);
  699.             $item->resetInstance();
  700.             $item_list[$item;
  701.         }
  702.         
  703.         $this->resetInstance();
  704.         return $item_list;
  705.     }
  706.     
  707.     /**
  708.      * paginationに対応したSELECTをします。
  709.      * @param int $limit 
  710.      * @param int $offset 
  711.      * @return array [0]=全件数, [1]=limit,offsetに対応した結果セット(entityの配列)
  712.      */
  713.     public function selectPage($limit$offset{
  714.         
  715.         $total $this->count();
  716.         if ($total == 0{
  717.             return array(0null);
  718.         }
  719.         $result $this->limit($limit)->offset($offset)->select();
  720.         
  721.         return array($total$result);
  722.     }
  723.     
  724.     /**
  725.      * 単一行の検索を実行します。
  726.      *
  727.      * @param mixed $id 配列で無い場合は、単一PKの値として扱います。配列の場合は、カラム名 => 値 のHashとして扱います。
  728.      * @return Teeple_ActiveRecord 
  729.      */
  730.     public function find($id=null{
  731.         
  732.         $this->_bindvalue = array();
  733.         
  734.         if ($id != null{
  735.             if (is_array($id)) {
  736.                 if (count($this->_pk!= 1{
  737.                     throw new TeepleActiveRecordException("pk is not single.");
  738.                 }
  739.                 $this->setConstraint($this->_pk[0]$id);
  740.             else {
  741.                 foreach($id as $col => $val{
  742.                     $this->setConstraint($col$val);
  743.                 }
  744.             }
  745.         }
  746.         
  747.         $sql $this->_buildSelectSql();
  748.         $this->_log->debug("exec find: $sql");
  749.         $this->_log->debug("param is: \n".@var_export($this->_bindvalue,TRUE));
  750.         
  751.         $sth $this->_pdo->prepare($sql);
  752.         if($sth{
  753.             $err $this->_pdo->errorInfo();
  754.             throw new TeepleActiveRecordException("pdo prepare failed: {$err[2]}:{$sql}");
  755.         }
  756.         if($sth->execute(@$this->_bindvalue)) {
  757.             $err $sth->errorInfo();
  758.             throw new TeepleActiveRecordException("pdo execute failed: {$err[2]}:{$sql}");
  759.         }
  760.         
  761.         $row_list $sth->fetchAll(PDO::FETCH_ASSOC);
  762.         $this->_log->debug("exec select result: \n".@var_export($row_listTRUE));
  763.         
  764.         if (count($row_list1{
  765.             throw new TeepleActiveRecordException("too many rows.");
  766.         }
  767.         if (count($row_list== 0{
  768.             return null;
  769.         }
  770.         
  771.         $item clone($this);
  772.         $item->_buildResultSet($row_list[0]);
  773.         $item->resetInstance();
  774.         
  775.         $this->resetInstance();
  776.         return $item;
  777.     }
  778.     
  779.     /**
  780.      * 条件に該当するレコード数を取得します。
  781.      * 
  782.      * @return int 該当件数
  783.      */
  784.     public function count()
  785.     {
  786.         $this->_bindvalue = array();
  787.         
  788.         $select_str "SELECT COUNT(*)";
  789.         $from_str $this->_buildFromClause();
  790.         $where_str $this->_buildWhereClause();
  791.         $other_str $this->_buildAfterWhereClause();
  792.         
  793.         $sql implode(" "array($select_str,$from_str,$where_str,$other_str));
  794.         $this->_log->debug("exec count: $sql");
  795.         $this->_log->debug("param is: \n".@var_export($this->_bindvalue,TRUE));
  796.         
  797.         $sth $this->_pdo->prepare($sql);
  798.         if($sth{
  799.             $err $this->_pdo->errorInfo();
  800.             throw new TeepleActiveRecordException("pdo prepare failed: {$err[2]}:{$sql}");
  801.         }
  802.         if($sth->execute(@$this->_bindvalue)) {
  803.             $err $sth->errorInfo();
  804.             throw new TeepleActiveRecordException("pdo execute failed: {$err[2]}:{$sql}");
  805.         }
  806.         
  807.         $count $sth->fetchColumn();
  808.         $this->_log->debug("count result: $count");
  809.         
  810.         //$this->resetInstance();
  811.         return $count;
  812.     }
  813.  
  814.     /**
  815.      * レコードを登録します。
  816.      * 
  817.      * <pre>
  818.      * constraintとrowに設定されている値で、レコードを1つ作成します。
  819.      * PKが単一カラムで、$_auto がTRUEに設定されている場合で、INSERT値にPKが設定されていなかった場合は、
  820.      * INSERT後、インスタンスにPK値をセットします。
  821.      * </pre>
  822.      * 
  823.      * @return bool 登録できたかどうか。
  824.      */
  825.     public function insert()
  826.     {
  827.         if (function_exists('teeple_activerecord_before_insert')) {
  828.             teeple_activerecord_before_insert($this);
  829.         }
  830.  
  831.         $this->_bindvalue = array();
  832.         $row $this->_convertObject2Array($thistrue);
  833.         $this->_log->info("insert "$this->_tablename": \n".@var_export($row,TRUE));
  834.         
  835.         $binding_params $this->_makeBindingParams($row);
  836.         $sql "INSERT INTO `"$this->_tablename ."` (" .
  837.             implode(', 'array_keys($row)) ') VALUES(' .
  838.             implode(', 'array_keys($binding_params)) ');';
  839.  
  840.         $this->_log->debug("sql: $sql");
  841.         $sth $this->_pdo->prepare($sql);
  842.         if($sth
  843.             $err $this->_pdo->errorInfo();
  844.             throw new TeepleActiveRecordException("pdo prepare failed: {$err[2]}:{$sql}");
  845.         }
  846.  
  847.         if($sth->execute($binding_params)) {
  848.             $err $sth->errorInfo();
  849.             throw new TeepleActiveRecordException("pdo execute failed: {$err[2]}:{$sql}");
  850.         }
  851.  
  852.         if (count($this->_pk== && $this->_auto && (isset($this->{$this->_pk[0]}|| $this->{$this->_pk[0]== "")) {
  853.             $this->{$this->_pk[0]$this->_pdo->lastInsertId();
  854.             $this->_log->info("AUTO: "$this->_pk[0." = {$this->{$this->_pk[0]}}");
  855.         }
  856.         
  857.         $this->_log->info("insert "$this->_tablename .": result=(".$sth->rowCount().")");
  858.         
  859.         $this->resetInstance();
  860.         return $sth->rowCount(0;
  861.     }
  862.     
  863.     /**
  864.      * レコードの更新を実行します。
  865.      * 
  866.      * <pre>
  867.      * rowにセットされているPKで更新を行ないます。
  868.      * </pre>
  869.      * 
  870.      * @return int 変更のあったレコード数
  871.      */
  872.     public function update()
  873.     {
  874.         if (function_exists('teeple_activerecord_before_update')) {
  875.             teeple_activerecord_before_update($this);
  876.         }
  877.         $this->_bindvalue array();
  878.         if ($this->isSetPk()) {
  879.             throw new TeepleActiveRecordException("primary key not set.");
  880.         }
  881.         
  882.         $values $this->_convertObject2Array($thisfalse);
  883.         $this->_log->info("update "$this->_tablename .": \n".@var_export($values,TRUE));
  884.         $pks array();
  885.         // primary key は 更新しない。
  886.         foreach ($this->_pk as $pk{
  887.             unset($values[$pk]);
  888.             $this->setConstraint($pk$this->$pk);
  889.         }
  890.         if (count($values)) {
  891.             throw new TeepleActiveRecordException("no columns to update.");
  892.         }
  893.  
  894.         $sql "UPDATE `"$this->_tablename ."` ".
  895.             $this->_buildSetClause($values).
  896.             " "$this->_buildConstraintClause(false);
  897.         
  898.         $this->_log->debug("update "$this->_tablename .": {$sql}");
  899.         $this->_log->debug(@var_export($this->_bindvalue,TRUE));
  900.         
  901.         $sth $this->_pdo->prepare($sql);
  902.         if ($sth{
  903.             $err $this->_pdo->errorInfo();
  904.             throw new TeepleActiveRecordException("pdo prepare failed: {$err[2]}:{$sql}");
  905.         }
  906.         if ($sth->execute($this->_bindvalue)) {
  907.             $err $sth->errorInfo();
  908.             throw new TeepleActiveRecordException("pdo execute failed: {$err[2]}:{$sql}");
  909.         }
  910.         
  911.         $this->_log->info("update "$this->_tablename .": result=(".$sth->rowCount().")");
  912.         
  913.         if ($sth->rowCount(!= 1{
  914.             throw new TeepleActiveRecordException('更新に失敗しました。他の処理と重なった可能性があります。');
  915.         }
  916.         
  917.         $this->resetInstance();
  918.         return $sth->rowCount();
  919.     }
  920.     
  921.     /**
  922.      * 条件に該当するレコードを全て更新します。
  923.      * 
  924.      * <pre>
  925.      * セットされているconstraints及びcriteriaに
  926.      * 該当するレコードを全て更新します。
  927.      * </pre>
  928.      * 
  929.      * @return int 更新件数
  930.      */
  931.     public function updateAll()
  932.     {
  933.         if (function_exists('teeple_activerecord_before_update')) {
  934.             teeple_activerecord_before_update($this);
  935.         }
  936.          
  937.         $this->_bindvalue array();
  938.         
  939.         $row $this->_convertObject2Array($thistrue);
  940.         $sql "UPDATE `"$this->_tablename ."` ".
  941.             $this->_buildSetClause($row).
  942.             " "$this->_buildWhereClause(false);
  943.         
  944.         $this->_log->info("updateAll "$this->_tablename ."$sql");
  945.         $this->_log->info("param is: \n"@var_export($this->_bindvalueTRUE));
  946.         $sth $this->_pdo->prepare($sql);
  947.         
  948.         if ($sth{
  949.             $err $this->_pdo->errorInfo();
  950.             throw new TeepleActiveRecordException("pdo prepare failed: {$err[2]}:{$sql}");
  951.         }
  952.         if ($sth->execute($this->_bindvalue)) {
  953.             $err $sth->errorInfo();
  954.             throw new TeepleActiveRecordException("pdo execute failed: {$err[2]}:{$sql}");
  955.         }
  956.         
  957.         $this->_log->info("updateAll "$this->_tablename .": result=(".$sth->rowCount().")");
  958.         
  959.         $this->resetInstance();
  960.         return $sth->rowCount();        
  961.     }
  962.     
  963.     /**
  964.      * 指定されたレコードを削除します。
  965.      * 
  966.      * <pre>
  967.      * constraintまたは $idパラメータで指定されたPKに該当するレコードを削除します。
  968.      * $id がハッシュでないときは、id列の値とみなしてDELETEします。
  969.      * $id がハッシュのときは、key値をPKのカラム名とみなしてDELETEします。
  970.      * </pre>
  971.      * 
  972.      * @param mixed $id PKの値
  973.      * @return bool 実行結果
  974.      */
  975.     public function delete($id null)
  976.     {
  977.         $this->_bindvalue array();
  978.         
  979.         // rowにセットされているPKがあればconstraintに
  980.         foreach ($this->_pk as $pk{
  981.             if (isset($this->$pk&& $this->getConstraint($pk== ""{
  982.                 $this->setConstraint($pk$this->$pk);
  983.             }
  984.         }
  985.         
  986.         if ($id != null{
  987.             if (is_array($id)) {
  988.                 if (count($this->_pk!= 1{
  989.                     throw new TeepleActiveRecordException("pk is not single.");
  990.                 }
  991.                 $this->setConstraint($this->_pk[0]$id);
  992.             else {
  993.                 foreach($id as $col => $val{
  994.                     $this->setConstraint($col$val);
  995.                 }
  996.             }
  997.         }
  998.         
  999.         $sql "DELETE FROM `"$this->_tablename ."` ".
  1000.             $this->_buildConstraintClause(false);
  1001.  
  1002.         $this->_log->info("delete "$this->_tablename ."$sql");
  1003.         $this->_log->debug("param is: \n"@var_export($this->_bindvalueTRUE));
  1004.         
  1005.         $sth $this->_pdo->prepare($sql);
  1006.         if ($sth{
  1007.             $err $this->_pdo->errorInfo();
  1008.             throw new TeepleActiveRecordException("pdo prepare failed: {$err[2]}:{$sql}");
  1009.         }
  1010.         if ($sth->execute($this->_bindvalue)) {
  1011.             $err $sth->errorInfo();
  1012.             throw new TeepleActiveRecordException("pdo execute failed: {$err[2]}:{$sql}");
  1013.         }
  1014.         
  1015.         $this->_log->info("delete "$this->_tablename .": result=(".$sth->rowCount().")");
  1016.  
  1017.         $props array_keys(get_class_vars(get_class($this)));
  1018.         foreach ($props as $key{
  1019.             $this->$key NULL;
  1020.         }
  1021.         
  1022.         $this->resetInstance();
  1023.         return $sth->rowCount(0;
  1024.     }
  1025.     
  1026.     /**
  1027.      * 条件に該当するレコードを全て削除します。
  1028.      * 
  1029.      * <pre>
  1030.      * セットされているconstraints及びcriteriaに
  1031.      * 該当するレコードを全て削除します。
  1032.      * </pre>
  1033.      * 
  1034.      * @return int 削除件数
  1035.      */
  1036.     public function deleteAll()
  1037.     {
  1038.         $this->_bindvalue array();
  1039.         
  1040.         $sql "DELETE FROM `"$this->_tablename ."` ".
  1041.             $this->_buildWhereClause(false);
  1042.         
  1043.         $this->_log->info("deleteAll "$this->_tablename ."$sql");
  1044.         $this->_log->info("param is: \n"@var_export($this->_bindvalueTRUE));
  1045.         $sth $this->_pdo->prepare($sql);
  1046.         
  1047.         if ($sth{
  1048.             $err $this->_pdo->errorInfo();
  1049.             throw new TeepleActiveRecordException("pdo prepare failed: {$err[2]}:{$sql}");
  1050.         }
  1051.         if ($sth->execute($this->_bindvalue)) {
  1052.             $err $sth->errorInfo();
  1053.             throw new TeepleActiveRecordException("pdo execute failed: {$err[2]}:{$sql}");
  1054.         }
  1055.         
  1056.         $this->_log->info("deleteAll "$this->_tablename .": result=(".$sth->rowCount().")");
  1057.         
  1058.         $this->resetInstance();
  1059.         return $sth->rowCount();
  1060.     }
  1061.     
  1062.     /**
  1063.      * 指定されたSELECT文を実行します。
  1064.      * 結果はstdClassの配列になります。
  1065.      * 結果が0行の場合は空の配列が返ります。
  1066.      *
  1067.      * @param string $query 
  1068.      * @param array $bindvalues 
  1069.      * @return array 
  1070.      */
  1071.     public function selectQuery($query$bindvalues{
  1072.         
  1073.         // prepare
  1074.         $sth $this->getPDO()->prepare($query)
  1075.         
  1076.         // bind
  1077.         if (is_array($bindvalues&& count($bindvalues0{
  1078.             foreach($bindvalues as $i => $value{
  1079.                 $sth->bindValue($i+1$value);
  1080.             }
  1081.         }
  1082.         
  1083.         // 実行
  1084.         $sth->execute();
  1085.         
  1086.         // 結果を取得
  1087.         $result array();
  1088.         while ($row $sth->fetch()) {
  1089.             $obj new stdClass;
  1090.             foreach($row as $col => $value{
  1091.                 $obj->$col $value;
  1092.             }
  1093.             array_push($result$obj);
  1094.         }
  1095.         return $result;
  1096.     }
  1097.     
  1098.     /**
  1099.      * 指定されたSELECT文を実行します。(単一行)
  1100.      * 結果はstdClassになります。
  1101.      * 結果が0行の場合はNULLが返ります。
  1102.      *
  1103.      * @param string $query 
  1104.      * @param array $bindvalues 
  1105.      * @return stdClass 
  1106.      */
  1107.     public function findQuery($query$bindValues{
  1108.         
  1109.         $result $this->selectQuery($query$bindValues);
  1110.         if (count($result0{
  1111.             return $result[0];
  1112.         }
  1113.         return NULL;
  1114.     }    
  1115.     
  1116.     /**
  1117.      * インスタンスのcriteriaをリセットします。
  1118.      * 値は保持されます。
  1119.      *
  1120.      */
  1121.     public function resetInstance({
  1122.         $this->_join array();
  1123.         $this->_criteria array();
  1124.         $this->_constraints array();
  1125.         $this->_bindvalue array();
  1126.         $this->_afterwhere array();
  1127.         
  1128.         return;
  1129.     }
  1130.     
  1131.     /**
  1132.      * ActionクラスのプロパティからEntityのプロパティを生成します。
  1133.      *
  1134.      * @param Object $obj Actionクラスのインスタンス
  1135.      * @param array $colmap 'entityのカラム名' => 'Actionのプロパティ名' の配列
  1136.      */
  1137.     public function convert2Entity($obj$colmap=null{
  1138.         
  1139.         if ($colmap == null{
  1140.             $colmap array();
  1141.         }
  1142.         
  1143.         $columns $this->_getColumns(get_class($this));
  1144.         foreach($columns as $column{
  1145.             $prop array_key_exists($column$colmap$colmap[$column$column;
  1146.             if (isset($obj->$prop)) {
  1147.                 $this->$column $obj->$prop;
  1148.             }
  1149.         }
  1150.         return;
  1151.     }
  1152.     
  1153.     /**
  1154.      * EntityのプロパティからActionクラスのプロパティを生成します。
  1155.      *
  1156.      * @param Object $obj Actionクラスのインスタンス
  1157.      * @param array $colmap 'entityのカラム名' => 'Actionのプロパティ名' の配列
  1158.      */
  1159.     public function convert2Page($obj$colmap=null{
  1160.  
  1161.         if ($colmap == null{
  1162.             $colmap array();
  1163.         }
  1164.         
  1165.         $columns $this->_getColumns(get_class($this));
  1166.         foreach($columns as $column{
  1167.             if (@isset($this->$column)) {
  1168.                 $prop array_key_exists($column$colmap$colmap[$column$column;
  1169.                 $obj->$prop $this->$column;
  1170.             }
  1171.         }
  1172.         return;
  1173.     }
  1174.     
  1175.     /**
  1176.      * 現在の時刻を返します。
  1177.      * @return string 
  1178.      */
  1179.     public function now({
  1180.         return date('Y-m-d H:i:s');
  1181.     }
  1182.     
  1183.     /**
  1184.      * SELECT文を構築します。
  1185.      *
  1186.      * @return String SELECT文
  1187.      */
  1188.     protected function _buildSelectSql({
  1189.         
  1190.         $select_str $this->_buildSelectClause();
  1191.         $from_str $this->_buildFromClause();
  1192.         $where_str $this->_buildWhereClause();
  1193.         $other_str $this->_buildAfterWhereClause();
  1194.         
  1195.         return implode(" \n"array($select_str$from_str$where_str$other_str));
  1196.     }
  1197.     
  1198.     /**
  1199.      * SELECT clause を構築します。
  1200.      *
  1201.      * @return String SELECT clause
  1202.      */
  1203.     protected function _buildSelectClause({
  1204.         
  1205.         $buff array();
  1206.         
  1207.         // 本クラスのカラム
  1208.         $columns $this->_getColumns(get_class($this));
  1209.         foreach($columns as $col{
  1210.             $buff["base.{$col} AS base\${$col}";
  1211.         }
  1212.         
  1213.         // JOINするテーブルのカラム
  1214.         if (count($this->_join)) {
  1215.             foreach($this->_join as $alias => $config{
  1216.                 $join_columns $this->_getColumns($config['entity']);
  1217.                 foreach ($join_columns as $col{
  1218.                     $buff["{$alias}.{$col} AS {$alias}\${$col}";
  1219.                 }
  1220.             }
  1221.         }
  1222.         
  1223.         return "SELECT "implode(', '$buff);
  1224.     }
  1225.  
  1226.     /**
  1227.      * FROM clause を構築します。
  1228.      *
  1229.      * @return unknown 
  1230.      */
  1231.     protected function _buildFromClause({
  1232.         
  1233.         $buff array();
  1234.         $buff["FROM "$this->_tablename ." base";
  1235.         
  1236.         // join
  1237.         if (count($this->_join)) {
  1238.             foreach ($this->_join as $alias => $conf{
  1239.                 $base 'base';
  1240.                 $alias_ar explode('$'$alias);
  1241.                 if (count($alias_ar1{
  1242.                     array_pop($alias_ar);
  1243.                     $base implode('$'$alias_ar);
  1244.                 }
  1245.                 
  1246.                 $tablename $this->_getEntityConfig($conf['entity']'_TABLENAME');
  1247.                 
  1248.                 if (isset($conf['type'])) {
  1249.                     $conf['type''INNER JOIN';
  1250.                 }
  1251.                 
  1252.                 $conds array();
  1253.                 foreach ($conf['relation'as $here => $there{
  1254.                     array_push($conds"{$base}.{$here} = {$alias}.{$there}");
  1255.                 }
  1256.                 if (isset($conf['condition'])) {
  1257.                     foreach($conf['condition'as $statement => $params{
  1258.                         array_push($conds" ( {$statement} ) ");
  1259.                         if (is_array($params)) {
  1260.                             foreach($params as $item{
  1261.                                 array_push($this->_bindvalue$item);
  1262.                             }
  1263.                         }
  1264.                     }
  1265.                 }
  1266.                 
  1267.                 $conditions "("implode(' AND '$conds.")";
  1268.                 
  1269.                 $buff["{$conf['type']} {$tablename} {$alias} ON {$conditions}";
  1270.             }
  1271.         }
  1272.         return implode(" \n"$buff);
  1273.     }
  1274.     
  1275.     /**
  1276.      * WHERE clause を構築します。
  1277.      *
  1278.      * @return unknown 
  1279.      */
  1280.     protected function _buildWhereClause($usebase=true{
  1281.         
  1282.         $buff array();
  1283.         
  1284.         // constraints
  1285.         if (count($this->_constraints)) {
  1286.             foreach($this->_constraints as $col => $val{
  1287.                 if ($val != null{
  1288.                     $buff[$usebase "base.{$col} = ?"{$col} = ?";
  1289.                     array_push($this->_bindvalue$val);
  1290.                 else {
  1291.                     $buff[$usebase "base.{$col} IS NULL"{$col} IS NULL";
  1292.                 }
  1293.             }
  1294.         }
  1295.         
  1296.         // criteria
  1297.         if (count($this->_criteria)) {
  1298.             //foreach($this->_criteria as $str => $val) {
  1299.             foreach($this->_criteria as $cri{
  1300.                 $str $cri['str'];
  1301.                 $val $cri['val'];
  1302.                 $buff[$str;
  1303.                 if ($val != null{
  1304.                     if (is_array($val)) {
  1305.                         foreach($val as $item{
  1306.                             array_push($this->_bindvalue$item);
  1307.                         }
  1308.                     }
  1309.                 }
  1310.             }
  1311.         }
  1312.         
  1313.         if (count($buff)) {
  1314.             return "WHERE ("implode(") \n AND ("$buff.")";
  1315.         }        
  1316.         return "";
  1317.     }
  1318.  
  1319.     /**
  1320.      * WHERE clause を構築します。
  1321.      *
  1322.      * @return unknown 
  1323.      */
  1324.     protected function _buildConstraintClause($usebase=true{
  1325.         
  1326.         $buff array();
  1327.         
  1328.         // constraints
  1329.         if (count($this->_constraints)) {
  1330.             foreach($this->_constraints as $col => $val{
  1331.                 if ($val != null{
  1332.                     $buff[$usebase "base.{$col} = ?"{$col} = ?";
  1333.                     array_push($this->_bindvalue$val);
  1334.                 else {
  1335.                     $buff[$usebase "base.{$col} IS NULL"{$col} IS NULL";
  1336.                 }
  1337.             }
  1338.         }
  1339.         if (count($buff)) {
  1340.             return "WHERE "implode(' AND '$buff);
  1341.         }
  1342.         return "";
  1343.     }    
  1344.     
  1345.     /**
  1346.      * WHERE以降の clause を作成します。
  1347.      *
  1348.      * @return unknown 
  1349.      */
  1350.     protected function _buildAfterWhereClause({
  1351.         
  1352.         $buff array();
  1353.         
  1354.         // ORDER BYから書かないとだめ!
  1355.         if (count($this->_afterwhere)) {
  1356.             if (isset($this->_afterwhere['order'])) {
  1357.                 $buff["ORDER BY {$this->_afterwhere['order']}";
  1358.             }
  1359.             if (isset($this->_afterwhere['limit'])) {
  1360.                 $buff["LIMIT {$this->_afterwhere['limit']}";
  1361.             }
  1362.             if (isset($this->_afterwhere['offset'])) {
  1363.                 $buff["OFFSET {$this->_afterwhere['offset']}";
  1364.             }
  1365.         }
  1366.         
  1367.         if (count($buff)) {
  1368.             return implode(' '$buff);
  1369.         }
  1370.         return "";
  1371.     }
  1372.     
  1373.     /**
  1374.      * UPDATE文のVALUES部分を作成します。
  1375.      *
  1376.      * @param array $array アップデートする値の配列
  1377.      * @return string SQL句の文字列
  1378.      */
  1379.     protected function _buildSetClause($array{
  1380.         foreach($array as $key => $value{
  1381.             $expressions[="{$key} = ?";
  1382.             array_push($this->_bindvalue$value);
  1383.         }
  1384.         return "SET "implode(', '$expressions);
  1385.     }
  1386.     
  1387.     /**
  1388.      * 単一レコードの値をセットします。
  1389.      *
  1390.      * @param unknown_type $row 
  1391.      */
  1392.     protected function _buildResultSet($row{
  1393.         
  1394.         foreach($row as $key => $val{
  1395.             $alias_ar explode('$'$key);
  1396.             $col array_pop($alias_ar)
  1397.             
  1398.             if (count($alias_ar== && $alias_ar[0== 'base'{
  1399.                 $this->$col $val;
  1400.                 continue;
  1401.             }
  1402.             
  1403.             $ref $this;
  1404.             $base "";
  1405.             while($alias array_shift($alias_ar)) {
  1406.                 $base .= $base == "" $alias "$".$alias;
  1407.                 if ($ref->$alias == NULL{
  1408.                     $class_name $this->_join[$base]['entity'];
  1409.                     $obj new $class_name($this->_pdo);
  1410.                     $ref->$alias $obj;
  1411.                 }
  1412.                 $ref $ref->$alias;
  1413.             }
  1414.             
  1415.             $ref->$col $val;
  1416.         }
  1417.         
  1418.         return;
  1419.     }
  1420.     
  1421.     protected function _checkPlaceHolder($condition$params{
  1422.         
  1423.         $param_num count($params);
  1424.         $holder_num substr_count($condition'?');
  1425.         if ($param_num != $holder_num{
  1426.             throw new TeepleActiveRecordException("The num of placeholder is wrong.");
  1427.         }
  1428.     }
  1429.     
  1430.     protected function _getEntityConfig($clsname$property{
  1431.         
  1432.         $ref new ReflectionClass($clsname);
  1433.         return $ref->getStaticPropertyValue($property);
  1434.     }
  1435.  
  1436.     /**
  1437.      * 設定された制約でWHERE句を作成します。
  1438.      *
  1439.      * @param array $array 制約値
  1440.      * @return string SQL句の文字列
  1441.      */
  1442.     protected function _makeUpdateConstraints($array{
  1443.         foreach($array as $key => $value{
  1444.             if(is_null($value)) {
  1445.                 $expressions["{$key} IS NULL";
  1446.             else {
  1447.                 $expressions["{$key}=:{$key}";
  1448.             }
  1449.         }
  1450.         return implode(' AND '$expressions);
  1451.     }
  1452.  
  1453.     /**
  1454.      * バインドするパラメータの配列を作成します。
  1455.      *
  1456.      * @param array $array バインドする値の配列
  1457.      * @return array バインドパラメータを名前にした配列
  1458.      */
  1459.     protected function _makeBindingParams$array )
  1460.     {
  1461.         $params array();
  1462.         foreach$array as $key=>$value )
  1463.         {
  1464.             $params[":{$key}"$value;
  1465.         }
  1466.         return $params;
  1467.     }
  1468.  
  1469.     /**
  1470.      * IN句に設定するIDのリストを作成します。
  1471.      * 
  1472.      * @param array $array IDの配列
  1473.      * @return string IN句に設定する文字列
  1474.      */
  1475.     protected function _makeIDList$array )
  1476.     {
  1477.         $expressions array();
  1478.         foreach ($array as $id{
  1479.             $expressions["`"$this->_tablename ."`.id=".
  1480.                 $this->_pdo->quote($idisset($this->has_string_idPDO::PARAM_INT PDO::PARAM_STR);
  1481.         }
  1482.         return '('.implode(' OR '$expressions).')';
  1483.     }
  1484.     
  1485.     /**
  1486.      * PKがセットされているかどうかをチェックします。
  1487.      * 
  1488.      * @return PKがセットされている場合はTRUE 
  1489.      */
  1490.     protected function isSetPk()
  1491.     {
  1492.         if (isset($this->_pk)) {
  1493.             return isset($this->id);
  1494.         }
  1495.         
  1496.         foreach ($this->_pk as $one{
  1497.             if (isset($this->$one)) {
  1498.                 return false;
  1499.             }
  1500.         }
  1501.         return true;
  1502.     }
  1503.     
  1504.     /**
  1505.      * Entityのカラム値をArrayとして取り出す
  1506.      *
  1507.      * @param Teeple_ActiveRecord $obj 
  1508.      * @param boolean $excludeNull 
  1509.      * @return array 
  1510.      */
  1511.     protected function _convertObject2Array($obj$excludeNull=false{
  1512.         
  1513.         $columns $this->_getColumns(get_class($obj));
  1514.         $result array();
  1515.         foreach ($columns as $name{
  1516.             $val $obj->$name;
  1517.             if (@is_array($val)) {
  1518.                 $result[$nameserialize($val);
  1519.             else if ($excludeNull || ($val !== NULL && strlen($val0)) {
  1520.                 $result[$name$this->_null($val);
  1521.             }
  1522.         }
  1523.  
  1524.         return $result;
  1525.     }
  1526.     
  1527.     /**
  1528.      * Entityクラスのカラム名一覧を取得する
  1529.      *
  1530.      * @param string $clsname 
  1531.      * @return array 
  1532.      */ 
  1533.     protected function _getColumns($clsname{
  1534.         
  1535.         $joinconfig $this->_getEntityConfig($clsname"_JOINCONFIG");
  1536.         $joinNames array_keys($joinconfig);
  1537.  
  1538.         $result array();
  1539.         $vars get_class_vars($clsname);
  1540.         foreach($vars as $name => $value{
  1541.             // _で始まるものは除外
  1542.             if (substr($name01=== '_'{
  1543.                 continue;
  1544.             }
  1545.             // _joinconfigで指定されている名前は除外
  1546.             if (in_array($name$joinNames)) {
  1547.                 continue;
  1548.             }
  1549.             
  1550.             array_push($result$name);
  1551.         }
  1552.         
  1553.         return $result;
  1554.     }
  1555.  
  1556.     protected function _null($str{
  1557.         return $str !== NULL && strlen($str$str NULL;
  1558.     }
  1559.  
  1560. }
  1561.  
  1562. ?>

Documentation generated on Mon, 26 Apr 2010 08:59:33 +0900 by phpDocumentor 1.4.3