Overview

Packages

  • alpha::controller
  • alpha::controller::front
  • alpha::exceptions
  • alpha::model
  • alpha::model::types
  • alpha::tasks
  • alpha::tests
  • alpha::util
  • alpha::util::cache
  • alpha::util::codehighlight
  • alpha::util::convertors
  • alpha::util::feeds
  • alpha::util::filters
  • alpha::util::graphs
  • alpha::util::helpers
  • alpha::util::metrics
  • alpha::view
  • alpha::view::renderers
  • alpha::view::widgets

Classes

  • AlphaDAO
  • AlphaDAOProviderFactory
  • AlphaDAOProviderMySQL
  • AlphaDAOProviderSQLite
  • ArticleCommentObject
  • ArticleObject
  • ArticleVoteObject
  • BadRequestObject
  • BlacklistedClientObject
  • BlacklistedIPObject
  • PersonObject
  • RightsObject
  • TagObject

Interfaces

  • AlphaDAOProviderInterface
  • Overview
  • Package
  • Class
  • Tree
  • Deprecated
   1: <?php
   2: 
   3: /**
   4:  * Base business object class definition providing DAO storage via the configured provider.
   5:  *
   6:  * @package alpha::model
   7:  * @since 1.0
   8:  * @author John Collins <dev@alphaframework.org>
   9:  * @version $Id: AlphaDAO.inc 1624 2012-12-21 12:17:55Z alphadevx $
  10:  * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
  11:  * @copyright Copyright (c) 2012, John Collins (founder of Alpha Framework).
  12:  * All rights reserved.
  13:  *
  14:  * <pre>
  15:  * Redistribution and use in source and binary forms, with or
  16:  * without modification, are permitted provided that the
  17:  * following conditions are met:
  18:  *
  19:  * * Redistributions of source code must retain the above
  20:  *   copyright notice, this list of conditions and the
  21:  *   following disclaimer.
  22:  * * Redistributions in binary form must reproduce the above
  23:  *   copyright notice, this list of conditions and the
  24:  *   following disclaimer in the documentation and/or other
  25:  *   materials provided with the distribution.
  26:  * * Neither the name of the Alpha Framework nor the names
  27:  *   of its contributors may be used to endorse or promote
  28:  *   products derived from this software without specific
  29:  *   prior written permission.
  30:  *
  31:  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  32:  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  33:  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  34:  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  35:  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  36:  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  37:  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  38:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  39:  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  40:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  41:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
  42:  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  43:  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  44:  * </pre>
  45:  *
  46:  */
  47: abstract class AlphaDAO {
  48:     /**
  49:      * The object ID
  50:      *
  51:      * @var integer
  52:      * @since 1.0
  53:      */
  54:     protected $OID;
  55: 
  56:     /**
  57:      * The last database query run by this object.  Useful for tracing an error.
  58:      *
  59:      * @var string
  60:      * @since 1.0
  61:      */
  62:     protected $lastQuery;
  63: 
  64:     /**
  65:      * The version number of the object, used for locking mechanism
  66:      *
  67:      * @var Integer
  68:      * @since 1.0
  69:      */
  70:     protected $version_num;
  71: 
  72:     /**
  73:      * The timestamp of creation
  74:      *
  75:      * @var Timestamp
  76:      * @since 1.0
  77:      */
  78:     protected $created_ts;
  79: 
  80:     /**
  81:      * The OID of the person who created this BO
  82:      *
  83:      * @var Integer
  84:      * @since 1.0
  85:      */
  86:     protected $created_by;
  87: 
  88:     /**
  89:      * The timestamp of the last update
  90:      *
  91:      * @var Timestamp
  92:      * @since 1.0
  93:      */
  94:     protected $updated_ts;
  95: 
  96:     /**
  97:      * The OID of the person who last updated this BO
  98:      *
  99:      * @var Integer
 100:      * @since 1.0
 101:      */
 102:     protected $updated_by;
 103: 
 104:     /**
 105:      * An array of the names of all of the default attributes of a persistent BO defined in this class
 106:      *
 107:      * @var array
 108:      * @since 1.0
 109:      */
 110:     protected $defaultAttributes = array("OID", "lastQuery", "version_num", "dataLabels", "created_ts", "created_by", "updated_ts", "updated_by", "defaultAttributes", "transientAttributes", "uniqueAttributes", "TABLE_NAME", "logger");
 111: 
 112:     /**
 113:      * An array of the names of all of the transient attributes of a persistent BO which are not saved to the DB
 114:      *
 115:      * @var array
 116:      * @since 1.0
 117:      */
 118:     protected $transientAttributes = array("lastQuery", "dataLabels", "defaultAttributes", "transientAttributes", "uniqueAttributes", "TABLE_NAME", "logger");
 119: 
 120:     /**
 121:      * An array of the uniquely-constained attributes of this persistent BO
 122:      *
 123:      * @var array
 124:      * @since 1.0
 125:      */
 126:     protected $uniqueAttributes = array();
 127: 
 128:     /**
 129:      * An array of the data labels used for displaying class attributes
 130:      *
 131:      * @var array
 132:      * @since 1.0
 133:      */
 134:     protected $dataLabels = array();
 135: 
 136:     /**
 137:      * Trace logger
 138:      *
 139:      * @var Logger
 140:      * @since 1.0
 141:      */
 142:     private static $logger = null;
 143: 
 144:     /**
 145:      * Determines if we will maintain a _history table for this DAO (default is false).
 146:      *
 147:      * @var boolean
 148:      * @since 1.2
 149:      */
 150:     private $maintainHistory = false;
 151: 
 152:     /**
 153:      * The constructor which sets up some housekeeping attributes
 154:      *
 155:      * @since 1.0
 156:      */
 157:     public function __construct() {
 158:         self::$logger = new Logger('AlphaDAO');
 159:         self::$logger->debug('>>__construct()');
 160: 
 161:         $this->version_num = new Integer(0);
 162:         $this->created_ts = new Timestamp(date("Y-m-d H:i:s"));
 163:         $person_ID = (isset($_SESSION['currentUser'])? $_SESSION['currentUser']->getOID(): 0);
 164:         $this->created_by = new Integer($person_ID);
 165:         $this->updated_ts = new Timestamp(date("Y-m-d H:i:s"));
 166:         $this->updated_by = new Integer($person_ID);
 167: 
 168:         self::$logger->debug('<<__construct');
 169:     }
 170: 
 171:     /**
 172:      * Gets the current connection singleton, or creates a new one if none exists
 173:      *
 174:      * @return mysqli
 175:      * @since 1.0
 176:      * @throws AlphaException
 177:      * @deprecated
 178:      */
 179:     public static function getConnection() {
 180:         throw new AlphaException('The static getConnection() method is not longer supported!  Please update your code to instantiate a AlphaDAOProviderInterface '.
 181:         'instance, then invoke the query() method on that instance to run a custom query.');
 182:     }
 183: 
 184:     /**
 185:      * Disconnects the current database connection if one exists
 186:      *
 187:      * @since 1.0
 188:      */
 189:     public static function disconnect() {
 190:         global $config;
 191: 
 192:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), new PersonObject());
 193:         $provider->disconnect();
 194:     }
 195: 
 196:     /**
 197:      * Returns a 2d array, where each element in the array is another array representing a database row.
 198:      *
 199:      * @param string $sqlQuery
 200:      * @return array
 201:      * @since 1.1
 202:      * @throws CustomQueryException
 203:      */
 204:     public function query($sqlQuery) {
 205:         self::$logger->debug('>>query(sqlQuery=['.$sqlQuery.'])');
 206: 
 207:         global $config;
 208: 
 209:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
 210:         $result = $provider->query($sqlQuery);
 211: 
 212:         self::$logger->debug('<<query ['.print_r($result, true).']');
 213:         return $result;
 214:     }
 215: 
 216:     /**
 217:      * Populates the child object with the properties retrived from the database for the object $OID.
 218:      *
 219:      * @param integer $OID The object ID of the business object to load.
 220:      * @since 1.0
 221:      * @throws BONotFoundException
 222:      */
 223:     public function load($OID) {
 224:         self::$logger->debug('>>load(OID=['.$OID.'])');
 225: 
 226:         if(method_exists($this, 'before_load_callback'))
 227:             $this->before_load_callback();
 228: 
 229:         global $config;
 230: 
 231:         $this->OID = $OID;
 232: 
 233:         if($config->get('cache.provider.name') != '' && $this->loadFromCache()) {
 234:             // BO was found in cache
 235:         }else{
 236:             $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
 237:             $provider->load($OID);
 238: 
 239:             if($config->get('cache.provider.name') != '')
 240:                 $this->addToCache();
 241:         }
 242: 
 243:         $this->setEnumOptions();
 244: 
 245:         if(method_exists($this, 'after_load_callback'))
 246:             $this->after_load_callback();
 247: 
 248:         self::$logger->debug('<<load');
 249:     }
 250: 
 251:     /**
 252:      * Populates the child object from the database table by the given attribute value.
 253:      *
 254:      * @param string $atribute The name of the attribute to load the object by.
 255:      * @param string $value The value of the attribute to load the object by.
 256:      * @param boolean $ignoreClassType Default is false, set to true if you want to load from overloaded tables and ignore the class type
 257:      * @param array $loadAttributes The attributes to load from the database to this object (leave blank to load all attributes)
 258:      * @since 1.0
 259:      * @throws BONotFoundException
 260:      */
 261:     public function loadByAttribute($attribute, $value, $ignoreClassType=false, $loadAttributes=array()) {
 262:         self::$logger->debug('>>loadByAttribute(attribute=['.$attribute.'], value=['.$value.'], ignoreClassType=['.$ignoreClassType.'], 
 263:             loadAttributes=['.var_export($loadAttributes, true).'])');
 264: 
 265:         if(method_exists($this, 'before_loadByAttribute_callback'))
 266:                 $this->before_loadByAttribute_callback();
 267: 
 268:         global $config;
 269: 
 270:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
 271:         $provider->loadByAttribute($attribute, $value, $ignoreClassType, $loadAttributes);
 272: 
 273:         $this->setEnumOptions();
 274: 
 275:         if($config->get('cache.provider.name') != '' && count($loadAttributes) == 0) // we will only cache fully-populated BOs
 276:             $this->addToCache();
 277: 
 278:         if(method_exists($this, 'after_loadByAttribute_callback'))
 279:             $this->after_loadByAttribute_callback();
 280: 
 281:         self::$logger->debug('<<loadByAttribute');
 282:     }
 283: 
 284:     /**
 285:      * Loads all of the objects of this class into an array which is returned.
 286:      *
 287:      * @param integer $start The start of the SQL LIMIT clause, useful for pagination.
 288:      * @param integer $limit The amount (limit) of objects to load, useful for pagination.
 289:      * @param string $orderBy The name of the field to sort the objects by.
 290:      * @param string $order The order to sort the objects by.
 291:      * @param boolean $ignoreClassType Default is false, set to true if you want to load from overloaded tables and ignore the class type
 292:      * @return array An array containing objects of this type of business object.
 293:      * @since 1.0
 294:      * @throws BONotFoundException
 295:      */
 296:     public function loadAll($start=0, $limit=0, $orderBy='OID', $order='ASC', $ignoreClassType=false) {
 297:         self::$logger->debug('>>loadAll(start=['.$start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.']');
 298: 
 299:         if(method_exists($this, 'before_loadAll_callback'))
 300:             $this->before_loadAll_callback();
 301: 
 302:         global $config;
 303: 
 304:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
 305:         $objects = $provider->loadAll($start, $limit, $orderBy, $order, $ignoreClassType);
 306: 
 307:         if(method_exists($this, 'after_loadAll_callback'))
 308:             $this->after_loadAll_callback();
 309: 
 310:         self::$logger->debug('<<loadAll ['.count($objects).']');
 311:         return $objects;
 312:     }
 313: 
 314:     /**
 315:      * Loads all of the objects of this class by the specified attribute into an array which is returned.
 316:      *
 317:      * @param string $atribute The attribute to load the objects by.
 318:      * @param string $value The value of the attribute to load the objects by.
 319:      * @param integer $start The start of the SQL LIMIT clause, useful for pagination.
 320:      * @param integer $limit The amount (limit) of objects to load, useful for pagination.
 321:      * @param string $orderBy The name of the field to sort the objects by.
 322:      * @param string $order The order to sort the objects by.
 323:      * @param boolean $ignoreClassType Default is false, set to true if you want to load from overloaded tables and ignore the class type.
 324:      * @param array $constructorArgs An optional array of contructor arguements to pass to the BOs that will be generated and returned.  Supports a maximum of 5 arguements.
 325:      * @return array An array containing objects of this type of business object.
 326:      * @since 1.0
 327:      * @throws BONotFoundException
 328:      * @throws IllegalArguementException
 329:      */
 330:     public function loadAllByAttribute($attribute, $value, $start=0, $limit=0, $orderBy="OID", $order="ASC", $ignoreClassType=false, $constructorArgs=array()) {
 331:         self::$logger->debug('>>loadAllByAttribute(attribute=['.$attribute.'], value=['.$value.'], start=['.$start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.'], constructorArgs=['.print_r($constructorArgs, true).']');
 332: 
 333:         if(method_exists($this, 'before_loadAllByAttribute_callback'))
 334:             $this->before_loadAllByAttribute_callback();
 335: 
 336:         global $config;
 337: 
 338:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
 339:         $objects = $provider->loadAllByAttribute($attribute, $value, $start, $limit, $orderBy, $order, $ignoreClassType);
 340: 
 341:         if(method_exists($this, 'after_loadAllByAttribute_callback'))
 342:             $this->after_loadAllByAttribute_callback();
 343: 
 344:         self::$logger->debug('<<loadAllByAttribute ['.count($objects).']');
 345:         return $objects;
 346:     }
 347: 
 348:     /**
 349:      * Loads all of the objects of this class by the specified attributes into an array which is returned.
 350:      *
 351:      * @param array $atributes The attributes to load the objects by.
 352:      * @param array $values The values of the attributes to load the objects by.
 353:      * @param integer $start The start of the SQL LIMIT clause, useful for pagination.
 354:      * @param integer $limit The amount (limit) of objects to load, useful for pagination.
 355:      * @param string $orderBy The name of the field to sort the objects by.
 356:      * @param string $order The order to sort the objects by.
 357:      * @param boolean $ignoreClassType Default is false, set to true if you want to load from overloaded tables and ignore the class type
 358:      * @return array An array containing objects of this type of business object.
 359:      * @since 1.0
 360:      * @throws BONotFoundException
 361:      * @throws IllegalArguementException
 362:      */
 363:     public function loadAllByAttributes($attributes=array(), $values=array(), $start=0, $limit=0, $orderBy='OID', $order='ASC', $ignoreClassType=false) {
 364:         self::$logger->debug('>>loadAllByAttributes(attributes=['.var_export($attributes, true).'], values=['.var_export($values, true).'], start=['.
 365:             $start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.']');
 366: 
 367:         if(method_exists($this, 'before_loadAllByAttributes_callback'))
 368:             $this->before_loadAllByAttributes_callback();
 369: 
 370:         global $config;
 371: 
 372:         if(!is_array($attributes) || !is_array($values)) {
 373:             throw new IllegalArguementException('Illegal arrays attributes=['.var_export($attributes, true).'] and values=['.var_export($values, true).
 374:                 '] provided to loadAllByAttributes');
 375:         }
 376: 
 377:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
 378:         $objects = $provider->loadAllByAttributes($attributes, $values, $start, $limit, $orderBy, $order, $ignoreClassType);
 379: 
 380:         if(method_exists($this, 'after_loadAllByAttributes_callback'))
 381:             $this->after_loadAllByAttributes_callback();
 382: 
 383:         self::$logger->debug('<<loadAllByAttributes ['.count($objects).']');
 384:         return $objects;
 385:     }
 386: 
 387:     /**
 388:      * Loads all of the objects of this class that where updated (updated_ts value) on the date indicated.
 389:      *
 390:      * @param string $date The date for which to load the objects updated on, in the format 'YYYY-MM-DD'.
 391:      * @param integer $start The start of the SQL LIMIT clause, useful for pagination.
 392:      * @param integer $limit The amount (limit) of objects to load, useful for pagination.
 393:      * @param string $orderBy The name of the field to sort the objects by.
 394:      * @param string $order The order to sort the objects by.
 395:      * @param boolean $ignoreClassType Default is false, set to true if you want to load from overloaded tables and ignore the class type
 396:      * @return array An array containing objects of this type of business object.
 397:      * @since 1.0
 398:      * @throws BONotFoundException
 399:      */
 400:     public function loadAllByDayUpdated($date, $start=0, $limit=0, $orderBy="OID", $order="ASC", $ignoreClassType=false) {
 401:         self::$logger->debug('>>loadAllByDayUpdated(date=['.$date.'], start=['.$start.'], limit=['.$limit.'], orderBy=['.$orderBy.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.']');
 402: 
 403:         if(method_exists($this, 'before_loadAllByDayUpdated_callback'))
 404:             $this->before_loadAllByDayUpdated_callback();
 405: 
 406:         global $config;
 407: 
 408:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
 409:         $objects = $provider->loadAllByDayUpdated($date, $start, $limit, $orderBy, $order, $ignoreClassType);
 410: 
 411:         if(method_exists($this, 'after_loadAllByDayUpdated_callback'))
 412:             $this->after_loadAllByDayUpdated_callback();
 413: 
 414:         self::$logger->debug('<<loadAllByDayUpdated ['.count($objects).']');
 415:         return $objects;
 416:     }
 417: 
 418:     /**
 419:      * Loads all of the specified attribute values of this class by the specified attribute into an
 420:      * array which is returned.
 421:      *
 422:      * @param string $attribute The attribute name to load the field values by.
 423:      * @param string $value The value of the attribute to load the field values by.
 424:      * @param string $returnAttribute The name of the attribute to return.
 425:      * @param string $order The order to sort the BOs by.
 426:      * @param boolean $ignoreClassType Default is false, set to true if you want to load from overloaded tables and ignore the class type.
 427:      * @return array An array of field values.
 428:      * @since 1.0
 429:      * @throws BONotFoundException
 430:      */
 431:     public function loadAllFieldValuesByAttribute($attribute, $value, $returnAttribute, $order='ASC', $ignoreClassType=false) {
 432:         self::$logger->debug('>>loadAllFieldValuesByAttribute(attribute=['.$attribute.'], value=['.$value.'], returnAttribute=['.$returnAttribute.'], order=['.$order.'], ignoreClassType=['.$ignoreClassType.']');
 433: 
 434:         global $config;
 435: 
 436:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
 437:         $values = $provider->loadAllFieldValuesByAttribute($attribute, $value, $returnAttribute, $order, $ignoreClassType);
 438: 
 439:         self::$logger->debug('<<loadAllFieldValuesByAttribute ['.count($values).']');
 440:         return $values;
 441:     }
 442: 
 443:     /**
 444:      * Saves the object.  If $this->OID is empty or null it will INSERT, otherwise UPDATE.
 445:      *
 446:      * @since 1.0
 447:      * @throws FailedSaveException
 448:      * @throws LockingException
 449:      * @throws ValidationException
 450:      */
 451:     public function save() {
 452:         self::$logger->debug('>>save()');
 453: 
 454:         if(method_exists($this, 'before_save_callback'))
 455:             $this->before_save_callback();
 456: 
 457:         global $config;
 458: 
 459:         // firstly we will validate the object before we try to save it
 460:         if(!$this->validate()) {
 461:             throw new FailedSaveException('Could not save due to a validation error.');
 462:             return;
 463:         }else{
 464:             $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
 465:             $provider->save();
 466: 
 467:             if($config->get('cache.provider.name') != '') {
 468:                 $this->removeFromCache();
 469:                 $this->addToCache();
 470:             }
 471: 
 472:             if(method_exists($this, 'after_save_callback'))
 473:                 $this->after_save_callback();
 474:         }
 475:     }
 476: 
 477:     /**
 478:      * Saves the field specified with the value supplied.  Only works for persistent BOs.  Note that no Alpha type
 479:      * validation is performed with this method!
 480:      *
 481:      * @param string $attribute The name of the attribute to save.
 482:      * @param mixed $value The value of the attribute to save.
 483:      * @since 1.0
 484:      * @throws IllegalArguementException
 485:      * @throws FailedSaveException
 486:      */
 487:     public function saveAttribute($attribute, $value) {
 488:         self::$logger->debug('>>saveAttribute(attribute=['.$attribute.'], value=['.$value.'])');
 489: 
 490:         if(method_exists($this, 'before_saveAttribute_callback'))
 491:             $this->before_saveAttribute_callback();
 492: 
 493:         global $config;
 494: 
 495:         if(!isset($this->$attribute))
 496:             throw new IllegalArguementException('Could not perform save, as the attribute ['.$attribute.'] is not present on the class['.get_class($this).']');
 497: 
 498:         if($this->isTransient())
 499:             throw new FailedSaveException('Cannot perform saveAttribute method on transient BO!');
 500: 
 501:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
 502:         $provider->saveAttribute($attribute, $value);
 503: 
 504:         if($config->get('cache.provider.name') != '') {
 505:             $this->removeFromCache();
 506:             $this->addToCache();
 507:         }
 508: 
 509:         if(method_exists($this, 'after_saveAttribute_callback'))
 510:             $this->after_saveAttribute_callback();
 511: 
 512:         self::$logger->debug('<<saveAttribute');
 513:     }
 514: 
 515:     /**
 516:      * Saves the history of the object in the [tablename]_history table. It will always perform an insert.
 517:      *
 518:      * @since 1.2
 519:      * @throws FailedSaveException
 520:      */
 521:     public function saveHistory() {
 522:         self::$logger->debug('>>saveHistory()');
 523: 
 524:         if(method_exists($this, 'before_saveHistory_callback'))
 525:             $this->before_saveHistory_callback();
 526: 
 527:         global $config;
 528: 
 529:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
 530:         $provider->saveHistory();
 531: 
 532:         if(method_exists($this, 'after_saveHistory_callback'))
 533:             $this->after_saveHistory_callback();
 534:     }
 535: 
 536:     /**
 537:      * Validates the object to be saved.
 538:      *
 539:      * @return boolean
 540:      * @since 1.0
 541:      * @throws ValidationException
 542:      */
 543:     protected function validate() {
 544:         self::$logger->debug('>>validate()');
 545: 
 546:         if(method_exists($this, 'before_validate_callback'))
 547:             $this->before_validate_callback();
 548: 
 549:         $valid = true;
 550: 
 551:         // get the class attributes
 552:         $reflection = new ReflectionClass(get_class($this));
 553:         $properties = $reflection->getProperties();
 554: 
 555:         foreach($properties as $propObj) {
 556:             $propName = $propObj->name;
 557:             if(!in_array($propName, $this->defaultAttributes) && !in_array($propName, $this->transientAttributes)) {
 558:                 $propClass = get_class($this->getPropObject($propName));
 559:                 if (strtoupper($propClass) != "ENUM" &&
 560:                 strtoupper($propClass) != "DENUM" &&
 561:                 strtoupper($propClass) != "DENUMITEM" && 
 562:                 strtoupper($propClass) != "BOOLEAN") {
 563:                     if ($this->getPropObject($propName) != false && !preg_match($this->getPropObject($propName)->getRule(), $this->getPropObject($propName)->getValue())) {
 564:                         throw new ValidationException('Failed to save, validation error is: '.$this->getPropObject($propName)->getHelper());
 565:                         $valid = false;
 566:                     }
 567:                 }
 568:             }
 569:         }
 570: 
 571:         if(method_exists($this, 'after_validate_callback'))
 572:             $this->after_validate_callback();
 573: 
 574:         self::$logger->debug('<<validate ['.$valid.']');
 575:         return $valid;
 576:     }
 577: 
 578:     /**
 579:      * Deletes the current object from the database.
 580:      *
 581:      * @since 1.0
 582:      * @throws FailedDeleteException
 583:      */
 584:     public function delete() {
 585:         self::$logger->debug('>>delete()');
 586: 
 587:         if(method_exists($this, 'before_delete_callback'))
 588:             $this->before_delete_callback();
 589: 
 590:         global $config;
 591: 
 592:         // get the class attributes
 593:         $reflection = new ReflectionClass(get_class($this));
 594:         $properties = $reflection->getProperties();
 595: 
 596:         // check for any relations on this object, then remove them to prevent orphaned data
 597:         foreach($properties as $propObj) {
 598:             $propName = $propObj->name;
 599: 
 600:             if(!$propObj->isPrivate() && isset($this->$propName) && $this->$propName instanceof Relation) {
 601:                 $prop = $this->getPropObject($propName);
 602: 
 603:                 // Handle MANY-TO-MANY rels
 604:                 if($prop->getRelationType() == 'MANY-TO-MANY') {
 605:                     self::$logger->debug('Deleting MANY-TO-MANY lookup objects...');
 606: 
 607:                     try{
 608:                         // check to see if the rel is on this class
 609:                         $side = $prop->getSide(get_class($this));
 610:                     }catch (IllegalArguementException $iae) {
 611:                         $side = $prop->getSide(ucfirst($this->getTableName()).'Object');
 612:                     }
 613: 
 614:                     self::$logger->debug('Side is ['.$side.']'.$this->getOID());
 615: 
 616:                     $lookUp = $prop->getLookup();
 617:                     self::$logger->debug('Lookup object['.var_export($lookUp, true).']');
 618: 
 619:                     // delete all of the old RelationLookup objects for this rel
 620:                     if($side == 'left')
 621:                         $lookUp->deleteAllByAttribute('leftID', $this->getOID());
 622:                     else
 623:                         $lookUp->deleteAllByAttribute('rightID', $this->getOID());
 624:                     self::$logger->debug('...done deleting!');
 625:                 }
 626: 
 627:                 // should set related field values to null (MySQL is doing this for us as-is)
 628:                 if($prop->getRelationType() == 'ONE-TO-MANY' && !$prop->getRelatedClass() == 'TagObject') {
 629:                     $relatedObjects = $prop->getRelatedObjects();
 630: 
 631:                     foreach($relatedObjects as $object) {
 632:                         $object->set($prop->getRelatedClassField(), null);
 633:                         $object->save();
 634:                     }
 635:                 }
 636: 
 637:                 // in the case of tags, we will always remove the related tags once the BO is deleted
 638:                 if($prop->getRelationType() == 'ONE-TO-MANY' && $prop->getRelatedClass() == 'TagObject') {
 639:                     // just making sure that the Relation is set to current OID as its transient
 640:                     $prop->setValue($this->getOID());
 641:                     $relatedObjects = $prop->getRelatedObjects();
 642: 
 643:                     foreach($relatedObjects as $object) {
 644:                         $object->delete();
 645:                     }
 646:                 }
 647:             }
 648:         }
 649: 
 650:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
 651:         $provider->delete();
 652: 
 653:         if($config->get('cache.provider.name') != '')
 654:             $this->removeFromCache();
 655: 
 656:         if(method_exists($this, 'after_delete_callback'))
 657:             $this->after_delete_callback();
 658: 
 659:         $this->clear();
 660:         self::$logger->debug('<<delete');
 661:     }
 662: 
 663:     /**
 664:      * Delete all object instances from the database by the specified attribute matching the value provided.
 665:      *
 666:      * @param string $attribute The name of the field to delete the objects by.
 667:      * @param mixed $value The value of the field to delete the objects by.
 668:      * @return integer The number of rows deleted.
 669:      * @since 1.0
 670:      * @throws FailedDeleteException
 671:      */
 672:     public function deleteAllByAttribute($attribute, $value) {
 673:         self::$logger->debug('>>deleteAllByAttribute(attribute=['.$attribute.'], value=['.$value.'])');
 674: 
 675:         if(method_exists($this, 'before_deleteAllByAttribute_callback'))
 676:             $this->before_deleteAllByAttribute_callback();
 677: 
 678:         try {
 679:             $doomedObjects = $this->loadAllByAttribute($attribute, $value);
 680:             $deletedRowCount = 0;
 681: 
 682:             foreach ($doomedObjects as $object) {
 683:                 $object->delete();
 684:                 $deletedRowCount++;
 685:             }
 686:         }catch (BONotFoundException $bonf) {
 687:             // nothing found to delete
 688:             self::$logger->warn($bonf->getMessage());
 689:             return 0;
 690:         }catch (AlphaException $e) {
 691:             throw new FailedDeleteException('Failed to delete objects, error is ['.$e->getMessage().']');
 692:             self::$logger->debug('<<deleteAllByAttribute [0]');
 693:             return 0;
 694:         }
 695: 
 696:         if(method_exists($this, 'after_deleteAllByAttribute_callback'))
 697:             $this->after_deleteAllByAttribute_callback();
 698: 
 699:         self::$logger->debug('<<deleteAllByAttribute ['.$deletedRowCount.']');
 700:         return $deletedRowCount;
 701:     }
 702: 
 703:     /**
 704:      * Gets the version_num of the object from the database (returns 0 if the BO is not saved yet).
 705:      *
 706:      * @return integer
 707:      * @since 1.0
 708:      * @throws BONotFoundException
 709:      */
 710:     public function getVersion() {
 711:         self::$logger->debug('>>getVersion()');
 712: 
 713:         if(method_exists($this, 'before_getVersion_callback'))
 714:             $this->before_getVersion_callback();
 715: 
 716:         global $config;
 717: 
 718:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
 719:         $ver = $provider->getVersion();
 720: 
 721:         if(method_exists($this, 'after_getVersion_callback'))
 722:             $this->after_getVersion_callback();
 723: 
 724:         self::$logger->debug('<<getVersion ['.$ver.']');
 725:         return $ver;
 726:     }
 727: 
 728:     /**
 729:      * Builds a new database table for the BO class.
 730:      *
 731:      * @since 1.0
 732:      * @throws AlphaException
 733:      */
 734:     public function makeTable() {
 735:         self::$logger->debug('>>makeTable()');
 736: 
 737:         if(method_exists($this, 'before_makeTable_callback'))
 738:             $this->before_makeTable_callback();
 739: 
 740:         global $config;
 741: 
 742:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
 743:         $provider->makeTable();
 744: 
 745:         if(method_exists($this, 'after_makeTable_callback'))
 746:             $this->after_makeTable_callback();
 747: 
 748:         self::$logger->info('Successfully created the table ['.$this->getTableName().'] for the class ['.get_class($this).']');
 749: 
 750:         self::$logger->debug('<<makeTable');
 751:     }
 752: 
 753:     /**
 754:      * Builds a new database table for the BO class to story it's history of changes
 755:      *
 756:      * @since 1.2
 757:      * @throws AlphaException
 758:      */
 759:     public function makeHistoryTable() {
 760:         self::$logger->debug('>>makeHistoryTable()');
 761: 
 762:         if(method_exists($this, 'before_makeHistoryTable_callback'))
 763:             $this->before_makeHistoryTable_callback();
 764: 
 765:         global $config;
 766: 
 767:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
 768:         $provider->makeHistoryTable();
 769: 
 770:         if(method_exists($this, 'after_makeHistoryTable_callback'))
 771:             $this->after_makeHistoryTable_callback();
 772: 
 773:         self::$logger->info('Successfully created the table ['.$this->getTableName().'_history] for the class ['.get_class($this).']');
 774: 
 775:         self::$logger->debug('<<makeHistoryTable');
 776:     }
 777: 
 778:     /**
 779:      * Re-builds the table if the model requirements have changed.  All data is lost!
 780:      *
 781:      * @since 1.0
 782:      * @throws AlphaException
 783:      */
 784:     public function rebuildTable() {
 785:         self::$logger->debug('>>rebuildTable()');
 786: 
 787:         if(method_exists($this, 'before_rebuildTable_callback'))
 788:             $this->before_rebuildTable_callback();
 789: 
 790:         global $config;
 791: 
 792:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
 793:         $provider->rebuildTable();
 794: 
 795:         if(method_exists($this, 'after_rebuildTable_callback'))
 796:             $this->after_rebuildTable_callback();
 797: 
 798:         self::$logger->debug('<<rebuildTable');
 799:     }
 800: 
 801:     /**
 802:      * Drops the table if the model requirements have changed.  All data is lost!
 803:      *
 804:      * @since 1.0
 805:      * @param string $tableName Optional table name, leave blank for the defined table for this class to be dropped
 806:      * @throws AlphaException
 807:      */
 808:     public function dropTable($tableName=null) {
 809:         self::$logger->debug('>>dropTable()');
 810: 
 811:         if(method_exists($this, 'before_dropTable_callback'))
 812:             $this->before_dropTable_callback();
 813: 
 814:         global $config;
 815: 
 816:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
 817:         $provider->dropTable($tableName);
 818: 
 819:         if(method_exists($this, 'after_dropTable_callback'))
 820:             $this->after_dropTable_callback();
 821: 
 822:         self::$logger->debug('<<dropTable');
 823:     }
 824: 
 825:     /**
 826:      * Adds in a new class property without loosing existing data (does an ALTER TABLE query on the
 827:      * database).
 828:      *
 829:      * @param string $propName The name of the new field to add to the database table.
 830:      * @since 1.0
 831:      * @throws AlphaException
 832:      */
 833:     public function addProperty($propName) {
 834:         self::$logger->debug('>>addProperty(propName=['.$propName.'])');
 835: 
 836:         global $config;
 837: 
 838:         if(method_exists($this, 'before_addProperty_callback'))
 839:             $this->before_addProperty_callback();
 840: 
 841:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
 842:         $provider->addProperty($propName);
 843: 
 844:         if(method_exists($this, 'after_addProperty_callback'))
 845:             $this->after_addProperty_callback();
 846: 
 847:         self::$logger->debug('<<addProperty');
 848:     }
 849: 
 850:     /**
 851:      * Populates the current business object from global POST data.
 852:      *
 853:      * @param boolean $filterAll Defaults to false, set to true if you want to strictly filter all POSTed user input using InputFilter::encode.
 854:      * @since 1.0
 855:      */
 856:     public function populateFromPost($filterAll=false) {
 857:         self::$logger->debug('>>populateFromPost(filterAll=['.$filterAll.'])');
 858: 
 859:         if(method_exists($this, 'before_populateFromPost_callback'))
 860:             $this->before_populateFromPost_callback();
 861: 
 862:         // get the class attributes
 863:         $reflection = new ReflectionClass(get_class($this));
 864:         $properties = $reflection->getProperties();
 865: 
 866:         foreach($properties as $propObj) {
 867:             $propName = $propObj->name;
 868: 
 869:             if(!in_array($propName, $this->defaultAttributes) && !in_array($propName, $this->transientAttributes)) {
 870:                 $propClass = get_class($this->getPropObject($propName));
 871: 
 872:                 if(isset($_POST[$propName])) {
 873:                     // we will accept "on" as a valid Boolean when dealing with checkboxes
 874:                     if(strtoupper($propClass) == 'BOOLEAN' && $_POST[$propName] == 'on') {
 875:                         $_POST[$propName] = 1;
 876:                     }
 877: 
 878:                     if (strtoupper($propClass) != 'DATE' && strtoupper($propClass) != 'TIMESTAMP') {
 879:                         if($filterAll) {
 880:                             $this->getPropObject($propName)->setValue(InputFilter::encode($_POST[$propName], false));
 881:                         }else{
 882:                             if(strtoupper($propClass) == 'TEXT' && !$this->getPropObject($propName)->getAllowHTML())
 883:                                 $this->getPropObject($propName)->setValue(InputFilter::encode($_POST[$propName], false));
 884:                             else
 885:                                 $this->getPropObject($propName)->setValue(InputFilter::encode($_POST[$propName], true));
 886:                         }
 887:                     }else{
 888:                         if($filterAll)
 889:                             $this->getPropObject($propName)->populateFromString(InputFilter::encode($_POST[$propName], false));
 890:                         else
 891:                             $this->getPropObject($propName)->populateFromString(InputFilter::encode($_POST[$propName], true));
 892:                     }
 893:                 }
 894:             }
 895:             if ($propName == 'version_num' && isset($_POST['version_num']))
 896:                 $this->version_num->setValue($_POST['version_num']);
 897:         }
 898:         if(method_exists($this, 'after_populateFromPost_callback'))
 899:             $this->after_populateFromPost_callback();
 900: 
 901:         self::$logger->debug('<<populateFromPost');
 902:     }
 903: 
 904:     /**
 905:      * Gets the maximum OID value from the database for this class type.
 906:      *
 907:      * @return integer The maximum OID value in the class table.
 908:      * @since 1.0
 909:      * @throws AlphaException
 910:      */
 911:     public function getMAX() {
 912:         self::$logger->debug('>>getMAX()');
 913: 
 914:         if(method_exists($this, 'before_getMAX_callback'))
 915:             $this->before_getMAX_callback();
 916: 
 917:         global $config;
 918: 
 919:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
 920:         $max = $provider->getMAX();
 921: 
 922:         if(method_exists($this, 'after_getMAX_callback'))
 923:             $this->after_getMAX_callback();
 924: 
 925:         self::$logger->debug('<<getMAX ['.$max.']');
 926:         return $max;
 927:     }
 928: 
 929:     /**
 930:      * Gets the count from the database for the amount of objects of this class.
 931:      *
 932:      * @param array $atributes The attributes to count the objects by (optional).
 933:      * @param array $values The values of the attributes to count the objects by (optional).
 934:      * @return integer
 935:      * @since 1.0
 936:      * @throws AlphaException
 937:      */
 938:     public function getCount($attributes=array(), $values=array()) {
 939:         self::$logger->debug('>>getCount(attributes=['.var_export($attributes, true).'], values=['.var_export($values, true).'])');
 940: 
 941:         if(method_exists($this, 'before_getCount_callback'))
 942:             $this->before_getCount_callback();
 943: 
 944:         global $config;
 945: 
 946:         if(!is_array($attributes) || !is_array($values)) {
 947:             throw new IllegalArguementException('Illegal arrays attributes=['.var_export($attributes, true).'] and values=['.var_export($values, true).'] provided to loadAllByAttributes');
 948:         }
 949: 
 950:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
 951:         $count = $provider->getCount($attributes, $values);
 952: 
 953:         if(method_exists($this, 'after_getCount_callback'))
 954:             $this->after_getCount_callback();
 955: 
 956:         self::$logger->debug('<<getCount ['.$count.']');
 957:         return $count;
 958:     }
 959: 
 960:     /**
 961:      * Gets the count from the database for the amount of entries in the [tableName]_history table for this business object.  Only call
 962:      * this method on classes where maintainHistory = true, otherwise an exception will be thrown.
 963:      *
 964:      * @return integer
 965:      * @since 1.2
 966:      * @throws AlphaException
 967:      */
 968:     public function getHistoryCount() {
 969:         self::$logger->debug('>>getHistoryCount()');
 970: 
 971:         if(method_exists($this, 'before_getHistoryCount_callback'))
 972:             $this->before_getHistoryCount_callback();
 973: 
 974:         global $config;
 975: 
 976:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
 977:         $count = $provider->getHistoryCount();
 978: 
 979:         if(method_exists($this, 'after_getHistoryCount_callback'))
 980:             $this->after_getHistoryCount_callback();
 981: 
 982:         self::$logger->debug('<<getHistoryCount ['.$count.']');
 983:         return $count;
 984:     }
 985: 
 986:     /**
 987:      * Gets the OID for the object in zero-padded format (same as getOID()).  This version is designed
 988:      * to be overridden in case you want to do something different with the ID of your objects outside
 989:      * of the standard 11 digit OID sequence used internally in Alpha.
 990:      *
 991:      * @return integer 11 digit zero-padded OID value.
 992:      * @since 1.0
 993:      */
 994:     public function getID() {
 995:         self::$logger->debug('>>getID()');
 996:         $oid = str_pad($this->OID, 11, '0', STR_PAD_LEFT);
 997:         self::$logger->debug('<<getID ['.$oid.']');
 998:         return $oid;
 999:     }
