Source for file jDaoFactoryBase.class.php

Documentation is available at jDaoFactoryBase.class.php

  1. <?php
  2. /**
  3.  * @package     jelix
  4.  * @subpackage  dao
  5.  * @author      Laurent Jouanneau
  6.  * @contributor Loic Mathaud
  7.  * @contributor Julien Issler
  8.  * @contributor Thomas
  9.  * @contributor Yoan Blanc
  10.  * @contributor Mickael Fradin
  11.  * @contributor Christophe Thiriot
  12.  * @contributor Yannick Le Guédart
  13.  * @contributor Steven Jehannet
  14.  * @copyright   2005-2011 Laurent Jouanneau
  15.  * @copyright   2007 Loic Mathaud
  16.  * @copyright   2007-2009 Julien Issler
  17.  * @copyright   2008 Thomas
  18.  * @copyright   2008 Yoan Blanc
  19.  * @copyright   2009 Mickael Fradin
  20.  * @copyright   2009 Christophe Thiriot
  21.  * @copyright   2010 Yannick Le Guédart
  22.  * @copyright   2010 Steven Jehannet
  23.  * @link        http://www.jelix.org
  24.  * @licence     http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public Licence, see LICENCE file
  25.  */
  26.  
  27. /**
  28.  * base class for all factory classes generated by the dao compiler
  29.  * @package  jelix
  30.  * @subpackage dao
  31.  */
  32. abstract class jDaoFactoryBase  {
  33.     /**
  34.      * informations on tables
  35.      *
  36.      * Keys of elements are the alias of the table. values are arrays like that :
  37.      * <pre> array (
  38.      *   'name' => ' the table alias',
  39.      *   'realname' => 'the real name of the table',
  40.      *   'pk' => array ( list of primary keys name ),
  41.      *   'fields' => array ( list of property name attached to this table )
  42.      * )
  43.      * </pre>
  44.      * @var array 
  45.      */
  46.     protected $_tables;
  47.     /**
  48.      * the id of the primary table
  49.      * @var string 
  50.      */
  51.     protected $_primaryTable;
  52.     /**
  53.      * the database connector
  54.      * @var jDbConnection 
  55.      */
  56.     protected $_conn;
  57.     /**
  58.      * the select clause you can reuse for a specific SELECT query
  59.      * @var string 
  60.      */
  61.     protected $_selectClause;
  62.     /**
  63.      * the from clause you can reuse for a specific SELECT query
  64.      * @var string 
  65.      */
  66.     protected $_fromClause;
  67.     /**
  68.      * the where clause you can reuse for a specific SELECT query
  69.      * @var string 
  70.      */
  71.     protected $_whereClause;
  72.     /**
  73.      * the class name of a dao record for this dao factory
  74.      * @var string 
  75.      */
  76.     protected $_DaoRecordClassName;
  77.  
  78.     /**
  79.      * the selector of the dao, to be sent with events
  80.      * @var string 
  81.      */
  82.     protected $_daoSelector;
  83.  
  84.     /**
  85.      * @since 1.0
  86.      */
  87.     protected $_deleteBeforeEvent = false;
  88.     /**
  89.      * @since 1.0
  90.      */
  91.     protected $_deleteAfterEvent = false;
  92.     /**
  93.      * @since 1.0
  94.      */
  95.     protected $_deleteByBeforeEvent = false;
  96.     /**
  97.      * @since 1.0
  98.      */
  99.     protected $_deleteByAfterEvent = false;
  100.  
  101.     /**
  102.      * @since 1.0
  103.      */
  104.     protected $trueValue = 1;
  105.     /**
  106.      * @since 1.0
  107.      */
  108.     protected $falseValue = 0;
  109.     /**
  110.      * @param jDbConnection $conn the database connection
  111.      */
  112.     function  __construct($conn){
  113.         $this->_conn = $conn;
  114.  
  115.         if($this->_conn->hasTablePrefix()){
  116.             foreach($this->_tables as $table_name=>$table){
  117.                 $this->_tables[$table_name]['realname'$this->_conn->prefixTable($table['realname']);
  118.             }
  119.         }
  120.     }
  121.  
  122.     /**
  123.      * informations on all properties
  124.      *
  125.      * keys are property name, and values are an array like that :
  126.      * <pre> array (
  127.      *  'name' => 'name of property',
  128.      *  'fieldName' => 'name of fieldname',
  129.      *  'regExp' => NULL, // or the regular expression to test the value
  130.      *  'required' => true/false,
  131.      *  'isPK' => true/false, //says if it is a primary key
  132.      *  'isFK' => true/false, //says if it is a foreign key
  133.      *  'datatype' => '', // type of data : string
  134.      *  'unifiedType'=> '' // the corresponding unified type
  135.      *  'table' => 'grp', // alias of the table the property is attached to
  136.      *  'updatePattern' => '%s',
  137.      *  'insertPattern' => '%s',
  138.      *  'selectPattern' => '%s',
  139.      *  'sequenceName' => '', // name of the sequence when field is autoincrement
  140.      *  'maxlength' => NULL, // or a number
  141.      *  'minlength' => NULL, // or a number
  142.      *  'ofPrimaryTable' => true/false
  143.      *  'autoIncrement'=> true/false
  144.      * ) </pre>
  145.      * @return array informations on all properties
  146.      * @since 1.0beta3
  147.      */
  148.     abstract public function getProperties();
  149.  
  150.     /**
  151.      * list of id of primary properties
  152.      * @return array list of properties name which contains primary keys
  153.      * @since 1.0beta3
  154.      */
  155.     abstract public function getPrimaryKeyNames();
  156.  
  157.     /**
  158.      * return all records
  159.      * @return jDbResultSet 
  160.      */
  161.     public function findAll(){
  162.         $rs $this->_conn->query ($this->_selectClause.$this->_fromClause.$this->_whereClause);
  163.         $this->finishInitResultSet($rs);
  164.         return $rs;
  165.     }
  166.  
  167.     /**
  168.      * return the number of all records
  169.      * @return int the count
  170.      */
  171.     public function countAll(){
  172.         $query 'SELECT COUNT(*) as c '.$this->_fromClause.$this->_whereClause;
  173.         $rs  $this->_conn->query ($query);
  174.         $res $rs->fetch ();
  175.         return intval($res->c);
  176.     }
  177.  
  178.     /**
  179.      * return the record corresponding to the given key
  180.      * @param string $key one or more primary key
  181.      * @return jDaoRecordBase 
  182.      */
  183.     final public function get(){
  184.         $args=func_get_args();
  185.         if(count($args)==&& is_array($args[0])){
  186.             $args=$args[0];
  187.         }
  188.         $keys @array_combine($this->getPrimaryKeyNames(),$args );
  189.  
  190.         if($keys === false){
  191.             throw new jException('jelix~dao.error.keys.missing');
  192.         }
  193.  
  194.         $q $this->_selectClause.$this->_fromClause.$this->_whereClause;
  195.         $q .= $this->_getPkWhereClauseForSelect($keys);
  196.  
  197.         $rs $this->_conn->query ($q);
  198.         $this->finishInitResultSet($rs);
  199.         $record =  $rs->fetch ();
  200.         return $record;
  201.     }
  202.  
  203.     /**
  204.      * delete a record corresponding to the given key
  205.      * @param string  $key one or more primary key
  206.      * @return int the number of deleted record
  207.      */
  208.     final public function delete(){
  209.         $args=func_get_args();
  210.         if(count($args)==&& is_array($args[0])){
  211.             $args=$args[0];
  212.         }
  213.         $keys array_combine($this->getPrimaryKeyNames()$args);
  214.         if($keys === false){
  215.             throw new jException('jelix~dao.error.keys.missing');
  216.         }
  217.         $q 'DELETE FROM '.$this->_conn->encloseName($this->_tables[$this->_primaryTable]['realname']).' ';
  218.         $q.= $this->_getPkWhereClauseForNonSelect($keys);
  219.  
  220.         if ($this->_deleteBeforeEvent{
  221.             jEvent::notify("daoDeleteBefore"array('dao'=>$this->_daoSelector'keys'=>$keys));
  222.         }
  223.         $result $this->_conn->exec ($q);
  224.         if ($this->_deleteAfterEvent{
  225.             jEvent::notify("daoDeleteAfter"array('dao'=>$this->_daoSelector'keys'=>$keys'result'=>$result));
  226.         }
  227.         return $result;
  228.     }
  229.  
  230.     /**
  231.      * save a new record into the database
  232.      * if the dao record has an autoincrement key, its corresponding property is updated
  233.      * @param jDaoRecordBase $record the record to save
  234.      */
  235.     abstract public function insert ($record);
  236.  
  237.     /**
  238.      * save a modified record into the database
  239.      * @param jDaoRecordBase $record the record to save
  240.      */
  241.     abstract public function update ($record);
  242.  
  243.     /**
  244.      * return all record corresponding to the conditions stored into the
  245.      * jDaoConditions object.
  246.      * you can limit the number of results by given an offset and a count
  247.      * @param jDaoConditions $searchcond 
  248.      * @param int $limitOffset 
  249.      * @param int $limitCount 
  250.      * @return jDbResultSet 
  251.      */
  252.     final public function findBy ($searchcond$limitOffset=0$limitCount=null){
  253.         $query $this->_selectClause.$this->_fromClause.$this->_whereClause;
  254.         if ($searchcond->hasConditions ()){
  255.             $query .= ($this->_whereClause !='' ' AND ' ' WHERE ');
  256.             $query .= $this->_createConditionsClause($searchcond);
  257.         }
  258.         $query.= $this->_createGroupClause($searchcond);
  259.         $query.= $this->_createOrderClause($searchcond);
  260.  
  261.         if($limitCount !== null){
  262.             $rs $this->_conn->limitQuery ($query$limitOffset$limitCount);
  263.         }else{
  264.             $rs $this->_conn->query ($query);
  265.         }
  266.         $this->finishInitResultSet($rs);
  267.         return $rs;
  268.     }
  269.  
  270.     /**
  271.      * return the number of records corresponding to the conditions stored into the
  272.      * jDaoConditions object.
  273.      * @author Loic Mathaud
  274.      * @contributor Steven Jehannet
  275.      * @copyright 2007 Loic Mathaud
  276.      * @since 1.0b2
  277.      * @param jDaoConditions $searchcond 
  278.      * @return int the count
  279.      */
  280.     final public function countBy($searchcond$distinct=null{
  281.         $count '*';
  282.         $sqlite false;
  283.         if ($distinct !== null{
  284.             $props $this->getProperties();
  285.             if (isset($props[$distinct]))
  286.                 $count 'DISTINCT '.$this->_tables[$props[$distinct]['table']]['name'].'.'.$props[$distinct]['fieldName'];
  287.             $sqlite ($this->_conn->dbms == 'sqlite');
  288.         }
  289.  
  290.         if (!$sqlite)
  291.             $query 'SELECT COUNT('.$count.') as c '.$this->_fromClause.$this->_whereClause;
  292.         else // specific query for sqlite, which doesn't support COUNT+DISTINCT
  293.             $query 'SELECT COUNT(*) as c FROM (SELECT '.$count.' '.$this->_fromClause.$this->_whereClause;
  294.  
  295.         if ($searchcond->hasConditions ()){
  296.             $query .= ($this->_whereClause !='' ' AND ' ' WHERE ');
  297.             $query .= $this->_createConditionsClause($searchcond);
  298.         }
  299.         if($sqlite$query .= ')';
  300.         $rs  $this->_conn->query ($query);
  301.         $res $rs->fetch();
  302.         return intval($res->c);
  303.     }
  304.  
  305.     /**
  306.      * delete all record corresponding to the conditions stored into the
  307.      * jDaoConditions object.
  308.      * @param jDaoConditions $searchcond 
  309.      * @return number of deleted rows
  310.      * @since 1.0beta3
  311.      */
  312.     final public function deleteBy ($searchcond){
  313.         if ($searchcond->isEmpty ()){
  314.             return 0;
  315.         }
  316.  
  317.         $query 'DELETE FROM '.$this->_conn->encloseName($this->_tables[$this->_primaryTable]['realname']).' WHERE ';
  318.         $query .= $this->_createConditionsClause($searchcondfalse);
  319.  
  320.         if ($this->_deleteByBeforeEvent{
  321.             jEvent::notify("daoDeleteByBefore"array('dao'=>$this->_daoSelector'criterias'=>$searchcond));
  322.         }
  323.         $result $this->_conn->exec($query);
  324.         if ($this->_deleteByAfterEvent{
  325.             jEvent::notify("daoDeleteByAfter"array('dao'=>$this->_daoSelector'criterias'=>$searchcond'result'=>$result));
  326.         }
  327.         return $result;
  328.     }
  329.  
  330.     /**
  331.      * create a WHERE clause with conditions on primary keys with given value. This method
  332.      * should be used for SELECT queries. You haven't to escape values.
  333.      *
  334.      * @param array $pk  associated array : keys = primary key name, values : value of a primary key
  335.      * @return string a 'where' clause (WHERE mypk = 'myvalue' ...)
  336.      */
  337.     abstract protected function _getPkWhereClauseForSelect($pk);
  338.  
  339.     /**
  340.      * create a WHERE clause with conditions on primary keys with given value. This method
  341.      * should be used for DELETE and UPDATE queries.
  342.      * @param array $pk  associated array : keys = primary key name, values : value of a primary key
  343.      * @return string a 'where' clause (WHERE mypk = 'myvalue' ...)
  344.      */
  345.     abstract protected function _getPkWhereClauseForNonSelect($pk);
  346.  
  347.     /**
  348.     * @internal
  349.     */
  350.     final protected function _createConditionsClause($daocond$forSelect=true){
  351.         $props $this->getProperties();
  352.         return $this->_generateCondition ($daocond->condition$props$forSelecttrue);
  353.     }
  354.  
  355.     /**
  356.      * @internal
  357.      */
  358.     final protected function _createOrderClause($daocond{
  359.         $order array ();
  360.         $props =$this->getProperties();
  361.         foreach ($daocond->order as $name => $way){
  362.             if (isset($props[$name])) {
  363.                 $order[$this->_conn->encloseName($props[$name]['table']).'.'.$this->_conn->encloseName($props[$name]['fieldName']).' '.$way;
  364.             }
  365.         }
  366.  
  367.         if(count ($order)){
  368.             return ' ORDER BY '.implode (', '$order);
  369.         }
  370.         return '';
  371.     }
  372.  
  373.     /**
  374.      * @internal
  375.      */
  376.     final protected function _createGroupClause($daocond{
  377.         $group array ();
  378.         $props $this->getProperties();
  379.         foreach ($daocond->group as $name{
  380.             if (isset($props[$name]))
  381.                 $group[$this->_conn->encloseName($name);
  382.         }
  383.  
  384.         if (count ($group)) {
  385.             return ' GROUP BY '.implode(', '$group);
  386.         }
  387.         return '';
  388.     }
  389.  
  390.     /**
  391.      * @internal it don't support isExpr property of a condition because of security issue (SQL injection)
  392.      *  because the value could be provided by a form, it is escaped in any case
  393.      */
  394.     final protected function _generateCondition($condition&$fields$forSelect$principal=true){
  395.         $r ' ';
  396.         $notfirst false;
  397.         foreach ($condition->conditions as $cond){
  398.             if ($notfirst){
  399.                 $r .= ' '.$condition->glueOp.' ';
  400.             }else
  401.                 $notfirst true;
  402.  
  403.             $prop=$fields[$cond['field_id']];
  404.  
  405.             if($forSelect)
  406.                 $prefixNoCondition $this->_conn->encloseName($this->_tables[$prop['table']]['name']).'.'.$this->_conn->encloseName($prop['fieldName']);
  407.             else
  408.                 $prefixNoCondition $this->_conn->encloseName($prop['fieldName']);
  409.  
  410.             $op strtoupper($cond['operator']);
  411.             $prefix $prefixNoCondition.' '.$op.' '// ' ' for LIKE
  412.  
  413.             if ($op == 'IN' || $op == 'NOT IN'){
  414.                 if(is_array($cond['value'])){
  415.                     $values array();
  416.                     foreach($cond['value'as $value)
  417.                         $values[$this->_prepareValue($value,$prop['unifiedType']);
  418.                     $values join(','$values);
  419.                 }
  420.                 else
  421.                     $values $cond['value'];
  422.  
  423.                 $r .= $prefix.'('.$values.')';
  424.             }
  425.             else {
  426.                 if ($op == 'LIKE' || $op == 'NOT LIKE'{
  427.                     $type 'varchar';
  428.                 }
  429.                 else {
  430.                     $type $prop['unifiedType'];
  431.                 }
  432.  
  433.                 if (!is_array($cond['value'])) {
  434.                     $value $this->_prepareValue($cond['value']$type);
  435.                     if ($cond['value'=== null{
  436.                         if (in_array($oparray('=','LIKE','IS','IS NULL'))) {
  437.                             $r .= $prefixNoCondition.' IS NULL';
  438.                         else {
  439.                             $r .= $prefixNoCondition.' IS NOT NULL';
  440.                         }
  441.                     else {
  442.                         $r .= $prefix.$value;
  443.                     }
  444.                 else {
  445.                     $r .= ' ( ';
  446.                     $firstCV true;
  447.                     foreach ($cond['value'as $conditionValue){
  448.                         if (!$firstCV{
  449.                             $r .= ' or ';
  450.                         }
  451.                         $value $this->_prepareValue($conditionValue$type);
  452.                         if ($conditionValue === null{
  453.                             if (in_array($oparray('=','LIKE','IS','IS NULL'))) {
  454.                                 $r .= $prefixNoCondition.' IS NULL';
  455.                             else {
  456.                                 $r .= $prefixNoCondition.' IS NOT NULL';
  457.                             }
  458.                         else {
  459.                             $r .= $prefix.$value;
  460.                         }
  461.                         $firstCV false;
  462.                     }
  463.                     $r .= ' ) ';
  464.                 }
  465.             }
  466.         }
  467.         //sub conditions
  468.         foreach ($condition->group as $conditionDetail){
  469.             if ($notfirst){
  470.                 $r .= ' '.$condition->glueOp.' ';
  471.             }else{
  472.                 $notfirst=true;
  473.             }
  474.             $r .= $this->_generateCondition($conditionDetail$fields$forSelectfalse);
  475.         }
  476.  
  477.         //adds parenthesis around the sql if needed (non empty)
  478.         if (strlen (trim ($r)) && !$principal){
  479.             $r '('.$r.')';
  480.         }
  481.         return $r;
  482.     }
  483.  
  484.     /**
  485.      * prepare the value ready to be used in a dynamic evaluation
  486.      */
  487.     final protected function _prepareValue($value$fieldType$notNull false){
  488.         if (!$notNull && $value === null)
  489.             return 'NULL';
  490.         
  491.         switch(strtolower($fieldType)){
  492.             case 'integer':
  493.                 return intval($value);
  494.             case 'double':
  495.             case 'float':
  496.             case 'numeric':
  497.             case 'decimal':
  498.                 return jDb::floatToStr($value);
  499.             case 'boolean':
  500.                 if ($value === true|| strtolower($value)=='true'|| intval($value=== || $value ==='t' || $value ==='on')
  501.                     return $this->trueValue;
  502.                 else
  503.                     return $this->falseValue;
  504.                 break;
  505.             default:
  506.                 return $this->_conn->quote2 ($valuetrue($fieldType == 'binary'));
  507.         }
  508.     }
  509.  
  510.     /**
  511.      * finish to initialise a record set. Could be redefined in child class
  512.      * to do additionnal processes
  513.      * @param jDbResultSet $rs the record set
  514.      */
  515.     protected function finishInitResultSet($rs{
  516.         $rs->setFetchMode(8$this->_DaoRecordClassName);
  517.     }
  518.  
  519.     /**
  520.      * a callback function for some array_map call in generated methods
  521.      * @since 1.2
  522.      */
  523.     protected function _callbackQuote($value{
  524.         return $this->_conn->quote2($value);
  525.     }
  526.  
  527.     /**
  528.      * a callback function for some array_map call in generated methods
  529.      * @since 1.2
  530.      */
  531.     protected function _callbackQuoteBin($value{
  532.         return $this->_conn->quote2($valuetruetrue);
  533.     }
  534.  
  535.     /**
  536.      * a callback function for some array_map call in generated methods
  537.      */
  538.     protected function _callbackBool($value{
  539.         if ($value === true|| strtolower($value)=='true'|| intval($value=== || $value ==='t' || $value ==='on')
  540.             return $this->trueValue;
  541.         else
  542.             return $this->falseValue;
  543.     }
  544. }

Documentation generated on Thu, 19 Sep 2013 00:03:12 +0200 by phpDocumentor 1.4.3