diff options
| author | xue <> | 2007-09-28 18:08:20 +0000 | 
|---|---|---|
| committer | xue <> | 2007-09-28 18:08:20 +0000 | 
| commit | 199fc1254f84f851a2894df94487a45ed68f7c98 (patch) | |
| tree | 55f899a4ed1a885ea2334db959acbe91e5e573a3 | |
| parent | 4b292ae910c08c68ee62e5a96d7b879573921eee (diff) | |
Fixed #665.
6 files changed, 141 insertions, 20 deletions
@@ -13,6 +13,7 @@ ENH: Ticket#370 - TDataGrid now supports grouping cells (Qiang)  ENH: Ticket#577 - Added image button support for TPager (Qiang)  ENH: Ticket#623 - TMemCache to support multiple servers (Carl)  ENH: Ticket#664 - Added support to styling thead, tbody, tfoot of TDataGrid (Qiang) +ENH: Ticket#665 - Added support to column mapping in Active Record (Qiang)  ENH: Ticket#667 - Added TFeedService.ContentType property (Qiang)  ENH: Ticket#672 - ForceSecureConnection to THttpRequest (Carl)  ENH: Ticket#676 - Updated ActiveRecord Oracle Drives (Qiang) diff --git a/demos/quickstart/protected/pages/Database/ActiveRecord.page b/demos/quickstart/protected/pages/Database/ActiveRecord.page index 481985e9..a28c3d5e 100644 --- a/demos/quickstart/protected/pages/Database/ActiveRecord.page +++ b/demos/quickstart/protected/pages/Database/ActiveRecord.page @@ -52,21 +52,21 @@      </ul>  <h2>Design Implications</h2>  <p> -Prado's implementation of Active Record does not maintain referential identity. Each object obtained using  -Active Record is a copy of the data in the database. For example, If you ask for  -a particular customer and get back a <tt>Customer</tt> object, the next time you ask for  +Prado's implementation of Active Record does not maintain referential identity. Each object obtained using +Active Record is a copy of the data in the database. For example, If you ask for +a particular customer and get back a <tt>Customer</tt> object, the next time you ask for  that customer you get back another instance of a <tt>Customer</tt> object. This implies that a strict  comparison (i.e., using <tt>===</tt>) will return false, while loose comparison (i.e., using <tt>==</tt>) will -return true if the object values are equal by loose comparison.  +return true if the object values are equal by loose comparison.  <p>  <p>  This is design implication related to the following question. -<i>"Do you think of the customer as an object, of which there's only one,  +<i>"Do you think of the customer as an object, of which there's only one,  or do you think of the objects you operate on as <b>copies</b> of the database?"</i> -Other O/R mappings will imply that there is only one Customer object with custID 100, and it literally is that customer.  -If you get the customer and change a field on it, then you have now changed that customer.  -<i>"That constrasts with: you have changed this copy of the customer, but not that copy.  -And if two people update the customer on two copies of the object, whoever updates first,  +Other O/R mappings will imply that there is only one Customer object with custID 100, and it literally is that customer. +If you get the customer and change a field on it, then you have now changed that customer. +<i>"That constrasts with: you have changed this copy of the customer, but not that copy. +And if two people update the customer on two copies of the object, whoever updates first,  or maybe last, wins."</i> [A. Hejlsberg 2003]  </p> @@ -1059,6 +1059,45 @@ arrays. E.g. <tt>$player->skills[] = new SkillRecord()</tt>. If <tt>array</tt> w  will be thrown.  </p> +<h2>Column Mapping</h2> +<p> +Since v3.1.1, Active Record starts to support column mapping. Column mapping allows developers +to address columns in Active Record using a more consistent naming convention. In particular, +using column mapping, one can access a column using whatever name he likes, rather than limited by +the name defined in the database schema. +</p> +<p> +To use column mapping, declare a static array named <tt>COLUMN_MAPPING</tt> in the Active Record class. +The keys of the array are column names (called <i>physical column names</i>) as defined in the database +schema, while the values are corresponding property names (called <i>logical column names</i>) defined +in the Active Record class. The property names can be either public class member variable names or +component property names defined via getters/setters. If a physical column name happens to be the same +as the logical column name, they do not need to be listed in <tt>COLUMN_MAPPING</tt>. +</p> +<com:TTextHighlighter Language="php" CssClass="source block-content"> +class UserRecord extends TActiveRecord +{ +	const TABLE='users'; +	protected static $COLUMN_MAPPING=array +	( +		'user_id'=>'id', +		'email_address'=>'email', +		'first_name'=>'firstName', +		'last_name'=>'lastName', +	); +	public $id; +	public $username; // the physical and logical column names are the same +	public $email; +	public $firstName; +	public $lastName; +	//.... +} +</com:TTextHighlighter> +<p> +With the above column mapping, we can address <tt>first_name</tt> using <tt>$userRecord->firstName</tt> +instead of <tt>$userRecord->first_name</tt>. This helps separation of logic and model. +</p> +  <h2 id="138054">References</h2>  <ul id="u3" class="block-content">      <li>Fowler et. al. <i>Patterns of Enterprise Application Architecture</i>, diff --git a/demos/quickstart/protected/pages/GettingStarted/NewFeatures.page b/demos/quickstart/protected/pages/GettingStarted/NewFeatures.page index 9db74476..595bda00 100644 --- a/demos/quickstart/protected/pages/GettingStarted/NewFeatures.page +++ b/demos/quickstart/protected/pages/GettingStarted/NewFeatures.page @@ -19,6 +19,7 @@ This page summarizes the main new features that are introduced in each PRADO rel  <li>Added a new page state persister <tt>TCachePageStatePersister</tt>. It allows page state to be stored using a cache module (e.g. TMemCache, TDbCache, etc.)
  <li>Added support to the <a href="?page=Advanced.Auth">auth framework</a> to allow remembering login.</li>
  <li>Added support to display a prompt item in TDropDownList and TListBox (something like 'Please select:' as the first list item.)</li>
 +<li>Added support to <a href="?page=Database.ActiveRecord">column mapping in Active Record</a>.</li>
  </ul>
  <h2 id="8006">Version 3.1.0</h2>
 diff --git a/framework/Data/ActiveRecord/TActiveRecord.php b/framework/Data/ActiveRecord/TActiveRecord.php index b7ccaa50..20463956 100644 --- a/framework/Data/ActiveRecord/TActiveRecord.php +++ b/framework/Data/ActiveRecord/TActiveRecord.php @@ -65,6 +65,28 @@ Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelationContext');   * $user->save(); //update the 'admin' record.   * </code>   * + * Since v3.1.1, TActiveRecord starts to support column mapping. The physical + * column names (defined in database) can be mapped to logical column names + * (defined in active classes as public properties.) To use this feature, declare + * a static class variable COLUMN_MAPPING like the following: + * <code> + * class UserRecord extends TActiveRecord + * { + *     const TABLE='users'; + *     protected static $COLUMN_MAPPING=array + *     ( + *         'user_id'=>'username', + *         'email_address'=>'email', + *     ); + *     public $username; + *     pulbic $email; + * } + * </code> + * In the above, the 'users' table consists of 'user_id' and 'email_address' columns, + * while the UserRecord class declares 'username' and 'email' properties. + * By using column mapping, we can regularize the naming convention of column names + * in active record. + *   * @author Wei Zhuo <weizho[at]gmail[dot]com>   * @version $Id$   * @package System.Data.ActiveRecord @@ -86,6 +108,17 @@ abstract class TActiveRecord extends TComponent  	 */  	private $_connection; +	private static $_columnMapping=array(); + +	/** +	 * This static variable defines the column mapping. +	 * The keys are physical column names as defined in database, +	 * and the values are logical column names as defined as public variable/property names +	 * for the corresponding active record class. +	 * @var array column mapping. Keys: physical column names, values: logical column names. +	 */ +	protected static $COLUMN_MAPPING=array(); +  	/**  	 * Prevent __call() method creating __sleep() when serializing.  	 */ @@ -97,7 +130,10 @@ abstract class TActiveRecord extends TComponent  	/**  	 * Prevent __call() method creating __wake() when unserializing.  	 */ -	public function __wake(){} +	public function __wake() +	{ +		$this->setupColumnMapping(); +	}  	/**  	 * Create a new instance of an active record with given $data. The record @@ -111,6 +147,20 @@ abstract class TActiveRecord extends TComponent  		$this->copyFrom($data);  		if($connection!==null)  			$this->_connection=$connection; +		$this->setupColumnMapping(); +	} + +	/** +	 * @since 3.1.1 +	 */ +	private function setupColumnMapping() +	{ +		$className=get_class($this); +		if(!isset(self::$_columnMapping[$className])) +		{ +			$class=new ReflectionClass($className); +			self::$_columnMapping[$className]=$class->getStaticPropertyValue('COLUMN_MAPPING'); +		}  	}  	/** @@ -170,9 +220,9 @@ abstract class TActiveRecord extends TComponent  		foreach($properties as $prop)  		{  			if($strict) -				$equals = $equals && $this->{$prop} === $record->{$prop}; +				$equals = $equals && $this->getColumnValue($prop) === $record->getColumnValue($prop);  			else -				$equals = $equals && $this->{$prop} == $record->{$prop}; +				$equals = $equals && $this->getColumnValue($prop) == $record->getColumnValue($prop);  			if(!$equals)  				return false;  		} @@ -325,12 +375,12 @@ abstract class TActiveRecord extends TComponent  		$obj = Prado::createComponent($type);  		$tableInfo = $this->getRecordGateway()->getRecordTableInfo($obj);  		foreach($data as $name=>$value) -			$obj->{$name} = $value; +			$obj->setColumnValue($name,$value);  		/*  		foreach($tableInfo->getColumns()->getKeys() as $name)  		{  			if(isset($data[$name])) -				$obj->{$name} = $data[$name]; +				$obj->setColumnValue($name,$data[$name]);  		}*/  		$obj->_readOnly = $tableInfo->getIsView();  		$this->getRecordManager()->getObjectStateRegistry()->registerClean($obj); @@ -624,5 +674,35 @@ abstract class TActiveRecord extends TComponent  	{  		$this->raiseEvent('OnExecuteCommand', $this, $param);  	} + +	/** +	 * Retrieves the column value according to column name. +	 * This method is used internally. +	 * @param string the column name (as defined in database schema) +	 * @return mixed the corresponding column value +	 * @since 3.1.1 +	 */ +	public function getColumnValue($columnName) +	{ +		$className=get_class($this); +		if(isset(self::$_columnMapping[$className][$columnName])) +			$columnName=self::$_columnMapping[$className][$columnName]; +		return $this->$columnName; +	} + +	/** +	 * Sets the column value according to column name. +	 * This method is used internally. +	 * @param string the column name (as defined in database schema) +	 * @param mixed the corresponding column value +	 * @since 3.1.1 +	 */ +	public function setColumnValue($columnName,$value) +	{ +		$className=get_class($this); +		if(isset(self::$_columnMapping[$className][$columnName])) +			$columnName=self::$_columnMapping[$className][$columnName]; +		$this->$columnName=$value; +	}  }  ?>
