diff options
24 files changed, 277 insertions, 78 deletions
| diff --git a/.gitattributes b/.gitattributes index eee67148..ca665df6 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1268,6 +1268,10 @@ demos/quickstart/protected/pages/Database/Samples/Scaffold/sqlite.db -text  demos/quickstart/protected/pages/Database/Samples/config.xml -text  demos/quickstart/protected/pages/Database/Scaffold.page -text  demos/quickstart/protected/pages/Database/SqlMap.page -text +demos/quickstart/protected/pages/Database/ar_objects.png -text +demos/quickstart/protected/pages/Database/ar_objects.vsd -text +demos/quickstart/protected/pages/Database/ar_relations.png -text +demos/quickstart/protected/pages/Database/ar_relations.vsd -text  demos/quickstart/protected/pages/Database/diagram.png -text  demos/quickstart/protected/pages/Database/object_states.png -text  demos/quickstart/protected/pages/Database/sqlmap_active_record.png -text @@ -1646,6 +1650,7 @@ framework/Data/Common/Mysql/TMysqlTableInfo.php -text  framework/Data/Common/Pgsql/TPgsqlMetaData.php -text  framework/Data/Common/Pgsql/TPgsqlTableColumn.php -text  framework/Data/Common/Pgsql/TPgsqlTableInfo.php -text +framework/Data/Common/Sqlite/TSqliteCommandBuilder.php -text  framework/Data/Common/Sqlite/TSqliteMetaData.php -text  framework/Data/Common/Sqlite/TSqliteTableColumn.php -text  framework/Data/Common/Sqlite/TSqliteTableInfo.php -text @@ -2540,6 +2545,7 @@ tests/FunctionalTests/tickets/protected/pages/Ticket587.page -text  tests/FunctionalTests/tickets/protected/pages/Ticket587.php -text  tests/FunctionalTests/tickets/protected/pages/Ticket605.page -text  tests/FunctionalTests/tickets/protected/pages/Ticket606.page -text +tests/FunctionalTests/tickets/protected/pages/Ticket614.page -text  tests/FunctionalTests/tickets/protected/pages/Ticket68.page -text  tests/FunctionalTests/tickets/protected/pages/Ticket72.page -text  tests/FunctionalTests/tickets/protected/pages/Ticket72.php -text diff --git a/demos/quickstart/protected/pages/Database/ActiveRecord.page b/demos/quickstart/protected/pages/Database/ActiveRecord.page index c3cf663b..967aae89 100644 --- a/demos/quickstart/protected/pages/Database/ActiveRecord.page +++ b/demos/quickstart/protected/pages/Database/ActiveRecord.page @@ -49,15 +49,20 @@          <li>Finder methods to wrap commonly used SQL queries and return Active Record objects.</li>          <li>Update existing records and insert new records into the database.</li>      </ul> +<h2>Database Supported</h2>  <p id="p1" class="block-content">  The Active Record implementation utilizes the <a href="?page=Database.DAO">Prado DAO</a> classes for data access.    -The current Active Record implementation supports  -<a href="http://www.mysql.com">MySQL</a>,  -<a href="http://www.postgres.com">Postgres SQL</a> and  -<a href="http://www.sqlite.org">SQLite</a> databases. -Support for other databases can be provided when there are sufficient demand. +The current Active Record implementation supports the following database.   </p> -<h2 id="138048">Defining an Active Record</h2> +<ul> +	<li><a href="http://www.mysql.com">MySQL 4.1 or later</a></li> +	<li><a href="http://www.postgres.com">Postgres SQL 7.3 or later</a></li> +	<li><a href="http://www.sqlite.org">SQLite 2 and 3</a></li> +	<li><a href="#">MS SQL 2000 or later</a></li> +</ul> +<p>Support for other databases can be provided when there are sufficient demand.</p> + +<h1 id="138048">Defining an Active Record</h1>  <p id="690483" class="block-content">Let us      consider the following "users" table that contains two columns named "username" and "email",       where "username" is also the primary key.  @@ -412,6 +417,18 @@ catch(Exception $e) // an exception is raised if a query fails  }  </com:TTextHighlighter> +<h1 id="ar_relations">Active Record Relationships</h1> +<p id="690504a" class="block-content"> +The Prado ActiveRecord implementation supports the foreign key mappings for database +that supports foreign key contraints. For ActiveRecord relationships to function the +underlying database must support foreign key constraints (e.g. MySQL with InnoDB). +</p> + +<h2>Foreign Key Mapping</h2> + + +<h2>Association Table Mapping</h2> +  <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/Database/ar_objects.png b/demos/quickstart/protected/pages/Database/ar_objects.pngBinary files differ new file mode 100644 index 00000000..027d1e96 --- /dev/null +++ b/demos/quickstart/protected/pages/Database/ar_objects.png diff --git a/demos/quickstart/protected/pages/Database/ar_objects.vsd b/demos/quickstart/protected/pages/Database/ar_objects.vsdBinary files differ new file mode 100644 index 00000000..595b7d86 --- /dev/null +++ b/demos/quickstart/protected/pages/Database/ar_objects.vsd diff --git a/demos/quickstart/protected/pages/Database/ar_relations.png b/demos/quickstart/protected/pages/Database/ar_relations.pngBinary files differ new file mode 100644 index 00000000..df72823c --- /dev/null +++ b/demos/quickstart/protected/pages/Database/ar_relations.png diff --git a/demos/quickstart/protected/pages/Database/ar_relations.vsd b/demos/quickstart/protected/pages/Database/ar_relations.vsdBinary files differ new file mode 100644 index 00000000..9b27e7e3 --- /dev/null +++ b/demos/quickstart/protected/pages/Database/ar_relations.vsd diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php b/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php index c72ba160..1168bf55 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php @@ -1,6 +1,6 @@  <?php
  /**
 - * TActiveRecordBelongsTo class file.	
 + * TActiveRecordBelongsTo class file.
   *
   * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
   * @link http://www.pradosoft.com/
 @@ -16,8 +16,8 @@  Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');
  /**
 - * Implements the foreign key relationship (TActiveRecord::BELONGS_TO) between 
 - * the source objects and the related foreign object. Consider the 
 + * Implements the foreign key relationship (TActiveRecord::BELONGS_TO) between
 + * the source objects and the related foreign object. Consider the
   * <b>entity</b> relationship between a Team and a Player.
   * <code>
   * +------+            +--------+
 @@ -38,17 +38,19 @@ Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');   *     public $team_name; //foreign key player.team_name <-> team.name
   * 	   public $age;
   *     public $team; //foreign object TeamRecord
 - * 
 - *     protected static $RELATIONS = array(
 - *			'team' => array(self::BELONGS_TO, 'TeamRecord'));
 - * 
 + *
 + *     protected static $RELATIONS = array
 + *     (
 + *			'team' => array(self::BELONGS_TO, 'TeamRecord')
 + *     );
 + *
   *	   public static function finder($className=__CLASS__)
   *	   {
   *		   return parent::finder($className);
   *	   }
   * }
   * </code>
 - * The <tt>$RELATIONS</tt> static property of PlayerRecord defines that the
 + * The static <tt>$RELATIONS</tt> property of PlayerRecord defines that the
   * property <tt>$team</tt> belongs to (or is a) <tt>TeamRecord</tt>s.
   *
   * The team object may be fetched as follows.
 @@ -58,7 +60,7 @@ Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');   * The method <tt>with_xxx()</tt> (where <tt>xxx</tt> is the relationship property
   * name, in this case, <tt>team</tt>) fetchs the corresponding TeamRecords using
   * a second query (not by using a join). The <tt>with_xxx()</tt> accepts the same
 - * arguments as other finder methods of TActiveRecord, e.g. 
 + * arguments as other finder methods of TActiveRecord, e.g.
   * <tt>with_team('location = ?', 'Madrid')</tt>.
   *
   * @author Wei Zhuo <weizho[at]gmail[dot]com>
 diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php b/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php index 795630ab..c5ce616e 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php @@ -34,8 +34,10 @@ Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');   *
   *     public $players=array(); //list of players
   *
 - *     protected static $RELATIONS=array(
 - *         'players' => array(self::HAS_MANY, 'PlayerRecord'));
 + *     protected static $RELATIONS=array
 + *     (
 + *         'players' => array(self::HAS_MANY, 'PlayerRecord')
 + *     );
   *
   *	   public static function finder($className=__CLASS__)
   *	   {
 @@ -47,7 +49,7 @@ Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');   *     // see TActiveRecordBelongsTo for detailed definition
   * }
   * </code>
 - * The <tt>$RELATIONS</tt> static property of TeamRecord defines that the
 + * The static <tt>$RELATIONS</tt> property of TeamRecord defines that the
   * property <tt>$players</tt> has many <tt>PlayerRecord</tt>s.
   *
   * The players list may be fetched as follows.
 diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php b/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php index 456848fe..50558a2b 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php @@ -1,16 +1,98 @@  <?php
 +/**
 + * TActiveRecordHasManyAssociation class file.
 + *
 + * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
 + * @link http://www.pradosoft.com/
 + * @copyright Copyright © 2005-2007 PradoSoft
 + * @license http://www.pradosoft.com/license/
 + * @version $Id$
 + * @package System.Data.ActiveRecord.Relations
 + */
  /**
   * Loads base active record relations class.
   */
  Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');
 +/**
 + * Implements the M-N (many to many) relationship via association table.
 + * Consider the <b>entity</b> relationship between Articles and Categories
 + * via the association table <tt>Article_Category</tt>.
 + * <code>
 + * +---------+            +------------------+            +----------+
 + * | Article | * -----> * | Article_Category | * <----- * | Category |
 + * +---------+            +------------------+            +----------+
 + * </code>
 + * Where one article may have 0 or more categories and each category may have 0
 + * or more articles. We may model Article-Category <b>object</b> relationship
 + * as active record as follows.
 + * <code>
 + * class ArticleRecord
 + * {
 + *     const TABLE='Article';
 + *     public $article_id;
 + *
 + *     public $Categories=array(); //foreign object collection.
 + *
 + *     protected static $RELATIONS = array
 + *     (
 + *         'Categories' => array(self::HAS_MANY, 'CategoryRecord', 'Article_Category')
 + *     );
 + *
 + *     public static function finder($className=__CLASS__)
 + *     {
 + *         return parent::finder($className);
 + *     }
 + * }
 + * class CategoryRecord
 + * {
 + *     const TABLE='Category';
 + *     public $category_id;
 + *
 + *     public $Articles=array();
 + *
 + *     protected static $RELATIONS = array
 + *     (
 + *         'Articles' => array(self::HAS_MANY, 'ArticleRecord', 'Article_Category')
 + *     );
 + *
 + *     public static function finder($className=__CLASS__)
 + *     {
 + *         return parent::finder($className);
 + *     }
 + * }
 + * </code>
 + *
 + * The static <tt>$RELATIONS</tt> property of ArticleRecord defines that the
 + * property <tt>$Categories</tt> has many <tt>CategoryRecord</tt>s. Similar, the
 + * static <tt>$RELATIONS</tt> property of CategoryRecord defines many ArticleRecords.
 + *
 + * The articles with categories list may be fetched as follows.
 + * <code>
 + * $articles = TeamRecord::finder()->withCategories()->findAll();
 + * </code>
 + * The method <tt>with_xxx()</tt> (where <tt>xxx</tt> is the relationship property
 + * name, in this case, <tt>Categories</tt>) fetchs the corresponding CategoryRecords using
 + * a second query (not by using a join). The <tt>with_xxx()</tt> accepts the same
 + * arguments as other finder methods of TActiveRecord.
 + *
 + * @author Wei Zhuo <weizho[at]gmail[dot]com>
 + * @version $Id$
 + * @package System.Data.ActiveRecord.Relations
 + * @since 3.1
 + */
  class TActiveRecordHasManyAssociation extends TActiveRecordRelation
  {
  	private $_association;
  	private $_sourceTable;
  	private $_foreignTable;
 +	/**
 +	* Get the foreign key index values from the results and make calls to the
 +	* database to find the corresponding foreign objects using association table.
 +	* @param array original results.
 +	*/
  	protected function collectForeignObjects(&$results)
  	{
  		$association = $this->getAssociationTable();
 @@ -26,6 +108,9 @@ class TActiveRecordHasManyAssociation extends TActiveRecordRelation  		$this->fetchForeignObjects($results, $foreignKeys,$indexValues,$sourceKeys);
  	}
 +	/**
 +	 * @return TDbTableInfo association table information.
 +	 */
  	protected function getAssociationTable()
  	{
  		if($this->_association===null)
 @@ -38,6 +123,9 @@ class TActiveRecordHasManyAssociation extends TActiveRecordRelation  		return $this->_association;
  	}
 +	/**
 +	 * @return TDbTableInfo source table information.
 +	 */
  	protected function getSourceTable()
  	{
  		if($this->_sourceTable===null)
 @@ -48,6 +136,9 @@ class TActiveRecordHasManyAssociation extends TActiveRecordRelation  		return $this->_sourceTable;
  	}
 +	/**
 +	 * @return TDbTableInfo foreign table information.
 +	 */
  	protected function getForeignTable()
  	{
  		if($this->_foreignTable===null)
 @@ -59,6 +150,9 @@ class TActiveRecordHasManyAssociation extends TActiveRecordRelation  		return $this->_foreignTable;
  	}
 +	/**
 +	 * @return TDbCommandBuilder
 +	 */
  	protected function getCommandBuilder()
  	{
  		return $this->getSourceRecord()->getRecordGateway()->getCommand($this->getSourceRecord());
 @@ -117,6 +211,10 @@ class TActiveRecordHasManyAssociation extends TActiveRecordRelation  		return $command;
  	}
 +	/**
 +	 * @param array source table column names.
 +	 * @return string comma separated source column names.
 +	 */
  	protected function getSourceColumns($sourceKeys)
  	{
  		$columns=array();
 @@ -127,6 +225,13 @@ class TActiveRecordHasManyAssociation extends TActiveRecordRelation  		return implode(', ', $columns);
  	}
 +	/**
 +	 * SQL inner join for M-N relationship via association table.
 +	 * @param array foreign table column key names.
 +	 * @param array source table index values.
 +	 * @param array source table column names.
 +	 * @return string inner join condition for M-N relationship via association table.
 +	 */
  	protected function getAssociationJoin($foreignKeys,$indexValues,$sourceKeys)
  	{
  		$refInfo= $this->getAssociationTable();
 @@ -143,9 +248,7 @@ class TActiveRecordHasManyAssociation extends TActiveRecordRelation  			$joins[] = "{$fkTable}.{$fkField} = {$refTable}.{$refField}";
  		}
  		$joinCondition = implode(' AND ', $joins);
 -
  		$index = $this->getCommandBuilder()->getIndexKeyCondition($refInfo,array_keys($sourceKeys), $indexValues);
 -
  		return "INNER JOIN {$refTable} ON ({$joinCondition}) AND {$index}";
  	}
  }
 diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php b/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php index 4286254b..6348f16b 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php @@ -21,7 +21,7 @@ Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');   * related to the source object. The HAS_ONE relation is very similar to the
   * HAS_MANY relationship (in fact, it is equivalent in the entities relationship point of view).
   *
 - * The difference of HAS_ONE from HAS_MANY is that the foreign object is singular. 
 + * The difference of HAS_ONE from HAS_MANY is that the foreign object is singular.
   * That is, HAS_MANY will return a collection of records while HAS_ONE returns the
   * corresponding record.
   *
 @@ -32,7 +32,7 @@ Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');   * +-----+            +--------+
   * </code>
   * Where each engine belongs to only one car, that is, the Engine entity has
 - * a foreign key to the Car's primary key. We may model 
 + * a foreign key to the Car's primary key. We may model
   * Engine-Car <b>object</b> relationship as active record as follows.
   * <code>
   * class CarRecord extends TActiveRecord
 @@ -43,8 +43,10 @@ Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');   *
   *     public $engine; //engine foreign object
   *
 - *     protected static $RELATIONS=array(
 - *         'engine' => array(self::HAS_ONE, 'EngineRecord'));
 + *     protected static $RELATIONS=array
 + *     (
 + *         'engine' => array(self::HAS_ONE, 'EngineRecord')
 + *     );
   *
   *	   public static function finder($className=__CLASS__)
   *	   {
 @@ -57,14 +59,14 @@ Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');   *     public $engine_id;
   *     public $capacity;
   *     public $car_id; //foreign key to cars
 - * 
 + *
   *	   public static function finder($className=__CLASS__)
   *	   {
   *		   return parent::finder($className);
   *	   }
   * }
   * </code>
 - * The <tt>$RELATIONS</tt> static property of CarRecord defines that the
 + * The static <tt>$RELATIONS</tt> property of CarRecord defines that the
   * property <tt>$engine</tt> that will reference an <tt>EngineRecord</tt> instance.
   *
   * The car record with engine property list may be fetched as follows.
 diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php b/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php index 38455309..46609095 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php @@ -18,8 +18,6 @@ Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelationContext');  /**
   * Base class for active record relationships.
   *
 - * description
 - *
   * @author Wei Zhuo <weizho[at]gmail[dot]com>
   * @version $Id$
   * @package System.Data.ActiveRecord.Relations
 @@ -66,7 +64,7 @@ abstract class TActiveRecordRelation  		static $stack=array();
  		$results = call_user_func_array(array($this->getSourceRecord(),$method),$args);
 -		if(is_array($results) || $results instanceof TActiveRecord)
 +		if(is_array($results) || $results instanceof ArrayAccess || $results instanceof TActiveRecord)
  		{
  			$this->collectForeignObjects($results);
  			while($obj = array_pop($stack))
 @@ -134,8 +132,9 @@ abstract class TActiveRecordRelation  	 */
  	protected function getIndexValues($keys, $results)
  	{
 -		if(!is_array($results))
 +		if(!is_array($results) && !$results instanceof ArrayAccess)
  			$results = array($results);
 +		$values=array();
  		foreach($results as $result)
  		{
  			$value = array();
 @@ -169,7 +168,7 @@ abstract class TActiveRecordRelation  	 */
  	protected function setResultCollection(&$results, &$collections, $properties)
  	{
 -		if(is_array($results))
 +		if(is_array($results) || $results instanceof ArrayAccess)
  		{
  			for($i=0,$k=count($results);$i<$k;$i++)
  				$this->setObjectProperty($results[$i], $properties, $collections);
 @@ -191,5 +190,5 @@ abstract class TActiveRecordRelation  		$source->{$prop} = isset($collections[$hash]) ? $collections[$hash] : array();
  	}
  }
 - +
  ?>
\ No newline at end of file diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php b/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php index 033d7638..167c90a5 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php @@ -57,11 +57,14 @@ class TActiveRecordRelationContext  		if(!isset($statics[self::RELATIONS_CONST]))
  			throw new TActiveRecordException('ar_relations_undefined',
  				get_class($this->_sourceRecord), self::RELATIONS_CONST);
 -		if(isset($statics[self::RELATIONS_CONST][$property]))
 -			return $statics[self::RELATIONS_CONST][$property];
 -		else
 -			throw new TActiveRecordException('ar_undefined_relation_prop',
 -				$property, get_class($this->_sourceRecord), self::RELATIONS_CONST);
 +		$property = strtolower($property);
 +		foreach($statics[self::RELATIONS_CONST] as $name => $relation)
 +		{
 +			if(strtolower($name)===$property)
 +				return $relation;
 +		}
 +		throw new TActiveRecordException('ar_undefined_relation_prop',
 +			$property, get_class($this->_sourceRecord), self::RELATIONS_CONST);
  	}
  	/**
 diff --git a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputCommon.php b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputCommon.php index e1d57124..84c381f0 100644 --- a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputCommon.php +++ b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputCommon.php @@ -190,6 +190,7 @@ class TScaffoldInputCommon extends TScaffoldInputBase  	{
  		$value = $this->getRecordPropertyValue($column, $record);
  		$control = new TDatePicker();
 +		$control->setFromYear(1900);
  		$control->setInputMode(TDatePickerInputMode::DropDownList);
  		$control->setDateFormat('yyyy-MM-dd');
  		if(!empty($value))
 diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php index 71dc83cd..34f8a592 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php @@ -51,16 +51,6 @@ Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');  class TScaffoldListView extends TScaffoldBase
  {
  	/**
 -	 * Initialize the sort drop down list in non post back mode (i.e. GET requests).
 -	 */
 -	public function onLoad($param)
 -	{
 -		parent::onLoad($param);
 -		if(!$this->getPage()->getIsPostBack())
 -			$this->initializeSort();
 -	}
 -
 -	/**
  	 * Initialize the sort drop down list and the column names repeater.
  	 */
  	protected function initializeSort()
 @@ -87,6 +77,8 @@ class TScaffoldListView extends TScaffoldBase  	public function onPreRender($param)
  	{
  		parent::onPreRender($param);
 +		if(!$this->getPage()->getIsPostBack())
 +			$this->initializeSort();
  		$this->loadRecordData();
  	}
 @@ -113,8 +105,10 @@ class TScaffoldListView extends TScaffoldBase  		if($offset + $limit > $total)
  			$limit = $total - $offset;
  		$criteria = new TActiveRecordCriteria($this->getSearchCondition(), $this->getSearchParameters());
 -		$criteria->setLimit($limit);
 -		$criteria->setOffset($offset);
 +		if($limit > 0)
 +			$criteria->setLimit($limit);
 +		if($offset <= $total)
 +			$criteria->setOffset($offset);
  		$order = explode(' ',$this->_sort->getSelectedValue(), 2);
  		if(is_array($order) && count($order) === 2)
  			$criteria->OrdersBy[$order[0]] = $order[1];
 diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php index c144fe37..ad7ba55d 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php @@ -44,9 +44,9 @@ class TScaffoldView extends TScaffoldBase  	/**
  	 * Copy basic record details to the list/edit/search controls.
  	 */
 -	public function onLoad($param)
 +	public function onPreRender($param)
  	{
 -		parent::onLoad($param);
 +		parent::onPreRender($param);
  		$this->getListView()->copyFrom($this);
  		$this->getEditView()->copyFrom($this);
  		$this->getSearchControl()->copyFrom($this);
 diff --git a/framework/Data/ActiveRecord/TActiveRecord.php b/framework/Data/ActiveRecord/TActiveRecord.php index cb3a1ebe..479f643b 100644 --- a/framework/Data/ActiveRecord/TActiveRecord.php +++ b/framework/Data/ActiveRecord/TActiveRecord.php @@ -328,6 +328,7 @@ abstract class TActiveRecord extends TComponent  	/**  	 * @param TDbDataReader data reader +	 * @return array  	 */  	protected function collectObjects($reader)  	{ diff --git a/framework/Data/Common/Mssql/TMssqlCommandBuilder.php b/framework/Data/Common/Mssql/TMssqlCommandBuilder.php index b22c08a5..3de6aa5e 100644 --- a/framework/Data/Common/Mssql/TMssqlCommandBuilder.php +++ b/framework/Data/Common/Mssql/TMssqlCommandBuilder.php @@ -1,6 +1,6 @@  <?php
  /**
 - * TDbCommandBuilder class file.
 + * TMsssqlCommandBuilder class file.
   *
   * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
   * @link http://www.pradosoft.com/
 @@ -13,8 +13,8 @@  Prado::using('System.Data.Common.TDbCommandBuilder');
  /**
 - * TDbCommandBuilder provides basic methods to create query commands for tables
 - * giving by {@link setTableInfo TableInfo} the property.
 + * TMssqlCommandBuilder provides specifics methods to create limit/offset query commands
 + * for MSSQL servers.
   *
   * @author Wei Zhuo <weizho[at]gmail[dot]com>
   * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
 diff --git a/framework/Data/Common/Mssql/TMssqlTableInfo.php b/framework/Data/Common/Mssql/TMssqlTableInfo.php index e408440b..ee9c500b 100644 --- a/framework/Data/Common/Mssql/TMssqlTableInfo.php +++ b/framework/Data/Common/Mssql/TMssqlTableInfo.php @@ -60,7 +60,6 @@ class TMssqlTableInfo extends TDbTableInfo  		Prado::using('System.Data.Common.Mssql.TMssqlCommandBuilder');
  		return new TMssqlCommandBuilder($connection,$this);
  	}
 -
  }
  ?>
\ No newline at end of file diff --git a/framework/Data/Common/Sqlite/TSqliteCommandBuilder.php b/framework/Data/Common/Sqlite/TSqliteCommandBuilder.php new file mode 100644 index 00000000..9f1253c9 --- /dev/null +++ b/framework/Data/Common/Sqlite/TSqliteCommandBuilder.php @@ -0,0 +1,48 @@ +<?php
 +/**
 + * TSqliteCommandBuilder class file.
 + *
 + * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
 + * @link http://www.pradosoft.com/
 + * @copyright Copyright © 2005-2007 PradoSoft
 + * @license http://www.pradosoft.com/license/
 + * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
 + * @package System.Data.Common
 + */
 +
 +Prado::using('System.Data.Common.TDbCommandBuilder');
 +
 +/**
 + * TSqliteCommandBuilder provides specifics methods to create limit/offset query commands
 + * for Sqlite database.
 + *
 + * @author Wei Zhuo <weizho[at]gmail[dot]com>
 + * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
 + * @package System.Data.Common
 + * @since 3.1
 + */
 +class TSqliteCommandBuilder extends TDbCommandBuilder
 +{
 +	/**
 +	 * Alters the sql to apply $limit and $offset.
 +	 * @param string SQL query string.
 +	 * @param integer maximum number of rows, -1 to ignore limit.
 +	 * @param integer row offset, -1 to ignore offset.
 +	 * @return string SQL with limit and offset.
 +	 */
 +	public function applyLimitOffset($sql, $limit=-1, $offset=-1)
 +	{
 +		$limit = $limit!==null ? intval($limit) : -1;
 +		$offset = $offset!==null ? intval($offset) : -1;
 +		if($limit > 0 || $offset > 0)
 +		{
 +			$limitStr = ' LIMIT '.$limit;
 +			$offsetStr = $offset >= 0 ? ' OFFSET '.$offset : '';
 +			return $sql.$limitStr.$offsetStr;
 +		}
 +		else
 +			return $sql;
 +	}
 +}
 +
 +?>
\ No newline at end of file diff --git a/framework/Data/Common/Sqlite/TSqliteMetaData.php b/framework/Data/Common/Sqlite/TSqliteMetaData.php index f9b49488..6c6ff232 100644 --- a/framework/Data/Common/Sqlite/TSqliteMetaData.php +++ b/framework/Data/Common/Sqlite/TSqliteMetaData.php @@ -49,7 +49,7 @@ class TSqliteMetaData extends TDbMetaData  			if($column->getIsPrimaryKey())
  				$primary[] = $col['name'];
  		}
 -		$info['TableName'] = $tableName;
 +		$info['TableName'] = $table;
  		if($this->getIsView($tableName))
  			$info['IsView'] = true;
  		if(count($columns)===0)
 diff --git a/framework/Data/Common/Sqlite/TSqliteTableInfo.php b/framework/Data/Common/Sqlite/TSqliteTableInfo.php index 1581c3cc..e0bcb484 100644 --- a/framework/Data/Common/Sqlite/TSqliteTableInfo.php +++ b/framework/Data/Common/Sqlite/TSqliteTableInfo.php @@ -26,6 +26,15 @@ Prado::using('System.Data.Common.Sqlite.TSqliteTableColumn');   */
  class TSqliteTableInfo extends TDbTableInfo
  {
 +	/**
 +	 * @param TDbConnection database connection.
 +	 * @return TDbCommandBuilder new command builder
 +	 */
 +	public function createCommandBuilder($connection)
 +	{
 +		Prado::using('System.Data.Common.Sqlite.TSqliteCommandBuilder');
 +		return new TSqliteCommandBuilder($connection,$this);
 +	}
  }
  ?>
\ No newline at end of file diff --git a/framework/Data/Common/TDbCommandBuilder.php b/framework/Data/Common/TDbCommandBuilder.php index 440d579d..3535100f 100644 --- a/framework/Data/Common/TDbCommandBuilder.php +++ b/framework/Data/Common/TDbCommandBuilder.php @@ -117,37 +117,38 @@ class TDbCommandBuilder extends TComponent  	}
  	/**
 -	 * NOT SAFE YET!
 +	 * Computes the SQL condition for search a set of column using regular expression
 +	 * (or LIKE, depending on database implementation) to match a string of
 +	 * keywords (default matches all keywords).
 +	 * @param array list of column id for potential search condition.
 +	 * @param string string of keywords
 +	 * @return string SQL search condition matching on a set of columns.
  	 */
  	public function getSearchExpression($fields, $keywords)
  	{
  		if(strlen(trim($keywords)) == 0) return '';
 -		$words = preg_split('/\s/', preg_quote($keywords, '\''));
 -		$result = array();
 +		$words = preg_split('/\s/u', $keywords);
 +		$conditions = array();
  		foreach($fields as $field)
  		{
  			$column = $this->getTableInfo()->getColumn($field)->getColumnName();
 -			$result[] = $this->getRegexpCriteriaStr($column, $words);
 +			$conditions[] = $this->getSearchCondition($column, $words);
  		}
 -		return '('.implode(' OR ', $result).')';
 -	}
 -
 -	protected function getRegexpCriteriaStr($column, $words)
 -	{
 -		$regexp = implode('|', $words);
 -		return "({$column} REGEXP  '{$regexp}')";
 +		return '('.implode(' OR ', $conditions).')';
  	}
  	/**
 -	 * Computes the SQL condition for search a set of column using regular expression
 -	 * to match a string of keywords. The implementation should only uses columns
 -	 * that permit regular expression matching. This method should be implemented in
 -	 * database specific command builder classes.
 -	 * @param array list of column id for potential search condition.
 -	 * @param string string of keywords
 -	 * @return string SQL condition for regular expression matching on a set of columns.
 +	 * @param string column name.
 +	 * @param array keywords
 +	 * @return string search condition for all words in one column.
  	 */
 -	//abstract public function createRegExpSearch($columnIds, $keywords);
 +	protected function getSearchCondition($column, $words)
 +	{
 +		$conditions=array();
 +		foreach($words as $word)
 +			$conditions[] = $column.' LIKE '.$this->getDbConnection()->quoteString('%'.$word.'%');
 +		return '('.implode(' AND ', $conditions).')';
 +	}
  	/**
  	 * Appends the $where condition to the string "SELECT * FROM tableName WHERE ".
 diff --git a/framework/prado-cli.php b/framework/prado-cli.php index ca3e1461..3839e816 100755 --- a/framework/prado-cli.php +++ b/framework/prado-cli.php @@ -691,7 +691,6 @@ class PradoCommandLineActiveRecordGen extends PradoCommandLineAction  		{  			$prop .= <<<EOD -  	/**  	 * @var $type $name  	 * @soapproperty @@ -699,13 +698,13 @@ class PradoCommandLineActiveRecordGen extends PradoCommandLineAction  EOD;  		} -		$prop .= "\tpublic $name;\n"; +		$prop .= "\tpublic $name;";  		return $prop;  	}  	protected function generateClass($properties, $tablename, $class)  	{ -		$props = implode("\n", $properties); +		$props = implode("\x0D\x0A", $properties);  		$date = date('Y-m-d h:i:s');  return <<<EOD  <?php diff --git a/tests/FunctionalTests/tickets/protected/pages/Ticket614.page b/tests/FunctionalTests/tickets/protected/pages/Ticket614.page new file mode 100644 index 00000000..0b1901ea --- /dev/null +++ b/tests/FunctionalTests/tickets/protected/pages/Ticket614.page @@ -0,0 +1,13 @@ +<com:TContent ID="Content">
 +
 +<com:TTextBox ID="text1" />
 +<com:TRegularExpressionValidator
 +	ID="validator1"
 +	ControlToValidate="text1"
 +	ErrorMessage="*"
 +	PatternModifiers="u"
 +	RegularExpression="[\x0400-\x04FF ]{2,60}" />
 +
 +<com:TButton ID="button1" Text="Submit!"/>
 +
 +</com:TContent>
\ No newline at end of file | 
