Alpha Framework alpha--model
[ class tree: alpha--model ] [ index: alpha--model ] [ all elements ]

Source for file AlphaDAOProviderMySQL.inc

Documentation is available at AlphaDAOProviderMySQL.inc

  1. <?php
  2.  
  3. require_once $config->get('sysRoot').'alpha/model/AlphaDAOProviderInterface.inc';
  4.  
  5. /**
  6.  * MySQL DAO provider (uses the MySQLi native API in PHP).
  7.  * 
  8.  * @package alpha::model
  9.  * @since 1.1
  10.  * @author John Collins <dev@alphaframework.org>
  11.  * @version $Id: AlphaDAOProviderMySQL.inc 1453 2011-12-04 15:12:54Z johnc $
  12.  * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
  13.  * @copyright Copyright (c) 2011, John Collins (founder of Alpha Framework).
  14.  *  All rights reserved.
  15.  * 
  16.  *  <pre>
  17.  *  Redistribution and use in source and binary forms, with or
  18.  *  without modification, are permitted provided that the
  19.  *  following conditions are met:
  20.  * 
  21.  *  * Redistributions of source code must retain the above
  22.  *    copyright notice, this list of conditions and the
  23.  *    following disclaimer.
  24.  *  * Redistributions in binary form must reproduce the above
  25.  *    copyright notice, this list of conditions and the
  26.  *    following disclaimer in the documentation and/or other
  27.  *    materials provided with the distribution.
  28.  *  * Neither the name of the Alpha Framework nor the names
  29.  *    of its contributors may be used to endorse or promote
  30.  *    products derived from this software without specific
  31.  *    prior written permission.
  32.  *   
  33.  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  34.  *  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  35.  *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  36.  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  37.  *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  38.  *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  39.  *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  40.  *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  41.  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  42.  *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  43.  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
  44.  *  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  45.  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  46.  *  </pre>
  47.  *  
  48.  */
  49. class AlphaDAOProviderMySQL implements AlphaDAOProviderInterface {
  50.     /**
  51.      * Trace logger
  52.      * 
  53.      * @var Logger 
  54.      * @since 1.1
  55.      */
  56.     private static $logger null;
  57.     
  58.     /**
  59.      * Datebase connection
  60.      * 
  61.      * @var mysqli 
  62.      * @since 1.1
  63.      */
  64.     private static $connection;
  65.     
  66.     /**
  67.      * The business object that we are mapping back to
  68.      * 
  69.      * @var AlphaDAO 
  70.      * @since 1.1
  71.      */
  72.     private $BO;
  73.     
  74.     /**
  75.      * The constructor
  76.      * 
  77.      * @since 1.1
  78.      */
  79.     public function __construct({
  80.         self::$logger new Logger('AlphaDAOProviderMySQL');
  81.         self::$logger->debug('>>__construct()');
  82.         
  83.         self::$logger->debug('<<__construct');
  84.     }
  85.     
  86.     /**
  87.      * (non-PHPdoc)
  88.      * @see alpha/model/AlphaDAOProviderInterface::getConnection()
  89.      */
  90.     public static function getConnection({
  91.         global $config;
  92.         
  93.         if (!isset(self::$connection)) {
  94.             self::$connection new mysqli($config->get('sysDBHost')$config->get('sysDBUsername')$config->get('sysDBPassword')$config->get('sysDB'));
  95.                 
  96.             if (mysqli_connect_error()) {
  97.                 self::$logger->fatal('Could not connect to database: ['.mysqli_connect_errno().'] '.mysqli_connect_error());
  98.             }
  99.         }
  100.         
  101.         return self::$connection;
  102.     }
  103.     
  104.     /**
  105.      * (non-PHPdoc)
  106.      * @see alpha/model/AlphaDAOProviderInterface::disconnect()
  107.      */
  108.     public static function disconnect({
  109.         if (isset(self::$connection)) {
  110.             self::$connection->close();
  111.             self::$connection null;
  112.         }
  113.     }
  114.     
  115.     /**
  116.      * (non-PHPdoc)
  117.      * @see alpha/model/AlphaDAOProviderInterface::getLastDatabaseError()
  118.      */
  119.     public static function getLastDatabaseError({
  120.         return self::getConnection()->error;
  121.     }
  122.     
  123.     /**
  124.      * (non-PHPdoc)
  125.      * @see alpha/model/AlphaDAOProviderInterface::query()
  126.      */
  127.     public function query($sqlQuery{
  128.         $this->BO->setLastQuery($sqlQuery);
  129.         
  130.         $resultArray array();
  131.         
  132.         if(!$result self::getConnection()->query($sqlQuery)) {
  133.             throw new CustomQueryException('Failed to run the custom query, MySql error is ['.self::getConnection()->error.'], query ['.$sqlQuery.']');
  134.             return array();
  135.         }else{
  136.             while($row $result->fetch_array(MYSQLI_ASSOC)) {            
  137.                 array_push($resultArray$row);
  138.             }
  139.                 
  140.             return $resultArray;
  141.         }
  142.     }
  143.     
  144.     /**
  145.      * (non-PHPdoc)
  146.      * @see alpha/model/AlphaDAOProviderInterface::load()
  147.      */
  148.     public function load($OID{
  149.         self::$logger->debug('>>load(OID=['.$OID.'])');
  150.  
  151.         global $config;
  152.         
  153.         $attributes $this->BO->getPersistentAttributes();
  154.         $fields '';
  155.         foreach($attributes as $att)
  156.             $fields .= $att.',';
  157.         $fields substr($fields0-1);
  158.             
  159.         $sqlQuery 'SELECT '.$fields.' FROM '.$this->BO->getTableName().' WHERE OID = ? LIMIT 1;';
  160.         $this->BO->setLastQuery($sqlQuery);
  161.         $stmt self::getConnection()->stmt_init();
  162.     
  163.         $row array();
  164.             
  165.         if($stmt->prepare($sqlQuery)) {
  166.             $stmt->bind_param('i'$OID);
  167.             $stmt->execute();
  168.                 
  169.             $result $this->bindResult($stmt);
  170.             if(isset($result[0]))
  171.                 $row $result[0];
  172.                 
  173.             $stmt->close();
  174.         }else{
  175.             self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
  176.             if(!$this->BO->checkTableExists()) {
  177.                 $this->BO->makeTable();
  178.                 throw new BONotFoundException('Failed to load object of OID ['.$OID.'], table ['.$this->BO->getTableName().'] did not exist so had to create!');
  179.             }
  180.             return;
  181.         }
  182.             
  183.         if(!isset($row['OID']|| $row['OID'1{
  184.             throw new BONotFoundException('Failed to load object of OID ['.$OID.'] not found in database.');            
  185.             self::$logger->debug('<<load');
  186.             return;
  187.         }
  188.             
  189.         // get the class attributes
  190.         $reflection new ReflectionClass(get_class($this->BO));
  191.         $properties $reflection->getProperties();
  192.     
  193.         try {
  194.             foreach($properties as $propObj{
  195.                 $propName $propObj->name;
  196.                                                 
  197.                 // filter transient attributes
  198.                 if(!in_array($propName$this->BO->getTransientAttributes())) {
  199.                     $this->BO->set($propName$row[$propName]);
  200.                 }elseif(!$propObj->isPrivate(&& $this->BO->getPropObject($propNameinstanceof Relation{
  201.                     $prop $this->BO->getPropObject($propName);
  202.                         
  203.                     // handle the setting of ONE-TO-MANY relation values
  204.                     if($prop->getRelationType(== 'ONE-TO-MANY'{
  205.                         $this->BO->set($propObj->name$this->BO->getOID());
  206.                     }
  207.                 }
  208.             }
  209.         }catch (IllegalArguementException $e{
  210.             self::$logger->warn('Bad data stored in the table ['.$this->BO->getTableName().'], field ['.$propObj->name.'] bad value['.$row[$propObj->name].'], exception ['.$e->getMessage().']');
  211.         }catch (PHPException $e{
  212.             // it is possible that the load failed due to the table not being up-to-date
  213.             if($this->BO->checkTableNeedsUpdate()) {                
  214.                 $missingFields $this->BO->findMissingFields();
  215.                 
  216.                 $count count($missingFields);
  217.                     
  218.                 for($i 0$i $count$i++)
  219.                     $this->BO->addProperty($missingFields[$i]);
  220.                         
  221.                 throw new BONotFoundException('Failed to load object of OID ['.$OID.'], table ['.$this->BO->getTableName().'] was out of sync with the database so had to be updated!');
  222.                 self::$logger->debug('<<load');
  223.                 return;
  224.             }
  225.         }
  226.         
  227.         self::$logger->debug('<<load');
  228.     }
  229.     
  230.     /**
  231.      * (non-PHPdoc)
  232.      * @see alpha/model/AlphaDAOProviderInterface::loadByAttribute()
  233.      */
  234.     public function loadByAttribute($attribute$value$ignoreClassType=false$loadAttributes=array()) {
  235.         self::$logger->debug('>>loadByAttribute(attribute=['.$attribute.'], value=['.$value.'], ignoreClassType=['.$ignoreClassType.'], 
  236.             loadAttributes=['.var_export($loadAttributestrue).'])');
  237.         
  238.         if(count($loadAttributes== 0)
  239.             $attributes $this->BO->getPersistentAttributes();
  240.         else
  241.             $attributes $loadAttributes;
  242.         
  243.         $fields '';
  244.         foreach($attributes as $att)
  245.             $fields .= $att.',';
  246.         $fields substr($fields0-1);
  247.         
  248.         if(!$ignoreClassType && $this->BO->isTableOverloaded())
  249.             $sqlQuery 'SELECT '.$fields.' FROM '.$this->BO->getTableName().' WHERE '.$attribute.' = ? AND classname = ? LIMIT 1;';
  250.         else
  251.             $sqlQuery 'SELECT '.$fields.' FROM '.$this->BO->getTableName().' WHERE '.$attribute.' = ? LIMIT 1;';
  252.         
  253.         self::$logger->debug('Query=['.$sqlQuery.']');
  254.         
  255.         $this->BO->setLastQuery($sqlQuery);
  256.         $stmt self::getConnection()->stmt_init();
  257.  
  258.         $row array();
  259.         
  260.         if($stmt->prepare($sqlQuery)) {
  261.             if($this->BO->getPropObject($attributeinstanceof Integer{
  262.                 if(!$ignoreClassType && $this->BO->isTableOverloaded()) {
  263.                     $stmt->bind_param('is'$valueget_class($this->BO));
  264.                 }else{
  265.                     $stmt->bind_param('i'$value);
  266.                 }
  267.             }else{
  268.                 if(!$ignoreClassType && $this->BO->isTableOverloaded()) {
  269.                     $stmt->bind_param('ss'$valueget_class($this->BO));
  270.                 }else{
  271.                     $stmt->bind_param('s'$value);
  272.                 }
  273.             }
  274.             
  275.             $stmt->execute();
  276.             
  277.             $result $this->bindResult($stmt);
  278.             
  279.             if(isset($result[0]))
  280.                 $row $result[0];
  281.                 
  282.             $stmt->close();
  283.         }else{
  284.             self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
  285.             if(!$this->BO->checkTableExists()) {
  286.                 $this->BO->makeTable();
  287.                 throw new BONotFoundException('Failed to load object by attribute ['.$attribute.'] and value ['.$value.'], table did not exist so had to create!');
  288.             }
  289.             return;
  290.         }
  291.         
  292.         if(!isset($row['OID']|| $row['OID'1{
  293.             throw new BONotFoundException('Failed to load object by attribute ['.$attribute.'] and value ['.$value.'], not found in database.');            
  294.             self::$logger->debug('<<loadByAttribute');
  295.             return;
  296.         }
  297.         
  298.         $this->OID $row['OID'];
  299.         
  300.         // get the class attributes
  301.         $reflection new ReflectionClass(get_class($this->BO));
  302.         $properties $reflection->getProperties();
  303.  
  304.         try {
  305.             foreach($properties as $propObj{
  306.                 $propName $propObj->name;
  307.  
  308.                 if(isset($row[$propName])) {
  309.                     // filter transient attributes
  310.                     if(!in_array($propName$this->BO->getTransientAttributes())) {
  311.                         $this->BO->set($propName$row[$propName]);
  312.                     }elseif(!$propObj->isPrivate(&& $this->BO->get($propName!= '' && $this->BO->getPropObject($propNameinstanceof Relation{
  313.                         $prop $this->BO->getPropObject($propName);
  314.                         
  315.                         // handle the setting of ONE-TO-MANY relation values
  316.                         if($prop->getRelationType(== 'ONE-TO-MANY'{
  317.                             $this->BO->set($propObj->name$this->BO->getOID());
  318.                         }
  319.                     }
  320.                 }
  321.             }
  322.         }catch (IllegalArguementException $e{
  323.             self::$logger->warn('Bad data stored in the table ['.$this->BO->getTableName().'], field ['.$propObj->name.'] bad value['.$row[$propObj->name].'], exception ['.$e->getMessage().']');
  324.         }catch (PHPException $e{
  325.             // it is possible that the load failed due to the table not being up-to-date
  326.             if($this->BO->checkTableNeedsUpdate()) {                
  327.                 $missingFields $this->BO->findMissingFields();
  328.             
  329.                 $count count($missingFields);
  330.                 
  331.                 for($i 0$i $count$i++)
  332.                     $this->BO->addProperty($missingFields[$i]);
  333.                     
  334.                 throw new BONotFoundException('Failed to load object by attribute ['.$attribute.'] and value ['.$value.'], table ['.$this->BO->getTableName().'] was out of sync with the database so had to be updated!');
  335.                 self::$logger->debug('<<loadByAttribute');
  336.                 return;
  337.             }
  338.         }
  339.         
  340.         self::$logger->debug('<<loadByAttribute');
  341.     }
  342.     
  343.     /**
  344.      * (non-PHPdoc)
  345.      * @see alpha/model/AlphaDAOProviderInterface::loadAll()
  346.      */
  347.     public function loadAll($start=0$limit=0$orderBy='OID'$order='ASC'$ignoreClassType=false{
  348.         self::$logger->debug('>>loadAll(start=['.$start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.']');
  349.         
  350.         // ensure that the field name provided in the orderBy param is legit
  351.         try {
  352.             $field $this->BO->get($orderBy);
  353.         }catch(AlphaException $e{
  354.             throw new AlphaException('The field name ['.$orderBy.'] provided in the param orderBy does not exist on the class ['.get_class($this->BO).']');
  355.         }
  356.         
  357.         if(!$ignoreClassType && $this->BO->isTableOverloaded()) {
  358.             if($limit == 0{
  359.                 $sqlQuery 'SELECT OID FROM '.$this->BO->getTableName().' WHERE classname=\''.get_class($this->BO).'\' ORDER BY '.$orderBy.' '.$order.';';
  360.             }else{
  361.                 $sqlQuery 'SELECT OID FROM '.$this->BO->getTableName().' WHERE classname=\''.get_class($this->BO).'\' ORDER BY '.$orderBy.' '.$order.' LIMIT '.
  362.                     $start.', '.$limit.';';
  363.             }
  364.         }else{                
  365.             if($limit == 0)
  366.                 $sqlQuery 'SELECT OID FROM '.$this->BO->getTableName().' ORDER BY '.$orderBy.' '.$order.';';
  367.             else
  368.                 $sqlQuery 'SELECT OID FROM '.$this->BO->getTableName().' ORDER BY '.$orderBy.' '.$order.' LIMIT '.$start.', '.$limit.';';
  369.         }
  370.         
  371.         $this->BO->setLastQuery($sqlQuery);
  372.         
  373.         if(!$result AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
  374.             throw new BONotFoundException('Failed to load object OIDs, MySql error is ['.AlphaDAOProviderMySQL::getConnection()->error.'], query ['.$this->BO->getLastQuery().']');
  375.             self::$logger->debug('<<loadAll [0]');
  376.             return array();
  377.         }
  378.         
  379.         // now build an array of objects to be returned
  380.         $objects array();
  381.         $count 0;
  382.         $BO_Class get_class($this->BO);
  383.         
  384.         while($row $result->fetch_array(MYSQLI_ASSOC)) {
  385.             try {
  386.                 $obj new $BO_Class();
  387.                 $obj->load($row['OID']);
  388.                 $objects[$count$obj;
  389.                 $count++;
  390.             }catch(ResourceNotAllowedException $e{
  391.                 // the resource not allowed will be absent from the list
  392.             }
  393.         }
  394.         
  395.         self::$logger->debug('<<loadAll ['.count($objects).']');
  396.         return $objects;
  397.     }
  398.     
  399.     /**
  400.      * (non-PHPdoc)
  401.      * @see alpha/model/AlphaDAOProviderInterface::loadAllByAttribute()
  402.      */
  403.     public function loadAllByAttribute($attribute$value$start=0$limit=0$orderBy="OID"$order="ASC"$ignoreClassType=false$constructorArgs=array()) {
  404.         self::$logger->debug('>>loadAllByAttribute(attribute=['.$attribute.'], value=['.$value.'], start=['.$start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.'], constructorArgs=['.print_r($constructorArgstrue).']');
  405.         
  406.         if ($start != && $limit != 0)
  407.             $limit ' LIMIT '.$start.', '.$limit.';';
  408.         else
  409.             $limit ';';
  410.         
  411.         if(!$ignoreClassType && $this->BO->isTableOverloaded())
  412.             $sqlQuery "SELECT OID FROM ".$this->BO->getTableName()." WHERE $attribute = ? AND classname = ? ORDER BY ".$orderBy." ".$order.$limit;
  413.         else
  414.             $sqlQuery "SELECT OID FROM ".$this->BO->getTableName()." WHERE $attribute = ? ORDER BY ".$orderBy." ".$order.$limit;            
  415.             
  416.         $this->BO->setLastQuery($sqlQuery);
  417.         self::$logger->debug($sqlQuery);
  418.         
  419.         $stmt AlphaDAOProviderMySQL::getConnection()->stmt_init();
  420.  
  421.         $row array();
  422.         
  423.         if($stmt->prepare($sqlQuery)) {
  424.             if($this->BO->getPropObject($attributeinstanceof Integer{
  425.                 if($this->BO->isTableOverloaded()) {
  426.                     $stmt->bind_param('is'$valueget_class($this->BO));
  427.                 }else{
  428.                     $stmt->bind_param('i'$value);
  429.                 }
  430.             }else{
  431.                 if($this->BO->isTableOverloaded()) {
  432.                     $stmt->bind_param('ss'$valueget_class($this->BO));
  433.                 }else{
  434.                     $stmt->bind_param('s'$value);
  435.                 }
  436.             }
  437.             
  438.             $stmt->execute();
  439.             
  440.             $result $this->bindResult($stmt);
  441.                 
  442.             $stmt->close();
  443.         }else{
  444.             self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
  445.             if(!$this->BO->checkTableExists()) {
  446.                 $this->BO->makeTable();
  447.                 throw new BONotFoundException('Failed to load objects by attribute ['.$attribute.'] and value ['.$value.'], table did not exist so had to create!');
  448.             }
  449.             self::$logger->debug('<<loadAllByAttribute []');
  450.             return array();
  451.         }
  452.         
  453.         // now build an array of objects to be returned
  454.         $objects array();
  455.         $count 0;
  456.         $BO_Class get_class($this->BO);
  457.         
  458.         foreach($result as $row{
  459.             try {
  460.                 $argsCount count($constructorArgs);
  461.                 
  462.                 if($argsCount 1{
  463.                     $obj new $BO_Class();
  464.                 }else{
  465.                     switch ($argsCount{
  466.                         case 1:
  467.                             $obj new $BO_Class($constructorArgs[0]);
  468.                         break;
  469.                         case 2:
  470.                             $obj new $BO_Class($constructorArgs[0],$constructorArgs[1]);
  471.                         break;
  472.                         case 3:
  473.                             $obj new $BO_Class($constructorArgs[0],$constructorArgs[1],$constructorArgs[2]);
  474.                         break;
  475.                         case 4:
  476.                             $obj new $BO_Class($constructorArgs[0],$constructorArgs[1],$constructorArgs[2],$constructorArgs[3]);
  477.                         break;
  478.                         case 5:
  479.                             $obj new $BO_Class($constructorArgs[0],$constructorArgs[1],$constructorArgs[2],$constructorArgs[3],$constructorArgs[4]);
  480.                         break;                        
  481.                         default:
  482.                             throw new IllegalArguementException('Too many elements in the $constructorArgs array passed to the loadAllByAttribute method!');
  483.                         break;
  484.                     }
  485.                 }
  486.                 
  487.                 $obj->load($row['OID']);
  488.                 $objects[$count$obj;
  489.                 $count++;
  490.             }catch(ResourceNotAllowedException $e{
  491.                 // the resource not allowed will be absent from the list
  492.             }
  493.         }
  494.         
  495.         self::$logger->debug('<<loadAllByAttribute ['.count($objects).']');
  496.         return $objects;    
  497.     }
  498.     
  499.     /**
  500.      * (non-PHPdoc)
  501.      * @see alpha/model/AlphaDAOProviderInterface::loadAllByAttributes()
  502.      */
  503.     public function loadAllByAttributes($attributes=array()$values=array()$start=0$limit=0$orderBy='OID'$order='ASC'$ignoreClassType=false{
  504.         self::$logger->debug('>>loadAllByAttributes(attributes=['.var_export($attributestrue).'], values=['.var_export($valuestrue).'], start=['.
  505.             $start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.']');
  506.         
  507.         $whereClause ' WHERE';
  508.         
  509.         $count count($attributes);
  510.         
  511.         for($i 0$i $count$i++{
  512.             $whereClause .= ' '.$attributes[$i].' = ? AND';
  513.             self::$logger->debug($whereClause);
  514.         }
  515.         
  516.         if(!$ignoreClassType && $this->BO->isTableOverloaded())
  517.             $whereClause .= ' classname = ? AND';
  518.         
  519.         // remove the last " AND"
  520.         $whereClause substr($whereClause0-4);
  521.         
  522.         if ($limit != 0)
  523.             $limit ' LIMIT '.$start.', '.$limit.';';
  524.         else
  525.             $limit ';';
  526.         
  527.         $sqlQuery "SELECT OID FROM ".$this->BO->getTableName().$whereClause." ORDER BY ".$orderBy." ".$order.$limit;
  528.             
  529.         $this->BO->setLastQuery($sqlQuery);
  530.         
  531.         $stmt AlphaDAOProviderMySQL::getConnection()->stmt_init();
  532.         
  533.         if($stmt->prepare($sqlQuery)) {            
  534.             // bind params where required attributes are provided
  535.             if(count($attributes&& count($attributes== count($values)) {
  536.                 $stmt $this->bindParams($stmt$attributes$values);
  537.             }else{
  538.                 // we'll still need to bind the "classname" for overloaded BOs...
  539.                 if($this->BO->isTableOverloaded())
  540.                     $stmt->bind_param('s'get_class($this->BO));
  541.             }
  542.             $stmt->execute();
  543.             
  544.             $result $this->bindResult($stmt);
  545.                 
  546.             $stmt->close();
  547.         }else{
  548.             self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
  549.             
  550.             if(!$this->BO->checkTableExists()) {
  551.                 $this->BO->makeTable();
  552.                 throw new BONotFoundException('Failed to load objects by attributes ['.var_export($attributestrue).'] and values ['.
  553.                     var_export($valuestrue).'], table did not exist so had to create!');
  554.             }
  555.             
  556.             self::$logger->debug('<<loadAllByAttributes []');
  557.             return array();
  558.         }
  559.         
  560.         // now build an array of objects to be returned
  561.         $objects array();
  562.         $count 0;
  563.         $BO_Class get_class($this->BO);
  564.         
  565.         foreach($result as $row{
  566.             try {
  567.                 $obj new $BO_Class();
  568.                 $obj->load($row['OID']);
  569.                 $objects[$count$obj;
  570.                 $count++;
  571.             }catch(ResourceNotAllowedException $e{
  572.                 // the resource not allowed will be absent from the list
  573.             }
  574.         }
  575.         
  576.         self::$logger->debug('<<loadAllByAttributes ['.count($objects).']');
  577.         return $objects;    
  578.     }
  579.     
  580.     /**
  581.      * (non-PHPdoc)
  582.      * @see alpha/model/AlphaDAOProviderInterface::loadAllByDayUpdated()
  583.      */
  584.     public function loadAllByDayUpdated($date$start=0$limit=0$orderBy="OID"$order="ASC"$ignoreClassType=false{
  585.         self::$logger->debug('>>loadAllByDayUpdated(date=['.$date.'], start=['.$start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.']');
  586.         
  587.         if ($start != && $limit != 0)
  588.             $limit ' LIMIT '.$start.', '.$limit.';';
  589.         else
  590.             $limit ';';
  591.         
  592.         if(!$ignoreClassType && $this->BO->isTableOverloaded())
  593.             $sqlQuery "SELECT OID FROM ".$this->BO->getTableName()." WHERE updated_ts >= '".$date." 00:00:00' AND updated_ts <= '".$date." 23:59:59' AND classname='".get_class($this->BO)."' ORDER BY ".$orderBy." ".$order.$limit;
  594.         else
  595.             $sqlQuery "SELECT OID FROM ".$this->BO->getTableName()." WHERE updated_ts >= '".$date." 00:00:00' AND updated_ts <= '".$date." 23:59:59' ORDER BY ".$orderBy." ".$order.$limit;
  596.             
  597.         $this->BO->setLastQuery($sqlQuery);
  598.  
  599.         if(!$result AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
  600.             throw new BONotFoundException('Failed to load object OIDs, MySql error is ['.self::getConnection()->error.'], query ['.$this->BO->getLastQuery().']');
  601.             self::$logger->debug('<<loadAllByDayUpdated []');
  602.             return array();
  603.         }
  604.         
  605.         // now build an array of objects to be returned
  606.         $objects array();
  607.         $count 0;
  608.         $BO_Class get_class($this->BO);
  609.         
  610.         while($row $result->fetch_array(MYSQLI_ASSOC)) {
  611.             $obj new $BO_Class();
  612.             $obj->load($row['OID']);
  613.             $objects[$count$obj;
  614.             $count++;
  615.         }
  616.         
  617.         self::$logger->debug('<<loadAllByDayUpdated ['.count($objects).']');
  618.         return $objects;    
  619.     }
  620.     
  621.     /**
  622.      * (non-PHPdoc)
  623.      * @see alpha/model/AlphaDAOProviderInterface::loadAllFieldValuesByAttribute()
  624.      */
  625.     public function loadAllFieldValuesByAttribute($attribute$value$returnAttribute$order='ASC'$ignoreClassType=false{
  626.         self::$logger->debug('>>loadAllFieldValuesByAttribute(attribute=['.$attribute.'], value=['.$value.'], returnAttribute=['.$returnAttribute.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.']');
  627.         
  628.         if(!$ignoreClassType && $this->BO->isTableOverloaded())
  629.             $sqlQuery "SELECT ".$returnAttribute." FROM ".$this->BO->getTableName()." WHERE $attribute = '$value' AND classname='".get_class($this->BO)."' ORDER BY OID ".$order.";";
  630.         else
  631.             $sqlQuery "SELECT ".$returnAttribute." FROM ".$this->BO->getTableName()." WHERE $attribute = '$value' ORDER BY OID ".$order.";";
  632.             
  633.         $this->BO->setLastQuery($sqlQuery);
  634.         
  635.         self::$logger->debug('lastQuery ['.$sqlQuery.']');
  636.  
  637.         if(!$result AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
  638.             throw new BONotFoundException('Failed to load field ['.$returnAttribute.'] values, MySql error is ['.AlphaDAOProviderMySQL::getConnection()->error.'], query ['.$this->getLastQuery().']');
  639.             self::$logger->debug('<<loadAllFieldValuesByAttribute []');
  640.             return array();
  641.         }
  642.         
  643.         // now build an array of attribute values to be returned
  644.         $values array();
  645.         $count 0;
  646.         $BO_Class get_class($this->BO);
  647.         
  648.         while($row $result->fetch_array(MYSQLI_ASSOC)) {
  649.             $values[$count$row[$returnAttribute];
  650.             $count++;
  651.         }
  652.         
  653.         self::$logger->debug('<<loadAllFieldValuesByAttribute ['.count($values).']');
  654.         return $values;
  655.     }
  656.  
  657.     /**
  658.      * (non-PHPdoc)
  659.      * @see alpha/model/AlphaDAOProviderInterface::save()
  660.      */
  661.     public function save({
  662.         self::$logger->debug('>>save()');
  663.             
  664.         // get the class attributes
  665.         $reflection new ReflectionClass(get_class($this->BO));
  666.         $properties $reflection->getProperties();
  667.         $sqlQuery '';
  668.         $stmt null;
  669.  
  670.         if($this->BO->getVersion(!= $this->BO->getVersionNumber()->getValue()){
  671.             throw new LockingException('Could not save the object as it has been updated by another user.  Please try saving again.');
  672.             return;
  673.         }
  674.  
  675.         // set the "updated by" fields, we can only set the user id if someone is logged in
  676.         if(isset($_SESSION['currentUser']))
  677.             $this->BO->set('updated_by'$_SESSION['currentUser']->getOID());
  678.             
  679.         $this->BO->set('updated_ts'new Timestamp(date("Y-m-d H:i:s")));
  680.             
  681.         // check to see if it is a transient object that needs to be inserted
  682.         if($this->BO->isTransient()) {
  683.             $savedFieldsCount 0;
  684.             $sqlQuery 'INSERT INTO '.$this->BO->getTableName().' (';
  685.     
  686.             foreach($properties as $propObj{
  687.                 $propName $propObj->name;
  688.                 if (!in_array($propName$this->BO->getTransientAttributes())) {
  689.                     // Skip the OID, database auto number takes care of this.
  690.                     if($propName != 'OID' && $propName != 'version_num'{
  691.                         $sqlQuery .= "$propName,";
  692.                         $savedFieldsCount++;
  693.                     }
  694.                     
  695.                     if($propName == 'version_num'{
  696.                         $sqlQuery .= 'version_num,';
  697.                         $savedFieldsCount++;
  698.                     }
  699.                 }
  700.             }
  701.             if($this->BO->isTableOverloaded())
  702.                 $sqlQuery .= 'classname,';
  703.     
  704.             $sqlQuery rtrim($sqlQuery",");
  705.     
  706.             $sqlQuery .= ') VALUES (';
  707.                 
  708.             for($i 0$i $savedFieldsCount$i++)
  709.                 $sqlQuery.= '?,';
  710.                 
  711.             if($this->BO->isTableOverloaded())
  712.                 $sqlQuery.= '?,';
  713.                 
  714.             $sqlQuery rtrim($sqlQuery',').')';
  715.                 
  716.             $this->BO->setLastQuery($sqlQuery);
  717.             self::$logger->debug('Query ['.$sqlQuery.']');
  718.             
  719.             $stmt AlphaDAOProviderMySQL::getConnection()->stmt_init();
  720.         
  721.             if($stmt->prepare($sqlQuery)) {            
  722.                 $stmt $this->bindParams($stmt);                    
  723.                 $stmt->execute();                    
  724.             }else{
  725.                 throw new FailedSaveException('Failed to save object, error is ['.$stmt->error.'], query ['.$this->BO->getLastQuery().']');
  726.             }
  727.         }else{
  728.             // assume that it is a persistent object that needs to be updated
  729.             $savedFieldsCount 0;
  730.             $sqlQuery 'UPDATE '.$this->BO->getTableName().' SET ';
  731.     
  732.             foreach($properties as $propObj{
  733.                 $propName $propObj->name;
  734.                 if (!in_array($propName$this->BO->getTransientAttributes())) {
  735.                     // Skip the OID, database auto number takes care of this.
  736.                     if($propName != 'OID' && $propName != 'version_num'{                            
  737.                         $sqlQuery .= "$propName = ?,";
  738.                         $savedFieldsCount++;
  739.                     }
  740.                         
  741.                     if($propName == 'version_num'{
  742.                         $sqlQuery .= 'version_num = ?,';
  743.                         $savedFieldsCount++;
  744.                     }
  745.                 }
  746.             }
  747.             
  748.             if($this->BO->isTableOverloaded())
  749.                 $sqlQuery .= 'classname = ?,';
  750.     
  751.             $sqlQuery rtrim($sqlQuery",");
  752.     
  753.             $sqlQuery .= " WHERE OID=?;";
  754.                 
  755.             $this->BO->setLastQuery($sqlQuery);
  756.             $stmt AlphaDAOProviderMySQL::getConnection()->stmt_init();
  757.                 
  758.             if($stmt->prepare($sqlQuery)) {                            
  759.                 $this->bindParams($stmt);
  760.                 $stmt->execute();                    
  761.             }else{
  762.                 throw new FailedSaveException('Failed to save object, error is ['.$stmt->error.'], query ['.$this->BO->getLastQuery().']');                
  763.             }                
  764.         }
  765.     
  766.         if ($stmt != null && $stmt->error == ''{
  767.             // populate the updated OID in case we just done an insert                
  768.             if($this->BO->isTransient())
  769.                 $this->BO->setOID(AlphaDAOProviderMySQL::getConnection()->insert_id);
  770.  
  771.             try {
  772.                 foreach($properties as $propObj{
  773.                     $propName $propObj->name;
  774.                     
  775.                     if(!$propObj->isPrivate(&& $this->BO->get($propName!= '' && $this->BO->getPropObject($propNameinstanceof Relation{                
  776.                         $prop $this->BO->getPropObject($propName);
  777.                             
  778.                         // handle the saving of MANY-TO-MANY relation values
  779.                         if($prop->getRelationType(== 'MANY-TO-MANY'{
  780.                             try {
  781.                                 try{
  782.                                     // check to see if the rel is on this class
  783.                                     $side $prop->getSide(get_class($this->BO));                                            
  784.                                 }catch (IllegalArguementException $iae{
  785.                                     $side $prop->getSide(ucfirst($this->BO->getTableName()).'Object');
  786.                                 }
  787.                                         
  788.                                 $lookUp $prop->getLookup();                                    
  789.                                         
  790.                                 // first delete all of the old RelationLookup objects for this rel
  791.                                 try {
  792.                                     if($side == 'left')
  793.                                         $lookUp->deleteAllByAttribute('leftID'$this->BO->getOID());
  794.                                     else
  795.                                         $lookUp->deleteAllByAttribute('rightID'$this->BO->getOID());                            
  796.                                 }catch (Exception $e{
  797.                                     throw new FailedSaveException('Failed to delete old RelationLookup objects on the table ['.$prop->getLookup()->getTableName().'], error is ['.$e->getMessage().']');
  798.                                 }
  799.                                             
  800.                                 if(isset($_POST[$propName]&& $_POST[$propName!= '00000000000')
  801.                                     $OIDs explode(','$_POST[$propName]);
  802.                                                                                     
  803.                                 if(isset($OIDs&& !empty($OIDs[0])) {                                        
  804.                                     // now for each posted OID, create a new RelationLookup record and save
  805.                                     foreach ($OIDs as $oid{                                            
  806.                                         $newLookUp new RelationLookup($lookUp->get('leftClassName')$lookUp->get('rightClassName'));
  807.                                         if($side == 'left'{
  808.                                             $newLookUp->set('leftID'$this->BO->getOID());
  809.                                             $newLookUp->set('rightID'$oid);
  810.                                         }else{
  811.                                             $newLookUp->set('rightID'$this->BO->getOID());
  812.                                             $newLookUp->set('leftID'$oid);
  813.                                         }
  814.                                         $newLookUp->save();
  815.                                     }                                    
  816.                                 }
  817.                             }catch (Exception $e{
  818.                                 throw new FailedSaveException('Failed to update a MANY-TO-MANY relation on the object, error is ['.$e->getMessage().']');
  819.                                 return;
  820.                             }
  821.                         }
  822.                             
  823.                         // handle the saving of ONE-TO-MANY relation values
  824.                         if($prop->getRelationType(== 'ONE-TO-MANY'{
  825.                             $prop->setValue($this->BO->getOID());
  826.                         }
  827.                     }                        
  828.                 }
  829.             }catch (Exception $e{
  830.                 throw new FailedSaveException('Failed to save object, error is ['.$e->getMessage().']');
  831.                 return;
  832.             }
  833.                 
  834.             $stmt->close();
  835.         }else{
  836.             // there has been an error, so decrement the version number back
  837.             $temp $this->BO->getVersionNumber()->getValue();                    
  838.             $this->BO->set('version_num'$temp-1);    
  839.                 
  840.             // check for unique violations            
  841.             if(AlphaDAOProviderMySQL::getConnection()->errno == '1062'{                    
  842.                 throw new ValidationException('Failed to save, the value '.$this->findOffendingValue(AlphaDAOProviderMySQL::getConnection()->error).' is already in use!');
  843.                 return;
  844.             }else{                    
  845.                 throw new FailedSaveException('Failed to save object, MySql error is ['.AlphaDAOProviderMySQL::getConnection()->error.'], query ['.$this->BO->getLastQuery().']');
  846.             }
  847.         }
  848.     }
  849.     
  850.     /**
  851.      * (non-PHPdoc)
  852.      * @see alpha/model/AlphaDAOProviderInterface::saveAttribute()
  853.      */
  854.     public function saveAttribute($attribute$value{
  855.         self::$logger->debug('>>saveAttribute(attribute=['.$attribute.'], value=['.$value.'])');
  856.         
  857.         // assume that it is a persistent object that needs to be updated
  858.         $sqlQuery 'UPDATE '.$this->BO->getTableName().' SET '.$attribute.'=? WHERE OID=?;';
  859.                 
  860.         $this->BO->setLastQuery($sqlQuery);
  861.         $stmt self::getConnection()->stmt_init();
  862.                 
  863.         if($stmt->prepare($sqlQuery)) {
  864.             if($this->BO->getPropObject($attributeinstanceof Integer)
  865.                 $bindingsType 'i';
  866.             else
  867.                 $bindingsType 's';
  868.             $stmt->bind_param($bindingsType.'i'$value$this->BO->getOID());
  869.             self::$logger->debug('Binding params ['.$bindingsType.'i, '.$value.', '.$this->BO->getOID().']');
  870.             $stmt->execute();
  871.         }else{
  872.             throw new FailedSaveException('Failed to save attribute, error is ['.$stmt->error.'], query ['.$this->BO->getLastQuery().']');                
  873.         }
  874.                 
  875.         $stmt->close();
  876.             
  877.         self::$logger->debug('<<saveAttribute');
  878.     }
  879.     
  880.     /**
  881.      * (non-PHPdoc)
  882.      * @see alpha/model/AlphaDAOProviderInterface::delete()
  883.      */
  884.     public function delete({
  885.         self::$logger->debug('>>delete()');
  886.                 
  887.         $sqlQuery "DELETE FROM ".$this->BO->getTableName()." WHERE OID = ?;";
  888.             
  889.         $this->BO->setLastQuery($sqlQuery);
  890.         
  891.         $stmt self::getConnection()->stmt_init();
  892.                 
  893.         if($stmt->prepare($sqlQuery)) {
  894.             $stmt->bind_param('i'$this->BO->getOID());            
  895.             $stmt->execute();
  896.             self::$logger->debug('Deleted the object ['.$this->BO->getOID().'] of class ['.get_class($this->BO).']');                    
  897.         }else{
  898.             throw new FailedDeleteException('Failed to delete object ['.$this->BO->getOID().'], error is ['.$stmt->error.'], query ['.$this->BO->getLastQuery().']');                
  899.         }
  900.                 
  901.         $stmt->close();
  902.         
  903.         self::$logger->debug('<<delete');
  904.     }
  905.     
  906.     /**
  907.      * (non-PHPdoc)
  908.      * @see alpha/model/AlphaDAOProviderInterface::getVersion()
  909.      */
  910.     public function getVersion({
  911.         self::$logger->debug('>>getVersion()');
  912.         
  913.         $sqlQuery 'SELECT version_num FROM '.$this->BO->getTableName().' WHERE OID = ?;';
  914.         $this->BO->setLastQuery($sqlQuery);
  915.             
  916.         $stmt AlphaDAOProviderMySQL::getConnection()->stmt_init();
  917.             
  918.         if($stmt->prepare($sqlQuery)) {
  919.             $stmt->bind_param('i'$this->BO->getOID());
  920.                 
  921.             $stmt->execute();
  922.                 
  923.             $result $this->bindResult($stmt);
  924.             if(isset($result[0]))
  925.                 $row $result[0];
  926.                     
  927.             $stmt->close();
  928.         }else{
  929.             self::$logger->warn('The following query caused an unexpected result ['.$sqlQuery.']');
  930.             if(!$this->BO->checkTableExists()) {
  931.                 $this->BO->makeTable();
  932.                 throw new BONotFoundException('Failed to get the version number, table did not exist so had to create!');
  933.             }
  934.             return;
  935.         }
  936.         
  937.         if(!isset($row['version_num']|| $row['version_num'1{
  938.             self::$logger->debug('<<getVersion [0]');
  939.             return 0;
  940.         }else{
  941.             $version_num $row['version_num'];
  942.             
  943.             self::$logger->debug('<<getVersion ['.$version_num.']');
  944.             return $version_num;
  945.         }
  946.     }
  947.  
  948.     /**
  949.      * (non-PHPdoc)
  950.      * @see alpha/model/AlphaDAOProviderInterface::makeTable()
  951.      */
  952.     public function makeTable({
  953.         self::$logger->debug('>>makeTable()');
  954.         
  955.         $sqlQuery "CREATE TABLE ".$this->BO->getTableName()." (OID INT(11) ZEROFILL NOT NULL AUTO_INCREMENT,";
  956.         
  957.         // get the class attributes
  958.         $reflection new ReflectionClass(get_class($this->BO));
  959.         $properties $reflection->getProperties();
  960.         
  961.         foreach($properties as $propObj{
  962.             $propName $propObj->name;
  963.             
  964.             if(!in_array($propName$this->BO->getTransientAttributes()) && $propName != "OID"{
  965.                 $propClass get_class($this->BO->getPropObject($propName));
  966.  
  967.                 switch (strtoupper($propClass)) {
  968.                     case "INTEGER":
  969.                         // special properties for RelationLookup OIDs
  970.                         if($this->BO instanceof RelationLookup && ($propName == 'leftID' || $propName == 'rightID'))
  971.                             $sqlQuery .= "$propName INT(".$this->BO->getPropObject($propName)->getSize().") ZEROFILL NOT NULL,";
  972.                         else
  973.                             $sqlQuery .= "$propName INT(".$this->BO->getPropObject($propName)->getSize()."),";
  974.                     break;
  975.                     case "DOUBLE":
  976.                         $sqlQuery .= "$propName DOUBLE(".$this->BO->getPropObject($propName)->getSize(true)."),";
  977.                     break;
  978.                     case "STRING":
  979.                         $sqlQuery .= "$propName VARCHAR(".$this->BO->getPropObject($propName)->getSize()."),";
  980.                     break;
  981.                     case "TEXT":
  982.                         $sqlQuery .= "$propName TEXT,";
  983.                     break;
  984.                     case "BOOLEAN":
  985.                         $sqlQuery .= "$propName CHAR(1) DEFAULT '0',";
  986.                     break;
  987.                     case "DATE":
  988.                         $sqlQuery .= "$propName DATE,";
  989.                     break;
  990.                     case "TIMESTAMP":
  991.                         $sqlQuery .= "$propName DATETIME,";
  992.                     break;
  993.                     case "ENUM":
  994.                         $sqlQuery .= "$propName ENUM(";
  995.                         $enumVals $this->BO->getPropObject($propName)->getOptions();
  996.                         foreach($enumVals as $val{
  997.                             $sqlQuery .= "'".$val."',";
  998.                         }
  999.                         $sqlQuery rtrim($sqlQuery",");
  1000.                         $sqlQuery .= "),";
  1001.                     break;
  1002.                     case "DENUM":
  1003.                         $tmp new DEnum(get_class($this->BO).'::'.$propName);
  1004.                         $sqlQuery .= "$propName INT(11) ZEROFILL,";
  1005.                     break;
  1006.                     case "RELATION":                        
  1007.                         $sqlQuery .= "$propName INT(11) ZEROFILL UNSIGNED,";
  1008.                     break;
  1009.                     default:
  1010.                         $sqlQuery .= "";
  1011.                     break;
  1012.                 }
  1013.             }            
  1014.         }
  1015.         if($this->BO->isTableOverloaded())
  1016.             $sqlQuery .= "classname VARCHAR(100),";
  1017.         
  1018.         $sqlQuery .= "PRIMARY KEY (OID)) ENGINE=InnoDB;";
  1019.         
  1020.         $this->BO->setLastQuery($sqlQuery);
  1021.         
  1022.         if(!$result AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
  1023.             throw new AlphaException('Failed to create the table ['.$this->BO->getTableName().'] for the class ['.get_class($this->BO).'], database error is ['.AlphaDAOProviderMySQL::getConnection()->error.']');
  1024.             self::$logger->debug('<<makeTable');
  1025.         }
  1026.                 
  1027.         // check the table indexes if any additional ones required
  1028.         $this->checkIndexes();
  1029.         
  1030.         self::$logger->debug('<<makeTable');
  1031.     }
  1032.  
  1033.     /**
  1034.      * (non-PHPdoc)
  1035.      * @see alpha/model/AlphaDAOProviderInterface::rebuildTable()
  1036.      */
  1037.     public function rebuildTable({
  1038.         self::$logger->debug('>>rebuildTable()');
  1039.         
  1040.         $sqlQuery 'DROP TABLE IF EXISTS '.$this->BO->getTableName().';';
  1041.  
  1042.         $this->BO->setLastQuery($sqlQuery);
  1043.  
  1044.         if(!$result AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
  1045.             throw new AlphaException('Failed to drop the table ['.$this->BO->getTableName().'] for the class ['.get_class($this->BO).'], database error is ['.AlphaDAOProviderMySQL::getConnection()->error.']');
  1046.             self::$logger->debug('<<rebuildTable');
  1047.         }
  1048.  
  1049.         $this->BO->makeTable();
  1050.         
  1051.         self::$logger->debug('<<rebuildTable');
  1052.     }
  1053.     
  1054.     /**
  1055.      * (non-PHPdoc)
  1056.      * @see alpha/model/AlphaDAOProviderInterface::dropTable()
  1057.      */
  1058.     public function dropTable($tableName=null{
  1059.         self::$logger->debug('>>dropTable()');
  1060.         
  1061.         if($tableName == null)
  1062.             $tableName $this->BO->getTableName();
  1063.             
  1064.         $sqlQuery 'DROP TABLE IF EXISTS '.$tableName.';';
  1065.  
  1066.         $this->BO->setLastQuery($sqlQuery);
  1067.  
  1068.         if(!$result AlphaDAOProviderMySQL::getConnection()->query($sqlQuery)) {
  1069.             throw new AlphaException('Failed to drop the table ['.$tableName.'] for the class ['.get_class($this->BO).'], query is ['.$this->BO->getLastQuery().']');
  1070.             self::$logger->debug('<<dropTable');
  1071.         }
  1072.         
  1073.         self::$logger->debug('<<dropTable');
  1074.     }
  1075.  
  1076.     /**
  1077.      * (non-PHPdoc)
  1078.      * @see alpha/model/AlphaDAOProviderInterface::addProperty()
  1079.      */
  1080.     public function addProperty($propName{
  1081.         self::$logger->debug('>>addProperty(propName=['.$propName.'])');
  1082.         
  1083.         $sqlQuery 'ALTER TABLE '.$this->BO->getTableName().' ADD ';
  1084.         
  1085.         if($this->isTableOverloaded(&& $propName == 'classname'{
  1086.             $sqlQuery .= 'classname VARCHAR(100)';
  1087.         }else{
  1088.             if(!in_array($propName$this->BO->getDefaultAttributes()) && !in_array($propName$this->BO->getTransientAttributes())) {
  1089.                 $propClass get_class($this->BO->getPropObject($propName));
  1090.     
  1091.                 switch (strtoupper($propClass)) {
  1092.                     case 'INTEGER':
  1093.                         $sqlQuery .= "$propName INT(".$this->BO->getPropObject($propName)->getSize().")";
  1094.                     break;
  1095.                     case 'DOUBLE':
  1096.                         $sqlQuery .= "$propName DOUBLE(".$this->BO->getPropObject($propName)->getSize(true).")";
  1097.                     break;
  1098.                     case 'STRING':
  1099.                         $sqlQuery .= "$propName VARCHAR(".$this->BO->getPropObject($propName)->getSize().")";
  1100.                     break;
  1101.                     case 'SEQUENCE':
  1102.                         $sqlQuery .= "$propName VARCHAR(".$this->BO->getPropObject($propName)->getSize().")";
  1103.                     break;
  1104.                     case 'TEXT':
  1105.                         $sqlQuery .= "$propName TEXT";
  1106.                     break;
  1107.                     case 'BOOLEAN':
  1108.                         $sqlQuery .= "$propName CHAR(1) DEFAULT '0'";
  1109.                     break;
  1110.                     case 'DATE':
  1111.                         $sqlQuery .= "$propName DATE";
  1112.                     break;
  1113.                     case 'TIMESTAMP':
  1114.                         $sqlQuery .= "$propName DATETIME";
  1115.                     break;
  1116.                     case 'ENUM':
  1117.                         $sqlQuery .= "$propName ENUM(";
  1118.                         $enumVals $this->BO->getPropObject($propName)->getOptions();
  1119.                         foreach($enumVals as $val{
  1120.                             $sqlQuery .= "'".$val."',";
  1121.                         }
  1122.                         $sqlQuery rtrim($sqlQuery",");
  1123.                         $sqlQuery .= ')';
  1124.                     break;
  1125.                     case 'DENUM':
  1126.                         $tmp new DEnum(get_class($this->BO).'::'.$propName);                        
  1127.                         $tmp->save();
  1128.                         $sqlQuery .= "$propName INT(11) ZEROFILL";
  1129.                     break;
  1130.                     case 'RELATION':                        
  1131.                         $sqlQuery .= "$propName INT(11) ZEROFILL UNSIGNED";
  1132.                     break;
  1133.                     default:
  1134.                         $sqlQuery .= '';
  1135.                     break;
  1136.                 }
  1137.             }
  1138.         }
  1139.  
  1140.         $this->BO->setLastQuery($sqlQuery);
  1141.  
  1142.         if(!$result self::getConnection()->query($sqlQuery)) {
  1143.             throw new AlphaException('Failed to add the new attribute ['.$propName.'] to the table ['.$this->BO->getTableName().'], query is ['.$this->BO->getLastQuery().']');
  1144.             self::$logger->debug('<<addProperty');
  1145.         }else{
  1146.             self::$logger->info('Successfully added the ['.$propName.'] column onto the ['.$this->BO->getTableName().'] table for the class ['.get_class($this->BO).']');
  1147.         }
  1148.  
  1149.         self::$logger->debug('<<addProperty');
  1150.     }
  1151.  
  1152.     /**
  1153.      * (non-PHPdoc)
  1154.      * @see alpha/model/AlphaDAOProviderInterface::getMAX()
  1155.      */
  1156.     public function getMAX({
  1157.         self::$logger->debug('>>getMAX()');
  1158.         
  1159.         $sqlQuery 'SELECT MAX(OID) AS max_OID FROM '.$this->BO->getTableName();
  1160.  
  1161.         $this->BO->setLastQuery($sqlQuery);
  1162.         
  1163.         try {
  1164.             $result $this->BO->query($sqlQuery);
  1165.     
  1166.             $row $result[0];
  1167.     
  1168.             if (isset($row['max_OID'])) {    
  1169.                 self::$logger->debug('<<getMAX ['.$row['max_OID'].']');
  1170.                 return $row['max_OID'];
  1171.             }else{            
  1172.                 throw new AlphaException('Failed to get the MAX ID for the class ['.get_class($this->BO).'] from the table ['.$this->BO->getTableName().'], query is ['.$this->BO->getLastQuery().']');
  1173.             }
  1174.         }catch (Exception $e{
  1175.             throw new AlphaException($e->getMessage());
  1176.             self::$logger->debug('<<getMAX [0]');
  1177.             return 0;
  1178.         }
  1179.     }
  1180.     
  1181.     /**
  1182.      * (non-PHPdoc)
  1183.      * @see alpha/model/AlphaDAOProviderInterface::getCount()
  1184.      */
  1185.     public function getCount($attributes=array()$values=array()) {
  1186.         self::$logger->debug('>>getCount(attributes=['.var_export($attributestrue).'], values=['.var_export($valuestrue).'])');
  1187.         
  1188.         if($this->BO->isTableOverloaded())
  1189.             $whereClause ' WHERE classname = \''.get_class($this->BO).'\' AND';
  1190.         else
  1191.             $whereClause ' WHERE';
  1192.             
  1193.         $count count($attributes);
  1194.         
  1195.         for($i 0$i $count$i++{
  1196.             $whereClause .= ' '.$attributes[$i].' = \''.$values[$i].'\' AND';
  1197.             self::$logger->debug($whereClause);
  1198.         }
  1199.         // remove the last " AND"
  1200.         $whereClause substr($whereClause0-4);
  1201.         
  1202.         if($whereClause != ' WHERE')
  1203.             $sqlQuery 'SELECT COUNT(OID) AS class_count FROM '.$this->BO->getTableName().$whereClause;
  1204.         else
  1205.             $sqlQuery 'SELECT COUNT(OID) AS class_count FROM '.$this->BO->getTableName();
  1206.  
  1207.         $this->BO->setLastQuery($sqlQuery);
  1208.         
  1209.         $result self::getConnection()->query($sqlQuery);
  1210.  
  1211.         if ($result{                
  1212.             $row $result->fetch_array(MYSQLI_ASSOC);
  1213.  
  1214.             self::$logger->debug('<<getCount ['.$row['class_count'].']');
  1215.             return $row['class_count'];
  1216.         }else{
  1217.             throw new AlphaException('Failed to get the count for the class ['.get_class($this->BO).'] from the table ['.$this->BO->getTableName().'], query is ['.$this->BO->getLastQuery().']');            
  1218.             self::$logger->debug('<<getCount [0]');
  1219.             return 0;
  1220.         }
  1221.     }
  1222.     
  1223.     /**
  1224.      * (non-PHPdoc)
  1225.      * @see alpha/model/AlphaDAOProviderInterface::setEnumOptions()
  1226.      * @since 1.1
  1227.      */
  1228.     public function setEnumOptions({
  1229.         self::$logger->debug('>>setEnumOptions()');
  1230.         
  1231.         // get the class attributes
  1232.         $reflection new ReflectionClass(get_class($this->BO));
  1233.         $properties $reflection->getProperties();
  1234.         
  1235.         // flag for any database errors
  1236.         $dbError false;
  1237.         
  1238.         foreach($properties as $propObj{
  1239.             $propName $propObj->name;
  1240.             if(!in_array($propName$this->BO->getDefaultAttributes()) && !in_array($propName$this->BO->getTransientAttributes())) {
  1241.                 $propClass get_class($this->BO->getPropObject($propName));
  1242.                 if ($propClass == 'Enum'{
  1243.                     $sqlQuery "SHOW COLUMNS FROM ".$this->BO->getTableName()." LIKE '$propName'";
  1244.                     
  1245.                     $this->BO->setLastQuery($sqlQuery);
  1246.                     
  1247.                     $result AlphaDAOProviderMySQL::getConnection()->query($sqlQuery);
  1248.                     
  1249.                     if ($result{                            
  1250.                         $row $result->fetch_array(MYSQLI_NUM);
  1251.                         $options explode("','",preg_replace("/(enum|set)\('(.+?)'\)/","\\2",$row[1]));
  1252.                         
  1253.                         $this->BO->getPropObject($propName)->setOptions($options);                        
  1254.                     }else{
  1255.                         $dbError true;
  1256.                         break;
  1257.                     }
  1258.                 }
  1259.             }
  1260.         }
  1261.         
  1262.         if (!$dbError{
  1263.             if(method_exists($this'after_setEnumOptions_callback'))
  1264.                 $this->after_setEnumOptions_callback();
  1265.         }else{            
  1266.             throw new AlphaException('Failed to load enum options correctly for object instance of class ['.get_class($this).']');
  1267.         }
  1268.         self::$logger->debug('<<setEnumOptions');
  1269.     }
  1270.     
  1271.     /**
  1272.      * (non-PHPdoc)
  1273.      * @see alpha/model/AlphaDAOProviderInterface::checkTableExists()
  1274.      */
  1275.     public function checkTableExists({
  1276.         self::$logger->debug('>>checkTableExists()');
  1277.         
  1278.         global $config;
  1279.                 
  1280.         $tableExists false;
  1281.         
  1282.         $sqlQuery 'SHOW TABLES;';
  1283.         $this->BO->setLastQuery($sqlQuery);
  1284.         
  1285.         $result self::getConnection()->query($sqlQuery);
  1286.         
  1287.         while ($row $result->fetch_array(MYSQLI_NUM)) {
  1288.             if (strtolower($row[0]== strtolower($this->BO->getTableName()))
  1289.                 $tableExists true;
  1290.         }        
  1291.         
  1292.         if ($result{            
  1293.             self::$logger->debug('<<checkTableExists ['.$tableExists.']');
  1294.             return $tableExists;
  1295.         }else{            
  1296.             throw new AlphaException('Failed to access the system database correctly, error is ['.self::getConnection()->error.']');
  1297.             self::$logger->debug('<<checkTableExists [false]');
  1298.             return false;
  1299.         }
  1300.     }
  1301.     
  1302.     /**
  1303.      * (non-PHPdoc)
  1304.      * @see alpha/model/AlphaDAOProviderInterface::checkBOTableExists()
  1305.      */
  1306.     public static function checkBOTableExists($BOClassName{
  1307.         if(self::$logger == null)
  1308.             self::$logger new Logger('AlphaDAOProvidermySQL');
  1309.         self::$logger->debug('>>checkBOTableExists(BOClassName=['.$BOClassName.'])');
  1310.         
  1311.         eval('$tableName = '.$BOClassName.'::TABLE_NAME;');
  1312.         
  1313.         if(empty($tableName))
  1314.             $tableName substr($BOClassName0strpos($BOClassName'_'));        
  1315.                 
  1316.         $tableExists false;
  1317.         
  1318.         $sqlQuery 'SHOW TABLES;';
  1319.         
  1320.         $result self::getConnection()->query($sqlQuery);
  1321.         
  1322.         while ($row $result->fetch_array(MYSQLI_NUM)) {            
  1323.             if ($row[0== $tableName)
  1324.                 $tableExists true;
  1325.         }        
  1326.         
  1327.         if ($result{            
  1328.             self::$logger->debug('<<checkBOTableExists ['.($tableExists 'true' 'false').']');
  1329.             return $tableExists;
  1330.         }else{            
  1331.             throw new AlphaException('Failed to access the system database correctly, error is ['.self::getConnection()->error.']');
  1332.             self::$logger->debug('<<checkBOTableExists [false]');
  1333.             return false;
  1334.         }
  1335.     }
  1336.     
  1337.     /**
  1338.      * (non-PHPdoc)
  1339.      * @see alpha/model/AlphaDAOProviderInterface::checkTableNeedsUpdate()
  1340.      */
  1341.     public function checkTableNeedsUpdate({
  1342.         self::$logger->debug('>>checkTableNeedsUpdate()');
  1343.                 
  1344.         $updateRequired false;
  1345.             
  1346.         $matchCount 0;
  1347.             
  1348.         $query 'SHOW COLUMNS FROM '.$this->BO->getTableName();
  1349.         $result self::getConnection()->query($query);
  1350.         $this->BO->setLastQuery($query);
  1351.             
  1352.         // get the class attributes
  1353.         $reflection new ReflectionClass(get_class($this->BO));
  1354.         $properties $reflection->getProperties();
  1355.             
  1356.         foreach($properties as $propObj{
  1357.             $propName $propObj->name;
  1358.             if (!in_array($propName$this->BO->getTransientAttributes())) {
  1359.                 
  1360.                 $foundMatch false;    
  1361.                     
  1362.                 while ($row $result->fetch_array(MYSQLI_ASSOC)) {                        
  1363.                     if ($propName == $row['Field']{
  1364.                         $foundMatch true;
  1365.                         break;
  1366.                     }
  1367.                 }
  1368.                     
  1369.                 if(!$foundMatch)
  1370.                     $matchCount--;                        
  1371.                     
  1372.                 $result->data_seek(0);                    
  1373.             }
  1374.         }
  1375.             
  1376.         // check for the "classname" field in overloaded tables
  1377.         if($this->BO->isTableOverloaded()) {
  1378.             $foundMatch false;
  1379.             
  1380.             while ($row $result->fetch_array(MYSQLI_ASSOC)) {
  1381.                 if ('classname' == $row['Field']{
  1382.                     $foundMatch true;
  1383.                     break;
  1384.                 }
  1385.             }
  1386.             if(!$foundMatch)                        
  1387.                 $matchCount--;
  1388.         }
  1389.             
  1390.         if ($matchCount != 0)
  1391.             $updateRequired true;
  1392.             
  1393.         if ($result{
  1394.             // check the table indexes
  1395.             try {
  1396.                 $this->checkIndexes();
  1397.             }catch (AlphaException $ae{
  1398.                 self::$logger->warn("Error while checking database indexes:\n\n".$ae->getMessage());
  1399.             }
  1400.                 
  1401.             self::$logger->debug('<<checkTableNeedsUpdate ['.$updateRequired.']');
  1402.             return $updateRequired;
  1403.         }else{                
  1404.             throw new AlphaException('Failed to access the system database correctly, error is ['.self::getConnection()->error.']');
  1405.             self::$logger->debug('<<checkTableNeedsUpdate [false]');
  1406.             return false;
  1407.         }
  1408.     }
  1409.     
  1410.     /**
  1411.      * (non-PHPdoc)
  1412.      * @see alpha/model/AlphaDAOProviderInterface::findMissingFields()
  1413.      */
  1414.     public function findMissingFields({
  1415.         self::$logger->debug('>>findMissingFields()');
  1416.         
  1417.         $missingFields array();            
  1418.         $matchCount 0;
  1419.             
  1420.         $sqlQuery 'SHOW COLUMNS FROM '.$this->BO->getTableName();
  1421.         
  1422.         $result self::getConnection()->query($sqlQuery);
  1423.         
  1424.         $this->BO->setLastQuery($sqlQuery);
  1425.             
  1426.         // get the class attributes
  1427.         $reflection new ReflectionClass(get_class($this->BO));
  1428.         $properties $reflection->getProperties();
  1429.         
  1430.         foreach($properties as $propObj{
  1431.             $propName $propObj->name;
  1432.             if (!in_array($propName$this->BO->getTransientAttributes())) {
  1433.                 while ($row $result->fetch_array(MYSQLI_ASSOC)) {
  1434.                     if ($propName == $row['Field']{
  1435.                         $matchCount++;                        
  1436.                         break;
  1437.                     }
  1438.                 }
  1439.                 $result->data_seek(0);
  1440.             }else{
  1441.                 $matchCount++;
  1442.             }
  1443.             
  1444.             if($matchCount==0{                    
  1445.                 array_push($missingFields$propName);
  1446.             }else{
  1447.                 $matchCount 0;
  1448.             }
  1449.         }
  1450.         
  1451.         // check for the "classname" field in overloaded tables
  1452.         if($this->BO->isTableOverloaded()) {
  1453.             $foundMatch false;
  1454.             
  1455.             while ($row $result->fetch_array(MYSQLI_ASSOC)) {
  1456.                 if ('classname' == $row['Field']{
  1457.                     $foundMatch true;
  1458.                     break;
  1459.                 }
  1460.             }
  1461.             if(!$foundMatch)                        
  1462.                 array_push($missingFields'classname');
  1463.         }
  1464.         
  1465.         if (!$result{
  1466.             throw new AlphaException('Failed to access the system database correctly, error is ['.self::getConnection()->error.']');
  1467.         }
  1468.         
  1469.         self::$logger->debug('<<findMissingFields ['.var_export($missingFieldstrue).']');
  1470.         return $missingFields;    
  1471.     }
  1472.     
  1473.     /**
  1474.      * (non-PHPdoc)
  1475.      * @see alpha/model/AlphaDAOProviderInterface::getIndexes()
  1476.      */
  1477.     public function getIndexes({
  1478.         self::$logger->debug('>>getIndexes()');
  1479.         
  1480.         $query 'SHOW INDEX FROM '.$this->BO->getTableName();
  1481.         
  1482.         $result self::getConnection()->query($query);
  1483.         
  1484.         $this->BO->setLastQuery($query);
  1485.         
  1486.         $indexNames array();
  1487.         
  1488.         if (!$result{
  1489.             throw new AlphaException('Failed to access the system database correctly, error is ['.self::getConnection()->error.']');
  1490.         }else{
  1491.             while ($row $result->fetch_array(MYSQLI_ASSOC)) {                
  1492.                 array_push($indexNames$row['Key_name']);
  1493.             }
  1494.         }
  1495.         
  1496.         self::$logger->debug('<<getIndexes');
  1497.         return $indexNames;
  1498.     }
  1499.     
  1500.     /**
  1501.      * Checks to see if all of the indexes are in place for the BO's table, creates those that are missing.
  1502.      * 
  1503.      * @since 1.1
  1504.      */
  1505.     private function checkIndexes({
  1506.         self::$logger->debug('>>checkIndexes()');        
  1507.         
  1508.         $indexNames $this->getIndexes();
  1509.         
  1510.         // process unique keys
  1511.         foreach($this->BO->getUniqueAttributes(as $prop{
  1512.             // check for composite indexes
  1513.             if(strpos($prop'+')) {
  1514.                 $attributes explode('+'$prop);
  1515.                 
  1516.                 $index_exists false;
  1517.                 foreach ($indexNames as $index{
  1518.                     if ($attributes[0].'_'.$attributes[1].'_unq_idx' == $index{
  1519.                         $index_exists true;
  1520.                     }
  1521.                     if(count($attributes== 3{
  1522.                         if ($attributes[0].'_'.$attributes[1].'_'.$attributes[2].'_unq_idx' == $index{
  1523.                             $index_exists true;
  1524.                         }
  1525.                     }
  1526.                 }
  1527.     
  1528.                 if(!$index_exists{
  1529.                     if(count($attributes== 3)
  1530.                         $this->BO->createUniqueIndex($attributes[0]$attributes[1]$attributes[2]);
  1531.                     else
  1532.                         $this->BO->createUniqueIndex($attributes[0]$attributes[1]);
  1533.                 }
  1534.             }else{
  1535.                 $index_exists false;
  1536.                 foreach ($indexNames as $index{                    
  1537.                     if ($prop.'_unq_idx' == $index{
  1538.                         $index_exists true;
  1539.                     }
  1540.                 }
  1541.     
  1542.                 if(!$index_exists)
  1543.                     $this->createUniqueIndex($prop);
  1544.             }
  1545.         }        
  1546.         
  1547.         // process foreign-key indexes
  1548.         // get the class attributes
  1549.         $reflection new ReflectionClass(get_class($this->BO));
  1550.         $properties $reflection->getProperties();
  1551.         
  1552.         foreach($properties as $propObj{
  1553.             $propName $propObj->name;            
  1554.             $prop $this->BO->getPropObject($propName);
  1555.             if($prop instanceof Relation{
  1556.                                 
  1557.                 if($prop->getRelationType(== 'MANY-TO-ONE'{
  1558.                     $indexExists false;
  1559.                     foreach ($indexNames as $index{
  1560.                         if ($propName.'_fk_idx' == $index{
  1561.                             $indexExists true;
  1562.                         }
  1563.                     }
  1564.         
  1565.                     if(!$indexExists{
  1566.                         $this->createForeignIndex($propName$prop->getRelatedClass()$prop->getRelatedClassField());
  1567.                     }
  1568.                 }
  1569.                 
  1570.                 if($prop->getRelationType(== 'MANY-TO-MANY'{                    
  1571.                     $lookup $prop->getLookup();
  1572.                     
  1573.                     if($lookup != null{
  1574.                         try {                    
  1575.                             $lookupIndexNames $lookup->getIndexes();
  1576.                             
  1577.                             // handle index check/creation on left side of Relation
  1578.                             $indexExists false;
  1579.                             foreach ($lookupIndexNames as $index{
  1580.                                 if ('leftID_fk_idx' == $index{
  1581.                                     $indexExists true;
  1582.                                 }
  1583.                             }
  1584.                             
  1585.                             if(!$indexExists{
  1586.                                 $lookup->createForeignIndex('leftID'$prop->getRelatedClass('left')'OID');
  1587.                             }
  1588.                             
  1589.                             // handle index check/creation on right side of Relation
  1590.                             $indexExists false;
  1591.                             foreach ($lookupIndexNames as $index{
  1592.                                 if ('rightID_fk_idx' == $index{
  1593.                                     $indexExists true;
  1594.                                 }
  1595.                             }
  1596.                             
  1597.                             if(!$indexExists{
  1598.                                 $lookup->createForeignIndex('rightID'$prop->getRelatedClass('right')'OID');
  1599.                             }
  1600.                         }catch(AlphaException $e{
  1601.                             self::$logger->error($e->getMessage());
  1602.                         }
  1603.                     }
  1604.                 }
  1605.                 
  1606.             }
  1607.         }
  1608.         
  1609.         self::$logger->debug('<<checkIndexes');
  1610.     }
  1611.     
  1612.     /**
  1613.      * (non-PHPdoc)
  1614.      * @see alpha/model/AlphaDAOProviderInterface::createForeignIndex()
  1615.      */
  1616.     public function createForeignIndex($attributeName$relatedClass$relatedClassAttribute{
  1617.         self::$logger->debug('>>createForeignIndex(attributeName=['.$attributeName.'], relatedClass=['.$relatedClass.'], relatedClassAttribute=['.$relatedClassAttribute.']');
  1618.         
  1619.         $result false;
  1620.         
  1621.         if(self::checkBOTableExists(ucfirst($tableName).'Object')) {
  1622.             $sqlQuery '';
  1623.             
  1624.             if($attributeName == 'leftID')
  1625.                 $sqlQuery 'ALTER TABLE '.$this->BO->getTableName().' ADD INDEX leftID_fk_idx (leftID);';
  1626.             if($attributeName == 'rightID')
  1627.                 $sqlQuery 'ALTER TABLE '.$this->BO->getTableName().' ADD INDEX rightID_fk_idx (rightID);';
  1628.                 
  1629.             if(!empty($sqlQuery)) {
  1630.                 $this->BO->setLastQuery($sqlQuery);
  1631.     
  1632.                 $result self::getConnection()->query($sqlQuery);
  1633.     
  1634.                 if (!$result{
  1635.                     throw new FailedIndexCreateException('Failed to create an index on ['.$this->BO->getTableName().'], error is ['.self::getConnection()->error.'], query ['.$this->BO->getLastQuery().']');
  1636.                 }
  1637.             }
  1638.  
  1639.             $sqlQuery 'ALTER TABLE '.$this->BO->getTableName().' ADD FOREIGN KEY '.$attributeName.'_fk_idx ('.$attributeName.') REFERENCES '.$tableName.' ('.$relatedClassAttribute.') ON DELETE SET NULL;';
  1640.             
  1641.             $this->BO->setLastQuery($sqlQuery);    
  1642.             $result self::getConnection()->query($sqlQuery);
  1643.         }
  1644.     
  1645.         if ($result{
  1646.             self::$logger->debug('Successfully created the foreign key index ['.$attributeName.'_fk_idx]');
  1647.         }else{            
  1648.             throw new FailedIndexCreateException('Failed to create the index ['.$attributeName.'_fk_idx] on ['.$this->BO->getTableName().'], error is ['.self::getConnection()->error.'], query ['.$this->BO->getLastQuery().']');
  1649.         }
  1650.         
  1651.         self::$logger->debug('<<createForeignIndex');
  1652.     }
  1653.     
  1654.     /**
  1655.      * (non-PHPdoc)
  1656.      * @see alpha/model/AlphaDAOProviderInterface::createUniqueIndex()
  1657.      */
  1658.     public function createUniqueIndex($attribute1Name$attribute2Name ''$attribute3Name ''{
  1659.         self::$logger->debug('>>createUniqueIndex(attribute1Name=['.$attribute1Name.'], attribute2Name=['.$attribute2Name.'], attribute3Name=['.$attribute3Name.'])');
  1660.         
  1661.         if($attribute2Name != '' && $attribute3Name != '')
  1662.             $sqlQuery 'CREATE UNIQUE INDEX '.$attribute1Name.'_'.$attribute2Name.'_'.$attribute3Name.'_unq_idx ON '.$this->BO->getTableName().' ('.$attribute1Name.','.$attribute2Name.','.$attribute3Name.');';
  1663.         
  1664.         if($attribute2Name != '' && $attribute3Name == '')
  1665.             $sqlQuery 'CREATE UNIQUE INDEX '.$attribute1Name.'_'.$attribute2Name.'_unq_idx ON '.$this->BO->getTableName().' ('.$attribute1Name.','.$attribute2Name.');';
  1666.             
  1667.         if($attribute2Name == '' && $attribute3Name == '')
  1668.             $sqlQuery 'CREATE UNIQUE INDEX '.$attribute1Name.'_unq_idx ON '.$this->BO->getTableName().' ('.$attribute1Name.');';
  1669.         
  1670.         $this->BO->setLastQuery($sqlQuery);
  1671.         
  1672.         $result self::getConnection()->query($sqlQuery);
  1673.         
  1674.         if ($result{
  1675.             self::$logger->debug('Successfully created the unique index on ['.$this->BO->getTableName().']');
  1676.         }else{
  1677.             throw new FailedIndexCreateException('Failed to create the unique index on ['.$this->BO->getTableName().'], error is ['.self::getConnection()->error.']');
  1678.         }
  1679.         
  1680.         self::$logger->debug('<<createUniqueIndex');
  1681.     }
  1682.     
  1683.     /**
  1684.      * (non-PHPdoc)
  1685.      * @see alpha/model/AlphaDAOProviderInterface::reload()
  1686.      */
  1687.     public function reload({
  1688.         self::$logger->debug('>>reload()');
  1689.         
  1690.         if(!$this->isTransient()) {
  1691.             $this->load($this->getOID());            
  1692.         }else{
  1693.             throw new AlphaException('Cannot reload transient object from database!');
  1694.         }        
  1695.         self::$logger->debug('<<reload');
  1696.     }
  1697.     
  1698.     /**
  1699.      * (non-PHPdoc)
  1700.      * @see alpha/model/AlphaDAOProviderInterface::checkRecordExists()
  1701.      */
  1702.     public function checkRecordExists($OID{
  1703.         self::$logger->debug('>>checkRecordExists(OID=['.$OID.'])');
  1704.         
  1705.         $sqlQuery 'SELECT OID FROM '.$this->BO->getTableName().' WHERE OID = ?;';
  1706.  
  1707.         $this->BO->setLastQuery($sqlQuery);
  1708.         
  1709.         $stmt self::getConnection()->stmt_init();
  1710.         
  1711.         if($stmt->prepare($sqlQuery)) {
  1712.             $stmt->bind_param('i'$OID);
  1713.             
  1714.             $stmt->execute();
  1715.             
  1716.             $result $this->bindResult($stmt);
  1717.                 
  1718.             $stmt->close();
  1719.  
  1720.             if ($result{                    
  1721.                 if(count($result0{
  1722.                     self::$logger->debug('<<checkRecordExists [true]');
  1723.                     return true;
  1724.                 }else{
  1725.                     self::$logger->debug('<<checkRecordExists [false]');
  1726.                     return false;
  1727.                 }
  1728.             }else{
  1729.                 throw new AlphaException('Failed to check for the record ['.$OID.'] on the class ['.get_class($this->BO).'] from the table ['.$this->BO->getTableName().'], query is ['.$this->BO->getLastQuery().']');            
  1730.                 self::$logger->debug('<<checkRecordExists [false]');
  1731.                 return false;
  1732.             }
  1733.         }else{
  1734.             throw new AlphaException('Failed to check for the record ['.$OID.'] on the class ['.get_class($this->BO).'] from the table ['.$this->BO->getTableName().'], query is ['.$this->BO->getLastQuery().']');            
  1735.             self::$logger->debug('<<checkRecordExists [false]');
  1736.             return false;
  1737.         }
  1738.     }
  1739.     
  1740.     /**
  1741.      * (non-PHPdoc)
  1742.      * @see alpha/model/AlphaDAOProviderInterface::isTableOverloaded()
  1743.      */
  1744.     public function isTableOverloaded({
  1745.         self::$logger->debug('>>isTableOverloaded()');
  1746.         
  1747.         $classname get_class($this->BO);
  1748.         $tablename ucfirst($this->BO->getTableName()).'Object';
  1749.         
  1750.         // use reflection to check to see if we are dealing with a persistent type (e.g. DEnum) which are never overloaded
  1751.         $reflection new ReflectionClass($classname);
  1752.         $implementedInterfaces $reflection->getInterfaces();
  1753.         
  1754.         foreach ($implementedInterfaces as $interface{
  1755.             if ($interface->name == 'AlphaTypeInterface'{
  1756.                 self::$logger->debug('<<isTableOverloaded [false]');
  1757.                 return false;
  1758.             }
  1759.         }
  1760.         
  1761.         if($classname != $tablename{
  1762.             // loop over all BOs to see if there is one using the same table as this BO
  1763.             
  1764.             $BOclasses AlphaDAO::getBOClassNames();
  1765.             
  1766.             foreach($BOclasses as $BOclassName{
  1767.                 if($tablename == $BOclassName{
  1768.                     self::$logger->debug('<<isTableOverloaded [true]');
  1769.                     return true;
  1770.                 }
  1771.             }
  1772.             throw new BadBOTableNameException('The table name ['.$tablename.'] for the class ['.$classname.'] is invalid as it does not match a BO definition in the system!');
  1773.             self::$logger->debug('<<isTableOverloaded [false]');
  1774.             return false;
  1775.         }else{
  1776.             // check to see if there is already a "classname" column in the database for this BO
  1777.             
  1778.             $query 'SHOW COLUMNS FROM '.$this->BO->getTableName();
  1779.             
  1780.             $result self::getConnection()->query($query);
  1781.             
  1782.             if($result{
  1783.                 while ($row $result->fetch_array(MYSQLI_ASSOC)) {                        
  1784.                     if ('classname' == $row['Field']{
  1785.                         self::$logger->debug('<<isTableOverloaded [true]');
  1786.                         return true;
  1787.                     }
  1788.                 }
  1789.             }else{
  1790.                 self::$logger->warn('Error during show columns ['.self::getConnection()->error.']');
  1791.             }
  1792.             
  1793.             self::$logger->debug('<<isTableOverloaded [false]');
  1794.             return false;
  1795.         }
  1796.     }
  1797.     
  1798.     /**
  1799.      * (non-PHPdoc)
  1800.      * @see alpha/model/AlphaDAOProviderInterface::begin()
  1801.      */
  1802.     public static function begin({
  1803.         if(self::$logger == null)
  1804.             self::$logger new Logger('AlphaDAOProviderMySQL');
  1805.         self::$logger->debug('>>begin()');         
  1806.          
  1807.          if (!self::getConnection()->autocommit(false))
  1808.              throw new AlphaException('Error beginning a new transaction, error is ['.self::getConnection()->error.']');
  1809.          
  1810.          self::$logger->debug('<<begin');
  1811.     }
  1812.     
  1813.     /**
  1814.      * (non-PHPdoc)
  1815.      * @see alpha/model/AlphaDAOProviderInterface::commit()
  1816.      */
  1817.     public static function commit({
  1818.         if(self::$logger == null)
  1819.             self::$logger new Logger('AlphaDAOProviderMySQL');
  1820.         self::$logger->debug('>>commit()');
  1821.         
  1822.         if (!self::getConnection()->commit())
  1823.              throw new FailedSaveException('Error commiting a transaction, error is ['.self::getConnection()->error.']');
  1824.         
  1825.         self::$logger->debug('<<commit');
  1826.       }
  1827.       
  1828.     /**
  1829.      * (non-PHPdoc)
  1830.      * @see alpha/model/AlphaDAOProviderInterface::rollback()
  1831.      */
  1832.     public static function rollback({
  1833.         if(self::$logger == null)
  1834.             self::$logger new Logger('AlphaDAOProviderMySQL');
  1835.         self::$logger->debug('>>rollback()');
  1836.         
  1837.         if (!self::getConnection()->rollback())
  1838.              throw new AlphaException('Error rolling back a transaction, error is ['.self::getConnection()->error.']');
  1839.         
  1840.         self::$logger->debug('<<rollback');
  1841.       }
  1842.     
  1843.       /**
  1844.        * (non-PHPdoc)
  1845.        * @see alpha/model/AlphaDAOProviderInterface::setBO()
  1846.        */
  1847.     public function setBO($BO{
  1848.         $this->BO $BO;
  1849.     }
  1850.     
  1851.     /**
  1852.        * Dynamically binds all of the attributes for the current BO to the supplied prepared statement
  1853.        * parameters.  If arrays of attribute names and values are provided, only those will be bound to
  1854.        * the supplied statement.
  1855.        *  
  1856.        * @param mysqli_stmt $stmt The SQL statement to bind to.
  1857.        * @param array Optional array of BO attributes.
  1858.        * @param array Optional array of BO values.
  1859.        * @return mysqli_stmt 
  1860.        * @since 1.1
  1861.        */
  1862.     private function bindParams($stmt$attributes=array()$values=array()) {
  1863.         self::$logger->debug('>>bindParams(stmt=['.var_export($stmttrue).'])');
  1864.         
  1865.         $bindingsTypes '';
  1866.         $params array();
  1867.         
  1868.         // here we are only binding the supplied attributes
  1869.         if(count($attributes&& count($attributes== count($values)) {
  1870.     
  1871.             $count count($values);
  1872.             
  1873.             for($i 0$i $count$i++{
  1874.                 if (AlphaValidator::isInteger($values[$i]))
  1875.                     $bindingsTypes .= 'i';
  1876.                 else
  1877.                     $bindingsTypes .= 's';
  1878.                 array_push($params$values[$i]);
  1879.             }
  1880.                 
  1881.             if($this->BO->isTableOverloaded()) {
  1882.                 if(isset($this->classname)) {
  1883.                     $bindingsTypes .= 's';
  1884.                     array_push($params$this->classname);                    
  1885.                 }else{                    
  1886.                     $bindingsTypes .= 's';                    
  1887.                     array_push($paramsget_class($this->BO));                    
  1888.                 }
  1889.             }
  1890.         }else// bind all attributes on the business object
  1891.         
  1892.             // get the class attributes
  1893.             $reflection new ReflectionClass(get_class($this->BO));
  1894.             $properties $reflection->getProperties();
  1895.     
  1896.             foreach($properties as $propObj{
  1897.                 $propName $propObj->name;
  1898.                 if (!in_array($propName$this->BO->getTransientAttributes())) {                
  1899.                     // Skip the OID, database auto number takes care of this.
  1900.                     if($propName != 'OID' && $propName != 'version_num'{
  1901.                         if($this->BO->getPropObject($propNameinstanceof Integer)
  1902.                             $bindingsTypes .= 'i';
  1903.                         else
  1904.                             $bindingsTypes .= 's';
  1905.                         array_push($params$this->BO->get($propName));
  1906.                     }
  1907.                                 
  1908.                     if($propName == 'version_num'{
  1909.                         $temp $this->BO->getVersionNumber()->getValue();
  1910.                         $this->BO->set('version_num'$temp+1);
  1911.                         $bindingsTypes .= 'i';
  1912.                         array_push($params$this->BO->getVersionNumber()->getValue());
  1913.                     }
  1914.                 }
  1915.             }
  1916.                 
  1917.             if($this->BO->isTableOverloaded()) {
  1918.                 if(isset($this->classname)) {
  1919.                     $bindingsTypes .= 's';
  1920.                     array_push($params$this->classname);                    
  1921.                 }else{                    
  1922.                     $bindingsTypes .= 's';                    
  1923.                     array_push($paramsget_class($this->BO));                    
  1924.                 }
  1925.             }
  1926.             
  1927.             // the OID may be on the WHERE clause for UPDATEs and DELETEs
  1928.             if(!$this->BO->isTransient()) {                    
  1929.                 $bindingsTypes .= 'i';
  1930.                 array_push($params$this->BO->getOID());
  1931.             }
  1932.         }
  1933.         
  1934.         self::$logger->debug('bindingsTypes=['.$bindingsTypes.'], count: ['.strlen($bindingsTypes).']');
  1935.         self::$logger->debug('params ['.var_export($paramstrue).']');
  1936.                     
  1937.         if ($params != null{         
  1938.             $bind_names[$bindingsTypes;
  1939.  
  1940.             $count count($params);
  1941.             
  1942.             for ($i 0$i $count$i++{
  1943.                 $bind_name 'bind'.$i;
  1944.                 $$bind_name $params[$i];
  1945.                 $bind_names[&$$bind_name;
  1946.             }         
  1947.  
  1948.             call_user_func_array(array($stmt,'bind_param')$bind_names);
  1949.            }
  1950.         
  1951.            self::$logger->debug('<<bindParams ['.var_export($stmttrue).']');
  1952.         return $stmt;
  1953.     }
  1954.     
  1955.     /**
  1956.      * Dynamically binds the result of the supplied prepared statement to a 2d array, where each element in the array is another array
  1957.      * representing a database row.
  1958.      * 
  1959.      * @param mysqli_stmt $stmt 
  1960.      * @return array A 2D array containing the query result.
  1961.      * @since 1.1
  1962.      */
  1963.     private function bindResult($stmt{
  1964.         $result array();
  1965.      
  1966.         $metadata $stmt->result_metadata();
  1967.           $fields $metadata->fetch_fields();
  1968.  
  1969.           while(true{
  1970.             $pointers array();
  1971.             $row array();
  1972.        
  1973.             $pointers[$stmt;
  1974.             foreach ($fields as $field{
  1975.                   $fieldname $field->name;
  1976.                   $pointers[&$row[$fieldname];
  1977.             }
  1978.        
  1979.             call_user_func_array('mysqli_stmt_bind_result'$pointers);
  1980.        
  1981.             if (!$stmt->fetch())
  1982.                   break;
  1983.        
  1984.                $result[$row;
  1985.           }
  1986.      
  1987.           $metadata->free();
  1988.      
  1989.           return $result;
  1990.     }
  1991.     
  1992.     /**
  1993.      * Parses a MySQL error for the value that violated a unique constraint.
  1994.      * 
  1995.      * @param string $error The MySQL error string.
  1996.      * @since 1.1
  1997.      */
  1998.     private function findOffendingValue($error{
  1999.         self::$logger->debug('>>findOffendingValue(error=['.$error.'])');
  2000.                 
  2001.         $singleQuote1 strpos($error,"'");
  2002.         $singleQuote2 strrpos($error,"'");
  2003.         
  2004.         $value substr($error$singleQuote1($singleQuote2-$singleQuote1)+1);
  2005.         self::$logger->debug('<<findOffendingValue ['.$value.'])');
  2006.         return $value;
  2007.     }
  2008. }
  2009.  
  2010. ?>

Documentation generated on Tue, 13 Dec 2011 20:25:58 +0000 by phpDocumentor 1.4.3