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, Didier Huguet
  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, 2010 Didier Huguet
  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.      * @since 1.3.2
  124.      * @return array informations on tables
  125.      * @see $_tables
  126.      */
  127.     public function getTables({
  128.         return $this->_tables;
  129.     }
  130.  
  131.     /**
  132.      * @since 1.3.2
  133.      * @return string the id (alias or realname) of the primary table
  134.      */
  135.     public function getPrimaryTable({
  136.         return $this->_primaryTable;
  137.     }
  138.  
  139.     /**
  140.      * informations on all properties
  141.      *
  142.      * keys are property name, and values are an array like that :
  143.      * <pre> array (
  144.      *  'name' => 'name of property',
  145.      *  'fieldName' => 'name of fieldname',
  146.      *  'regExp' => NULL, // or the regular expression to test the value
  147.      *  'required' => true/false,
  148.      *  'isPK' => true/false, //says if it is a primary key
  149.      *  'isFK' => true/false, //says if it is a foreign key
  150.      *  'datatype' => '', // type of data : string
  151.      *  'unifiedType'=> '' // the corresponding unified type
  152.      *  'table' => 'grp', // alias of the table the property is attached to
  153.      *  'updatePattern' => '%s',
  154.      *  'insertPattern' => '%s',
  155.      *  'selectPattern' => '%s',
  156.      *  'sequenceName' => '', // name of the sequence when field is autoincrement
  157.      *  'maxlength' => NULL, // or a number
  158.      *  'minlength' => NULL, // or a number
  159.      *  'ofPrimaryTable' => true/false
  160.      *  'autoIncrement'=> true/false
  161.      * ) </pre>
  162.      * @return array informations on all properties
  163.      * @since 1.0beta3
  164.      */
  165.     public function getProperties(return static::$_properties}
  166.  
  167.     /**
  168.      * list of id of primary properties
  169.      * @return array list of properties name which contains primary keys
  170.      * @since 1.0beta3
  171.      */
  172.     public function getPrimaryKeyNames(return static::$_pkFields}
  173.  
  174.     /**
  175.      * return all records
  176.      * @return jDbResultSet 
  177.      */
  178.     public function findAll(){
  179.         $rs $this->_conn->query ($this->_selectClause.$this->_fromClause.$this->_whereClause);
  180.         $this->finishInitResultSet($rs);
  181.         return $rs;
  182.     }
  183.  
  184.     /**
  185.      * return the number of all records
  186.      * @return int the count
  187.      */
  188.     public function countAll(){
  189.         $query 'SELECT COUNT(*) as c '.$this->_fromClause.$this->_whereClause;
  190.         $rs  $this->_conn->query ($query);
  191.         $res $rs->fetch ();
  192.         return intval($res->c);
  193.     }
  194.  
  195.     /**
  196.      * return the record corresponding to the given key
  197.      * @param string $key one or more primary key
  198.      * @return jDaoRecordBase 
  199.      */
  200.     final public function get(){
  201.         $args=func_get_args();
  202.         if(count($args)==&& is_array($args[0])){
  203.             $args=$args[0];
  204.         }
  205.         $keys @array_combine(static::$_pkFields$args );
  206.  
  207.         if($keys === false){
  208.             throw new jException('jelix~dao.error.keys.missing');
  209.         }
  210.  
  211.         $q $this->_selectClause.$this->_fromClause.$this->_whereClause;
  212.         $q .= $this->_getPkWhereClauseForSelect($keys);
  213.  
  214.         $rs $this->_conn->query ($q);
  215.         $this->finishInitResultSet($rs);
  216.         $record =  $rs->fetch ();
  217.         return $record;
  218.     }
  219.  
  220.     /**
  221.      * delete a record corresponding to the given key
  222.      * @param string  $key one or more primary key
  223.      * @return int the number of deleted record
  224.      */
  225.     final public function delete(){
  226.         $args=func_get_args();
  227.         if(count($args)==&& is_array($args[0])){
  228.             $args=$args[0];
  229.         }
  230.         $keys array_combine(static::$_pkFields$args);
  231.         if($keys === false){
  232.             throw new jException('jelix~dao.error.keys.missing');
  233.         }
  234.         $q 'DELETE FROM '.$this->_conn->encloseName($this->_tables[$this->_primaryTable]['realname']).' ';
  235.         $q.= $this->_getPkWhereClauseForNonSelect($keys);
  236.  
  237.         if ($this->_deleteBeforeEvent{
  238.             jEvent::notify("daoDeleteBefore"array('dao'=>$this->_daoSelector'keys'=>$keys));
  239.         }
  240.         $result $this->_conn->exec ($q);
  241.         if ($this->_deleteAfterEvent{
  242.             jEvent::notify("daoDeleteAfter"array('dao'=>$this->_daoSelector'keys'=>$keys'result'=>$result));
  243.         }
  244.         return $result;
  245.     }
  246.  
  247.     /**
  248.      * save a new record into the database
  249.      * if the dao record has an autoincrement key, its corresponding property is updated
  250.      * @param jDaoRecordBase $record the record to save
  251.      * @return integer  1 if success (the number of affected rows). False if the query has failed.
  252.      */
  253.     abstract public function insert ($record);
  254.  
  255.     /**
  256.      * save a modified record into the database
  257.      * @param jDaoRecordBase $record the record to save
  258.      * @return integer  1 if success (the number of affected rows). False if the query has failed.
  259.      */
  260.     abstract public function update ($record);
  261.  
  262.     /**
  263.      * return all record corresponding to the conditions stored into the
  264.      * jDaoConditions object.
  265.      * you can limit the number of results by given an offset and a count
  266.      * @param jDaoConditions $searchcond 
  267.      * @param int $limitOffset 
  268.      * @param int $limitCount 
  269.      * @return jDbResultSet 
  270.      */
  271.     final public function findBy ($searchcond$limitOffset=0$limitCount=null){
  272.         $query $this->_selectClause.$this->_fromClause.$this->_whereClause;
  273.         if ($searchcond->hasConditions ()){
  274.             $query .= ($this->_whereClause !='' ' AND ' ' WHERE ');
  275.             $query .= $this->_createConditionsClause($searchcond);
  276.         }
  277.         $query.= $this->_createGroupClause($searchcond);
  278.         $query.= $this->_createOrderClause($searchcond);
  279.  
  280.         if($limitCount !== null){
  281.             $rs $this->_conn->limitQuery ($query$limitOffset$limitCount);
  282.         }else{
  283.             $rs $this->_conn->query ($query);
  284.         }
  285.         $this->finishInitResultSet($rs);
  286.         return $rs;
  287.     }
  288.  
  289.     /**
  290.      * return the number of records corresponding to the conditions stored into the
  291.      * jDaoConditions object.
  292.      * @author Loic Mathaud
  293.      * @contributor Steven Jehannet
  294.      * @copyright 2007 Loic Mathaud
  295.      * @since 1.0b2
  296.      * @param jDaoConditions $searchcond 
  297.      * @return int the count
  298.      */
  299.     final public function countBy($searchcond$distinct=null{
  300.         $count '*';
  301.         $sqlite false;
  302.         if ($distinct !== null{
  303.             $props static::$_properties;
  304.             if (isset($props[$distinct]))
  305.                 $count 'DISTINCT '.$this->_tables[$props[$distinct]['table']]['name'].'.'.$props[$distinct]['fieldName'];
  306.             $sqlite ($this->_conn->dbms == 'sqlite');
  307.         }
  308.  
  309.         if (!$sqlite)
  310.             $query 'SELECT COUNT('.$count.') as c '.$this->_fromClause.$this->_whereClause;
  311.         else // specific query for sqlite, which doesn't support COUNT+DISTINCT
  312.             $query 'SELECT COUNT(*) as c FROM (SELECT '.$count.' '.$this->_fromClause.$this->_whereClause;
  313.  
  314.         if ($searchcond->hasConditions ()){
  315.             $query .= ($this->_whereClause !='' ' AND ' ' WHERE ');
  316.             $query .= $this->_createConditionsClause($searchcond);
  317.         }
  318.         if($sqlite$query .= ')';
  319.         $rs  $this->_conn->query ($query);
  320.         $res $rs->fetch();
  321.         return intval($res->c);
  322.     }
  323.  
  324.     /**
  325.      * delete all record corresponding to the conditions stored into the
  326.      * jDaoConditions object.
  327.      * @param jDaoConditions $searchcond 
  328.      * @return number of deleted rows
  329.      * @since 1.0beta3
  330.      */
  331.     final public function deleteBy ($searchcond){
  332.         if ($searchcond->isEmpty ()){
  333.             return 0;
  334.         }
  335.  
  336.         $query 'DELETE FROM '.$this->_conn->encloseName($this->_tables[$this->_primaryTable]['realname']).' WHERE ';
  337.         $query .= $this->_createConditionsClause($searchcondfalse);
  338.  
  339.         if ($this->_deleteByBeforeEvent{
  340.             jEvent::notify("daoDeleteByBefore"array('dao'=>$this->_daoSelector'criterias'=>$searchcond));
  341.         }
  342.         $result $this->_conn->exec($query);
  343.         if ($this->_deleteByAfterEvent{
  344.             jEvent::notify("daoDeleteByAfter"array('dao'=>$this->_daoSelector'criterias'=>$searchcond'result'=>$result));
  345.         }
  346.         return $result;
  347.     }
  348.  
  349.     /**
  350.      * create a WHERE clause with conditions on primary keys with given value. This method
  351.      * should be used for SELECT queries. You haven't to escape values.
  352.      *
  353.      * @param array $pk  associated array : keys = primary key name, values : value of a primary key
  354.      * @return string a 'where' clause (WHERE mypk = 'myvalue' ...)
  355.      */
  356.     abstract protected function _getPkWhereClauseForSelect($pk);
  357.  
  358.     /**
  359.      * create a WHERE clause with conditions on primary keys with given value. This method
  360.      * should be used for DELETE and UPDATE queries.
  361.      * @param array $pk  associated array : keys = primary key name, values : value of a primary key
  362.      * @return string a 'where' clause (WHERE mypk = 'myvalue' ...)
  363.      */
  364.     abstract protected function _getPkWhereClauseForNonSelect($pk);
  365.  
  366.     /**
  367.     * @internal
  368.     */
  369.     final protected function _createConditionsClause($daocond$forSelect=true){
  370.         return $this->_generateCondition ($daocond->conditionstatic::$_properties$forSelecttrue);
  371.     }
  372.  
  373.     /**
  374.      * @internal
  375.      */
  376.     final protected function _createOrderClause($daocond{
  377.         $order array ();
  378.         foreach ($daocond->order as $name => $way){
  379.             if (isset(static::$_properties[$name])) {
  380.                 $order[$this->_conn->encloseName(static::$_properties[$name]['table']).'.'.$this->_conn->encloseName(static::$_properties[$name]['fieldName']).' '.$way;
  381.             }
  382.         }
  383.  
  384.         if(count ($order)){
  385.             return ' ORDER BY '.implode (', '$order);
  386.         }
  387.         return '';
  388.     }
  389.  
  390.     /**
  391.      * @internal
  392.      */
  393.     final protected function _createGroupClause($daocond{
  394.         $group array ();
  395.         foreach ($daocond->group as $name{
  396.             if (isset(static::$_properties[$name]))
  397.                 $group[$this->_conn->encloseName($name);
  398.         }
  399.  
  400.         if (count ($group)) {
  401.             return ' GROUP BY '.implode(', '$group);
  402.         }
  403.         return '';
  404.     }
  405.  
  406.     /**
  407.      * @internal it don't support isExpr property of a condition because of security issue (SQL injection)
  408.      *  because the value could be provided by a form, it is escaped in any case
  409.      */
  410.     final protected function _generateCondition($condition&$fields$forSelect$principal=true){
  411.         $r ' ';
  412.         $notfirst false;
  413.         foreach ($condition->conditions as $cond){
  414.             if ($notfirst){
  415.                 $r .= ' '.$condition->glueOp.' ';
  416.             }else
  417.                 $notfirst true;
  418.  
  419.             if (!isset($fields[$cond['field_id']])) {
  420.                 throw new jException('jelix~dao.error.property.unknown'$cond['field_id']);
  421.             }
  422.  
  423.             $prop=$fields[$cond['field_id']];
  424.  
  425.             if($forSelect)
  426.                 $prefixNoCondition $this->_conn->encloseName($this->_tables[$prop['table']]['name']).'.'.$this->_conn->encloseName($prop['fieldName']);
  427.             else
  428.                 $prefixNoCondition $this->_conn->encloseName($prop['fieldName']);
  429.  
  430.             $op strtoupper($cond['operator']);
  431.             $prefix $prefixNoCondition.' '.$op.' '// ' ' for LIKE
  432.  
  433.             if ($op == 'IN' || $op == 'NOT IN'){
  434.                 if(is_array($cond['value'])){
  435.                     $values array();
  436.                     foreach($cond['value'as $value)
  437.                         $values[$this->_prepareValue($value,$prop['unifiedType']);
  438.                     $values join(','$values);
  439.                 }
  440.                 else
  441.                     $values $cond['value'];
  442.  
  443.                 $r .= $prefix.'('.$values.')';
  444.             }
  445.             else {
  446.                 if ($op == 'LIKE' || $op == 'NOT LIKE'{
  447.                     $type 'varchar';
  448.                 }
  449.                 else {
  450.                     $type $prop['unifiedType'];
  451.                 }
  452.  
  453.                 if (!is_array($cond['value'])) {
  454.                     $value $this->_prepareValue($cond['value']$type);
  455.                     if ($cond['value'=== null{
  456.                         if (in_array($oparray('=','LIKE','IS','IS NULL'))) {
  457.                             $r .= $prefixNoCondition.' IS NULL';
  458.                         else {
  459.                             $r .= $prefixNoCondition.' IS NOT NULL';
  460.                         }
  461.                     else {
  462.                         $r .= $prefix.$value;
  463.                     }
  464.                 else {
  465.                     $r .= ' ( ';
  466.                     $firstCV true;
  467.                     foreach ($cond['value'as $conditionValue){
  468.                         if (!$firstCV{
  469.                             $r .= ' or ';
  470.                         }
  471.                         $value $this->_prepareValue($conditionValue$type);
  472.                         if ($conditionValue === null{
  473.                             if (in_array($oparray('=','LIKE','IS','IS NULL'))) {
  474.                                 $r .= $prefixNoCondition.' IS NULL';
  475.                             else {
  476.                                 $r .= $prefixNoCondition.' IS NOT NULL';
  477.                             }
  478.                         else {
  479.                             $r .= $prefix.$value;
  480.                         }
  481.                         $firstCV false;
  482.                     }
  483.                     $r .= ' ) ';
  484.                 }
  485.             }
  486.         }
  487.         //sub conditions
  488.         foreach ($condition->group as $conditionDetail){
  489.             if ($notfirst){
  490.                 $r .= ' '.$condition->glueOp.' ';
  491.             }else{
  492.                 $notfirst=true;
  493.             }
  494.             $r .= $this->_generateCondition($conditionDetail$fields$forSelectfalse);
  495.         }
  496.  
  497.         //adds parenthesis around the sql if needed (non empty)
  498.         if (strlen (trim ($r)) && !$principal){
  499.             $r '('.$r.')';
  500.         }
  501.         return $r;
  502.     }
  503.  
  504.     /**
  505.      * prepare the value ready to be used in a dynamic evaluation
  506.      */
  507.     final protected function _prepareValue($value$fieldType$notNull false){
  508.         if (!$notNull && $value === null)
  509.             return 'NULL';
  510.         
  511.         switch(strtolower($fieldType)){
  512.             case 'integer':
  513.                 return intval($value);
  514.             case 'double':
  515.             case 'float':
  516.             case 'numeric':
  517.             case 'decimal':
  518.                 return jDb::floatToStr($value);
  519.             case 'boolean':
  520.                 if ($value === true|| strtolower($value)=='true'|| intval($value=== || $value ==='t' || $value ==='on')
  521.                     return $this->trueValue;
  522.                 else
  523.                     return $this->falseValue;
  524.                 break;
  525.             default:
  526.                 return $this->_conn->quote2 ($valuetrue($fieldType == 'binary'));
  527.         }
  528.     }
  529.  
  530.     /**
  531.      * finish to initialise a record set. Could be redefined in child class
  532.      * to do additionnal processes
  533.      * @param jDbResultSet $rs the record set
  534.      */
  535.     protected function finishInitResultSet($rs{
  536.         $rs->setFetchMode(8$this->_DaoRecordClassName);
  537.     }
  538.  
  539.     /**
  540.      * a callback function for some array_map call in generated methods
  541.      * @since 1.2
  542.      */
  543.     protected function _callbackQuote($value{
  544.         return $this->_conn->quote2($value);
  545.     }
  546.  
  547.     /**
  548.      * a callback function for some array_map call in generated methods
  549.      * @since 1.2
  550.      */
  551.     protected function _callbackQuoteBin($value{
  552.         return $this->_conn->quote2($valuetruetrue);
  553.     }
  554.  
  555.     /**
  556.      * a callback function for some array_map call in generated methods
  557.      */
  558.     protected function _callbackBool($value{
  559.         if ($value === true|| strtolower($value)=='true'|| intval($value=== || $value ==='t' || $value ==='on')
  560.             return $this->trueValue;
  561.         else
  562.             return $this->falseValue;
  563.     }
  564. }

Documentation generated on Wed, 04 Jan 2017 22:53:01 +0100 by phpDocumentor 1.4.3