Source for file AlphaDAO.inc
Documentation is available at AlphaDAO.inc
require_once $config->get('sysRoot'). 'alpha/util/AlphaErrorHandlers.inc';
require_once $config->get('sysRoot'). 'alpha/util/Logger.inc';
require_once $config->get('sysRoot'). 'alpha/util/InputFilter.inc';
require_once $config->get('sysRoot'). 'alpha/util/cache/AlphaCacheProviderFactory.inc';
require_once $config->get('sysRoot'). 'alpha/exceptions/BONotFoundException.inc';
require_once $config->get('sysRoot'). 'alpha/exceptions/FailedSaveException.inc';
require_once $config->get('sysRoot'). 'alpha/exceptions/FailedDeleteException.inc';
require_once $config->get('sysRoot'). 'alpha/exceptions/LockingException.inc';
require_once $config->get('sysRoot'). 'alpha/exceptions/ValidationException.inc';
require_once $config->get('sysRoot'). 'alpha/exceptions/IllegalArguementException.inc';
require_once $config->get('sysRoot'). 'alpha/exceptions/MailNotSentException.inc';
require_once $config->get('sysRoot'). 'alpha/exceptions/BadBOTableNameException.inc';
require_once $config->get('sysRoot'). 'alpha/exceptions/FailedIndexCreateException.inc';
require_once $config->get('sysRoot'). 'alpha/exceptions/FailedLookupCreateException.inc';
require_once $config->get('sysRoot'). 'alpha/exceptions/CustomQueryException.inc';
require_once $config->get('sysRoot'). 'alpha/model/types/Date.inc';
require_once $config->get('sysRoot'). 'alpha/model/types/Timestamp.inc';
require_once $config->get('sysRoot'). 'alpha/model/types/Double.inc';
require_once $config->get('sysRoot'). 'alpha/model/types/Integer.inc';
require_once $config->get('sysRoot'). 'alpha/model/types/String.inc';
require_once $config->get('sysRoot'). 'alpha/model/types/Text.inc';
require_once $config->get('sysRoot'). 'alpha/model/types/Enum.inc';
require_once $config->get('sysRoot'). 'alpha/model/types/DEnum.inc';
require_once $config->get('sysRoot'). 'alpha/model/types/DEnumItem.inc';
require_once $config->get('sysRoot'). 'alpha/model/types/Boolean.inc';
require_once $config->get('sysRoot'). 'alpha/model/types/Relation.inc';
require_once $config->get('sysRoot'). 'alpha/model/types/Sequence.inc';
require_once $config->get('sysRoot'). 'alpha/model/AlphaDAOProviderFactory.inc';
* Base business object class definition providing DAO storage via the configured provider.
* @author John Collins <dev@alphaframework.org>
* @version $Id: AlphaDAO.inc 1464 2011-12-08 21:18:22Z johnc $
* @license http://www.opensource.org/licenses/bsd-license.php The BSD License
* @copyright Copyright (c) 2011, John Collins (founder of Alpha Framework).
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the
* following conditions are met:
* * Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
* * Neither the name of the Alpha Framework nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* The last database query run by this object. Useful for tracing an error.
* The version number of the object, used for locking mechanism
* The timestamp of creation
* The OID of the person who created this BO
* The timestamp of the last update
* The OID of the person who last updated this BO
* An array of the names of all of the default attributes of a persistent BO defined in this class
protected $defaultAttributes = array("OID", "lastQuery", "version_num", "dataLabels", "created_ts", "created_by", "updated_ts", "updated_by", "defaultAttributes", "transientAttributes", "uniqueAttributes", "TABLE_NAME", "logger");
* An array of the names of all of the transient attributes of a persistent BO which are not saved to the DB
protected $transientAttributes = array("lastQuery", "dataLabels", "defaultAttributes", "transientAttributes", "uniqueAttributes", "TABLE_NAME", "logger");
* An array of the uniquely-constained attributes of this persistent BO
* An array of the data labels used for displaying class attributes
private static $logger = null;
* The constructor which sets up some housekeeping attributes
self::$logger = new Logger('AlphaDAO');
self::$logger->debug('>>__construct()');
$person_ID = (isset ($_SESSION['currentUser'])? $_SESSION['currentUser']->getOID(): 0);
self::$logger->debug('<<__construct');
* Gets the current connection singleton, or creates a new one if none exists
throw new AlphaException('The static getConnection() method is not longer supported! Please update your code to instantiate a AlphaDAOProviderInterface '.
'instance, then invoke the query() method on that instance to run a custom query.');
* Disconnects the current database connection if one exists
* Returns a 2d array, where each element in the array is another array representing a database row.
* @param string $sqlQuery
* @throws CustomQueryException
public function query($sqlQuery) {
return $provider->query($sqlQuery);
* Populates the child object with the properties retrived from the database for the object $OID.
* @param integer $OID The object ID of the business object to load.
* @throws BONotFoundException
public function load($OID) {
self::$logger->debug('>>load(OID=['. $OID. '])');
if(method_exists($this, 'before_load_callback'))
$this->before_load_callback();
if($config->get('sysCacheProviderName') != '' && $this->loadFromCache()) {
if($config->get('sysCacheProviderName') != '')
$this->after_load_callback();
self::$logger->debug('<<load');
* Populates the child object from the database table by the given attribute value.
* @param string $atribute The name of the attribute to load the object by.
* @param string $value The value of the attribute to load the object by.
* @param boolean $ignoreClassType Default is false, set to true if you want to load from overloaded tables and ignore the class type
* @param array $loadAttributes The attributes to load from the database to this object (leave blank to load all attributes)
* @throws BONotFoundException
public function loadByAttribute($attribute, $value, $ignoreClassType= false, $loadAttributes= array()) {
self::$logger->debug('>>loadByAttribute(attribute=['. $attribute. '], value=['. $value. '], ignoreClassType=['. $ignoreClassType. '],
loadAttributes=['. var_export($loadAttributes, true). '])');
$this->before_loadByAttribute_callback();
$provider->loadByAttribute($attribute, $value, $ignoreClassType, $loadAttributes);
if($config->get('sysCacheProviderName') != '' && count($loadAttributes) == 0) // we will only cache fully-populated BOs
$this->after_loadByAttribute_callback();
self::$logger->debug('<<loadByAttribute');
* Loads all of the objects of this class into an array which is returned.
* @param integer $start The start of the SQL LIMIT clause, useful for pagination.
* @param integer $limit The amount (limit) of objects to load, useful for pagination.
* @param string $orderBy The name of the field to sort the objects by.
* @param string $order The order to sort the objects by.
* @param boolean $ignoreClassType Default is false, set to true if you want to load from overloaded tables and ignore the class type
* @return array An array containing objects of this type of business object.
* @throws BONotFoundException
public function loadAll($start= 0, $limit= 0, $orderBy= 'OID', $order= 'ASC', $ignoreClassType= false) {
self::$logger->debug('>>loadAll(start=['. $start. '], limit=['. $limit. '], orderBy=['. $orderBy. '], order=['. $order. '], ignoreClassType=['. $ignoreClassType. ']');
if(method_exists($this, 'before_loadAll_callback'))
$this->before_loadAll_callback();
$objects = $provider->loadAll($start, $limit, $orderBy, $order, $ignoreClassType);
$this->after_loadAll_callback();
self::$logger->debug('<<loadAll ['. count($objects). ']');
* Loads all of the objects of this class by the specified attribute into an array which is returned.
* @param string $atribute The attribute to load the objects by.
* @param string $value The value of the attribute to load the objects by.
* @param integer $start The start of the SQL LIMIT clause, useful for pagination.
* @param integer $limit The amount (limit) of objects to load, useful for pagination.
* @param string $orderBy The name of the field to sort the objects by.
* @param string $order The order to sort the objects by.
* @param boolean $ignoreClassType Default is false, set to true if you want to load from overloaded tables and ignore the class type.
* @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.
* @return array An array containing objects of this type of business object.
* @throws BONotFoundException
* @throws IllegalArguementException
public function loadAllByAttribute($attribute, $value, $start= 0, $limit= 0, $orderBy= "OID", $order= "ASC", $ignoreClassType= false, $constructorArgs= array()) {
self::$logger->debug('>>loadAllByAttribute(attribute=['. $attribute. '], value=['. $value. '], start=['. $start. '], limit=['. $limit. '], orderBy=['. $orderBy. '], order=['. $order. '], ignoreClassType=['. $ignoreClassType. '], constructorArgs=['. print_r($constructorArgs, true). ']');
$this->before_loadAllByAttribute_callback();
$objects = $provider->loadAllByAttribute($attribute, $value, $start, $limit, $orderBy, $order, $ignoreClassType);
$this->after_loadAllByAttribute_callback();
self::$logger->debug('<<loadAllByAttribute ['. count($objects). ']');
* Loads all of the objects of this class by the specified attributes into an array which is returned.
* @param array $atributes The attributes to load the objects by.
* @param array $values The values of the attributes to load the objects by.
* @param integer $start The start of the SQL LIMIT clause, useful for pagination.
* @param integer $limit The amount (limit) of objects to load, useful for pagination.
* @param string $orderBy The name of the field to sort the objects by.
* @param string $order The order to sort the objects by.
* @param boolean $ignoreClassType Default is false, set to true if you want to load from overloaded tables and ignore the class type
* @return array An array containing objects of this type of business object.
* @throws BONotFoundException
* @throws IllegalArguementException
public function loadAllByAttributes($attributes= array(), $values= array(), $start= 0, $limit= 0, $orderBy= 'OID', $order= 'ASC', $ignoreClassType= false) {
self::$logger->debug('>>loadAllByAttributes(attributes=['. var_export($attributes, true). '], values=['. var_export($values, true). '], start=['.
$start. '], limit=['. $limit. '], orderBy=['. $orderBy. '], order=['. $order. '], ignoreClassType=['. $ignoreClassType. ']');
$this->before_loadAllByAttributes_callback();
'] provided to loadAllByAttributes');
$objects = $provider->loadAllByAttributes($attributes, $values, $start, $limit, $orderBy, $order, $ignoreClassType);
$this->after_loadAllByAttributes_callback();
self::$logger->debug('<<loadAllByAttributes ['. count($objects). ']');
* Loads all of the objects of this class that where updated (updated_ts value) on the date indicated.
* @param string $date The date for which to load the objects updated on, in the format 'YYYY-MM-DD'.
* @param integer $start The start of the SQL LIMIT clause, useful for pagination.
* @param integer $limit The amount (limit) of objects to load, useful for pagination.
* @param string $orderBy The name of the field to sort the objects by.
* @param string $order The order to sort the objects by.
* @param boolean $ignoreClassType Default is false, set to true if you want to load from overloaded tables and ignore the class type
* @return array An array containing objects of this type of business object.
* @throws BONotFoundException
public function loadAllByDayUpdated($date, $start= 0, $limit= 0, $orderBy= "OID", $order= "ASC", $ignoreClassType= false) {
self::$logger->debug('>>loadAllByDayUpdated(date=['. $date. '], start=['. $start. '], limit=['. $limit. '], orderBy=['. $orderBy. '], order=['. $order. '], ignoreClassType=['. $ignoreClassType. ']');
if(method_exists($this, 'before_loadAllByDayUpdated_callback'))
$this->before_loadAllByDayUpdated_callback();
$objects = $provider->loadAllByDayUpdated($date, $start, $limit, $orderBy, $order, $ignoreClassType);
$this->after_loadAllByDayUpdated_callback();
self::$logger->debug('<<loadAllByDayUpdated ['. count($objects). ']');
* Loads all of the specified attribute values of this class by the specified attribute into an
* array which is returned.
* @param string $attribute The attribute name to load the field values by.
* @param string $value The value of the attribute to load the field values by.
* @param string $returnAttribute The name of the attribute to return.
* @param string $order The order to sort the BOs by.
* @param boolean $ignoreClassType Default is false, set to true if you want to load from overloaded tables and ignore the class type.
* @return array An array of field values.
* @throws BONotFoundException
self::$logger->debug('>>loadAllFieldValuesByAttribute(attribute=['. $attribute. '], value=['. $value. '], returnAttribute=['. $returnAttribute. '], order=['. $order. '], ignoreClassType=['. $ignoreClassType. ']');
$provider = AlphaDAOProviderFactory::getInstance($config->get('sysDBProviderName'), $this);
$values = $provider->loadAllFieldValuesByAttribute($attribute, $value, $returnAttribute, $order, $ignoreClassType);
self::$logger->debug('<<loadAllFieldValuesByAttribute ['. count($values). ']');
* Saves the object. If $this->OID is empty or null it will INSERT, otherwise UPDATE.
* @throws FailedSaveException
* @throws LockingException
* @throws ValidationException
self::$logger->debug('>>save()');
if(method_exists($this, 'before_save_callback'))
$this->before_save_callback();
// firstly we will validate the object before we try to save it
if($config->get('sysCacheProviderName') != '')
$this->after_save_callback();
* Saves the field specified with the value supplied. Only works for persistent BOs. Note that no Alpha type
* validation is performed with this method!
* @param string $attribute The name of the attribute to save.
* @param mixed $value The value of the attribute to save.
* @throws IllegalArguementException
* @throws FailedSaveException
self::$logger->debug('>>saveAttribute(attribute=['. $attribute. '], value=['. $value. '])');
if(method_exists($this, 'before_saveAttribute_callback'))
$this->before_saveAttribute_callback();
if(!isset ($this->$attribute))
$provider->saveAttribute($attribute, $value);
$this->set($attribute, $value);
if($config->get('sysCacheProviderName') != '')
$this->after_saveAttribute_callback();
self::$logger->debug('<<saveAttribute');
* Validates the object to be saved.
* @throws ValidationException
self::$logger->debug('>>validate()');
if(method_exists($this, 'before_validate_callback'))
$this->before_validate_callback();
// get the class attributes
$reflection = new ReflectionClass(get_class($this));
$properties = $reflection->getProperties();
foreach($properties as $propObj) {
$propName = $propObj->name;
$this->after_validate_callback();
self::$logger->debug('<<validate ['. $valid. ']');
* Deletes the current object from the database.
* @throws FailedDeleteException
self::$logger->debug('>>delete()');
if(method_exists($this, 'before_delete_callback'))
$this->before_delete_callback();
// get the class attributes
$reflection = new ReflectionClass(get_class($this));
$properties = $reflection->getProperties();
// check for any relations on this object, then remove them to prevent orphaned data
foreach($properties as $propObj) {
$propName = $propObj->name;
if(!$propObj->isPrivate() && isset ($this->$propName) && $this->$propName instanceof Relation) {
// Handle MANY-TO-MANY rels
if($prop->getRelationType() == 'MANY-TO-MANY') {
self::$logger->debug('Deleting MANY-TO-MANY lookup objects...');
// check to see if the rel is on this class
$side = $prop->getSide(get_class($this));
self::$logger->debug('Side is ['. $side. ']'. $this->getOID());
$lookUp = $prop->getLookup();
self::$logger->debug('Lookup object['. var_export($lookUp, true). ']');
// delete all of the old RelationLookup objects for this rel
$lookUp->deleteAllByAttribute('leftID', $this->getOID());
$lookUp->deleteAllByAttribute('rightID', $this->getOID());
self::$logger->debug('...done deleting!');
// should set related field values to null (MySQL is doing this for us as-is)
if($prop->getRelationType() == 'ONE-TO-MANY' && !$prop->getRelatedClass() == 'TagObject') {
$relatedObjects = $prop->getRelatedObjects();
foreach($relatedObjects as $object) {
$object->set($prop->getRelatedClassField(), null);
// in the case of tags, we will always remove the related tags once the BO is deleted
if($prop->getRelationType() == 'ONE-TO-MANY' && $prop->getRelatedClass() == 'TagObject') {
// just making sure that the Relation is set to current OID as its transient
$prop->setValue($this->getOID());
$relatedObjects = $prop->getRelatedObjects();
foreach($relatedObjects as $object) {
if($config->get('sysCacheProviderName') != '')
$this->after_delete_callback();
self::$logger->debug('<<delete');
* Delete all object instances from the database by the specified attribute matching the value provided.
* @param string $attribute The name of the field to delete the objects by.
* @param mixed $value The value of the field to delete the objects by.
* @return integer The number of rows deleted.
* @throws FailedDeleteException
self::$logger->debug('>>deleteAllByAttribute(attribute=['. $attribute. '], value=['. $value. '])');
if(method_exists($this, 'before_deleteAllByAttribute_callback'))
$this->before_deleteAllByAttribute_callback();
foreach ($doomedObjects as $object) {
// nothing found to delete
self::$logger->warn($bonf->getMessage());
}catch (AlphaException $e) {
self::$logger->debug('<<deleteAllByAttribute [0]');
if(method_exists($this, 'after_deleteAllByAttribute_callback'))
$this->after_deleteAllByAttribute_callback();
self::$logger->debug('<<deleteAllByAttribute ['. $deletedRowCount. ']');
* Gets the version_num of the object from the database (returns 0 if the BO is not saved yet).
* @throws BONotFoundException
self::$logger->debug('>>getVersion()');
if(method_exists($this, 'before_getVersion_callback'))
$this->before_getVersion_callback();
$ver = $provider->getVersion();
$this->after_getVersion_callback();
self::$logger->debug('<<getVersion ['. $ver. ']');
* Builds a new database table for the BO class.
self::$logger->debug('>>makeTable()');
if(method_exists($this, 'before_makeTable_callback'))
$this->before_makeTable_callback();
$this->after_makeTable_callback();
self::$logger->info('Successfully created the table ['. $this->getTableName(). '] for the class ['. get_class($this). ']');
self::$logger->debug('<<makeTable');
* Re-builds the table if the model requirements have changed. All data is lost!
self::$logger->debug('>>rebuildTable()');
if(method_exists($this, 'before_rebuildTable_callback'))
$this->before_rebuildTable_callback();
$provider->rebuildTable();
$this->after_rebuildTable_callback();
self::$logger->debug('<<rebuildTable');
* Drops the table if the model requirements have changed. All data is lost!
* @param string $tableName Optional table name, leave blank for the defined table for this class to be dropped
self::$logger->debug('>>dropTable()');
if(method_exists($this, 'before_dropTable_callback'))
$this->before_dropTable_callback();
$provider->dropTable($tableName);
$this->after_dropTable_callback();
self::$logger->debug('<<dropTable');
* Adds in a new class property without loosing existing data (does an ALTER TABLE query on the
* @param string $propName The name of the new field to add to the database table.
self::$logger->debug('>>addProperty(propName=['. $propName. '])');
if(method_exists($this, 'before_addProperty_callback'))
$this->before_addProperty_callback();
$provider->addProperty($propName);
$this->after_addProperty_callback();
self::$logger->debug('<<addProperty');
* Populates the current business object from global POST data.
* @param boolean $filterAll Defaults to false, set to true if you want to strictly filter all POSTed user input using InputFilter::encode.
self::$logger->debug('>>populateFromPost(filterAll=['. $filterAll. '])');
if(method_exists($this, 'before_populateFromPost_callback'))
$this->before_populateFromPost_callback();
// get the class attributes
$reflection = new ReflectionClass(get_class($this));
$properties = $reflection->getProperties();
foreach($properties as $propObj) {
$propName = $propObj->name;
if(isset ($_POST[$propName])) {
if ($propName == 'version_num' && isset ($_POST['version_num']))
$this->after_populateFromPost_callback();
self::$logger->debug('<<populateFromPost');
* Gets the maximum OID value from the database for this class type.
* @return integer The maximum OID value in the class table.
self::$logger->debug('>>getMAX()');
if(method_exists($this, 'before_getMAX_callback'))
$this->before_getMAX_callback();
$max = $provider->getMAX();
$this->after_getMAX_callback();
self::$logger->debug('<<getMAX ['. $max. ']');
* Gets the count from the database for the amount of objects of this class.
* @param array $atributes The attributes to count the objects by (optional).
* @param array $values The values of the attributes to count the objects by (optional).
public function getCount($attributes= array(), $values= array()) {
self::$logger->debug('>>getCount(attributes=['. var_export($attributes, true). '], values=['. var_export($values, true). '])');
$this->before_getCount_callback();
$count = $provider->getCount($attributes, $values);
$this->after_getCount_callback();
self::$logger->debug('<<getCount ['. $count. ']');
* Gets the OID for the object in zero-padded format (same as getOID()). This version is designed
* to be overridden in case you want to do something different with the ID of your objects outside
* of the standard 11 digit OID sequence used internally in Alpha.
* @return integer 11 digit zero-padded OID value.
public function getID() {
self::$logger->debug('>>getID()');
$oid = str_pad($this->OID, 11, '0', STR_PAD_LEFT);
self::$logger->debug('<<getID ['. $oid. ']');
* Gets the OID for the object in zero-padded format (same as getID()). This version is final so cannot
* @return integer 11 digit zero-padded OID value.
public final function getOID() {
if(self::$logger == null)
self::$logger = new Logger('AlphaDAO');
self::$logger->debug('>>getOID()');
$oid = str_pad($this->OID, 11, '0', STR_PAD_LEFT);
self::$logger->debug('<<getOID ['. $oid. ']');
* Method for getting version number of the object.
* @return Integer The object version number.
self::$logger->debug('>>getVersionNumber()');
self::$logger->debug('<<getVersionNumber ['. $this->version_num. ']');
* Populate all of the enum options for this object from the database.
self::$logger->debug('>>setEnumOptions()');
if(method_exists($this, 'before_setEnumOptions_callback'))
$this->before_setEnumOptions_callback();
$provider->setEnumOptions();
self::$logger->debug('<<setEnumOptions');
* Generic getter method for accessing class properties. Will use the method get.ucfirst($prop) instead if that
* method exists at a child level (by default). Set $noChildMethods to true if you don't want to use any
* get.ucfirst($prop) method even if it exists, false otherwise (default).
* @param string $prop The name of the object property to get.
* @param boolean $noChildMethods Set to true if you do not want to use getters in the child object, defaults
* @return mixed The property value.
* @throws IllegalArguementException
public function get($prop, $noChildMethods = false) {
if(self::$logger == null)
self::$logger = new Logger('AlphaDAO');
self::$logger->debug('>>get(prop=['. $prop. '], noChildMethods=['. $noChildMethods. '])');
if(method_exists($this, 'before_get_callback'))
$this->before_get_callback();
// handle attributes with a get.ucfirst($prop) method
$this->after_get_callback();
self::$logger->debug('<<get ['.eval ('return $this->get'. ucfirst($prop). '();'). '])');
return eval ('return $this->get'. ucfirst($prop). '();');
// handle attributes with no dedicated child get.ucfirst($prop) method
if(isset ($this->$prop) && method_exists($this->$prop, 'getValue')) {
$this->after_get_callback();
// complex types will have a getValue() method, return the value of that
self::$logger->debug('<<get ['. $this->$prop->getValue(). '])');
return $this->$prop->getValue();
}elseif(isset ($this->$prop)) {
if(method_exists($this, 'after_get_callback'))
$this->after_get_callback();
// simple types returned as-is
self::$logger->debug('<<get ['. $this->$prop. '])');
throw new AlphaException('Could not access the property ['. $prop. '] on the object of class ['. get_class($this). ']');
self::$logger->debug('<<get [false])');
* Generic setter method for setting class properties. Will use the method set.ucfirst($prop) instead if that
* method exists at a child level (by default). Set $noChildMethods to true if you don't want to use
* any get.ucfirst($prop) method even if it exists, false otherwise (default).
* @param string $prop The name of the property to set.
* @param mixed $value The value of the property to set.
* @param boolean $noChildMethods Set to true if you do not want to use setters in the child object, defaults
public function set($prop, $value, $noChildMethods = false) {
self::$logger->debug('>>set(prop=['. $prop. '], $value=['. $value. '], noChildMethods=['. $noChildMethods. '])');
if(method_exists($this, 'before_set_callback'))
$this->before_set_callback();
// handle attributes with a set.ucfirst($prop) method
$this->after_set_callback();
eval ('$this->set'. ucfirst($prop). "('$value');");
// handle attributes with no dedicated child set.ucfirst($prop) method
if(isset ($this->$prop)) {
$this->after_set_callback();
// complex types will have a setValue() method to call
$this->$prop->setValue($value);
// Date and Timestamp objects have a special setter accepting a string
$this->$prop->populateFromString($value);
// simple types set directly
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.');
self::$logger->debug('<<set');
* Gets the property object rather than the value for complex attributes. Returns false if
* the property exists but is private.
* @param string $prop The name of the property we are getting.
* @return AlphaType The complex type object found.
* @throws IllegalArguementException
self::$logger->debug('>>getPropObject(prop=['. $prop. '])');
if(method_exists($this, 'before_getPropObject_callback'))
$this->before_getPropObject_callback();
// get the class attributes
$reflection = new ReflectionClass(get_class($this));
$properties = $reflection->getProperties();
// firstly, check for private
$attribute = new ReflectionProperty(get_class($this), $prop);
if($attribute->isPrivate()) {
$this->after_getPropObject_callback();
self::$logger->debug('<<getPropObject [false]');
foreach($properties as $propObj) {
$propName = $propObj->name;
$this->after_getPropObject_callback();
self::$logger->debug('<<getPropObject ['. var_export($this->$prop, true). ']');
self::$logger->debug('<<getPropObject [false]');
* Checks to see if the table exists in the database for the current business class.
self::$logger->debug('>>checkTableExists()');
if(method_exists($this, 'before_checkTableExists_callback'))
$this->before_checkTableExists_callback();
$tableExists = $provider->checkTableExists();
$this->after_checkTableExists_callback();
self::$logger->debug('<<checkTableExists ['. $tableExists. ']');
* Static method to check the database and see if the table for the indicated BO class name
* exists (assumes table name will be $BOClassName less "Object").
* @param string $BOClassName The name of the business object class we are checking.
if(self::$logger == null)
self::$logger = new Logger('AlphaDAO');
self::$logger->debug('>>checkBOTableExists(BOClassName=['. $BOClassName. '])');
require_once $config->get('sysRoot'). 'alpha/model/'. $config->get('sysDBProviderName'). '.inc';
eval ('$tableExists = '. $config->get('sysDBProviderName'). '::checkBOTableExists($BOClassName);');
self::$logger->debug('<<checkBOTableExists ['. ($tableExists ? 'true' : 'false'). ']');
* Checks to see if the table in the database matches (for fields) the business class definition, i.e. if the
* database table is in sync with the class definition.
self::$logger->debug('>>checkTableNeedsUpdate()');
if(method_exists($this, 'before_checkTableNeedsUpdate_callback'))
$this->before_checkTableNeedsUpdate_callback();
self::$logger->debug('<<checkTableNeedsUpdate [true]');
$updateRequired = $provider->checkTableNeedsUpdate();
$this->after_checkTableNeedsUpdate_callback();
self::$logger->debug('<<checkTableNeedsUpdate ['. $updateRequired. ']');
* Returns an array containing any properties on the class which have not been created on the database
* @return array An array of missing fields in the database table.
self::$logger->debug('>>findMissingFields()');
if(method_exists($this, 'before_findMissingFields_callback'))
$this->before_findMissingFields_callback();
$missingFields = $provider->findMissingFields();
$this->after_findMissingFields_callback();
self::$logger->debug('<<findMissingFields ['. var_export($missingFields, true). ']');
* Getter for the TABLE_NAME, which should be set by a child of this class.
* @return string The table name in the database.
self::$logger->debug('>>getTableName()');
eval ('$TABLE_NAME = '. get_class($this). '::TABLE_NAME;');
if(!empty($TABLE_NAME)) {
self::$logger->debug('<<getTableName ['. $TABLE_NAME. ']');
throw new AlphaException('Error: no TABLE_NAME constant set for the class '. get_class($this));
self::$logger->debug('<<getTableName []');
* Method for getting the OID of the person who created this BO.
* @return Integer The OID of the creator.
self::$logger->debug('>>getCreatorId()');
self::$logger->debug('<<getCreatorId ['. $this->created_by. ']');
* Method for getting the OID of the person who updated this BO.
* @return Integer The OID of the updator.
self::$logger->debug('>>getUpdatorId()');
self::$logger->debug('<<getUpdatorId ['. $this->updated_by. ']');
* Method for getting the date/time of when the BO was created.
self::$logger->debug('>>getCreateTS()');
self::$logger->debug('<<getCreateTS ['. $this->created_ts. ']');
* Method for getting the date/time of when the BO was last updated.
self::$logger->debug('>>getUpdateTS()');
self::$logger->debug('<<getUpdateTS ['. $this->updated_ts. ']');
* Adds the name of the attribute provided to the list of transient (non-saved) attributes for this BO.
* @param string $attributeName The name of the attribute to not save.
self::$logger->debug('>>markTransient(attributeName=['. $attributeName. '])');
self::$logger->debug('<<markTransient');
* Removes the name of the attribute provided from the list of transient (non-saved) attributes for this BO,
* ensuring that it will be saved on the next attempt.
* @param string $attributeName The name of the attribute to save.
self::$logger->debug('>>markPersistent(attributeName=['. $attributeName. '])');
self::$logger->debug('<<markPersistent');
* Adds the name of the attribute(s) provided to the list of unique (constrained) attributes for this BO.
* @param string $attribute1Name The first attribute to mark unique in the database.
* @param string $attribute2Name The second attribute to mark unique in the databse (optional, use only for composite keys).
* @param string $attribute3Name The third attribute to mark unique in the databse (optional, use only for composite keys).
protected function markUnique($attribute1Name, $attribute2Name= '', $attribute3Name= '') {
self::$logger->debug('>>markUnique(attribute1Name=['. $attribute1Name. '], attribute2Name=['. $attribute2Name. '], attribute3Name=['. $attribute3Name. '])');
if(empty($attribute2Name)) {
// Process composite unique keys: add them seperated by a + sign
if($attribute3Name == '')
$attributes = $attribute1Name. '+'. $attribute2Name;
$attributes = $attribute1Name. '+'. $attribute2Name. '+'. $attribute3Name;
self::$logger->debug('<<markUnique');
* Returns the array of names of unique attributes on this BO
self::$logger->debug('>>getUniqueAttributes()');
self::$logger->debug('<<getUniqueAttributes: ['. print_r($this->uniqueAttributes, true). ']');
* Gets an array of all of the names of the active database indexes for this class.
* @return array An array of database indexes on this table.
self::$logger->debug('>>getIndexes()');
$provider = AlphaDAOProviderFactory::getInstance($config->get('sysDBProviderName'), $this);
$indexNames = $provider->getIndexes();
self::$logger->debug('<<getIndexes ['. print_r($indexNames, true). ']');
* Creates a foreign key constraint (index) in the database on the given attribute.
* @param string $attributeName The name of the attribute to apply the index on.
* @param string $relatedClass The name of the related class in the format "NameObject".
* @param string $relatedClassAttribute The name of the field to relate to on the related class.
* @param bool $allowNullValues For foreign key indexes that don't allow null values, set this to false (default is true).
* @throws FailedIndexCreateException
public function createForeignIndex($attributeName, $relatedClass, $relatedClassAttribute) {
self::$logger->debug('>>createForeignIndex(attributeName=['. $attributeName. '], relatedClass=['. $relatedClass. '], relatedClassAttribute=['. $relatedClassAttribute. ']');
if(method_exists($this, 'before_createForeignIndex_callback'))
$this->before_createForeignIndex_callback();
$relatedBO = new $relatedClass;
$tableName = $relatedBO->getTableName();
// if the relation is on itself (table-wise), exist without attempting to create the foreign keys
self::$logger->debug('<<createForeignIndex');
$provider = AlphaDAOProviderFactory::getInstance($config->get('sysDBProviderName'), $this);
$provider->createForeignIndex($attributeName, $relatedClass, $relatedClassAttribute);
$this->after_createForeignIndex_callback();
self::$logger->debug('<<createForeignIndex');
* Creates a unique index in the database on the given attribute(s).
* @param string $attribute1Name The first attribute to mark unique in the database.
* @param string $attribute2Name The second attribute to mark unique in the databse (optional, use only for composite keys).
* @param string $attribute3Name The third attribute to mark unique in the databse (optional, use only for composite keys).
* @throws FailedIndexCreateException
public function createUniqueIndex($attribute1Name, $attribute2Name = '', $attribute3Name = '') {
self::$logger->debug('>>createUniqueIndex(attribute1Name=['. $attribute1Name. '], attribute2Name=['. $attribute2Name. '], attribute3Name=['. $attribute3Name. '])');
if(method_exists($this, 'before_createUniqueIndex_callback'))
$this->before_createUniqueIndex_callback();
$provider->createUniqueIndex($attribute1Name, $attribute2Name, $attribute3Name);
$this->before_createUniqueIndex_callback();
self::$logger->debug('<<createUniqueIndex');
* Gets the data labels array.
* @return array An array of attribute labels.
self::$logger->debug('>>getDataLabels()');
self::$logger->debug('<<getDataLabels() ['. var_export($this->dataLabels, true). '])');
* Gets the data label for the given attribute name.
* @param $att The attribute name to get the label for.
* @throws IllegalArguementException
self::$logger->debug('>>getDataLabel(att=['. $att. '])');
self::$logger->debug('<<getDataLabel ['. $this->dataLabels[$att]. '])');
throw new IllegalArguementException('No data label found on the class ['. get_class($this). '] for the attribute ['. $att. ']');
self::$logger->debug('<<getDataLabel [])');
* Loops over the core and custom BO directories and builds an array of all of the BO class names in the system.
* @return array An array of business object class names.
if(self::$logger == null)
self::$logger = new Logger('AlphaDAO');
self::$logger->debug('>>getBOClassNames()');
$classNameArray = array();
if(file_exists($config->get('sysRoot'). 'model')) { // it is possible it has not been created yet...
// first get any custom BOs
$handle = opendir($config->get('sysRoot'). 'model');
// loop over the business object directory
while (false !== ($file = readdir($handle))) {
$classname = substr($file, 0, - 4);
// now loop over the core BOs provided with Alpha
$handle = opendir($config->get('sysRoot'). 'alpha/model');
// loop over the business object directory
while (false !== ($file = readdir($handle))) {
$classname = substr($file, 0, - 4);
self::$logger->debug('<<getBOClassNames ['. var_export($classNameArray, true). ']');
* Get the array of default attribute names.
* @return array An array of attribute names.
self::$logger->debug('>>getDefaultAttributes()');
self::$logger->debug('<<getDefaultAttributes ['. var_export($this->defaultAttributes, true). ']');
* Get the array of transient attribute names.
* @return array An array of attribute names.
self::$logger->debug('>>getTransientAttributes()');
self::$logger->debug('<<getTransientAttributes ['. var_export($this->transientAttributes, true). ']');
* Get the array of persistent attribute names, i.e. those that are saved in the database.
* @return array An array of attribute names.
self::$logger->debug('>>getPersistentAttributes()');
// get the class attributes
$reflection = new ReflectionClass(get_class($this));
$properties = $reflection->getProperties();
foreach($properties as $propObj) {
$propName = $propObj->name;
// filter transient attributes
self::$logger->debug('<<getPersistentAttributes ['. var_export($attributes, true). ']');
* Setter for the Object ID (OID)
* @param integer $OID The Object ID.
public function setOID($OID) {
self::$logger->debug('>>setOID(OID=['. $OID. '])');
self::$logger->debug('<<setOID');
* Inspector to see if the business object is transient (not presently stored in the database).
self::$logger->debug('>>isTransient()');
if(empty($this->OID) || !isset ($this->OID) || $this->OID == '00000000000') {
self::$logger->debug('<<isTransient [true]');
self::$logger->debug('<<isTransient [false]');
* Get the last database query run on this object.
* @return string An SQL query string.
self::$logger->debug('>>getLastQuery()');
self::$logger->debug('<<getLastQuery ['. $this->lastQuery. ']');
* Unsets all of the attributes of this object to null.
private function clear() {
self::$logger->debug('>>clear()');
// get the class attributes
$reflection = new ReflectionClass(get_class($this));
$properties = $reflection->getProperties();
foreach($properties as $propObj) {
$propName = $propObj->name;
if(!$propObj->isPrivate())
self::$logger->debug('<<clear');
* Reloads the object from the database, overwritting any attribute values in memory.
self::$logger->debug('>>reload()');
throw new AlphaException('Cannot reload transient object from database!');
self::$logger->debug('<<reload');
* Loads the definition from the file system for the BO class name provided.
* @param string $classname The name of the business object class name.
* @throws IllegalArguementException
if(self::$logger == null)
self::$logger = new Logger('AlphaDAO');
self::$logger->debug('>>loadClassDef(classname=['. $classname. '])');
if(file_exists($config->get('sysRoot'). 'model/'. $classname. '.inc'))
require_once $config->get('sysRoot'). 'model/'. $classname. '.inc';
elseif(file_exists($config->get('sysRoot'). 'alpha/model/'. $classname. '.inc'))
require_once $config->get('sysRoot'). 'alpha/model/'. $classname. '.inc';
elseif(file_exists($config->get('sysRoot'). 'alpha/model/types/'. $classname. '.inc'))
require_once $config->get('sysRoot'). 'alpha/model/types/'. $classname. '.inc';
self::$logger->debug('<<loadClassDef');
* Checks if the definition for the BO class name provided exists on the file system.
* @param string $classname The name of the business object class name.
if(self::$logger == null)
self::$logger = new Logger('AlphaDAO');
self::$logger->debug('>>checkClassDefExists(classname=['. $classname. '])');
if(file_exists($config->get('sysRoot'). 'model/'. $classname. '.inc'))
if(file_exists($config->get('sysRoot'). 'alpha/model/'. $classname. '.inc'))
if(file_exists($config->get('sysRoot'). 'alpha/model/types/'. $classname. '.inc'))
self::$logger->debug('<<checkClassDefExists ['. $exists. ']');
* Checks that a record exists for the BO in the database.
* @param int $OID The Object ID of the object we want to see whether it exists or not.
self::$logger->debug('>>checkRecordExists(OID=['. $OID. '])');
if(method_exists($this, 'before_checkRecordExists_callback'))
$this->before_checkRecordExists_callback();
$recordExists = $provider->checkRecordExists($OID);
$this->after_checkRecordExists_callback();
self::$logger->debug('<<checkRecordExists ['. $recordExists. ']');
* Checks to see if the table name matches the classname, and if not if the table
* name matches the classname name of another BO, i.e. the table is used to store
* @throws BadBOTableNameException
self::$logger->debug('>>isTableOverloaded()');
$provider = AlphaDAOProviderFactory::getInstance($config->get('sysDBProviderName'), $this);
$isOverloaded = $provider->isTableOverloaded();
self::$logger->debug('<<isTableOverloaded ['. $isOverloaded. ']');
* Starts a new database transaction.
public static function begin() {
if(self::$logger == null)
self::$logger = new Logger('AlphaDAO');
self::$logger->debug('>>begin()');
$provider = AlphaDAOProviderFactory::getInstance($config->get('sysDBProviderName'), new PersonObject());
throw new AlphaException('Error beginning a new transaction, error is ['. $e->getMessage(). ']');
self::$logger->debug('<<begin');
* Commits the current database transaction.
* @throws FailedSaveException
public static function commit() {
if(self::$logger == null)
self::$logger = new Logger('AlphaDAO');
self::$logger->debug('>>commit()');
$provider = AlphaDAOProviderFactory::getInstance($config->get('sysDBProviderName'), new PersonObject());
throw new FailedSaveException('Error commiting a transaction, error is ['. $e->getMessage(). ']');
self::$logger->debug('<<commit');
* Aborts the current database transaction.
if(self::$logger == null)
self::$logger = new Logger('AlphaDAO');
self::$logger->debug('>>rollback()');
$provider = AlphaDAOProviderFactory::getInstance($config->get('sysDBProviderName'), new PersonObject());
throw new FailedSaveException('Error aborting a transaction, error is ['. $e->getMessage(). ']');
self::$logger->debug('<<rollback');
* Static method that tries to determine if the system database has been installed or not.
if(self::$logger == null)
self::$logger = new Logger('AlphaDAO');
self::$logger->debug('>>isInstalled()');
* Install conditions are:
self::$logger->debug('<<isInstalled [true]');
self::$logger->debug('<<isInstalled [false]');
* Returns true if the BO has a Relation property called tags, false otherwise.
if(isset ($this->taggedAttributes) && isset ($this->tags) && $this->tags instanceof Relation)
* Setter for the BO version number.
* @param integer $versionNumber The version number.
private function setVersion($versionNumber) {
* Cast a BO to another type of BO. A new BO will be returned with the same OID and
* version_num as the old BO, so this is NOT a true cast but is a copy. All attribute
* values will be copied accross.
* @param string $targetClassName The name of the target BO class.
* @param AlphaDAO $originalBO The original business object.
* @return AlphaDAO The new business object resulting from the cast.
public function cast($targetClassName, $originalBO) {
$BO = new $targetClassName;
$BO->setOID($originalBO->getOID());
$BO->setVersion($originalBO->getVersion());
// get the class attributes
$originalBOreflection = new ReflectionClass(get_class($originalBO));
$originalBOproperties = $originalBOreflection->getProperties();
$newBOreflection = new ReflectionClass($targetClassName);
$newBOproperties = $newBOreflection->getProperties();
// copy the property values from the old BO to the new BO
if(count($originalBOproperties) < count($newBOproperties)) {
// the original BO is smaller, so loop over its properties
foreach($originalBOproperties as $propObj) {
$propName = $propObj->name;
$BO->set($propName, $originalBO->get($propName));
// the new BO is smaller, so loop over its properties
foreach($newBOproperties as $propObj) {
$propName = $propObj->name;
$BO->set($propName, $originalBO->get($propName));
* Converts "BusinessObject" to "Business" and returns.
* Check to see if an attribute exists on the BO.
* @param $attribute The attribute name.
$exists = $this->$attribute;
* Stores the business object to the configured cache instance
self::$logger->debug('>>addToCache()');
$cache = AlphaCacheProviderFactory::getInstance($config->get('sysCacheProviderName'));
self::$logger->error('Error while attempting to store a business object to the ['. $config->get('sysCacheProviderName'). ']
instance: ['. $e->getMessage(). ']');
self::$logger->debug('<<addToCache');
* Removes the business object from the configured cache instance
self::$logger->debug('>>removeFromCache()');
$cache = AlphaCacheProviderFactory::getInstance($config->get('sysCacheProviderName'));
self::$logger->error('Error while attempting to remove a business object from ['. $config->get('sysCacheProviderName'). ']
instance: ['. $e->getMessage(). ']');
self::$logger->debug('<<removeFromCache');
* Attempts to load the business object from the configured cache instance
self::$logger->debug('>>loadFromCache()');
$cache = AlphaCacheProviderFactory::getInstance($config->get('sysCacheProviderName'));
self::$logger->debug('Cache miss on key ['. get_class($this). '-'. $this->getOID(). ']');
self::$logger->debug('<<loadFromCache: [false]');
// get the class attributes
$reflection = new ReflectionClass(get_class($this));
$properties = $reflection->getProperties();
foreach($properties as $propObj) {
$propName = $propObj->name;
// filter transient attributes
$this->set($propName, $BO->get($propName));
}elseif(!$propObj->isPrivate() && isset ($this->$propName) && $this->$propName instanceof Relation) {
// handle the setting of ONE-TO-MANY relation values
if($prop->getRelationType() == 'ONE-TO-MANY') {
self::$logger->debug('<<loadFromCache: [true]');
self::$logger->error('Error while attempting to load a business object from ['. $config->get('sysCacheProviderName'). ']
instance: ['. $e->getMessage(). ']');
self::$logger->debug('<<loadFromCache: [false]');
* Sets the last query executed on this business object
self::$logger->sql($query);
|