Location: PHPKode > projects > Anahita Social Engine > dependencies/plg_system_socialengine/socialengine/domain/model/abstract.php
<?php
/**
 * @version		1.0.0
 * @category	Anahita Social Engine™
 * @copyright	Copyright (C) 2008 - 2010 rmdStudio Inc. and Peerglobe Technology Inc. All rights reserved.
 * @license		GNU GPLv2 <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
 * @link     	http://www.anahitapolis.com
 */

abstract class AnDomainModelAbstract extends KObject implements AnDomainModelInterface
{	
	/**
	 * 
	 */
	protected $_command_chain = null;
	
	/**
	 * 
	 */
	protected $_identifier    = null;
	
	/**
	 * 
	 */
	protected $_context       = null;
	
	/**
	 * 
	 */	
	protected $_mapper 	      = null;
	
		
	/**
	 * 
	 */
	public $_properties = array();
		
	/**
	 * 
	 * @return 
	 * @param $model Object
	 */			
	abstract static function describe($model);
	
	/**
	 * 
	 * @return 
	 * @param $options Object[optional]
	 */	
	public function __construct($options = array())
	{							
		$this->_identifier = $options['identifier'];
		
		$this->_mapper	   = $options['mapper'];
		
		$options = $this->_initialize($options);				
		
		$this->_context	   = KFactory::get('lib.anahita.domain.context');			
				
		$this->_properties = $this->_mapper->getDefaultValues();
		
		$this->_initialize();
	}
			
	public function inheritanceTree($class=null, array $plist=array())
	{
	    $class  = $class ? $class : $this;
	    
		$parent = get_parent_class($class);
		

		if  ( $parent == __CLASS__ ) {
			return $plist;
		}
					
        $plist[] = $parent;
		
        $plist = self::inheritanceTree($parent, $plist);
		
	    return $plist;
	}
	
	/**
	 * Called when a model is fetched from a database
	 */
	public function awakeFromFetch() 
	{
		
	}
	
	/**
	 * Called when an enity life cycle has begun. it's used to set default values
	 */
	public function awakeFromInsert()
	{
		//sub class can implement that to set initializtion scripts
	}
	
	/**	
	 * Called after a new instance has been created or clone. Entities are usually cloned for performance
	 * so this method allows to initialize the entitty state even if it's cloned
	 * @return 
	 */
	protected function _initialize()
	{
		//sub class can implement that to set initializtion scripts
	}
		
	/**
	 * Called before a db operation. This gives the entities a chacne to register before/after 
	 * hooks for database operation
	 * @return 
	 */
	protected function _registerHooks()
	{
		//sub classes can implement this method to add custom hooks 
	}
	
	/**
	 * Valiate the state of the model by validating it's properties and calling appropicate hooks
	 * @return BOOL
	 * @param $context KCommandContext Object
	 */
	public function isValid(KCommandContext $context = null)
	{
		if ( is_null($context) )$context = KFactory::tmp('lib.koowa.command.context');

		if ( $this->isClean() )
			return true;
		
		//don't need to validate properties if we are deleting an entity
		if ( $this->isDirty() || $this->isNew() )	
			if ( $this->validateProperties($context) === false ) 
				return false;
					
		if ( $this->isNew() ) {
			$ret =  $this->validateForInsert($context);
		} else if ( $this->isDirty() )
			$ret = $this->validateForUpdate($context);
		else 
			$ret = $this->validateForDelete($context);
		
		if ( $ret !== false )
			$ret =  $this->validate($context);
	
		return $ret === false ? false : true;
	}
	
	
	/**
	 * Validate model properties
	 * @return 
	 * @param $context Object[optional]
	 */	
	public function validateProperties(KCommandContext $context)
	{
		$properties = $this->getMapper()->getProperties();
		
		foreach($properties as $name => $property) {
			
			if ( $property instanceof AnDomainDescriptionRelationshipOnetomany ) 
				continue;
			
			$value =  $this->$name;
				
			if ( $property->isRequired() && !$property->validatePresenceOf( $value ) )
			{
				$context->setError(get_class($this)." requires {$name}");
				return false;
			}
			
			if ( $property instanceof AnDomainDescriptionAttribute && $filter = $property->getFilter() ) 
			{			
				if ( !$filter->validate($value) ) 
				{
					$context->setError('Property validation failed for '.get_class($this).'::'.$name);
					return false;
				}
			}
							
			$method = 'validate'.ucfirst($name);

			if ( method_exists($this, $method ) ) 
				return $this->$method($value, $context);
			
		}

		return true;
	}
	
	/**
	 * Validate an entity for all of its state
	 * @return BOOL
	 * @param $context KCommandContext Object
	 */	
	public function validate(KCommandContext $context) {}
	
