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

Documentation generated on Thu, 22 Mar 2012 22:14:40 +0100 by phpDocumentor 1.4.3