\ No newline at end of file diff --git a/framework/Data/ActiveRecord/TActiveRecordGateway.php b/framework/Data/ActiveRecord/TActiveRecordGateway.php index e081e19c..6c5bc07c 100644 --- a/framework/Data/ActiveRecord/TActiveRecordGateway.php +++ b/framework/Data/ActiveRecord/TActiveRecordGateway.php @@ -277,7 +277,7 @@ class TActiveRecordGateway extends TComponent  		foreach($tableInfo->getColumns() as $name => $column)
  		{
  			if($column->hasSequence())
 -				$record->{$name} = $command->getLastInsertID($column->getSequenceName());
 +				$record->setColumnValue($name,$command->getLastInsertID($column->getSequenceName()));
  		}
  	}
 @@ -293,7 +293,7 @@ class TActiveRecordGateway extends TComponent  		{
  			if($column->getIsExcluded())
  				continue;
 -			$value = $record->{$name};
 +			$value = $record->getColumnValue($name);
  			if(!$column->getAllowNull() && $value===null && !$column->hasSequence())
  			{
  				throw new TActiveRecordException(
 @@ -329,7 +329,7 @@ class TActiveRecordGateway extends TComponent  		{
  			if($column->getIsExcluded())
  				continue;
 -			$value = $record->{$name};
 +			$value = $record->getColumnValue($name);
  			if(!$column->getAllowNull() && $value===null)
  			{
  				throw new TActiveRecordException(
 @@ -367,7 +367,7 @@ class TActiveRecordGateway extends TComponent  		foreach($tableInfo->getColumns() as $name=>$column)
  		{
  			if($column->getIsPrimaryKey())
 -				$primary[$name] = $record->{$name};
 +				$primary[$name] = $record->getColumnValue($name);
  		}
  		return $primary;
  	}
 diff --git a/tests/FunctionalTests/active-controls/tests/ActiveDropDownListTestCase.php b/tests/FunctionalTests/active-controls/tests/ActiveDropDownListTestCase.php index 5899a6ba..cb5d349c 100644 --- a/tests/FunctionalTests/active-controls/tests/ActiveDropDownListTestCase.php +++ b/tests/FunctionalTests/active-controls/tests/ActiveDropDownListTestCase.php @@ -29,13 +29,13 @@ class ActiveDropDownListTestCase extends SeleniumTestCase  		$this->pause(800);  		$this->select("list2", "value 1 - item 4");  		$this->pause(800); -		$this->assertText("label1", "Selection 2: value 1 - item 4"); +		$this->assertText("label2", "Selection 2: value 1 - item 4");  		$this->select("list1", "item 3");  		$this->pause(800);  		$this->select("list2", "value 3 - item 5");  		$this->pause(800); -		$this->assertText("label1", "Selection 2: value 3 - item 5"); +		$this->assertText("label2", "Selection 2: value 3 - item 5");  		$this->click('button4');  		$this->pause(800);  | 