	/**
	 * Validate before an entity is deleted
	 * @return BOOL
	 * @param $context KCommandContext Object
	 */
	public function validateForDelete(KCommandContext $context) {}
	
	/**
	 * Validate before an entity is inserted
	 * @return BOOL
	 * @param $context KCommandContext Object
	 */
	public function validateForInsert(KCommandContext $context) {}
	
	/**
	 * Validate before an entity is updated
	 * @return 
	 * @param $context KCommandContext Object
	 */
	public function validateForUpdate(KCommandContext $context) {}
	
				
	/**
	 * @TODO must be removed
	 * sets the primary key value of this model
	 * @return 
	 * @param $id Object
	 */
	final public function setId($id)
	{				
		$this->_set('id', $id);
	}
	
	/**
	 * @TODO must be removed
	 * get the primary key value of the model
	 * @return 
	 */
	final public function getId()
	{
		return 	$this->_get('id');
	}
	
	public function uniqueValues()
	{
		$values = array();
		
		foreach($this->getMapper()->getUniqueKeys() as $name => $property) {
			if ( $value = $this->$name ) {
				$values[$name] = $value;
			}
		}
		return $values;
	}
			
	/**
	 * gets the primitive value of a property
	 * @return 
	 * @param $property Object
	 */
	protected function _get($name)
	{
		return isset($this->_properties[$name]) ? $this->_properties[$name] : null;		
	}
		
	/**
	 * sets the primitive value of a property
	 * @return 
	 * @param $property Object
	 * @param $value Object
	 */
	protected function _set($name, $value)
	{
		$property		   = $this->getMapper()->getProperty($name);
		
		if ( !$property ) {
			//wrong 
			$this->_properties[$name] 	= $value;
			return;
		}
		
		if ( $property instanceof AnDomainRelationshipOnetomany )
			throw new Exception("Trying to set a many relationship");
		
		$type = $property->getType();
		
		if ( $value && $type && !$value instanceof $type ) {
			throw new Exception("{$name} must be an instance of {$type}");
		}
			
		if ( $property instanceof AnDomainDescriptionAttribute && $filter = $property->getFilter() ) {			
			$value	= $filter->sanitize($value);
		}

		$this->_properties[$name] 	= $value;
					
		$unique_properties = $this->getMapper()->getUniqueKeys();
		
		$this->_context->propertyValueChanged($this, $name, $value);
	}
		
	/**
	 * sets a property of the entity - it check to see if a set{$property} exists
	 * @return 
	 * @param $property Object
	 * @param $value Object[optional]
	 */	
    public final function setProperty( $property, $value = null )
    {
    	if(is_object($property)) 
    		$property = (array) $property;
    	
    	if(is_array($property)) {
        	foreach ($property as $k => $v) 
            	$this->setProperty($k, $v);
        	
        } else {

			$method   = 'set'.ucfirst($property);

			if (method_exists($this, $method)) {
				$this->$method($value);
				return ;
			}
		
			$this->_set($property, $value);
        }

        return $this;
    }	
	
	/**
	 * gets a value of a property
	 * @return 
	 * @param $property Object[optional]
	 * @param $default Object[optional]
	 */	
    public function getProperty($property = null, $default = null)
    {
		if ( is_null($property) )
			return $this->_properties;
		
		$method = 'get'.ucfirst($property);
		
		if (method_exists($this,$method)) {
			$value = $this->$method();
		} else {
			$value = $this->_get($property);
		}
		
		if ( is_null($value) ) $value = $default;
		
		return $value;
    }
	
	/**
	 * Return a list of properties whose value have changed 
	 * @return Array
	 */
	public function updatedProperties()
	{
		$changes = $this->_context->propertyChangeTracks($this);
		
		if ( $changes ) 
			return $changes;
			
		return array();
	}
	
	/**
	 * Return true or false depending if the value of the property has changed 
	 * @return Bool
	 * @param $name String
	 */
	public function trackChanges($name)
	{
		$changes = $this->_context->propertyChangeTracks($this);
		if ( !isset($changes[$name]) ) return null;
		return $changes[$name];
	}
	
	public function __set($name, $value)
	{		
		$this->setProperty($name, $value);
	}
	
	public function __get($name)
	{
		return $this->getProperty($name, null);
	}

	public function __isset($name)
	{
		return isset($this->_properties[$name]);
	}
	
	public function __unset($name)
	{
		unset($this->_properties[$name]);
	}

	public function eql($entity)
	{
		return $entity instanceof AnDomainProxyEntity ? $entity->getObject() === $entity : $this === $entity;
	}
		
	public function markNew()
	{
		$this->_context->addNew($this);
	}
	
