SmartAR.php 7.06 KB
<?php
/**
* SmartAR class file
* 
* @author Vitaliy Stepanenko <mail@vitaliy.in>
* @package SmartActiveRecord
* @version $Id: SmartAR.php 29314 2011-06-10 15:38:17Z stepanenko $
* @license BSD
* 
* @todo check populateRecords
* 
*/

/**
* This class gives possibility to use power of Yii ActiveRecord without describing new model classes.
* It can be useful when you work with dynamically generated tables in database, 
* or when you don't want to create a lot of code to do some simple operations using convenient ActiveRecord interface.
* 
* <p><b>=== Usage ===</b>:</p>
* <code>
* <?php
* 	Yii::import('ext.components.SmartActiveRecord.SmartAR');
*	$model = SmartAR::model('tbl_MyTable')->findByAttributes('id'=>2);
* 	$model->somefield = someValue;
* 	$model->save();
* 	
* ?>
* </code> 
* 
* <p><b>=== Some logics of creating models ===</b>:</p>
* SAR::create(config)
*     $instance->new SAR, AR::__construct(null)
* 	  SAR::$_lastTableName	== config.tableName
*     $instance->AR::refreshMetaData
* 	  	AR::model(get_class($this)==SAR)
* 			AR::$_models['SAR'] = new SAR, AR::__construct(null)			
* 		AR::$_models['SAR']->metadata = new metadata(AR::$_models['SAR']->tableName() ====> SAR::$_lastTableName) //SQL query?
*       $instance->metadata = AR::$_models['SAR']->metadata ====> new metadata(SAR::$_lastTableName)
* --> configured instance is not pushed fo SAR::$_models[tableName], SAR::$_models[tableName] is null, SAR::$_models[tableName] is not used.
* 
* 
*/
class SmartAR extends CActiveRecord implements IApplicationComponent
{
	
	const USE_STATIC_MODEL = 0;
	
	private $_initialized = false;
	
	/**
	* Static models
	* 
	* @var array
	*/
	protected static $_models;	
	
	/**
	* Table name stores here intermediately after SmartAR::model() and before first call of SmartAR::tableName
	* 
	* @var string
	*/
	protected static $_lastTableName;
	
	/**
	* Table name
	* 
	* @var string
	*/
	protected $_tableName;
	
	/**
	* list of related object declarations
	* 
	* @var array
	*/
	public $relations = array();
	public $scopes = array();
	public $rules = array();
	public $behaviors = array();
	public $attributeLabels = array();
	private $_setOnlySafeAttributes = false;
	
	/**
	* Returns the static model (instance of SmartAR) for specified DB table.		
	* @param string $tableName db table name
	* @return SmartAR active record model instance.
	*/
	public static function model($tableName)
	{		        
		/** @todo replace self::* to static::* when we will use only PHP 5.3+ that have a support of LSB */
		if(!isset(self::$_models[$tableName])) {
			self::create($tableName,true);
		}
		return self::$_models[$tableName];
	}
	
	/**
	* Factory method that creates model.
	* To prevent metadata refreshing use static model instead of this method if it is possible.
	* This method can be useful when you use named scopes, have alot of work with AR DB criteria, etc.
	* 
	* @todo test it
	* 
	* @param string $tableName
	* @return SmartAR
	*/
	public static function create($config, $useAsStaticModel=false)
	{
		if (is_string($config)) {
			$model = new SmartAR(null);
			$model->setTableName($config);	
		} else {
			$model = Yii::createComponent($config,null);
		}	
		
		if (!$useAsStaticModel) {
			$model->useStaticModelOptions();
		} else {
			self::$_models[$config] = $model;//@todo!!!
		}					
		
		$model->init();
		return $model;
	}
	
	/**
	* Deny instantiating not via SmartAR::create
	* 
	* @param mixed $scenario
	* @return SmartAR
	*/
	public function __construct($scenario = 'insert') {
		if ($scenario!==null) {
			throw new CException('SmartAR class don\'t allow instantiating objects,  you must use SmartAR::create("<tableName|config>") fabric method instead of it.');
		}
	}
	
	public function setTableName($tableName)
	{
	   $this->_tableName  = self::$_lastTableName = $tableName;
	   $this->refreshMetaData();
	   self::$_lastTableName = null;
	}
	
		
	
	/**
	* Options from static model will used instead of instance options
	* @return SmartAR
	* 
	*/
	protected function useStaticModelOptions()
	{
		$this->relations 
		= $this->scopes 
		= $this->rules 
		= $this->behaviors
		= $this->attributeLabels 
		= $this->_setOnlySafeAttributes = self::USE_STATIC_MODEL;
		return $this;		
	}
	
	
	/**
	* Configure model instance options.
	* It's most convenient way of configuring static models (simple model instace we can configure when creating)
	* 
	* @param array $config
	* @return SmartAR
	*/
	public function configure($config)
	{		
		if (isset($config['class'])) {
			unset($config['class']);
		}
			
		foreach ($config as $field=>$value) {
			$this->$field = $value;
		}
		
		return $this;
	}
	  
	/**
	* Mark all table columns as safe attributes by default.
	* @return array list of safe attributes (equal to table columns).
	* @todo do something with it, coz it's security fail
	*/
	public function getSafeAttributeNames()
	{
		if ($this->setOnlySafeAttributes) {
			return parent::getSafeAttributeNames();
		} else {
			return array_keys($this->getMetaData()->columns);	
		}
	}
	
	public function setAttributes($values, $safeOnly=null)
	{
		 if ($safeOnly === null) {
			 $safeOnly = $this->setOnlySafeAttributes;
		 }
		 return parent::setAttributes($values, $safeOnly);
	}
	
	public function setSetOnlySafeAttributes($value) 
	{
		$this->_setOnlySafeAttributes = $value;
	}
	
 	public function getSetOnlySafeAttributes() 
 	{
		if ($this->_setOnlySafeAttributes === self::USE_STATIC_MODEL) {
			return self::model($this->tableName())->setOnlySafeAttributes;
		}
		return $this->_setOnlySafeAttributes;
	}
	
	public function init()
	{								
		$this->attachBehaviors($this->behaviors());
		$this->afterConstruct();
		$this->_initialized = true;
	}
	
	public function getIsInitialized()
	{
		return $this->_initialized;
	}
	
	/**
	* @return name of DB table that is associated with this instance of SmartAR.
	*/
	public function tableName()
	{			
		if ($this->_tableName) 		return $this->_tableName;	   	
		if (self::$_lastTableName) 	return self::$_lastTableName;
		else throw new CException('Table name not specified.');
	}	
	
	public function relations()
	{
		if ($this->relations === self::USE_STATIC_MODEL) {
			return self::model($this->tableName())->relations;
		}
		return $this->relations;
	}
	
	public function scopes()
	{
		if ($this->scopes === self::USE_STATIC_MODEL) {
			return self::model($this->tableName())->scopes;
		}
		return $this->scopes;
	}
	
	public function rules()
	{
		if ($this->rules === self::USE_STATIC_MODEL) {
			return self::model($this->tableName())->rules;
		}
		return $this->rules;
	}
	
	public function behaviors()
	{
		if ($this->behaviors === self::USE_STATIC_MODEL) {
			return self::model($this->tableName())->behaviors;
		}		
		return $this->behaviors;
	}
	
	public function attributeLabels()
	{
		if ($this->attributeLabels === self::USE_STATIC_MODEL) {
			return self::model($this->tableName())->attributeLabels;
		}	
		return $this->attributeLabels;
	}   
}