Source for file jDaoGenerator.class.php

Documentation is available at jDaoGenerator.class.php

  1. <?php
  2. /**
  3. @package    jelix
  4. @subpackage dao
  5. @author     GĂ©rald Croes, Laurent Jouanneau
  6. @contributor Laurent Jouanneau
  7. @contributor Bastien Jaillot (bug fix)
  8. @contributor Julien Issler
  9. @copyright  2001-2005 CopixTeam, 2005-2012 Laurent Jouanneau
  10. @copyright  2007-2008 Julien Issler
  11. *  This class was get originally from the Copix project (CopixDAOGeneratorV1, Copix 2.3dev20050901, http://www.copix.org)
  12. *  Few lines of code are still copyrighted 2001-2005 CopixTeam (LGPL licence).
  13. *  Initial authors of this Copix class are Gerald Croes and Laurent Jouanneau,
  14. *  and this class was rewrited for Jelix by Laurent Jouanneau
  15. *
  16. @link        http://www.jelix.org
  17. @licence  http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public Licence, see LICENCE file
  18. */
  19.  
  20. /**
  21. * This is a generator which creates php class from dao xml file.
  22. *
  23. * It is called by jDaoCompiler
  24. @package  jelix
  25. @subpackage dao
  26. @see jDaoCompiler
  27. */
  28. class jDaoGenerator {
  29.  
  30.     /**
  31.     * the dao definition.
  32.     * @var jDaoParser 
  33.     */
  34.     protected $_dataParser = null;
  35.  
  36.     /**
  37.     * The DaoRecord ClassName
  38.     * @var string 
  39.     */
  40.     protected $_DaoRecordClassName = null;
  41.  
  42.     /**
  43.     * the DAO classname
  44.     * @var string 
  45.     */
  46.     protected $_DaoClassName = null;
  47.  
  48.     protected $propertiesListForInsert = 'PrimaryTable';
  49.  
  50.     protected $aliasWord = ' AS ';
  51.  
  52.     /**
  53.      * @var jDbTools 
  54.     */
  55.     protected $tools;
  56.  
  57.     protected $_daoId;
  58.     protected $_daoPath;
  59.     protected $_dbType;
  60.  
  61.     /**
  62.      * the real name of the main table
  63.      */
  64.     protected $tableRealName = '';
  65.  
  66.     /**
  67.      * the real name of the main table, escaped in SQL
  68.      * so it is ready to include into a SQL query.
  69.      */
  70.     protected $tableRealNameEsc = '';
  71.     
  72.     protected $sqlWhereClause = '';
  73.     
  74.     protected $sqlFromClause = '';
  75.     
  76.     protected $sqlSelectClause = '';
  77.  
  78.     /**
  79.     * constructor
  80.     * @param jDaoParser $daoDefinition 
  81.     */
  82.     function __construct($selector$tools$daoParser){
  83.         $this->_daoId = $selector->toString();
  84.         $this->_daoPath = $selector->getPath();
  85.         $this->_dbType = $selector->driver;
  86.         $this->_dataParser = $daoParser;
  87.         $this->_DaoClassName = $selector->getDaoClass();
  88.         $this->_DaoRecordClassName = $selector->getDaoRecordClass();
  89.         $this->tools = $tools;
  90.     }
  91.  
  92.     /**
  93.     * build all classes
  94.     */
  95.     public function buildClasses ({
  96.  
  97.         $src array();
  98.         $src[' require_once ( JELIX_LIB_PATH .\'dao/jDaoRecordBase.class.php\');';
  99.         $src[' require_once ( JELIX_LIB_PATH .\'dao/jDaoFactoryBase.class.php\');';
  100.  
  101.         // prepare some values to generate properties and methods
  102.  
  103.         $this->buildFromWhereClause();
  104.         $this->sqlSelectClause   = $this->buildSelectClause();
  105.  
  106.         $tables            $this->_dataParser->getTables();
  107.         $pkFields          $this->_getPrimaryFieldsList();
  108.         $this->tableRealName    = $tables[$this->_dataParser->getPrimaryTable()]['realname'];
  109.         $this->tableRealNameEsc = $this->_encloseName('\'.$this->_conn->prefixTable(\''.$this->tableRealName.'\').\'');
  110.  
  111.         $sqlPkCondition    $this->buildSimpleConditions($pkFields);
  112.         if ($sqlPkCondition != ''{
  113.             $sqlPkCondition($this->sqlWhereClause !='' ' AND ':' WHERE ').$sqlPkCondition;
  114.         }
  115.  
  116.         //-----------------------
  117.         // Build the record class
  118.         //-----------------------
  119.  
  120.         $src["\nclass ".$this->_DaoRecordClassName.' extends jDaoRecordBase {';
  121.  
  122.         $properties=array();
  123.  
  124.         foreach ($this->_dataParser->getProperties(as $id=>$field{
  125.             $properties[$idget_object_vars($field);
  126.             if ($field->defaultValue !== null{
  127.                 $src[=' public $'.$id.'='.var_export($field->defaultValuetrue).';';
  128.             }
  129.             else
  130.                 $src[=' public $'.$id.';';
  131.         }
  132.  
  133.         $src['   public function getSelector() { return "'.$this->_daoId.'"; }';
  134.         // TODO PHP 5.3 : we could remove that
  135.         $src['   public function getProperties() { return '.$this->_DaoClassName.'::$_properties; }';
  136.         $src['   public function getPrimaryKeyNames() { return '.$this->_DaoClassName.'::$_pkFields; }';
  137.         $src['}';
  138.  
  139.         //--------------------
  140.         // Build the dao class
  141.         //--------------------
  142.  
  143.         $src["\nclass ".$this->_DaoClassName.' extends jDaoFactoryBase {';
  144.         $src['   protected $_tables = '.var_export($tablestrue).';';
  145.         $src['   protected $_primaryTable = \''.$this->_dataParser->getPrimaryTable().'\';';
  146.         $src['   protected $_selectClause=\''.$this->sqlSelectClause.'\';';
  147.         $src['   protected $_fromClause;';
  148.         $src['   protected $_whereClause=\''.$this->sqlWhereClause.'\';';
  149.         $src['   protected $_DaoRecordClassName=\''.$this->_DaoRecordClassName.'\';';
  150.         $src['   protected $_daoSelector = \''.$this->_daoId.'\';';
  151.  
  152.         if($this->tools->trueValue != '1'){
  153.             $src[]='   protected $trueValue ='.var_export($this->tools->trueValuetrue).';';
  154.             $src[]='   protected $falseValue ='.var_export($this->tools->falseValuetrue).';';
  155.         }
  156.  
  157.         if($this->_dataParser->hasEvent('deletebefore'|| $this->_dataParser->hasEvent('delete'))
  158.             $src['   protected $_deleteBeforeEvent = true;';
  159.         if ($this->_dataParser->hasEvent('deleteafter'|| $this->_dataParser->hasEvent('delete'))
  160.             $src['   protected $_deleteAfterEvent = true;';
  161.         if ($this->_dataParser->hasEvent('deletebybefore'|| $this->_dataParser->hasEvent('deleteby'))
  162.             $src['   protected $_deleteByBeforeEvent = true;';
  163.         if ($this->_dataParser->hasEvent('deletebyafter'|| $this->_dataParser->hasEvent('deleteby'))
  164.             $src['   protected $_deleteByAfterEvent = true;';
  165.  
  166.         $src['   public static $_properties = '.var_export($propertiestrue).';';
  167.         $src['   public static $_pkFields = array('.$this->_writeFieldNamesWith ($start '\''$end='\''$beetween ','$pkFields).');';
  168.  
  169.         $src[' ';
  170.         $src['public function __construct($conn){';
  171.         $src['   parent::__construct($conn);';
  172.         $src['   $this->_fromClause = \''.$this->sqlFromClause.'\';';
  173.         $src['}';
  174.  
  175.         // cannot put this methods directly into jDaoBase because self cannot refer to a child class
  176.         // FIXME PHP53, we could use the static keyword instead of self
  177.         $src['   public function getProperties() { return self::$_properties; }';
  178.         $src['   public function getPrimaryKeyNames() { return self::$_pkFields;}';
  179.  
  180.         $src[' ';
  181.         $src[' protected function _getPkWhereClauseForSelect($pk){';
  182.         $src['   extract($pk);';
  183.         $src[' return \''.$sqlPkCondition.'\';';
  184.         $src['}';
  185.  
  186.         $src[' ';
  187.         $src['protected function _getPkWhereClauseForNonSelect($pk){';
  188.         $src['   extract($pk);';
  189.         $src['   return \' where '.$this->buildSimpleConditions($pkFields,'',false).'\';';
  190.         $src['}';
  191.  
  192.         //----- Insert method
  193.  
  194.         $src[$this->buildInsertMethod($pkFields);
  195.  
  196.         //-----  update method
  197.  
  198.         $src[$this->buildUpdateMethod($pkFields);
  199.  
  200.         //----- other user methods
  201.  
  202.         $src[$this->buildUserMethods();
  203.  
  204.         $src[$this->buildEndOfClass();
  205.  
  206.         $src['}';//end of class
  207.  
  208.         return implode("\n",$src);
  209.     }
  210.  
  211.     /**
  212.      * build the insert() method in the final class
  213.      * @return string the source of the method
  214.      */
  215.     protected function buildInsertMethod($pkFields{
  216.         $pkai $this->getAutoIncrementPKField();
  217.         $src array();
  218.         $src['public function insert ($record){';
  219.  
  220.         if($pkai !== null){
  221.             // if there is an autoincrement field as primary key
  222.  
  223.             // if a value is given for the autoincrement field, then with do a full insert
  224.             $src[]=' if($record->'.$pkai->name.' > 0 ){';
  225.             $src['    $query = \'INSERT INTO '.$this->tableRealNameEsc.' (';
  226.             $fields $this->_getPropertiesBy('PrimaryTable');
  227.             list($fields$values$this->_prepareValues($fields,'insertPattern''record->');
  228.  
  229.             $src[implode(',',$fields);
  230.             $src[') VALUES (';
  231.             $src[implode(', ',$values);
  232.             $src[")';";
  233.  
  234.             $src['}else{';
  235.  
  236.             $fields $this->_getPropertiesBy($this->propertiesListForInsert);
  237.         }else{
  238.             $fields $this->_getPropertiesBy('PrimaryTable');
  239.         }
  240.  
  241.         if($this->_dataParser->hasEvent('insertbefore'|| $this->_dataParser->hasEvent('insert')){
  242.             $src['   jEvent::notify("daoInsertBefore", array(\'dao\'=>$this->_daoSelector, \'record\'=>$record));';
  243.         }
  244.         
  245.         // if there isn't a autoincrement as primary key, then we do a full insert.
  246.         // if there isn't a value for the autoincrement field and if this is a mysql/sqlserver and pgsql,
  247.         // we do an insert without given primary key. In other case, we do a full insert.
  248.  
  249.         $src['    $query = \'INSERT INTO '.$this->tableRealNameEsc.' (';
  250.  
  251.         list($fields$values$this->_prepareValues($fields,'insertPattern''record->');
  252.  
  253.         $src[implode(',',$fields);
  254.         $src[') VALUES (';
  255.         $src[implode(', ',$values);
  256.         $src[")';";
  257.  
  258.         if($pkai !== null)
  259.             $src['}';
  260.  
  261.         $src['   $result = $this->_conn->exec ($query);';
  262.  
  263.         if($pkai !== null){
  264.             $src['   if(!$result)';
  265.             $src['       return false;';
  266.  
  267.             $src['   if($record->'.$pkai->name.' < 1 ) ';
  268.             $src[$this->buildUpdateAutoIncrementPK($pkai);
  269.         }
  270.  
  271.         // we generate a SELECT query to update field on the record object, which are autoincrement or calculated
  272.         $fields $this->_getPropertiesBy('FieldToUpdate');
  273.         if (count($fields)) {
  274.             $result array();
  275.             foreach ($fields as $id=>$prop){
  276.                 $result[]$this->buildSelectPattern($prop->selectPattern''$prop->fieldName$prop->name);
  277.             }
  278.  
  279.             $sql 'SELECT '.(implode (', ',$result))' FROM '.$this->tableRealNameEsc.' WHERE ';
  280.             $sql.= $this->buildSimpleConditions($pkFields'record->'false);
  281.  
  282.             $src['  $query =\''.$sql.'\';';
  283.             $src['  $rs  =  $this->_conn->query ($query);';
  284.             $src['  $newrecord =  $rs->fetch ();';
  285.             foreach ($fields as $id=>$prop){
  286.                 $src['  $record->'.$prop->name.' = $newrecord->'.$prop->name.';';
  287.             }
  288.         }
  289.  
  290.         if($this->_dataParser->hasEvent('insertafter'|| $this->_dataParser->hasEvent('insert')){
  291.             $src['   jEvent::notify("daoInsertAfter", array(\'dao\'=>$this->_daoSelector, \'record\'=>$record));';
  292.         }
  293.  
  294.         $src['    return $result;';
  295.         $src['}';
  296.  
  297.         return implode("\n",$src);
  298.     }
  299.  
  300.     /**
  301.      * build the update() method for the final class
  302.      * @return string the source of the method
  303.      */
  304.     protected function buildUpdateMethod($pkFields{
  305.         $src array();
  306.         
  307.         $src['public function update ($record){';
  308.         list($fields$values$this->_prepareValues($this->_getPropertiesBy('PrimaryFieldsExcludePk'),'updatePattern''record->');
  309.         
  310.         if(count($fields)){
  311.             
  312.             if($this->_dataParser->hasEvent('updatebefore'|| $this->_dataParser->hasEvent('update')){
  313.                 $src['   jEvent::notify("daoUpdateBefore", array(\'dao\'=>$this->_daoSelector, \'record\'=>$record));';
  314.             }
  315.             
  316.             $src['   $query = \'UPDATE '.$this->tableRealNameEsc.' SET ';
  317.             $sqlSet='';
  318.             foreach($fields as $k=> $fname){
  319.                 $sqlSet.= ', '.$fname'= '$values[$k];
  320.             }
  321.             $src[substr($sqlSet,1);
  322.  
  323.             $sqlCondition $this->buildSimpleConditions($pkFields'record->'false);
  324.             if($sqlCondition!='')
  325.                 $src[' where '.$sqlCondition;
  326.  
  327.             $src["';";
  328.  
  329.             $src['   $result = $this->_conn->exec ($query);';
  330.  
  331.             // we generate a SELECT query to update field on the record object, which are autoincrement or calculated
  332.             $fields $this->_getPropertiesBy('FieldToUpdateOnUpdate');
  333.             if (count($fields)) {
  334.                 $result array();
  335.                 foreach ($fields as $id=>$prop){
  336.                     $result[]$this->buildSelectPattern($prop->selectPattern''$prop->fieldName$prop->name);
  337.                 }
  338.  
  339.                 $sql 'SELECT '.(implode (', ',$result))' FROM '.$this->tableRealNameEsc.' WHERE ';
  340.                 $sql.= $this->buildSimpleConditions($pkFields'record->'false);
  341.  
  342.                 $src['  $query =\''.$sql.'\';';
  343.                 $src['  $rs  =  $this->_conn->query ($query, jDbConnection::FETCH_INTO, $record);';
  344.                 $src['  $record =  $rs->fetch ();';
  345.             }
  346.  
  347.             if($this->_dataParser->hasEvent('updateafter'|| $this->_dataParser->hasEvent('update'))
  348.                 $src['   jEvent::notify("daoUpdateAfter", array(\'dao\'=>$this->_daoSelector, \'record\'=>$record));';
  349.  
  350.             $src['   return $result;';
  351.         }else{
  352.             //the dao is mapped on a table which contains only primary key : update is impossible
  353.             // so we will generate an error on update
  354.             $src["     throw new jException('jelix~dao.error.update.impossible',array('".$this->_daoId."','".$this->_daoPath."'));";
  355.         }
  356.  
  357.         $src[' }';//ends the update function
  358.         return implode("\n",$src);
  359.     }
  360.  
  361.     /**
  362.      * build all methods defined by the developer in the dao file
  363.      * @return string the source of the methods
  364.      */
  365.     protected function buildUserMethods({
  366.         
  367.         $allField $this->_getPropertiesBy('All');
  368.         $primaryFields $this->_getPropertiesBy('PrimaryTable');
  369.         $src array();
  370.  
  371.         foreach($this->_dataParser->getMethods(as $name=>$method){
  372.  
  373.             $defval $method->getParametersDefaultValues();
  374.             if(count($defval)){
  375.                 $mparam='';
  376.                 foreach($method->getParameters(as $param){
  377.                     $mparam.=', $'.$param;
  378.                     if(isset($defval[$param]))
  379.                         $mparam.='=\''.str_replace("'","\'",$defval[$param]).'\'';
  380.                 }
  381.                 $mparam substr($mparam,1);
  382.             }else{
  383.                 $mparam=implode(', $',$method->getParameters());
  384.                 if($mparam != ''$mparam ='$'.$mparam;
  385.             }
  386.  
  387.             $src[' function '.$method->name.' ('$mparam.'){';
  388.  
  389.             $limit='';
  390.  
  391.             $glueCondition =' WHERE ';
  392.             switch($method->type){
  393.                 case 'delete':
  394.                     $this->buildDeleteUserQuery($method$src$primaryFields);
  395.                     break;
  396.                 case 'update':
  397.                     $this->buildUpdateUserQuery($method$src$primaryFields);
  398.                     break;
  399.                 case 'php':
  400.                     $src[$method->getBody();
  401.                     $src['}';
  402.                     break;
  403.  
  404.                 case 'count':
  405.                     $this->buildCountUserQuery($method$src$allField);
  406.                     break;
  407.                 case 'selectfirst':
  408.                 case 'select':
  409.                 default:
  410.                     $limit $this->buildSelectUserQuery($method$src$allField);
  411.             }
  412.  
  413.             if($method->type == 'php')
  414.                 continue;
  415.  
  416.  
  417.             switch($method->type){
  418.                 case 'delete':
  419.                 case 'update' :
  420.                     if ($method->eventBeforeEnabled || $method->eventAfterEnabled{
  421.                         $src['   $args = func_get_args();';
  422.                         $methname ($method->type == 'update'?'Update':'Delete');
  423.                         if ($method->eventBeforeEnabled{
  424.                             $src['   jEvent::notify("daoSpecific'.$methname.'Before", array(\'dao\'=>$this->_daoSelector,\'method\'=>\''.
  425.                             $method->name.'\', \'params\'=>$args));';
  426.                         }
  427.                         if ($method->eventAfterEnabled{
  428.                             $src['   $result = $this->_conn->exec ($__query);';
  429.                             $src['   jEvent::notify("daoSpecific'.$methname.'After", array(\'dao\'=>$this->_daoSelector,\'method\'=>\''.
  430.                                 $method->name.'\', \'params\'=>$args));';
  431.                             $src['   return $result;';
  432.                         else {
  433.                             $src['    return $this->_conn->exec ($__query);';
  434.                         }
  435.                     else {
  436.                         $src['    return $this->_conn->exec ($__query);';
  437.                     }
  438.                     break;
  439.                 case 'count':
  440.                     $src['    $__rs = $this->_conn->query($__query);';
  441.                     $src['    $__res = $__rs->fetch();';
  442.                     $src['    return intval($__res->c);';
  443.                     break;
  444.                 case 'selectfirst':
  445.                     $src['    $__rs = $this->_conn->limitQuery($__query,0,1);';
  446.                     $src['    $this->finishInitResultSet($__rs);';
  447.                     $src['    return $__rs->fetch();';
  448.                     break;
  449.                 case 'select':
  450.                 default:
  451.                     if($limit)
  452.                         $src['    $__rs = $this->_conn->limitQuery($__query'.$limit.');';
  453.                     else
  454.                         $src['    $__rs = $this->_conn->query($__query);';
  455.                     $src['    $this->finishInitResultSet($__rs);';
  456.                     $src['    return $__rs;';
  457.             }
  458.             $src['}';
  459.         }
  460.         return implode("\n",$src);
  461.     }
  462.  
  463.     /**
  464.      *
  465.      */
  466.     protected function buildDeleteUserQuery($method&$src&$primaryFields{
  467.         $src['    $__query = \'DELETE FROM '.$this->tableRealNameEsc.' \';';
  468.         $cond $method->getConditions();
  469.         if($cond !== null{
  470.             $sqlCond $this->buildConditions($cond$primaryFields$method->getParameters()false);
  471.             if(trim($sqlCond!= '')
  472.                 $src['$__query .=\' WHERE '.$sqlCond."';";
  473.         }
  474.     }
  475.  
  476.     /**
  477.      *
  478.      */
  479.     protected function buildUpdateUserQuery($method&$src&$primaryFields{
  480.         $src['    $__query = \'UPDATE '.$this->tableRealNameEsc.' SET ';
  481.         $updatefields $this->_getPropertiesBy('PrimaryFieldsExcludePk');
  482.         $sqlSet='';
  483.  
  484.         foreach($method->getValues(as $propname=>$value){
  485.             if($value[1]){
  486.                 preg_match_all('/\$([a-zA-Z0-9_]+)/'$value[0]$varMatchesPREG_OFFSET_CAPTURE );
  487.                 $parameters $method->getParameters();
  488.                 if (count($varMatches[0])) {
  489.                     $result '';
  490.                     $len 0;
  491.                     foreach($varMatches[1as $k=>$var{
  492.                         $result .= substr($value[0]$len$len+$varMatches[0][$k][1]);
  493.                         $len += $varMatches[0][$k][1strlen($varMatches[0][$k][0]);
  494.                         if (in_array($var[0]$parameters)) {
  495.                             $result .= '\'.'.$this->_preparePHPExpr($varMatches[0][$k][0]$updatefields[$propname],true).'.\'';
  496.                         }
  497.                         else {
  498.                             $result .= $varMatches[0][$k][0];
  499.                         }
  500.                     }
  501.                     $value[0$result;
  502.                 }
  503.                 $sqlSet.= ', '.$this->_encloseName($updatefields[$propname]->fieldName)'= '$value[0];
  504.             }else{
  505.                 $sqlSet.= ', '.$this->_encloseName($updatefields[$propname]->fieldName)'= '.
  506.                     $this->tools->escapeValue($updatefields[$propname]->unifiedType$value[0]falsetrue);
  507.             }
  508.         }
  509.         $src[substr($sqlSet,1).'\';';
  510.         $cond $method->getConditions();
  511.         if($cond !== null{
  512.             $sqlCond $this->buildConditions($cond$primaryFields$method->getParameters()false);
  513.             if(trim($sqlCond!= '')
  514.                 $src['$__query .=\' WHERE '.$sqlCond."';";
  515.         }
  516.     }
  517.  
  518.     /**
  519.      *
  520.      */
  521.     protected function buildCountUserQuery($method&$src&$allField{
  522.         if ($method->distinct !=''{
  523.             $properties $this->_dataParser->getProperties ();
  524.             $tables $this->_dataParser->getTables();
  525.             $prop $properties[$method->distinct];
  526.             $count=' DISTINCT '.$this->_encloseName($tables[$prop->table]['name'].'.'.$this->_encloseName($prop->fieldName);
  527.         }
  528.         else {
  529.             $count='*';
  530.         }
  531.         $src['    $__query = \'SELECT COUNT('.$count.') as c \'.$this->_fromClause.$this->_whereClause;';
  532.         $glueCondition ($this->sqlWhereClause !='' ' AND ':' WHERE ');
  533.  
  534.         $cond $method->getConditions();
  535.         if($cond !== null{
  536.             $sqlCond $this->buildConditions($cond$allField$method->getParameters()true);
  537.             if(trim($sqlCond!= '')
  538.                 $src['$__query .=\''.$glueCondition.$sqlCond."';";
  539.         }
  540.     }
  541.  
  542.     /**
  543.      *
  544.      */
  545.     protected function buildSelectUserQuery($method&$src&$allField{
  546.         $limit '';
  547.         if($method->distinct !=''){
  548.             $select '\''.$this->buildSelectClause($method->distinct).'\'';
  549.         }
  550.         else{
  551.             $select=' $this->_selectClause';
  552.         }
  553.         $src['    $__query = '.$select.'.$this->_fromClause.$this->_whereClause;';
  554.         $glueCondition ($this->sqlWhereClause !='' ' AND ':' WHERE ');
  555.         if$method->type == 'select' && ($lim $method->getLimit ()) !==null){
  556.             $limit =', '.$lim['offset'].', '.$lim['count'];
  557.         }
  558.  
  559.         $sqlCond $this->buildConditions($method->getConditions()$allField$method->getParameters()true$method->getGroupBy());
  560.  
  561.         if(trim($sqlCond!= '')
  562.             $src['$__query .=\''.$glueCondition.$sqlCond."';";
  563.  
  564.         return $limit;
  565.     }
  566.  
  567.  
  568.     /**
  569.     * create FROM clause and WHERE clause for all SELECT query
  570.     */
  571.     protected function buildFromWhereClause(){
  572.  
  573.         $tables $this->_dataParser->getTables();
  574.  
  575.         foreach($tables as $table_name => $table){
  576.             $tables[$table_name]['realname''\'.$this->_conn->prefixTable(\''.$table['realname'].'\').\'';
  577.         }
  578.  
  579.         $primarytable $tables[$this->_dataParser->getPrimaryTable()];
  580.         $ptrealname $this->_encloseName($primarytable['realname']);
  581.         $ptname $this->_encloseName($primarytable['name']);
  582.  
  583.         list($sqlFrom$sqlWhere$this->buildOuterJoins($tables$ptname);
  584.  
  585.         $sqlFrom =$ptrealname.$this->aliasWord.$ptname.$sqlFrom;
  586.  
  587.         foreach($this->_dataParser->getInnerJoins(as $tablejoin){
  588.             $table$tables[$tablejoin];
  589.             $tablename $this->_encloseName($table['name']);
  590.             $sqlFrom .=', '.$this->_encloseName($table['realname']).$this->aliasWord.$tablename;
  591.  
  592.             foreach($table['fk'as $k => $fk){
  593.                 $sqlWhere.=' AND '.$ptname.'.'.$this->_encloseName($fk).'='.$tablename.'.'.$this->_encloseName($table['pk'][$k]);
  594.             }
  595.         }
  596.  
  597.         $this->sqlWhereClause = ($sqlWhere !='' ' WHERE '.substr($sqlWhere,4:'');
  598.         $this->sqlFromClause = ' FROM '.$sqlFrom;
  599.     }
  600.  
  601.     /**
  602.      * generates the part of the FROM clause for outer joins
  603.      * @return array  [0]=> the part of the FROM clause, [1]=> the part to add to the WHERE clause when needed
  604.      */
  605.     protected function buildOuterJoins(&$tables$primaryTableName){
  606.         $sqlFrom '';
  607.         foreach($this->_dataParser->getOuterJoins(as $tablejoin){
  608.             $table$tables[$tablejoin[0]];
  609.             $tablename $this->_encloseName($table['name']);
  610.  
  611.             $r =$this->_encloseName($table['realname']).$this->aliasWord.$tablename;
  612.  
  613.             $fieldjoin='';
  614.             foreach($table['fk'as $k => $fk){
  615.                 $fieldjoin.=' AND '.$primaryTableName.'.'.$this->_encloseName($fk).'='.$tablename.'.'.$this->_encloseName($table['pk'][$k]);
  616.             }
  617.             $fieldjoin=substr($fieldjoin,4);
  618.  
  619.             if($tablejoin[1== 0){
  620.                 $sqlFrom.=' LEFT JOIN '.$r.' ON ('.$fieldjoin.')';
  621.             }elseif($tablejoin[1== 1){
  622.                 $sqlFrom.=' RIGHT JOIN '.$r.' ON ('.$fieldjoin.')';
  623.             }
  624.         }
  625.         return array($sqlFrom'');
  626.     }
  627.  
  628.     /**
  629.     * build a SELECT clause for all SELECT queries
  630.     * @return string the select clause.
  631.     */
  632.     protected function buildSelectClause ($distinct=false){
  633.         $result array();
  634.  
  635.         $tables $this->_dataParser->getTables();
  636.         foreach ($this->_dataParser->getProperties (as $id=>$prop){
  637.  
  638.             $table $this->_encloseName($tables[$prop->table]['name'].'.';
  639.  
  640.             if ($prop->selectPattern !=''){
  641.                 $result[]$this->buildSelectPattern($prop->selectPattern$table$prop->fieldName$prop->name);
  642.             }
  643.         }
  644.  
  645.         return 'SELECT '.($distinct?'DISTINCT ':'').(implode (', ',$result));
  646.     }
  647.  
  648.     /**
  649.      * build an item for the select clause
  650.     */
  651.     protected function buildSelectPattern ($pattern$table$fieldname$propname ){
  652.         if ($pattern =='%s'){
  653.             $field $table.$this->_encloseName($fieldname);
  654.             if ($fieldname != $propname){
  655.                 $field .= ' as '.$this->_encloseName($propname);    
  656.             }
  657.         }else{
  658.             $field str_replace(array("'""%s")array("\\'",$table.$this->_encloseName($fieldname)),$pattern).' as '.$this->_encloseName($propname);
  659.         }
  660.         return $field;
  661.     }
  662.  
  663.     protected function buildEndOfClass({
  664.         return '';
  665.     }
  666.  
  667.     /**
  668.     * format field names with start, end and between strings.
  669.     *   will write the field named info.
  670.     *   eg info == name
  671.     *   echo $field->name
  672.     * @param string   $info    property to get from objects in $using
  673.     * @param string   $start   string to add before the info
  674.     * @param string   $end     string to add after the info
  675.     * @param string   $beetween string to add between each info
  676.     * @param array    $using     list of CopixPropertiesForDAO object. if null, get default fields list
  677.     * @see  jDaoProperty
  678.     */
  679.     protected function _writeFieldsInfoWith ($info$start ''$end=''$beetween ''$using null){
  680.         $result array();
  681.         if ($using === null){
  682.             //if no fields are provided, using _dataParser's as default.
  683.             $using $this->_dataParser->getProperties ();
  684.         }
  685.  
  686.         foreach ($using as $id=>$field){
  687.             $result[$start $field->$info $end;
  688.         }
  689.  
  690.         return implode ($beetween,$result);
  691.     }
  692.  
  693.     /**
  694.     * format field names with start, end and between strings.
  695.     */
  696.     protected function _writeFieldNamesWith ($start ''$end=''$beetween ''$using null){
  697.         return $this->_writeFieldsInfoWith ('name'$start$end$beetween$using);
  698.     }
  699.  
  700.     protected function _getPrimaryFieldsList({
  701.         $tables            $this->_dataParser->getTables();
  702.         $pkFields          array();
  703.  
  704.         $primTable $tables[$this->_dataParser->getPrimaryTable()];
  705.         $props  $this->_dataParser->getProperties();
  706.         // we want to have primary keys as the same order indicated into primarykey attr
  707.         foreach($primTable['pk'as $pkname{
  708.             foreach($primTable['fields'as $f){
  709.                 if ($props[$f]->fieldName == $pkname{
  710.                     $pkFields[$props[$f]->name$props[$f];
  711.                     break;
  712.                 }
  713.             }
  714.         }
  715.         return $pkFields;
  716.     }
  717.  
  718.     /**
  719.     * gets fields that match a condition returned by the $captureMethod
  720.     * @internal
  721.     */
  722.     protected function _getPropertiesBy ($captureMethod){
  723.         $captureMethod '_capture'.$captureMethod;
  724.         $result array ();
  725.  
  726.         foreach ($this->_dataParser->getProperties(as $field){
  727.             if $this->$captureMethod($field)){
  728.                 $result[$field->name$field;
  729.             }
  730.         }
  731.         return $result;
  732.     }
  733.  
  734.     protected function _capturePrimaryFieldsExcludeAutoIncrement(&$field){
  735.         return ($field->table == $this->_dataParser->getPrimaryTable(&& !$field->autoIncrement);
  736.     }
  737.  
  738.     protected function _capturePrimaryFieldsExcludePk(&$field){
  739.         return ($field->table == $this->_dataParser->getPrimaryTable()) && !$field->isPK;
  740.     }
  741.  
  742.     protected function _capturePrimaryTable(&$field){
  743.         return ($field->table == $this->_dataParser->getPrimaryTable());
  744.     }
  745.  
  746.     protected function _captureAll(&$field){
  747.         return true;
  748.     }
  749.  
  750.     protected function _captureFieldToUpdate(&$field){
  751.         return ($field->table == $this->_dataParser->getPrimaryTable()
  752.                 && !$field->isPK
  753.                 && !$field->isFK
  754.                 && $field->autoIncrement || ($field->insertPattern != '%s' && $field->selectPattern != '')));
  755.     }
  756.  
  757.     protected function _captureFieldToUpdateOnUpdate(&$field){
  758.         return ($field->table == $this->_dataParser->getPrimaryTable()
  759.                 && !$field->isPK
  760.                 && !$field->isFK
  761.                 && $field->autoIncrement || ($field->updatePattern != '%s' && $field->selectPattern != '')));
  762.     }
  763.  
  764.     protected function _captureBinaryField(&$field{
  765.         return ($field->unifiedType == 'binary' || $field->unifiedType == 'varbinary');
  766.     }
  767.  
  768.     /**
  769.     * get autoincrement PK field
  770.     */
  771.     protected function getAutoIncrementPKField ($using null){
  772.         if ($using === null){
  773.             $using $this->_dataParser->getProperties ();
  774.         }
  775.  
  776.         $tb $this->_dataParser->getTables();
  777.         $tb $tb[$this->_dataParser->getPrimaryTable()]['realname'];
  778.  
  779.         foreach ($using as $id=>$field{
  780.             if(!$field->isPK)
  781.                 continue;
  782.             if ($field->autoIncrement{
  783.                 return $field;
  784.             }
  785.         }
  786.         return null;
  787.     }
  788.  
  789.     /**
  790.      * build a WHERE clause with conditions on given properties : conditions are
  791.      * equality between a variable and the field.
  792.      * the variable name is the name of the property, made with an optional prefix
  793.      * given in $fieldPrefix parameter.
  794.      * This method is called to generate WHERE clause for primary keys.
  795.      * @param array $fields  list of jDaoPropery objects
  796.      * @param string $fieldPrefix  an optional prefix to prefix variable names
  797.      * @param boolean $forSelect  if true, the table name or table alias will prefix
  798.      *                             the field name in the query
  799.      * @return string the WHERE clause (without the WHERE keyword)
  800.      * @internal
  801.      */
  802.     protected function buildSimpleConditions (&$fields$fieldPrefix=''$forSelect=true){
  803.         $r ' ';
  804.  
  805.         $first true;
  806.         foreach($fields as $field){
  807.             if (!$first){
  808.                 $r .= ' AND ';
  809.             }else{
  810.                 $first false;
  811.             }
  812.  
  813.             if($forSelect){
  814.                 $condition $this->_encloseName($field->table).'.'.$this->_encloseName($field->fieldName);
  815.             }else{
  816.                 $condition $this->_encloseName($field->fieldName);
  817.             }
  818.  
  819.             $var '$'.$fieldPrefix.$field->name;
  820.             $value $this->_preparePHPExpr($var$field!$field->requiredInConditions'=');
  821.  
  822.             $r .= $condition.'\'.'.$value.'.\'';
  823.         }
  824.  
  825.         return $r;
  826.     }
  827.  
  828.  
  829.     protected function _prepareValues ($fieldList$pattern=''$prefixfield=''){
  830.         $values $fields array();
  831.  
  832.         foreach ((array)$fieldList as $fieldName=>$field{
  833.             if ($pattern != '' && $field->$pattern == ''{
  834.                 continue;
  835.             }
  836.  
  837.             $value $this->_preparePHPExpr('$'.$prefixfield.$fieldName$fieldtrue);
  838.  
  839.             if($pattern != ''){
  840.                 if(strpos($field->$pattern"'"!== false && strpos($field->$pattern"\\'"=== false{
  841.                     $values[$field->namesprintf(str_replace("'""\\'"$field->$pattern),'\'.'.$value.'.\'');
  842.                 else {
  843.                     $values[$field->namesprintf($field->$pattern,'\'.'.$value.'.\'');
  844.                 }
  845.             }else{
  846.                 $values[$field->name'\'.'.$value.'.\'';
  847.             }
  848.  
  849.             $fields[$field->name$this->_encloseName($field->fieldName);
  850.         }
  851.         return array($fields$values);
  852.     }
  853.  
  854.  
  855.     /**
  856.      * build 'where' clause from conditions declared with condition tag in a user method
  857.      * @param jDaoConditions $cond the condition object which contains conditions data
  858.      * @param array $fields  array of jDaoProperty
  859.      * @param array $params  list of parameters name of the method
  860.      * @param boolean $withPrefix true if the field name should be preceded by the table name/table alias
  861.      * @param array $groupby  list of properties to use in a groupby
  862.      * @return string a WHERE clause (without the WHERE keyword) with eventually an ORDER clause
  863.      * @internal
  864.      */
  865.     protected function buildConditions ($cond$fields$params=array()$withPrefix=true$groupby=null){
  866.         if($cond)
  867.             $sql $this->buildOneSQLCondition ($cond->condition$fields$params$withPrefixtrue);
  868.         else
  869.             $sql '';
  870.  
  871.         if($groupby && count($groupby)) {
  872.             if(trim($sql==''{
  873.                 $sql ' 1=1 ';
  874.             }
  875.             foreach($groupby as $k=>$f{
  876.                 if ($withPrefix)
  877.                     $groupby[$k]$this->_encloseName($fields[$f]->table).'.'.$this->_encloseName($fields[$f]->fieldName);
  878.                 else
  879.                     $groupby[$k]$this->_encloseName($fields[$f]->fieldName);
  880.             }
  881.             $sql .= ' GROUP BY '.implode (', '$groupby);
  882.         }
  883.  
  884.         $order array ();
  885.         foreach ($cond->order as $name => $way){
  886.             $ord='';
  887.             if (isset($fields[$name])){
  888.                 if ($withPrefix)
  889.                     $ord $this->_encloseName($fields[$name]->table).'.'.$this->_encloseName($fields[$name]->fieldName);
  890.                 else
  891.                     $ord $this->_encloseName($fields[$name]->fieldName);
  892.             }elseif($name[0== '$'){
  893.                 $ord '\'.'.$name.'.\'';
  894.             }else{
  895.                 continue;
  896.             }
  897.             if($way[0== '$'){
  898.                 $order[]=$ord.' \'.( strtolower('.$way.') ==\'asc\'?\'asc\':\'desc\').\'';
  899.             }else{
  900.                 $order[]=$ord.' '.$way;
  901.             }
  902.         }
  903.         if(count ($order0){
  904.             if(trim($sql==''{
  905.                 $sql ' 1=1 ';
  906.             }
  907.             $sql.=' ORDER BY '.implode (', '$order);
  908.         }
  909.         return $sql;
  910.     }
  911.  
  912.     /**
  913.      * build a condition for the SQL WHERE clause.
  914.      * this method call itself recursively.
  915.      * @param jDaoCondition $cond a condition object which contains conditions data
  916.      * @param array $fields  array of jDaoProperty
  917.      * @param array $params  list of parameters name of the method
  918.      * @param boolean $withPrefix true if the field name should be preceded by the table name/table alias
  919.      * @param boolean $principal  should be true for the first call, and false for recursive call
  920.      * @return string a WHERE clause (without the WHERE keyword)
  921.      * @see jDaoGenerator::buildConditions
  922.      * @internal
  923.      */
  924.     protected function buildOneSQLCondition ($condition$fields$params$withPrefix$principal=false){
  925.  
  926.         $r ' ';
  927.  
  928.         //direct conditions for the group
  929.         $first true;
  930.         foreach ($condition->conditions as $cond){
  931.             if (!$first){
  932.                 $r .= ' '.$condition->glueOp.' ';
  933.             }
  934.             $first false;
  935.  
  936.             $prop $fields[$cond['field_id']];
  937.  
  938.             if($withPrefix){
  939.                 $f $this->_encloseName($prop->table).'.'.$this->_encloseName($prop->fieldName);
  940.             }else{
  941.                 $f $this->_encloseName($prop->fieldName);
  942.             }
  943.  
  944.             $r .= $f.' ';
  945.  
  946.             if($cond['operator'== 'IN' || $cond['operator'== 'NOT IN'){
  947.                 if($cond['isExpr']){
  948.                     $phpexpr $this->_preparePHPCallbackExpr($prop);
  949.                     $phpvalue 'implode(\',\', array_map( '.$phpexpr.', '.$cond['value'].'))';
  950.                     $value'(\'.'.$phpvalue.'.\')';
  951.                 }else{
  952.                     $value'('.str_replace("'""\\'"$cond['value']).')';
  953.                 }
  954.                 $r.=$cond['operator'].' '.$value;
  955.             }elseif($cond['operator'== 'IS NULL' || $cond['operator'== 'IS NOT NULL'){
  956.                 $r.=$cond['operator'].' ';
  957.             }else{
  958.                 if($cond['isExpr']){
  959.                     $value=str_replace("'","\\'",$cond['value']);
  960.                     // we need to know if the expression is like "$foo" (1) or a thing like "concat($foo,'bla')" (2)
  961.                     // because of the nullability of the parameter. If the value of the parameter is null and the operator
  962.                     // is = or <>, then we need to generate a thing like :
  963.                     // - in case 1: ($foo === null ? 'IS NULL' : '='.$this->_conn->quote($foo))
  964.                     // - in case 2: '= concat('.($foo === null ? 'NULL' : $this->_conn->quote($foo)).' ,\'bla\')'
  965.                     if($value[0== '$'){
  966.                         $value '\'.'.$this->_preparePHPExpr($value$prop!$prop->requiredInConditions,$cond['operator']).'.\'';
  967.                     }else{
  968.                         foreach($params as $param){
  969.                             $value str_replace('$'.$param'\'.'.$this->_preparePHPExpr('$'.$param$prop!$prop->requiredInConditions).'.\'',$value);
  970.                         }
  971.                         $value $cond['operator'].' '.$value;
  972.                     }
  973.                 else {
  974.                     $value $cond['operator'].' ';
  975.                     if ($cond['operator'== 'LIKE' || $cond['operator'== 'NOT LIKE'{
  976.                         $value .= $this->tools->escapeValue('varchar'$cond['value']falsetrue);
  977.                     else {
  978.                         $value .= $this->tools->escapeValue($prop->unifiedType$cond['value']falsetrue);
  979.                     }
  980.                 }
  981.                 $r.=$value;
  982.             }
  983.         }
  984.         //sub conditions
  985.         foreach ($condition->group as $conditionDetail){
  986.             if (!$first){
  987.                 $r .= ' '.$condition->glueOp.' ';
  988.             }
  989.             $r .= $this->buildOneSQLCondition($conditionDetail$fields$params$withPrefix);
  990.             $first=false;
  991.         }
  992.  
  993.         //adds parenthesis around the sql if needed (non empty)
  994.         if (strlen (trim ($r)) && (!$principal ||($principal && $condition->glueOp != 'AND'))){
  995.             $r '('.$r.')';
  996.         }
  997.         return $r;
  998.     }
  999.  
  1000.     protected function _preparePHPExpr($expr$field$checknull=true$forCondition=''){
  1001.         $opnull $opval '';
  1002.         if($checknull && $forCondition != ''){
  1003.             if($forCondition == '=')
  1004.                 $opnull 'IS ';
  1005.             elseif($forCondition == '<>')
  1006.                 $opnull 'IS NOT ';
  1007.             else
  1008.                 $checknull=false;
  1009.         }
  1010.         $type '';
  1011.         if ($forCondition != 'LIKE' && $forCondition != 'NOT LIKE')
  1012.             $type strtolower($field->unifiedType);
  1013.  
  1014.         if ($forCondition != ''{
  1015.             $forCondition '\' '.$forCondition.' \'.'// spaces for operators like LIKE
  1016.         }
  1017.  
  1018.         switch($type){
  1019.             case 'integer':
  1020.                 if($checknull){
  1021.                     $expr'('.$expr.' === null ? \''.$opnull.'NULL\' : '.$forCondition.'intval('.$expr.'))';
  1022.                 }else{
  1023.                     $expr$forCondition.'intval('.$expr.')';
  1024.                 }
  1025.                 break;
  1026.             case 'double':
  1027.             case 'float':
  1028.             case 'numeric':
  1029.             case 'decimal':
  1030.                 if($checknull){
  1031.                     $expr='('.$expr.' === null ? \''.$opnull.'NULL\' : '.$forCondition.'jDb::floatToStr('.$expr.'))';
  1032.                 }else{
  1033.                     $expr=$forCondition.'jDb::floatToStr('.$expr.')';
  1034.                 }
  1035.                 break;
  1036.             case 'boolean':
  1037.                 if($checknull){
  1038.                     $expr'('.$expr.' === null ? \''.$opnull.'NULL\' : '.$forCondition.'$this->_prepareValue('.$expr.', "boolean", true))';
  1039.                 }else{
  1040.                     $expr$forCondition.'$this->_prepareValue('.$expr.', "boolean", true)';
  1041.                 }
  1042.                 break;
  1043.             default:
  1044.                 if ($type=='varbinary' || $type=='binary')
  1045.                     $qparam ',true';
  1046.                 else
  1047.                     $qparam '';
  1048.  
  1049.                 if ($checknull{
  1050.                    $expr '('.$expr.' === null ? \''.$opnull.'NULL\' : '.$forCondition.'$this->_conn->quote2('.$expr.',false'.$qparam.'))';
  1051.                 }
  1052.                 else {
  1053.                    $expr $forCondition.'$this->_conn->quote'.($qparam?'2('.$expr.',true,true)':'('.$expr.')');
  1054.                 }
  1055.         }
  1056.         return $expr;
  1057.     }
  1058.  
  1059.     protected function _preparePHPCallbackExpr($field){
  1060.         $type strtolower($field->unifiedType);
  1061.         // TODO PHP53: generate a closure instead of create_function
  1062.         switch($type){
  1063.             case 'integer':
  1064.                 return 'create_function(\'$__e\',\'return intval($__e);\')';
  1065.             case 'double':
  1066.             case 'float':
  1067.             case 'numeric':
  1068.             case 'decimal':
  1069.                 return 'create_function(\'$__e\',\'return jDb::floatToStr($__e);\')';
  1070.             case 'boolean':
  1071.                 return 'array($this, \'_callbackBool\')';
  1072.             default:
  1073.                 if ($type=='varbinary' || $type=='binary')
  1074.                     return 'array($this, \'_callbackQuoteBin\')';
  1075.                 else
  1076.                     return 'array($this, \'_callbackQuote\')';
  1077.         }
  1078.     }
  1079.  
  1080.     protected function _encloseName($name){
  1081.         return $this->tools->encloseName($name);
  1082.     }
  1083.  
  1084.     protected function buildUpdateAutoIncrementPK($pkai{
  1085.         return '       $record->'.$pkai->name.'= $this->_conn->lastInsertId();';
  1086.     }
  1087. }

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