	public function markDeleted()
	{
		$this->_context->addDelete($this);
	}

	public function markDirty()
	{
		$this->_context->addDirty($this);
	}	

	public function markClean()
	{
		$this->_context->addClean($this);
	}	
	
	public function isClean()
	{
		return !$this->isDirty() && !$this->isNew() && !$this->isDeleted();
	}
	
	public function isDirty()
	{
		return $this->_context->isDirty($this);
	}
				
	public function isNew()
	{
		return $this->_context->isNew($this);		
	}
	
	public function isDeleted()
	{
		return $this->_context->isDeleted($this);
	}
	
	public function delete($context = null)
	{
		$this->markDeleted();
		return $this->save($context);
	}
		
	/**
	 * Hook called before an entity is inserted into the database
	 * @return 
	 * @param $context KCommandContext Object
	 */	
	protected function _beforeInsert(KCommandContext $context) {}
	
	/**
	 * Hook called after an entity is inserted into the database
	 * @param $context KCommandContext Object
	 */
	protected function _afterInsert(KCommandContext $context)  {}
	
	/**
	 * Hook called before an entity is updated 
	 * @param $context KCommandContext Object
	 */
	protected function _beforeUpdate(KCommandContext $context) {}
	
	/**
	 * Hook called after an entity is updated 
	 * @return 
	 * @param $context KCommandContext Object
	 */
	protected function _afterUpdate(KCommandContext $context)  {}
	
	/**
	 * Hook called before an entity is deleted 
	 * @return 
	 * @param $context KCommandContext Object
	 */
	protected function _beforeDelete(KCommandContext $context) {}
	
	/**
	 * Hook called after an entity is deleted 
	 * @param $context KCommandContext Object
	 */
	protected function _afterDelete(KCommandContext $context)  {}
	
	/**
	 * Hook called before any database commit - It can be insert, update or delete. The state of the entity can
	 * be checked by $entity->isNew(), $entity->isDeleted() or $entity->isDirty()
	 * @param $context KCommandContext Object
	 */
	protected function _beforeSave(KCommandContext $context)  {}
	
	/**
	 * Hook called after any database commit - It can be insert, update or delete. The state of the entity can
	 * @return 
	 * @param $context Object
	 */
	protected function _afterSave(KCommandContext $context)   {}
	
	/**
	 * Persist the entity in to the database. It first validates the entity, if valid then it's stored in the 
	 * database
	 * @return 
	 * @param $context Object[optional]
	 */
	public function save(KCommandContext $context = null)
	{
		if ( $this->isClean() )
			return;
					
		if ( !$context )
			$context = KFactory::tmp('lib.koowa.command.context');
		
		if ( $this->isValid($context) === false )
			return false;
		
		$command = strtolower($this->isNew() ? 'Insert' : ( $this->isDirty() ? 'Update' : 'Delete' ));		
		
		$context['data'] 	= array();
		
		$context['command'] = $command;
		
//		$this->getRepository()->getCommandChain()->run("entity.before.$command", $context);		
		$this->getCommandChain()->run("entity.before.$command", $context);
		$this->{"_before$command"}($context);
		$this->_beforeSave($context);
		$this->getMapper()->commit($this, $context['data']);
		$this->_afterSave($context);
		$this->{"_after$command"}($context);
		$this->getCommandChain()->run("entity.after.$command", $context);
//		$this->getRepository()->getCommandChain()->run("entity.after.$command", $context);		
	}
	
	/**
	 * Return the command chain
	 * @return 
	 */
	public function getCommandChain()
	{	
		if ( !($this->_command_chain) ) {
			$this->_command_chain = new KCommandChain();
			$this->mixin( KFactory::tmp('lib.koowa.mixin.command', array('command_chain'=>$this->_command_chain)));			
			$this->_registerHooks();
		}
		return $this->_command_chain;
	}
	
	public function getIdentifier()
	{
		return $this->_identifier;	
	}

	public function getDomainContext()
	{
		return $this->_context;
	}
	
	public function setMapper($mapper)
	{
		$this->_mapper = $mapper;
		return $this;
	}
	
	public function getMapper()
	{
		return 	$this->_mapper;
	}
	
	public function serialize()
	{		
		return $this->getMapper()->serialize($this->_properties);
	}
	
	public function getRepository()
	{
		return $this->getMapper()->getRepository();
	}
	 
	/**
	 * clones a model - this is for when creating a list of models and we don't want to instantiate them
	 * @return 
	 */   
	public function __clone()
	{
		$this->_properties = $this->_mapper->getDefaultValues();
		$this->_initialize();
	}
	
	public function toArray()
	{
		return $this->getProperty();
	}

	
}
Return current item: Anahita Social Engine