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.      */
  252.     abstract public function insert ($record);
  253.  
  254.     /**
  255.      * save a modified record into the database
  256.      * @param jDaoRecordBase $record the record to save
  257.      */
  258.     abstract public function update ($record);
  259.  
  260.     /**
  261.      * return all record corresponding to the conditions stored into the
  262.      * jDaoConditions object.
  263.      * you can limit the number of results by given an offset and a count
  264.      * @param jDaoConditions $searchcond 
  265.      * @param int $limitOffset 
  266.      * @param int $limitCount 
  267.      * @return jDbResultSet 
  268.      */
  269.     final public function findBy ($searchcond$limitOffset=0$limitCount=null){
  270.         $query $this->_selectClause.$this->_fromClause.$this->_whereClause;
  271.         if ($searchcond->hasConditions ()){
  272.             $query .= ($this->_whereClause !='' ' AND ' ' WHERE ');
  273.             $query .= $this->_createConditionsClause($searchcond);
  274.         }
  275.         $query.= $this->_createGroupClause($searchcond);
  276.         $query.= $this->_createOrderClause($searchcond);
  277.  
  278.         if($limitCount !== null){
  279.             $rs $this->_conn->limitQuery ($query$limitOffset$limitCount);
  280.         }else{
  281.             $rs $this->_conn->query ($query);
  282.         }
  283.         $this->finishInitResultSet($rs);
  284.         return $rs;
  285.     }
  286.  
  287.     /**
  288.      * return the number of records corresponding to the conditions stored into the
  289.      * jDaoConditions object.
  290.      * @author Loic Mathaud
  291.      * @contributor Steven Jehannet
  292.      * @copyright 2007 Loic Mathaud
  293.      * @since 1.0b2
  294.      * @param jDaoConditions $searchcond 
  295.      * @return int the count
  296.      */
  297.     final public function countBy($searchcond$distinct=null{
  298.         $count '*';
  299.         $sqlite false;
  300.         if ($distinct !== null{
  301.             $props $this->getProperties();
  302.             if (isset($props[$distinct]))
  303.                 $count 'DISTINCT '.$this->_tables[$props[$distinct]['table']]['name'].'.'.$props[$distinct]['fieldName'];
  304.             $sqlite ($this->_conn->dbms == 'sqlite');
  305.         }
  306.  
  307.         if (!$sqlite)
  308.             $query 'SELECT COUNT('.$count.') as c '.$this->_fromClause.$this->_whereClause;
  309.         else // specific query for sqlite, which doesn't support COUNT+DISTINCT
  310.             $query 'SELECT COUNT(*) as c FROM (SELECT '.$count.' '.$this->_fromClause.$this->_whereClause;
  311.  
  312.         if ($searchcond->hasConditions ()){
  313.             $query .= ($this->_whereClause !='' ' AND ' ' WHERE ');
  314.             $query .= $this->_createConditionsClause($searchcond);
  315.         }
  316.         if($sqlite$query .= ')';
  317.         $rs  $this->_conn->query ($query);
  318.         $res $rs->fetch();
  319.         return intval($res->c);
  320.     }
  321.  
  322.     /**
  323.      * delete all record corresponding to the conditions stored into the
  324.      * jDaoConditions object.
  325.      * @param jDaoConditions $searchcond 
  326.      * @return number of deleted rows
  327.      * @since 1.0beta3
  328.      */
  329.     final public function deleteBy ($searchcond){
  330.         if ($searchcond->isEmpty ()){
  331.             return 0;
  332.         }
  333.  
  334.         $query 'DELETE FROM '.$this->_conn->encloseName($this->_tables[$this->_primaryTable]['realname']).' WHERE ';
  335.         $query .= $this->_createConditionsClause($searchcondfalse);
  336.  
  337.         if ($this->_deleteByBeforeEvent{
  338.             jEvent::notify("daoDeleteByBefore"array('dao'=>$this->_daoSelector'criterias'=>$searchcond));
  339.         }
  340.         $result $this->_conn->exec($query);
  341.         if ($this->_deleteByAfterEvent{
  342.             jEvent::notify("daoDeleteByAfter"array('dao'=>$this->_daoSelector'criterias'=>$searchcond'result'=>$result));
  343.         }
  344.         return $result;
  345.     }
  346.  
  347.     /**
  348.      * create a WHERE clause with conditions on primary keys with given value. This method
  349.      * should be used for SELECT queries. You haven't to escape values.
  350.      *
  351.      * @param array $pk  associated array : keys = primary key name, values : value of a primary key
  352.      * @return string a 'where' clause (WHERE mypk = 'myvalue' ...)
  353.      */
  354.     abstract protected function _getPkWhereClauseForSelect($pk);
  355.  
  356.     /**
  357.      * create a WHERE clause with conditions on primary keys with given value. This method
  358.      * should be used for DELETE and UPDATE queries.
  359.      * @param array $pk  associated array : keys = primary key name, values : value of a primary key
  360.      * @return string a 'where' clause (WHERE mypk = 'myvalue' ...)
  361.      */
  362.     abstract protected function _getPkWhereClauseForNonSelect($pk);
  363.  
  364.     /**
  365.     * @internal
  366.     */
  367.     final protected function _createConditionsClause($daocond$forSelect=true){
  368.         $props $this->getProperties();
  369.         return $this->_generateCondition ($daocond->condition$props$forSelecttrue);
  370.     }
  371.  
  372.     /**
  373.      * @internal
  374.      */
  375.     final protected function _createOrderClause($daocond{
  376.         $order array ();
  377.         $props =$this->getProperties();
  378.         foreach ($daocond->order as $name => $way){
  379.             if (isset($props[$name])) {
  380.                 $order[$this->_conn->encloseName($props[$name]['table']).'.'.$this->_conn->encloseName($props[$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.         $props $this->getProperties();
  396.         foreach ($daocond->group as $name{
  397.             if (isset($props[$name]))
  398.                 $group[$this->_conn->encloseName($name);
  399.         }
  400.  
  401.         if (count ($group)) {
  402.             return ' GROUP BY '.implode(', '$group);
  403.         }
  404.         return '';
  405.     }
  406.  
  407.     /**
  408.      * @internal it don't support isExpr property of a condition because of security issue (SQL injection)
  409.      *  because the value could be provided by a form, it is escaped in any case
  410.      */
  411.     final protected function _generateCondition($condition&$fields$forSelect$principal=true){
  412.         $r ' ';
  413.         $notfirst false;
  414.         foreach ($condition->conditions as $cond){
  415.             if ($notfirst){
  416.                 $r .= ' '.$condition->glueOp.' ';
  417.             }else
  418.                 $notfirst true;
  419.  
  420.             if (!isset($fields[$cond['field_id']])) {
  421.                 throw new jException('jelix~dao.error.property.unknown'$cond['field_id']);
  422.             }
  423.  
  424.             $prop=$fields[$cond['field_id']];
  425.  
  426.             if($forSelect)
  427.                 $prefixNoCondition $this->_conn->encloseName($this->_tables[$prop['table']]['name']).'.'.$this->_conn->encloseName($prop['fieldName']);
  428.             else
  429.                 $prefixNoCondition $this->_conn->encloseName($prop['fieldName']);
  430.  
  431.             $op strtoupper($cond['operator']);
  432.             $prefix $prefixNoCondition.' '.$op.' '// ' ' for LIKE
  433.  
  434.             if ($op == 'IN' || $op == 'NOT IN'){
  435.                 if(is_array($cond['value'])){
  436.                     $values array();
  437.                     foreach($cond['value'as $value)
  438.                         $values[$this->_prepareValue($value,$prop['unifiedType']);
  439.                     $values join(','$values);
  440.                 }
  441.                 else
  442.                     $values $cond['value'];
  443.  
  444.                 $r .= $prefix.'('.$values.')';
  445.             }
  446.             else {
  447.                 if ($op == 'LIKE' || $op == 'NOT LIKE'{
  448.                     $type 'varchar';
  449.                 }
  450.                 else {
  451.                     $type $prop['unifiedType'];
  452.                 }
  453.  
  454.                 if (!is_array($cond['value'])) {
  455.                     $value $this->_prepareValue($cond['value']$type);
  456.                     if ($cond['value'=== null{
  457.                         if (in_array($oparray('=','LIKE','IS','IS NULL'))) {
  458.                             $r .= $prefixNoCondition.' IS NULL';
  459.                         else {
  460.                             $r .= $prefixNoCondition.' IS NOT NULL';
  461.                         }
  462.                     else {
  463.                         $r .= $prefix.$value;
  464.                     }
  465.                 else {
  466.                     $r .= ' ( ';
  467.                     $firstCV true;
  468.                     foreach ($cond['value'as $conditionValue){
  469.                         if (!$firstCV{
  470.                             $r .= ' or ';
  471.                         }
  472.                         $value $this->_prepareValue($conditionValue$type);
  473.                         if ($conditionValue === null{
  474.                             if (in_array($oparray('=','LIKE','IS','IS NULL'))) {
  475.                                 $r .= $prefixNoCondition.' IS NULL';
  476.                             else {
  477.                                 $r .= $prefixNoCondition.' IS NOT NULL';
  478.                             }
  479.                         else {
  480.                             $r .= $prefix.$value;
  481.                         }
  482.                         $firstCV false;
  483.                     }
  484.                     $r .= ' ) ';
  485.                 }
  486.             }
  487.         }
  488.         //sub conditions
  489.         foreach ($condition->group as $conditionDetail){
  490.             if ($notfirst){
  491.                 $r .= ' '.$condition->glueOp.' ';
  492.             }else{
  493.                 $notfirst=true;
  494.             }
  495.             $r .= $this->_generateCondition($conditionDetail$fields$forSelectfalse);
  496.         }
  497.  
  498.         //adds parenthesis around the sql if needed (non empty)
  499.         if (strlen (trim ($r)) && !$principal){
  500.             $r '('.$r.')';
  501.         }
  502.         return $r;
  503.     }
  504.  
  505.     /**
  506.      * prepare the value ready to be used in a dynamic evaluation
  507.      */
  508.     final protected function _prepareValue($value$fieldType$notNull false){
  509.         if (!$notNull && $value === null)
  510.             return 'NULL';
  511.         
  512.         switch(strtolower($fieldType)){
  513.             case 'integer':
  514.                 return intval($value);
  515.             case 'double':
  516.             case 'float':
  517.             case 'numeric':
  518.             case 'decimal':
  519.                 return jDb::floatToStr($value);
  520.             case 'boolean':
  521.                 if ($value === true|| strtolower($value)=='true'|| intval($value=== || $value ==='t' || $value ==='on')
  522.                     return $this->trueValue;
  523.                 else
  524.                     return $this->falseValue;
  525.                 break;
  526.             default:
  527.                 return $this->_conn->quote2 ($valuetrue($fieldType == 'binary'));
  528.         }
  529.     }
  530.  
  531.     /**
  532.      * finish to initialise a record set. Could be redefined in child class
  533.      * to do additionnal processes
  534.      * @param jDbResultSet $rs the record set
  535.      */
  536.     protected function finishInitResultSet($rs{
  537.         $rs->setFetchMode(8$this->_DaoRecordClassName);
  538.     }
  539.  
  540.     /**
  541.      * a callback function for some array_map call in generated methods
  542.      * @since 1.2
  543.      */
  544.     protected function _callbackQuote($value{
  545.         return $this->_conn->quote2($value);
  546.     }
  547.  
  548.     /**
  549.      * a callback function for some array_map call in generated methods
  550.      * @since 1.2
  551.      */
  552.     protected function _callbackQuoteBin($value{
  553.         return $this->_conn->quote2($valuetruetrue);
  554.     }
  555.  
  556.     /**
  557.      * a callback function for some array_map call in generated methods
  558.      */
  559.     protected function _callbackBool($value{
  560.         if ($value === true|| strtolower($value)=='true'|| intval($value=== || $value ==='t' || $value ==='on')
  561.             return $this->trueValue;
  562.         else
  563.             return $this->falseValue;
  564.     }
  565. }

Documentation generated on Wed, 24 Sep 2014 21:57:13 +0200 by phpDocumentor 1.4.3