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

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