Overview

Namespaces

  • Alpha
    • Controller
      • Front
    • Exception
    • Model
      • Type
    • Task
    • Util
      • Backup
      • Cache
      • Code
        • Highlight
        • Metric
      • Config
      • Convertor
      • Email
      • Extension
      • Feed
      • File
      • Graph
      • Helper
      • Http
        • Filter
        • Session
      • Image
      • Logging
      • Search
      • Security
    • View
      • Renderer
        • Html
        • Json
      • Widget

Classes

  • ActionLog
  • ActiveRecord
  • ActiveRecordProviderFactory
  • ActiveRecordProviderMySQL
  • ActiveRecordProviderSQLite
  • Article
  • ArticleComment
  • ArticleVote
  • BadRequest
  • BlacklistedClient
  • BlacklistedIP
  • Person
  • Rights
  • Tag

Interfaces

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