1000: 
1001:     /**
1002:      * Gets the OID for the object in zero-padded format (same as getID()).  This version is final so cannot
1003:      * be overridden.
1004:      *
1005:      * @return integer 11 digit zero-padded OID value.
1006:      * @since 1.0
1007:      */
1008:     public final function getOID() {
1009:         if(self::$logger == null)
1010:             self::$logger = new Logger('AlphaDAO');
1011:         self::$logger->debug('>>getOID()');
1012:         $oid = str_pad($this->OID, 11, '0', STR_PAD_LEFT);
1013:         self::$logger->debug('<<getOID ['.$oid.']');
1014:         return $oid;
1015:     }
1016: 
1017:     /**
1018:      * Method for getting version number of the object.
1019:      *
1020:      * @return Integer The object version number.
1021:      * @since 1.0
1022:      */
1023:     public function getVersionNumber() {
1024:         self::$logger->debug('>>getVersionNumber()');
1025:         self::$logger->debug('<<getVersionNumber ['.$this->version_num.']');
1026:         return $this->version_num;
1027:     }
1028: 
1029:     /**
1030:      * Populate all of the enum options for this object from the database.
1031:      *
1032:      * @since 1.0
1033:      * @throws AlphaException
1034:      */
1035:     protected function setEnumOptions() {
1036:         self::$logger->debug('>>setEnumOptions()');
1037: 
1038:         if(method_exists($this, 'before_setEnumOptions_callback'))
1039:             $this->before_setEnumOptions_callback();
1040: 
1041:         global $config;
1042: 
1043:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
1044:         try {
1045:             $provider->setEnumOptions();
1046:         }catch (NotImplementedException $e) {
1047:             self::$logger->info($e->getMessage());
1048:         }
1049: 
1050:         self::$logger->debug('<<setEnumOptions');
1051:     }
1052: 
1053:     /**
1054:      * Generic getter method for accessing class properties.  Will use the method get.ucfirst($prop) instead if that
1055:      * method exists at a child level (by default).  Set $noChildMethods to true if you don't want to use any 
1056:      * get.ucfirst($prop) method even if it exists, false otherwise (default).
1057:      *
1058:      * @param string $prop The name of the object property to get.
1059:      * @param boolean $noChildMethods Set to true if you do not want to use getters in the child object, defaults to false.
1060:      * @return mixed The property value.
1061:      * @since 1.0
1062:      * @throws IllegalArguementException
1063:      * @throws AlphaException
1064:      */
1065:     public function get($prop, $noChildMethods = false) {
1066:         if(self::$logger == null)
1067:             self::$logger = new Logger('AlphaDAO');
1068: 
1069:         self::$logger->debug('>>get(prop=['.$prop.'], noChildMethods=['.$noChildMethods.'])');
1070: 
1071:         if(method_exists($this, 'before_get_callback'))
1072:             $this->before_get_callback();
1073: 
1074:         if(empty($prop))
1075:             throw new IllegalArguementException('Cannot call get with empty $prop arguement!');
1076: 
1077:         // handle attributes with a get.ucfirst($prop) method
1078:         if(!$noChildMethods && method_exists($this, 'get'.ucfirst($prop))) {
1079:             if(method_exists($this, 'after_get_callback'))
1080:                 $this->after_get_callback();
1081: 
1082:             self::$logger->debug('<<get ['.eval('return print_r($this->get'.ucfirst($prop).'(), true);').'])');
1083:             return eval('return $this->get'.ucfirst($prop).'();');
1084:         }else{
1085:             // handle attributes with no dedicated child get.ucfirst($prop) method
1086:             if(isset($this->$prop) && is_object($this->$prop) && method_exists($this->$prop, 'getValue')) {
1087:                 if(method_exists($this, 'after_get_callback'))
1088:                     $this->after_get_callback();
1089: 
1090:                 // complex types will have a getValue() method, return the value of that
1091:                 self::$logger->debug('<<get ['.$this->$prop->getValue().'])');
1092:                 return $this->$prop->getValue();
1093:             }elseif(isset($this->$prop)) {
1094:                 if(method_exists($this, 'after_get_callback'))
1095:                     $this->after_get_callback();
1096: 
1097:                 // simple types returned as-is
1098:                 self::$logger->debug('<<get ['.print_r($this->$prop, true).'])');
1099:                 return $this->$prop;
1100:             }else{
1101:                 throw new AlphaException('Could not access the property ['.$prop.'] on the object of class ['.get_class($this).']');
1102:                 self::$logger->debug('<<get [false])');
1103:                 return false;
1104:             }
1105:         }
1106:     }
1107: 
1108:     /**
1109:      * Generic setter method for setting class properties.  Will use the method set.ucfirst($prop) instead if that
1110:      * method exists at a child level (by default).  Set $noChildMethods to true if you don't want to use 
1111:      * any get.ucfirst($prop) method even if it exists, false otherwise (default).
1112:      *
1113:      * @param string $prop The name of the property to set.
1114:      * @param mixed $value The value of the property to set.
1115:      * @param boolean $noChildMethods Set to true if you do not want to use setters in the child object, defaults
1116:      * to false.
1117:      * @since 1.0
1118:      * @throws AlphaException
1119:      */
1120:     public function set($prop, $value, $noChildMethods = false) {
1121:         self::$logger->debug('>>set(prop=['.$prop.'], $value=['.print_r($value, true).'], noChildMethods=['.$noChildMethods.'])');
1122: 
1123:         if(method_exists($this, 'before_set_callback'))
1124:             $this->before_set_callback();
1125: 
1126:         // handle attributes with a set.ucfirst($prop) method
1127:         if(!$noChildMethods && method_exists($this, 'set'.ucfirst($prop))) {
1128:             if(method_exists($this, 'after_set_callback'))
1129:                 $this->after_set_callback();
1130: 
1131:             eval('$this->set'.ucfirst($prop).'($value);');
1132:         }else{
1133:             // handle attributes with no dedicated child set.ucfirst($prop) method
1134:             if(isset($this->$prop)) {
1135:                 if(method_exists($this, 'after_set_callback'))
1136:                     $this->after_set_callback();
1137: 
1138:                 // complex types will have a setValue() method to call
1139:                 if (is_object($this->$prop) && get_class($this->$prop) != false) {
1140:                     if (strtoupper(get_class($this->$prop)) != 'DATE' && strtoupper(get_class($this->$prop)) != 'TIMESTAMP') {
1141:                         $this->$prop->setValue($value);
1142:                     }else{
1143:                         // Date and Timestamp objects have a special setter accepting a string
1144:                         $this->$prop->populateFromString($value);
1145:                     }
1146:                 }else{
1147:                     // simple types set directly
1148:                     $this->$prop = $value;
1149:                 }
1150:             }else{
1151:                 throw new AlphaException('Could not set the property ['.$prop.'] on the object of the class ['.get_class($this).'].  Property may not exist, or else does not have a setValue() method and is private or protected.');
1152:             }
1153:         }
1154:         self::$logger->debug('<<set');
1155:     }
1156: 
1157:     /**
1158:      * Gets the property object rather than the value for complex attributes.  Returns false if 
1159:      * the property exists but is private.
1160:      *
1161:      * @param string $prop The name of the property we are getting.
1162:      * @return AlphaType The complex type object found.
1163:      * @since 1.0
1164:      * @throws IllegalArguementException
1165:      */
1166:     public function getPropObject($prop) {
1167:         self::$logger->debug('>>getPropObject(prop=['.$prop.'])');
1168: 
1169:         if(method_exists($this, 'before_getPropObject_callback'))
1170:             $this->before_getPropObject_callback();
1171: 
1172:         // get the class attributes
1173:         $reflection = new ReflectionClass(get_class($this));
1174:         $properties = $reflection->getProperties();
1175: 
1176:         // firstly, check for private
1177:         $attribute = new ReflectionProperty(get_class($this), $prop);
1178: 
1179:         if($attribute->isPrivate()) {
1180:             if(method_exists($this, 'after_getPropObject_callback'))
1181:                 $this->after_getPropObject_callback();
1182: 
1183:             self::$logger->debug('<<getPropObject [false]');
1184:             return false;
1185:         }
1186: 
1187:         foreach($properties as $propObj) {
1188:             $propName = $propObj->name;
1189: 
1190:             if($prop == $propName) {
1191:                 if(method_exists($this, 'after_getPropObject_callback'))
1192:                     $this->after_getPropObject_callback();
1193: 
1194:                 self::$logger->debug('<<getPropObject ['.var_export($this->$prop, true).']');
1195:                 return $this->$prop;
1196:             }
1197:         }
1198:         throw new IllegalArguementException('Could not access the property object ['.$prop.'] on the object of class ['.get_class($this).']');
1199:         self::$logger->debug('<<getPropObject [false]');
1200:         return false;
1201:     }
1202: 
1203:     /**
1204:      * Checks to see if the table exists in the database for the current business class.
1205:      *
1206:      * @param boolean $checkHistoryTable Set to true if you want to check for the existance of the _history table for this DAO.
1207:      * @return boolean
1208:      * @since 1.0
1209:      * @throws AlphaException
1210:      */
1211:     public function checkTableExists($checkHistoryTable = false) {
1212:         self::$logger->debug('>>checkTableExists()');
1213: 
1214:         if(method_exists($this, 'before_checkTableExists_callback'))
1215:             $this->before_checkTableExists_callback();
1216: 
1217:         global $config;
1218: 
1219:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
1220:         $tableExists = $provider->checkTableExists($checkHistoryTable);
1221: 
1222:         if(method_exists($this, 'after_checkTableExists_callback'))
1223:             $this->after_checkTableExists_callback();
1224: 
1225:         self::$logger->debug('<<checkTableExists ['.$tableExists.']');
1226:         return $tableExists;
1227:     }
1228: 
1229:     /**
1230:      * Static method to check the database and see if the table for the indicated BO class name
1231:      * exists (assumes table name will be $BOClassName less "Object").
1232:      *
1233:      * @param string $BOClassName The name of the business object class we are checking.
1234:      * @param boolean $checkHistoryTable Set to true if you want to check for the existance of the _history table for this DAO.
1235:      * @return boolean
1236:      * @since 1.0
1237:      * @throws AlphaException
1238:      */
1239:     public static function checkBOTableExists($BOClassName, $checkHistoryTable = false) {
1240:         if(self::$logger == null)
1241:             self::$logger = new Logger('AlphaDAO');
1242:         self::$logger->debug('>>checkBOTableExists(BOClassName=['.$BOClassName.'])');
1243: 
1244:         global $config;
1245: 
1246:         require_once $config->get('app.root').'alpha/model/'.$config->get('db.provider.name').'.inc';
1247:         eval('$tableExists = '.$config->get('db.provider.name').'::checkBOTableExists($BOClassName, $checkHistoryTable);');
1248: 
1249:         self::$logger->debug('<<checkBOTableExists ['.($tableExists ? 'true' : 'false').']');
1250:         return $tableExists;
1251:     }
1252: 
1253:     /**
1254:      * Checks to see if the table in the database matches (for fields) the business class definition, i.e. if the
1255:      * database table is in sync with the class definition.
1256:      *
1257:      * @return boolean
1258:      * @since 1.0
1259:      * @throws AlphaException
1260:      */
1261:     public function checkTableNeedsUpdate() {
1262:         self::$logger->debug('>>checkTableNeedsUpdate()');
1263: 
1264:         global $config;
1265: 
1266:         if(method_exists($this, 'before_checkTableNeedsUpdate_callback'))
1267:             $this->before_checkTableNeedsUpdate_callback();
1268: 
1269:         $tableExists = $this->checkTableExists();
1270: 
1271:         if (!$tableExists) {
1272:             self::$logger->debug('<<checkTableNeedsUpdate [true]');
1273:             return true;
1274:         }else{
1275: 
1276:             $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
1277:             $updateRequired = $provider->checkTableNeedsUpdate();
1278: 
1279:             if(method_exists($this, 'after_checkTableNeedsUpdate_callback'))
1280:                 $this->after_checkTableNeedsUpdate_callback();
1281: 
1282:             self::$logger->debug('<<checkTableNeedsUpdate ['.$updateRequired.']');
1283:             return $updateRequired;
1284:         }
1285:     }
1286: 
1287:     /**
1288:      * Returns an array containing any properties on the class which have not been created on the database 
1289:      * table yet.
1290:      *
1291:      * @return array An array of missing fields in the database table.
1292:      * @since 1.0
1293:      * @throws AlphaException
1294:      */
1295:     public function findMissingFields() {
1296:         self::$logger->debug('>>findMissingFields()');
1297: 
1298:         global $config;
1299: 
1300:         if(method_exists($this, 'before_findMissingFields_callback'))
1301:             $this->before_findMissingFields_callback();
1302: 
1303:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
1304:         $missingFields = $provider->findMissingFields();
1305: 
1306:         if(method_exists($this, 'after_findMissingFields_callback'))
1307:             $this->after_findMissingFields_callback();
1308: 
1309:         self::$logger->debug('<<findMissingFields ['.var_export($missingFields, true).']');
1310:         return $missingFields;
1311:     }
1312: 
1313:     /**
1314:      * Getter for the TABLE_NAME, which should be set by a child of this class.
1315:      *
1316:      * @return string The table name in the database.
1317:      * @since 1.0
1318:      * @throws AlphaException
1319:      */
1320:     public function getTableName() {
1321:         self::$logger->debug('>>getTableName()');
1322: 
1323:         eval('$TABLE_NAME = '.get_class($this).'::TABLE_NAME;');
1324: 
1325:         if(!empty($TABLE_NAME)) {
1326:             self::$logger->debug('<<getTableName ['.$TABLE_NAME.']');
1327:             return $TABLE_NAME;
1328:         }else{
1329:             throw new AlphaException('Error: no TABLE_NAME constant set for the class '.get_class($this));
1330:             self::$logger->debug('<<getTableName []');
1331:             return '';
1332:         }
1333:     }
1334: 
1335:     /**
1336:      * Method for getting the OID of the person who created this BO.
1337:      *
1338:      * @return Integer The OID of the creator.
1339:      * @since 1.0
1340:      */
1341:     public function getCreatorId() {
1342:         self::$logger->debug('>>getCreatorId()');
1343:         self::$logger->debug('<<getCreatorId ['.$this->created_by.']');
1344:         return $this->created_by;
1345:     }
1346: 
1347:     /**
1348:      * Method for getting the OID of the person who updated this BO.
1349:      *
1350:      * @return Integer The OID of the updator.
1351:      * @since 1.0
1352:      */
1353:     public function getUpdatorId() {
1354:         self::$logger->debug('>>getUpdatorId()');
1355:         self::$logger->debug('<<getUpdatorId ['.$this->updated_by.']');
1356:         return $this->updated_by;
1357:     }
1358: 
1359:     /**
1360:      * Method for getting the date/time of when the BO was created.
1361:      *
1362:      * @return Timestamp
1363:      * @since 1.0
1364:      */
1365:     public function getCreateTS() {
1366:         self::$logger->debug('>>getCreateTS()');
1367:         self::$logger->debug('<<getCreateTS ['.$this->created_ts.']');
1368:         return $this->created_ts;
1369:     }
1370: 
1371:     /**
1372:      * Method for getting the date/time of when the BO was last updated.
1373:      *
1374:      * @return Timestamp
1375:      * @since 1.0
1376:      */
1377:     public function getUpdateTS() {
1378:         self::$logger->debug('>>getUpdateTS()');
1379:         self::$logger->debug('<<getUpdateTS ['.$this->updated_ts.']');
1380:         return $this->updated_ts;
1381:     }
1382: 
1383:     /**
1384:      * Adds the name of the attribute provided to the list of transient (non-saved) attributes for this BO.
1385:      *
1386:      * @param string $attributeName The name of the attribute to not save.
1387:      * @since 1.0
1388:      */
1389:     public function markTransient($attributeName) {
1390:         self::$logger->debug('>>markTransient(attributeName=['.$attributeName.'])');
1391:         self::$logger->debug('<<markTransient');
1392:         array_push($this->transientAttributes, $attributeName);
1393:     }
1394: 
1395:     /**
1396:      * Removes the name of the attribute provided from the list of transient (non-saved) attributes for this BO, 
1397:      * ensuring that it will be saved on the next attempt.
1398:      *
1399:      * @param string $attributeName The name of the attribute to save.
1400:      * @since 1.0
1401:      */
1402:     public function markPersistent($attributeName) {
1403:         self::$logger->debug('>>markPersistent(attributeName=['.$attributeName.'])');
1404:         self::$logger->debug('<<markPersistent');
1405:         $this->transientAttributes = array_diff($this->transientAttributes, array($attributeName));
1406:     }
1407: 
1408:     /**
1409:      * Adds the name of the attribute(s) provided to the list of unique (constrained) attributes for this BO.
1410:      *
1411:      * @param string $attribute1Name The first attribute to mark unique in the database.
1412:      * @param string $attribute2Name The second attribute to mark unique in the databse (optional, use only for composite keys).
1413:      * @param string $attribute3Name The third attribute to mark unique in the databse (optional, use only for composite keys).
1414:      * @since 1.0
1415:      */
1416:     protected function markUnique($attribute1Name, $attribute2Name='', $attribute3Name='') {
1417:         self::$logger->debug('>>markUnique(attribute1Name=['.$attribute1Name.'], attribute2Name=['.$attribute2Name.'], attribute3Name=['.$attribute3Name.'])');
1418: 
1419:         if(empty($attribute2Name)) {
1420:             array_push($this->uniqueAttributes, $attribute1Name);
1421:         }else{
1422:             // Process composite unique keys: add them seperated by a + sign
1423:             if($attribute3Name == '')
1424:                 $attributes = $attribute1Name.'+'.$attribute2Name;
1425:             else
1426:                 $attributes = $attribute1Name.'+'.$attribute2Name.'+'.$attribute3Name;
1427: 
1428:             array_push($this->uniqueAttributes, $attributes);
1429:         }
1430: 
1431:         self::$logger->debug('<<markUnique');
1432:     }
1433: 
1434:     /**
1435:      * Returns the array of names of unique attributes on this BO
1436:      *
1437:      * @return array
1438:      * @since 1.1
1439:      */
1440:     public function getUniqueAttributes() {
1441:         self::$logger->debug('>>getUniqueAttributes()');
1442:         self::$logger->debug('<<getUniqueAttributes: ['.print_r($this->uniqueAttributes, true).']');
1443:         return $this->uniqueAttributes;
1444:     }
1445: 
1446:     /**
1447:      * Gets an array of all of the names of the active database indexes for this class.
1448:      *
1449:      * @return array An array of database indexes on this table.
1450:      * @since 1.0
1451:      * @throws AlphaException
1452:      */
1453:     public function getIndexes() {
1454:         self::$logger->debug('>>getIndexes()');
1455: 
1456:         global $config;
1457: 
1458:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
1459:         $indexNames = $provider->getIndexes();
1460: 
1461:         self::$logger->debug('<<getIndexes ['.print_r($indexNames, true).']');
1462:         return $indexNames;
1463:     }
1464: 
1465:     /**
1466:      * Creates a foreign key constraint (index) in the database on the given attribute.
1467:      *
1468:      * @param string $attributeName The name of the attribute to apply the index on.
1469:      * @param string $relatedClass The name of the related class in the format "NameObject".
1470:      * @param string $relatedClassAttribute The name of the field to relate to on the related class.
1471:      * @param bool $allowNullValues For foreign key indexes that don't allow null values, set this to false (default is true).
1472:      * @since 1.0
1473:      * @throws FailedIndexCreateException
1474:      */
1475:     public function createForeignIndex($attributeName, $relatedClass, $relatedClassAttribute) {
1476:         self::$logger->debug('>>createForeignIndex(attributeName=['.$attributeName.'], relatedClass=['.$relatedClass.'], relatedClassAttribute=['.$relatedClassAttribute.']');
1477: 
1478:         global $config;
1479: 
1480:         if(method_exists($this, 'before_createForeignIndex_callback'))
1481:             $this->before_createForeignIndex_callback();
1482: 
1483:         AlphaDAO::loadClassDef($relatedClass);
1484:         $relatedBO = new $relatedClass;
1485:         $tableName = $relatedBO->getTableName();
1486: 
1487:         // if the relation is on itself (table-wise), exit without attempting to create the foreign keys
1488:         if($this->getTableName() == $tableName) {
1489:             self::$logger->debug('<<createForeignIndex');
1490:             return;
1491:         }
1492: 
1493:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
1494:         $provider->createForeignIndex($attributeName, $relatedClass, $relatedClassAttribute);
1495: 
1496:         if(method_exists($this, 'after_createForeignIndex_callback'))
1497:             $this->after_createForeignIndex_callback();
1498: 
1499:         self::$logger->debug('<<createForeignIndex');
1500:     }
1501: 
1502:     /**
1503:      * Creates a unique index in the database on the given attribute(s).
1504:      *
1505:      * @param string $attribute1Name The first attribute to mark unique in the database.
1506:      * @param string $attribute2Name The second attribute to mark unique in the databse (optional, use only for composite keys).
1507:      * @param string $attribute3Name The third attribute to mark unique in the databse (optional, use only for composite keys).
1508:      * @since 1.0
1509:      * @throws FailedIndexCreateException
1510:      */
1511:     public function createUniqueIndex($attribute1Name, $attribute2Name = '', $attribute3Name = '') {
1512:         self::$logger->debug('>>createUniqueIndex(attribute1Name=['.$attribute1Name.'], attribute2Name=['.$attribute2Name.'], attribute3Name=['.$attribute3Name.'])');
1513: 
1514:         if(method_exists($this, 'before_createUniqueIndex_callback'))
1515:             $this->before_createUniqueIndex_callback();
1516: 
1517:         global $config;
1518: 
1519:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
1520:         $provider->createUniqueIndex($attribute1Name, $attribute2Name, $attribute3Name);
1521: 
1522:         if(method_exists($this, 'after_createUniqueIndex_callback'))
1523:             $this->before_createUniqueIndex_callback();
1524: 
1525:         self::$logger->debug('<<createUniqueIndex');
1526:     }
1527: 
1528:     /**
1529:      * Gets the data labels array.
1530:      *
1531:      * @return array An array of attribute labels.
1532:      * @since 1.0
1533:      */
1534:     public function getDataLabels() {
1535:         self::$logger->debug('>>getDataLabels()');
1536:         self::$logger->debug('<<getDataLabels() ['.var_export($this->dataLabels, true).'])');
1537:         return $this->dataLabels;
1538:     }
1539: 
1540:     /**
1541:      * Sets the data labels array.
1542:      *
1543:      * @param array $labels
1544:      * @throws IllegalArguementException
1545:      * @since 1.2
1546:      */
1547:     public function setDataLabels($labels) {
1548:         self::$logger->debug('>>setDataLabels(labels=['.print_r($labels, true).'])');
1549: 
1550:         if(is_array($labels))
1551:             $this->dataLabels = $labels;
1552:         else
1553:             throw new IllegalArguementException('The value ['.print_r($labels, true).'] provided to setDataLabels() is not a valid array!');
1554: 
1555:         self::$logger->debug('<<setDataLabels()');
1556:     }
1557: 
1558:     /**
1559:      * Gets the data label for the given attribute name.
1560:      *
1561:      * @param $att The attribute name to get the label for.
1562:      * @return string
1563:      * @since 1.0
1564:      * @throws IllegalArguementException
1565:      */
1566:     public function getDataLabel($att) {
1567:         self::$logger->debug('>>getDataLabel(att=['.$att.'])');
1568: 
1569:         if(in_array($att, array_keys($this->dataLabels))) {
1570:             self::$logger->debug('<<getDataLabel ['.$this->dataLabels[$att].'])');
1571:             return $this->dataLabels[$att];
1572:         }else{
1573:             throw new IllegalArguementException('No data label found on the class ['.get_class($this).'] for the attribute ['.$att.']');
1574:             self::$logger->debug('<<getDataLabel [])');
1575:             return '';
1576:         }
1577:     }
1578: 
1579:     /**
1580:      * Loops over the core and custom BO directories and builds an array of all of the BO class names in the system.
1581:      *
1582:      * @return array An array of business object class names.
1583:      * @since 1.0
1584:      */
1585:     public static function getBOClassNames() {
1586:         if(self::$logger == null)
1587:             self::$logger = new Logger('AlphaDAO');
1588:         self::$logger->debug('>>getBOClassNames()');
1589: 
1590:         global $config;
1591: 
1592:         $classNameArray = array();
1593: 
1594: 
1595:         if(file_exists($config->get('app.root').'model')) { // it is possible it has not been created yet...
1596:             // first get any custom BOs
1597:             $handle = opendir($config->get('app.root').'model');
1598: 
1599:             // loop over the business object directory
1600:             while (false !== ($file = readdir($handle))) {
1601:                 if (preg_match("/Object.inc/", $file)) {
1602:                     $classname = substr($file, 0, -4);
1603: 
1604:                     array_push($classNameArray, $classname);
1605:                 }
1606:             }
1607:         }
1608: 
1609:         // now loop over the core BOs provided with Alpha
1610: 
1611:         $handle = opendir($config->get('app.root').'alpha/model');
1612: 
1613:         // loop over the business object directory
1614:         while (false !== ($file = readdir($handle))) {
1615:             if (preg_match("/Object.inc/", $file)) {
1616:                 $classname = substr($file, 0, -4);
1617: 
1618:                 array_push($classNameArray, $classname);
1619:             }
1620:         }
1621: 
1622:         asort($classNameArray);
1623:         self::$logger->debug('<<getBOClassNames ['.var_export($classNameArray, true).']');
1624:         return $classNameArray;
1625:     }
1626: 
1627:     /**
1628:      * Get the array of default attribute names.
1629:      *
1630:      * @return array An array of attribute names.
1631:      * @since 1.0
1632:      */
1633:     public function getDefaultAttributes() {
1634:         self::$logger->debug('>>getDefaultAttributes()');
1635:         self::$logger->debug('<<getDefaultAttributes ['.var_export($this->defaultAttributes, true).']');
1636:         return $this->defaultAttributes;
1637:     }
1638: 
1639:     /**
1640:      * Get the array of transient attribute names.
1641:      *
1642:      * @return array An array of attribute names.
1643:      * @since 1.0
1644:      */
1645:     public function getTransientAttributes() {
1646:         self::$logger->debug('>>getTransientAttributes()');
1647:         self::$logger->debug('<<getTransientAttributes ['.var_export($this->transientAttributes, true).']');
1648:         return $this->transientAttributes;
1649:     }
1650: 
1651:     /**
1652:      * Get the array of persistent attribute names, i.e. those that are saved in the database.
1653:      *
1654:      * @return array An array of attribute names.
1655:      * @since 1.0
1656:      */
1657:     public function getPersistentAttributes() {
1658:         self::$logger->debug('>>getPersistentAttributes()');
1659: 
1660:         $attributes = array();
1661: 
1662:         // get the class attributes
1663:         $reflection = new ReflectionClass(get_class($this));
1664:         $properties = $reflection->getProperties();
1665: 
1666:         foreach($properties as $propObj) {
1667:             $propName = $propObj->name;
1668: 
1669:             // filter transient attributes
1670:             if(!in_array($propName, $this->transientAttributes)) {
1671:                 array_push($attributes, $propName);
1672:             }
1673:         }
1674: 
1675:         self::$logger->debug('<<getPersistentAttributes ['.var_export($attributes, true).']');
1676:         return $attributes;
1677:     }
1678: 
1679:     /**
1680:      * Setter for the Object ID (OID)
1681:      *
1682:      * @param integer $OID The Object ID.
1683:      * @since 1.0
1684:      */
1685:     public function setOID($OID) {
1686:         self::$logger->debug('>>setOID(OID=['.$OID.'])');
1687:         self::$logger->debug('<<setOID');
1688:         $this->OID = $OID;
1689:     }
1690: 
1691:     /**
1692:      * Inspector to see if the business object is transient (not presently stored in the database).
1693:      *
1694:      * @return boolean
1695:      * @since 1.0
1696:      */
1697:     public function isTransient() {
1698:         self::$logger->debug('>>isTransient()');
1699: 
1700:         if(empty($this->OID) || !isset($this->OID) || $this->OID == '00000000000') {
1701:             self::$logger->debug('<<isTransient [true]');
1702:             return true;
1703:         }else{
1704:             self::$logger->debug('<<isTransient [false]');
1705:             return false;
1706:         }
1707:     }
1708: 
1709:     /**
1710:      * Get the last database query run on this object.
1711:      *
1712:      * @return string An SQL query string.
1713:      * @since 1.0
1714:      */
1715:     public function getLastQuery() {
1716:         self::$logger->debug('>>getLastQuery()');
1717:         self::$logger->debug('<<getLastQuery ['.$this->lastQuery.']');
1718:         return $this->lastQuery;
1719:     }
1720: 
1721:     /**
1722:      * Unsets all of the attributes of this object to null.
1723:      *
1724:      * @since 1.0
1725:      */
1726:     private function clear() {
1727:         self::$logger->debug('>>clear()');
1728: 
1729:         // get the class attributes
1730:         $reflection = new ReflectionClass(get_class($this));
1731:         $properties = $reflection->getProperties();
1732: 
1733:         foreach($properties as $propObj) {
1734:             $propName = $propObj->name;
1735:             if(!$propObj->isPrivate())
1736:                 unset($this->$propName);
1737:         }
1738: 
1739:         self::$logger->debug('<<clear');
1740:     }
1741: 
1742:     /**
1743:      * Reloads the object from the database, overwritting any attribute values in memory.
1744:      *
1745:      * @since 1.0
1746:      * @throws AlphaException
1747:      */
1748:     public function reload() {
1749:         self::$logger->debug('>>reload()');
1750: 
1751:         if(!$this->isTransient()) {
1752:             $this->load($this->getOID());
1753:         }else{
1754:             throw new AlphaException('Cannot reload transient object from database!');
1755:         }
1756:         self::$logger->debug('<<reload');
1757:     }
1758: 
1759:     /**
1760:      * Loads the definition from the file system for the BO class name provided.
1761:      *
1762:      * @param string $classname The name of the business object class name.
1763:      * @since 1.0
1764:      * @throws IllegalArguementException
1765:      */
1766:     public static function loadClassDef($classname) {
1767:         if(self::$logger == null)
1768:             self::$logger = new Logger('AlphaDAO');
1769:         self::$logger->debug('>>loadClassDef(classname=['.$classname.'])');
1770: 
1771:         global $config;
1772: 
1773:         if(file_exists($config->get('app.root').'model/'.$classname.'.inc'))
1774:             require_once $config->get('app.root').'model/'.$classname.'.inc';
1775:         elseif(file_exists($config->get('app.root').'alpha/model/'.$classname.'.inc'))
1776:             require_once $config->get('app.root').'alpha/model/'.$classname.'.inc';
1777:         elseif(file_exists($config->get('app.root').'alpha/model/types/'.$classname.'.inc'))
1778:             require_once $config->get('app.root').'alpha/model/types/'.$classname.'.inc';
1779:         else
1780:             throw new IllegalArguementException('The class ['.$classname.'] is not defined anywhere!');
1781: 
1782:         self::$logger->debug('<<loadClassDef');
1783:     }
1784: 
1785:     /**
1786:      * Checks if the definition for the BO class name provided exists on the file system.
1787:      *
1788:      * @param string $classname The name of the business object class name.
1789:      * @return boolean
1790:      * @since 1.0
1791:      */
1792:     public static function checkClassDefExists($classname) {
1793:         if(self::$logger == null)
1794:             self::$logger = new Logger('AlphaDAO');
1795:         self::$logger->debug('>>checkClassDefExists(classname=['.$classname.'])');
1796: 
1797:         global $config;
1798: 
1799:         $exists = false;
1800: 
1801:         if(file_exists($config->get('app.root').'model/'.$classname.'.inc'))
1802:             $exists = true;
1803:         if(file_exists($config->get('app.root').'alpha/model/'.$classname.'.inc'))
1804:             $exists = true;
1805:         if(file_exists($config->get('app.root').'alpha/model/types/'.$classname.'.inc'))
1806:             $exists = true;
1807: 
1808:         self::$logger->debug('<<checkClassDefExists ['.$exists.']');
1809:         return $exists;
1810:     }
1811: 
1812:     /**
1813:      * Checks that a record exists for the BO in the database.
1814:      *
1815:      * @param int $OID The Object ID of the object we want to see whether it exists or not.
1816:      * @return boolean
1817:      * @since 1.0
1818:      * @throws AlphaException
1819:      */
1820:     public function checkRecordExists($OID) {
1821:         self::$logger->debug('>>checkRecordExists(OID=['.$OID.'])');
1822: 
1823:         if(method_exists($this, 'before_checkRecordExists_callback'))
1824:             $this->before_checkRecordExists_callback();
1825: 
1826:         global $config;
1827: 
1828:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
1829:         $recordExists = $provider->checkRecordExists($OID);
1830: 
1831:         if(method_exists($this, 'after_checkRecordExists_callback'))
1832:             $this->after_checkRecordExists_callback();
1833: 
1834:         self::$logger->debug('<<checkRecordExists ['.$recordExists.']');
1835:         return $recordExists;
1836:     }
1837: 
1838:     /**
1839:      * Checks to see if the table name matches the classname, and if not if the table
1840:      * name matches the classname name of another BO, i.e. the table is used to store 
1841:      * multiple types of BOs.
1842:      *
1843:      * @return bool
1844:      * @since 1.0
1845:      * @throws BadBOTableNameException
1846:      */
1847:     public function isTableOverloaded() {
1848:         self::$logger->debug('>>isTableOverloaded()');
1849: 
1850:         global $config;
1851: 
1852:         $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $this);
1853:         $isOverloaded = $provider->isTableOverloaded();
1854: 
1855:         self::$logger->debug('<<isTableOverloaded ['.$isOverloaded.']');
1856:         return $isOverloaded;
1857:     }
1858: 
1859:     /**
1860:      * Starts a new database transaction.
1861:      *
1862:      * @param $BO The AlphaDAO instance to pass to the database provider. Leave empty to have a new PersonObject passed.
1863:      * @since 1.0
1864:      * @throws AlphaException
1865:      */
1866:     public static function begin($BO = null) {
1867:         if(self::$logger == null)
1868:             self::$logger = new Logger('AlphaDAO');
1869:         self::$logger->debug('>>begin()');
1870: 
1871:         global $config;
1872: 
1873:         if(isset($BO))
1874:             $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $BO);
1875:         else
1876:             $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), new PersonObject());
1877: 
1878:         try{
1879:             $provider->begin();
1880:         }catch (Exception $e) {
1881:             throw new AlphaException('Error beginning a new transaction, error is ['.$e->getMessage().']');
1882:         }
1883: 
1884:         self::$logger->debug('<<begin');
1885:     }
1886: 
1887:     /**
1888:      * Commits the current database transaction.
1889:      *
1890:      * @param $BO The AlphaDAO instance to pass to the database provider. Leave empty to have a new PersonObject passed.
1891:      * @since 1.0
1892:      * @throws FailedSaveException
1893:      */
1894:     public static function commit($BO = null) {
1895:         if(self::$logger == null)
1896:             self::$logger = new Logger('AlphaDAO');
1897:         self::$logger->debug('>>commit()');
1898: 
1899:         global $config;
1900: 
1901:         if(isset($BO))
1902:             $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $BO);
1903:         else
1904:             $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), new PersonObject());
1905: 
1906:         try{
1907:             $provider->commit();
1908:         }catch (Exception $e) {
1909:             throw new FailedSaveException('Error commiting a transaction, error is ['.$e->getMessage().']');
1910:         }
1911: 
1912:         self::$logger->debug('<<commit');
1913:     }
1914: 
1915:     /**
1916:      * Aborts the current database transaction.
1917:      *
1918:      * @param $BO The AlphaDAO instance to pass to the database provider. Leave empty to have a new PersonObject passed.
1919:      * @since 1.0
1920:      * @throws AlphaException
1921:      */
1922:     public static function rollback($BO = null) {
1923:         if(self::$logger == null)
1924:             self::$logger = new Logger('AlphaDAO');
1925:         self::$logger->debug('>>rollback()');
1926: 
1927:         global $config;
1928: 
1929:         if(isset($BO))
1930:             $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), $BO);
1931:         else
1932:             $provider = AlphaDAOProviderFactory::getInstance($config->get('db.provider.name'), new PersonObject());
1933: 
1934:         try{
1935:             $provider->rollback();
1936:         }catch (Exception $e) {
1937:             throw new FailedSaveException('Error aborting a transaction, error is ['.$e->getMessage().']');
1938:         }
1939: 
1940:         self::$logger->debug('<<rollback');
1941:     }
1942: 
1943:     /**
1944:      * Static method that tries to determine if the system database has been installed or not.
1945:      *
1946:      * @return boolean
1947:      * @since 1.0
1948:      */
1949:     public static function isInstalled() {
1950:         if(self::$logger == null)
1951:             self::$logger = new Logger('AlphaDAO');
1952:         self::$logger->debug('>>isInstalled()');
1953: 
1954:         global $config;
1955: 
1956:         /*
1957:          * Install conditions are:
1958:          *
1959:          * 1. person table exists
1960:          * 2. rights table exists
1961:          */
1962:         if(AlphaDAO::checkBOTableExists('PersonObject') && AlphaDAO::checkBOTableExists('RightsObject')) {
1963:             self::$logger->debug('<<isInstalled [true]');
1964:             return true;
1965:         }else{
1966:             self::$logger->debug('<<isInstalled [false]');
1967:             return false;
1968:         }
1969:     }
1970: 
1971:     /**
1972:      * Returns true if the BO has a Relation property called tags, false otherwise.
1973:      *
1974:      * @return boolean
1975:      * @since 1.0
1976:      */
1977:     public function isTagged() {
1978:         if(isset($this->taggedAttributes) && isset($this->tags) && $this->tags instanceof Relation)
1979:             return true;
1980:         else
1981:             return false;
1982:     }
1983: 
1984:     /**
1985:      * Setter for the BO version number.
1986:      *
1987:      * @param integer $versionNumber The version number.
1988:      * @since 1.0
1989:      */
1990:     private function setVersion($versionNumber) {
1991:         $this->version_num->setValue($versionNumber);
1992:     }
1993: 
1994:     /**
1995:      * Cast a BO to another type of BO.  A new BO will be returned with the same OID and
1996:      * version_num as the old BO, so this is NOT a true cast but is a copy.  All attribute
1997:      * values will be copied accross.
1998:      *
1999:      * @param string $targetClassName The name of the target BO class.
2000:      * @param AlphaDAO $originalBO The original business object.
2001:      * @return AlphaDAO The new business object resulting from the cast.
2002:      * @since 1.0
2003:      */
2004:     public function cast($targetClassName, $originalBO) {
2005:         AlphaDAO::loadClassDef($targetClassName);
2006: 
2007:         $BO = new $targetClassName;
2008:         $BO->setOID($originalBO->getOID());
2009:         $BO->setVersion($originalBO->getVersion());
2010: 
2011:         // get the class attributes
2012:         $originalBOreflection = new ReflectionClass(get_class($originalBO));
2013:         $originalBOproperties = $originalBOreflection->getProperties();
2014:         $newBOreflection = new ReflectionClass($targetClassName);
2015:         $newBOproperties = $newBOreflection->getProperties();
2016: 
2017:         // copy the property values from the old BO to the new BO
2018: 
2019:         if(count($originalBOproperties) < count($newBOproperties)) {
2020:             // the original BO is smaller, so loop over its properties
2021:             foreach($originalBOproperties as $propObj) {
2022:                 $propName = $propObj->name;
2023:                 if(!in_array($propName, $this->transientAttributes))
2024:                     $BO->set($propName, $originalBO->get($propName));
2025:             }
2026:         }else{
2027:             // the new BO is smaller, so loop over its properties
2028:             foreach($newBOproperties as $propObj) {
2029:                 $propName = $propObj->name;
2030:                 if(!in_array($propName, $this->transientAttributes))
2031:                     $BO->set($propName, $originalBO->get($propName));
2032:             }
2033:         }
2034: 
2035:         return $BO;
2036:     }
2037: 
2038:     /**
2039:      * Converts "BusinessObject" to "Business" and returns.
2040:      *
2041:      * @return string
2042:      * @since 1.0
2043:      */
2044:     public function getFriendlyClassName() {
2045:         $name = substr(get_class($this), 0, -6);
2046: 
2047:         return $name;
2048:     }
2049: 
2050:     /**
2051:      * Check to see if an attribute exists on the BO.
2052:      *
2053:      * @param $attribute The attribute name.
2054:      * @return boolean
2055:      * @since 1.0
2056:      */
2057:     public function hasAttribute($attribute) {
2058:         try{
2059:             $exists = $this->$attribute;
2060:             return true;
2061:         }catch(Exception $e) {
2062:             return false;
2063:         }
2064:     }
2065: 
2066:     /**
2067:      * Stores the business object to the configured cache instance
2068:      *
2069:      * @since 1.1
2070:      */
2071:     public function addToCache() {
2072:         self::$logger->debug('>>addToCache()');
2073:         global $config;
2074: 
2075:         try {
2076:             $cache = AlphaCacheProviderFactory::getInstance($config->get('cache.provider.name'));
2077:             $cache->set(get_class($this).'-'.$this->getOID(), $this, 3600);
2078: 
2079:         }catch(Exception $e) {
2080:             self::$logger->error('Error while attempting to store a business object to the ['.$config->get('cache.provider.name').'] 
2081:                 instance: ['.$e->getMessage().']');
2082:         }
2083: 
2084:         self::$logger->debug('<<addToCache');
2085:     }
2086: 
2087:     /**
2088:      * Removes the business object from the configured cache instance
2089:      *
2090:      * @since 1.1
2091:      */
2092:     public function removeFromCache() {
2093:         self::$logger->debug('>>removeFromCache()');
2094:         global $config;
2095: 
2096:         try {
2097:             $cache = AlphaCacheProviderFactory::getInstance($config->get('cache.provider.name'));
2098:             $cache->delete(get_class($this).'-'.$this->getOID());
2099:         }catch(Exception $e) {
2100:             self::$logger->error('Error while attempting to remove a business object from ['.$config->get('cache.provider.name').']
2101:                 instance: ['.$e->getMessage().']');
2102:         }
2103: 
2104:         self::$logger->debug('<<removeFromCache');
2105:     }
2106: 
2107:     /**
2108:      * Attempts to load the business object from the configured cache instance
2109:      *
2110:      * @since 1.1
2111:      * @return boolean
2112:      */
2113:     public function loadFromCache() {
2114:         self::$logger->debug('>>loadFromCache()');
2115:         global $config;
2116: 
2117:         try {
2118:             $cache = AlphaCacheProviderFactory::getInstance($config->get('cache.provider.name'));
2119:             $BO = $cache->get(get_class($this).'-'.$this->getOID());
2120: 
2121:             if(!$BO) {
2122:                 self::$logger->debug('Cache miss on key ['.get_class($this).'-'.$this->getOID().']');
2123:                 self::$logger->debug('<<loadFromCache: [false]');
2124:                 return false;
2125:             }else{
2126:                 // get the class attributes
2127:                 $reflection = new ReflectionClass(get_class($this));
2128:                 $properties = $reflection->getProperties();
2129: 
2130:                 foreach($properties as $propObj) {
2131:                     $propName = $propObj->name;
2132: 
2133:                     // filter transient attributes
2134:                     if(!in_array($propName, $this->transientAttributes)) {
2135:                         $this->set($propName, $BO->get($propName, true));
2136:                     }elseif(!$propObj->isPrivate() && isset($this->$propName) && $this->$propName instanceof Relation) {
2137:                         $prop = $this->getPropObject($propName);
2138: 
2139:                         // handle the setting of ONE-TO-MANY relation values
2140:                         if($prop->getRelationType() == 'ONE-TO-MANY') {
2141:                             $this->set($propObj->name, $this->getOID());
2142:                         }
2143:                     }
2144:                 }
2145: 
2146:                 self::$logger->debug('<<loadFromCache: [true]');
2147:                 return true;
2148:             }
2149:         }catch(Exception $e) {
2150:             self::$logger->error('Error while attempting to load a business object from ['.$config->get('cache.provider.name').']
2151:              instance: ['.$e->getMessage().']');
2152: 
2153:             self::$logger->debug('<<loadFromCache: [false]');
2154:             return false;
2155:         }
2156:     }
2157: 
2158:     /**
2159:      * Sets the last query executed on this business object
2160:      *
2161:      * @param string $query
2162:      * @since 1.1
2163:      */
2164:     public function setLastQuery($query) {
2165:         self::$logger->sql($query);
2166:         $this->lastQuery = $query;
2167:     }
2168: 
2169:     /**
2170:      * Re-initialize the static logger property on the BO after de-serialize, as PHP does
2171:      * not serialize static properties.
2172:      *
2173:      * @since 1.2
2174:      */
2175:     public function __wakeup() {
2176:         if(self::$logger == null)
2177:             self::$logger = new Logger(get_class($this));
2178:     }
2179: 
2180:     /**
2181:      * Sets maintainHistory attribute on this DAO
2182:      *
2183:      * @param boolean $maintainHistory
2184:      * @throws IllegalArguementException
2185:      * @since 1.2
2186:      */
2187:     public function setMaintainHistory($maintainHistory) {
2188:         if(!is_bool($maintainHistory))
2189:             throw new IllegalArguementException('Non-boolean value ['.$maintainHistory.'] passed to setMaintainHistory method!');
2190: 
2191:         $this->maintainHistory = $maintainHistory;
2192:     }
2193: 
2194:     /**
2195:      * Gets the value of the  maintainHistory attribute
2196:      *
2197:      * @returns boolean
2198:      * @since 1.2
2199:      */
2200:     public function getMaintainHistory() {
2201:         return $this->maintainHistory;
2202:     }
2203: }
2204: 
2205: ?>
Alpha Framework ${alpha.version.new} API Documentation API documentation generated by ApiGen 2.8.0