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.     abstract public function getProperties();
  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.     abstract public function getPrimaryKeyNames();
  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($this->getPrimaryKeyNames(),$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($this->getPrimaryKeyNames()$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 $this->getProperties();
  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.         $props $this->getProperties();
  371.         return $this->_generateCondition ($daocond->condition$props$forSelecttrue);
  372.     }
  373.  
  374.     /**
  375.      * @internal
  376.      */
  377.     final protected function _createOrderClause($daocond{
  378.         $order array ();
  379.         $props =$this->getProperties();
  380.         foreach ($daocond->order as $name => $way){
  381.             if (isset($props[$name])) {
  382.                 $order[$this->_conn->encloseName($props[$name]['table']).'.'.$this->_conn->encloseName($props[$name]['fieldName']).' '.$way;
  383.             }
  384.         }
  385.  
  386.         if(count ($order)){
  387.             return ' ORDER BY '.implode (', '$order);
  388.         }
  389.         return '';
  390.     }
  391.  
  392.     /**
  393.      * @internal
  394.      */
  395.     final protected function _createGroupClause($daocond{
  396.         $group array ();
  397.         $props $this->getProperties();
  398.         foreach ($daocond->group as $name{
  399.             if (isset($props[$name]))
  400.                 $group[$this->_conn->encloseName($name);
  401.         }
  402.  
  403.         if (count ($group)) {
  404.             return ' GROUP BY '.implode(', '$group);
  405.         }
  406.         return '';
  407.     }
  408.  
  409.     /**
  410.      * @internal it don't support isExpr property of a condition because of security issue (SQL injection)
  411.      *  because the value could be provided by a form, it is escaped in any case
  412.      */
  413.     final protected function _generateCondition($condition&$fields$forSelect$principal=true){
  414.         $r ' ';
  415.         $notfirst false;
  416.         foreach ($condition->conditions as $cond){
  417.             if ($notfirst){
  418.                 $r .= ' '.$condition->glueOp.' ';
  419.             }else
  420.                 $notfirst true;
  421.  
  422.             if (!isset($fields[$cond['field_id']])) {
  423.                 throw new jException('jelix~dao.error.property.unknown'$cond['field_id']);
  424.             }
  425.  
  426.             $prop=$fields[$cond['field_id']];
  427.  
  428.             if($forSelect)
  429.                 $prefixNoCondition $this->_conn->encloseName($this->_tables[$prop['table']]['name']).'.'.$this->_conn->encloseName($prop['fieldName']);
  430.             else
  431.                 $prefixNoCondition $this->_conn->encloseName($prop['fieldName']);
  432.  
  433.             $op strtoupper($cond['operator']);
  434.             $prefix $prefixNoCondition.' '.$op.' '// ' ' for LIKE
  435.  
  436.             if ($op == 'IN' || $op == 'NOT IN'){
  437.                 if(is_array($cond['value'])){
  438.                     $values array();
  439.                     foreach($cond['value'as $value)
  440.                         $values[$this->_prepareValue($value,$prop['unifiedType']);
  441.                     $values join(','$values);
  442.                 }
  443.                 else
  444.                     $values $cond['value'];
  445.  
  446.                 $r .= $prefix.'('.$values.')';
  447.             }
  448.             else {
  449.                 if ($op == 'LIKE' || $op == 'NOT LIKE'{
  450.                     $type 'varchar';
  451.                 }
  452.                 else {
  453.                     $type $prop['unifiedType'];
  454.                 }
  455.  
  456.                 if (!is_array($cond['value'])) {
  457.                     $value $this->_prepareValue($cond['value']$type);
  458.                     if ($cond['value'=== null{
  459.                         if (in_array($oparray('=','LIKE','IS','IS NULL'))) {
  460.                             $r .= $prefixNoCondition.' IS NULL';
  461.                         else {
  462.                             $r .= $prefixNoCondition.' IS NOT NULL';
  463.                         }
  464.                     else {
  465.                         $r .= $prefix.$value;
  466.                     }
  467.                 else {
  468.                     $r .= ' ( ';
  469.                     $firstCV true;
  470.                     foreach ($cond['value'as $conditionValue){
  471.                         if (!$firstCV{
  472.                             $r .= ' or ';
  473.                         }
  474.                         $value $this->_prepareValue($conditionValue$type);
  475.                         if ($conditionValue === null{
  476.                             if (in_array($oparray('=','LIKE','IS','IS NULL'))) {
  477.                                 $r .= $prefixNoCondition.' IS NULL';
  478.                             else {
  479.                                 $r .= $prefixNoCondition.' IS NOT NULL';
  480.                             }
  481.                         else {
  482.                             $r .= $prefix.$value;
  483.                         }
  484.                         $firstCV false;
  485.                     }
  486.                     $r .= ' ) ';
  487.                 }
  488.             }
  489.         }
  490.         //sub conditions
  491.         foreach ($condition->group as $conditionDetail){
  492.             if ($notfirst){
  493.                 $r .= ' '.$condition->glueOp.' ';
  494.             }else{
  495.                 $notfirst=true;
  496.             }
  497.             $r .= $this->_generateCondition($conditionDetail$fields$forSelectfalse);
  498.         }
  499.  
  500.         //adds parenthesis around the sql if needed (non empty)
  501.         if (strlen (trim ($r)) && !$principal){
  502.             $r '('.$r.')';
  503.         }
  504.         return $r;
  505.     }
  506.  
  507.     /**
  508.      * prepare the value ready to be used in a dynamic evaluation
  509.      */
  510.     final protected function _prepareValue($value$fieldType$notNull false){
  511.         if (!$notNull && $value === null)
  512.             return 'NULL';
  513.         
  514.         switch(strtolower($fieldType)){
  515.             case 'integer':
  516.                 return intval($value);
  517.             case 'double':
  518.             case 'float':
  519.             case 'numeric':
  520.             case 'decimal':
  521.                 return jDb::floatToStr($value);
  522.             case 'boolean':
  523.                 if ($value === true|| strtolower($value)=='true'|| intval($value=== || $value ==='t' || $value ==='on')
  524.                     return $this->trueValue;
  525.                 else
  526.                     return $this->falseValue;
  527.                 break;
  528.             default:
  529.                 return $this->_conn->quote2 ($valuetrue($fieldType == 'binary'));
  530.         }
  531.     }
  532.  
  533.     /**
  534.      * finish to initialise a record set. Could be redefined in child class
  535.      * to do additionnal processes
  536.      * @param jDbResultSet $rs the record set
  537.      */
  538.     protected function finishInitResultSet($rs{
  539.         $rs->setFetchMode(8$this->_DaoRecordClassName);
  540.     }
  541.  
  542.     /**
  543.      * a callback function for some array_map call in generated methods
  544.      * @since 1.2
  545.      */
  546.     protected function _callbackQuote($value{
  547.         return $this->_conn->quote2($value);
  548.     }
  549.  
  550.     /**
  551.      * a callback function for some array_map call in generated methods
  552.      * @since 1.2
  553.      */
  554.     protected function _callbackQuoteBin($value{
  555.         return $this->_conn->quote2($valuetruetrue);
  556.     }
  557.  
  558.     /**
  559.      * a callback function for some array_map call in generated methods
  560.      */
  561.     protected function _callbackBool($value{
  562.         if ($value === true|| strtolower($value)=='true'|| intval($value=== || $value ==='t' || $value ==='on')
  563.             return $this->trueValue;
  564.         else
  565.             return $this->falseValue;
  566.     }
  567. }

Documentation generated on Mon, 26 Oct 2015 21:52:34 +0100 by phpDocumentor 1.4.3