diff options
Diffstat (limited to 'demos/quickstart/protected/pages')
| -rw-r--r-- | demos/quickstart/protected/pages/Database/ActiveRecord.page | 360 | ||||
| -rw-r--r-- | demos/quickstart/protected/pages/Database/SqlMap.page | 265 | ||||
| -rw-r--r-- | demos/quickstart/protected/pages/Database/diagram.png | bin | 0 -> 30320 bytes | |||
| -rwxr-xr-x | demos/quickstart/protected/pages/Database/object_states.png | bin | 0 -> 9596 bytes | |||
| -rwxr-xr-x | demos/quickstart/protected/pages/Database/sqlmap_active_record.png | bin | 0 -> 17351 bytes | 
5 files changed, 625 insertions, 0 deletions
diff --git a/demos/quickstart/protected/pages/Database/ActiveRecord.page b/demos/quickstart/protected/pages/Database/ActiveRecord.page new file mode 100644 index 00000000..017b8d45 --- /dev/null +++ b/demos/quickstart/protected/pages/Database/ActiveRecord.page @@ -0,0 +1,360 @@ +<com:TContent ID="body" > +<!-- $Id $ --> +<h1>Active Record</h1> +<p>Active Records are objects that wrap a row in a database table or view, +	encapsulates the database access and adds domain logic on that data. +	The basics of an Active Record is a business object class, e.g., a  +	<tt>Products</tt> class,  that match very closely the record structure +	of an underlying database table. Each Active Record will be responsible for +	saving and loading data to and from the database. </p> +<div class="info"><b class="note">Info:</b> +	The data structure of an Active Record should  match exactly that of a table  +	in the database. +	Each field in the class must correspond to one column in the table. +</div> + +<h2>When to Use It</h2> +<p>Active Record is a good choice for domain logic that isn't too complex,  +	such as creates, reads, updates, and deletes. Derivations and validations +	based on a single record work well in this structure. Active Record has the +	primary advantage of simplicity. It's easy to build  +	Active Records, and they are easy to understand.</p> + +	<p>However, as your business logic grows in complexity, you'll soon want  +	to use your object's direct relationships, collections, inheritance, and so +    forth. These don't map easily onto Active Record, and adding them piecemeal  +	gets very messy. +	Another argument against Active Record is the fact that it couples the object  +	design to the database design. This makes it more difficult to refactor as a project goes forward.</p> +	 +	<p>The alternative is to use a Data Mapper that separates the roles of the +		business object and how these objects are stored. 		 +		Prado provides a complimentary choice between Active Record and  +		<a href="?page=Database.SqlMap">SqlMap Data Mapper</a>.  +	  	A SqlMap Data Mapper can be used to load Active Record objects, in turn, these +		Active Record objects can be used to update the database.  +		The "relationship" between Active Records and <a href="?page=Database.SqlMap">SqlMap</a> is illustrated in the +	following diagram. More details regarding the SqlMap Data Mapper can be found in +	the <a href="http://www.pradosoft.com/demos/sqlmap/">SqlMap Manual</a>. +	<img src=<%~ sqlmap_active_record.png %> alt="Active Records and SqlMap DataMapper" id="fig:diagram.png" class="figure"/> +	</p> +	 +	<p> +		The Active Record class has methods that do the following: +	<ul> +		<li>Construct an instance of the Active Record from a SQL result set row.</li> +		<li>Construct a new instance for later insertion into the table.</li> +		<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> +	</p> +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. +<h2>Defining an Active Record</h2> +<p>Let us +	consider the following "users" table that contains two columns named "username" and "email",  +	where "username" is also the primary key.  +<com:TTextHighlighter Language="sql" CssClass="source"> +CREATE TABLE users +( +	username VARCHAR( 20 ) NOT NULL , +	email VARCHAR( 200 ) , +	PRIMARY KEY ( username ) +); +</com:TTextHighlighter> +</p> +<p>Next we define our Active Record class that corresponds to the "users" table. +<com:TTextHighlighter Language="php" CssClass="source"> +class UserRecord extends TActiveRecord +{ +	public $username; //the column named "username" in the "users" table +	public $email; +	 +	private static $_tablename='users'; //table name  +	 +	/** +	 * @return TActiveRecord active record finder instance +	 */ +	public static function finder() +	{ +		return self::getRecordFinder('UserRecord'); +	} +} +</com:TTextHighlighter> +</p> +<p>Each property of the <tt>UserRecord</tt> class must correspond to a +	column with the same name in the "users" table. The static class variable +	<tt>$_tablename</tt> is optional when the class name is the same as +	the table name in the database, otherwise <tt>$_tablename</tt> must +	specify the table name that corresponds to your Active Record class. +</p> + +<div class="tip"><b class="note">Tip:</b> +	Since <tt>TActiveRecord</tt> extends <tt>TComponent</tt>, setter and +	getter methods can be defined to allow control over how variables +	are set and returned. For example, adding a <tt>$level</tt> property to the UserRecord class: +<com:TTextHighlighter Language="php" CssClass="source"> +class UserRecord extends TActiveRecord { +	... //existing definitions as above +	 +	private $_level; +	public function setLevel($value) { +		$this->_level=TPropertyValue::ensureInteger($value,0); +	} +	public function getLevel($value){ +		return $this->_level; +	} +} +</com:TTextHighlighter> +</div> + +<p> +	The static method <tt>finder()</tt> returns an <tt>UserRecord</tt> instance +	that can be used to load records from the database. The loading of records +	using the finer methods is discuss a little later. The <tt>TActiveRecord::getRecordFinder()</tt> +	static method takes the name of the current Active Record class as parameter. +</p> + +<h2>Setting up a database connection</h2> +<p> +	A default database connection for Active Record can be set as follows. +	See <a href="?page=Database.DAO">Establishing Database Connection</a> for +	futher details regarding creation of database connection in general. +<com:TTextHighlighter Language="php" CssClass="source"> +//create a connection and give it to the ActiveRecord manager. +$dsn = 'pgsql:host=localhost;dbname=test'; //Postgres SQL +$conn = new TDbConnection($dsn, 'dbuser','dbpass'); +TActiveRecordManager::getInstance()->setDbConnection($conn); +</com:TTextHighlighter>	 +</p> + +<p> +	The default database connection can also be configured using a <tt><module></tt> +	tag in the <a href="?page=Configurations.AppConfig">application.xml</a>  +	or <a href="?page=Configurations.PageConfig">config.xml</a> as follows. +<com:TTextHighlighter Language="xml" CssClass="source"> +<modules> +  <module class="System.Data.ActiveRecord.TActiveRecordConfig" EnableCache="true"> +    <database ConnectionString="pgsql:host=localhost;dbname=test" +        Username="dbuser" Password="dbpass" /> +  </module> +</modules>	 +</com:TTextHighlighter>	 +<div class="tip"><b class="note">Tip:</b> +	The <tt>EnableCache</tt> attribute when set to "true" will cache the table +	meta data, that is, the table columns names, indexes and constraints are +	saved in the cache and reused. You must clear or disable the cache if you +	wish to see chanages made to your table definitions. A <a href="?page=Advanced.Performance#6402">cache +	module</a> must also be defined for the cache to function. +</div> +</p> + +<p>A <tt>ConnectionID</tt> property can be specified with value corresponding +	to another <tt>TDataSourceConfig</tt> module configuration's ID value. This allows +	the same database connection to be used in other modules such as <a href="?page=Database.SqlMap">SqlMap</a>. +<com:TTextHighlighter Language="xml" CssClass="source"> +<modules> +  <module class="System.Data.TDataSourceConfig" ID="db1"> +    <database ConnectionString="pgsql:host=localhost;dbname=test" +        Username="dbuser" Password="dbpass" /> +  </module> + +  <module class="System.Data.ActiveRecord.TActiveRecordConfig"  +        ConnectionID="db1" EnableCache="true"  /> + +  <module class="System.Data.SqlMap.TSqlMapConfig" +        ConnectionID="db1"  ... /> +</modules>	 +</com:TTextHighlighter>		 +</p> + +<h2>Loading data from the database</h2> +<p> +	The <tt>TActiveRecord</tt> class provides many convenient methods to find +	records from the database. The simplest is finding records by matching primary keys. +	See the <com:DocLink ClassPath="System.Data.ActiveRecord.TActiveRecord" /> for +	more details. +</p> +	<h3><tt>findByPk()</tt></h3> +	<p>Finds one record using only the primary key or composite primary keys. +<com:TTextHighlighter Language="php" CssClass="source"> +$finder = UserRecord::finder(); +$user = $finder->findByPk($primaryKey); + +//when the table uses composite keys +$record = $finder->findByPk($key1, $key2, ...); //for composite keys +$record = $finder->findByPk(array($key1, $key2,...)); //same as above +</com:TTextHighlighter> +</p> + +<h3><tt>find()</tt></h3> +<p>Finds <b>one single record</b> that matches the criteria. The criteria +	can be a partial SQL string or a <tt>TActiveRecordCriteria</tt> object. +<com:TTextHighlighter Language="php" CssClass="source"> +$finder = UserRecord::finder(); + +//:name and :pass are place holders for specific values of $name and $pass +$finder->find('username = :name AND password = :pass', +                          array(':name'=>$name, ':pass'=>$pass)); + +//using position place holders +$finder->find('username = ? AND password = ?', array($name, $pass)); +//same as above +$finder->find('username = ? AND password = ?', $name, $pass); + +//$criteria is of TActiveRecordCriteria +$finder->find($criteria); //the 2nd parameter for find() is ignored. +</com:TTextHighlighter> +</p> + +<p>The <tt>TActiveRecordCriteria</tt> class has the following properties: +	<ul> +		<li><tt>Parameters</tt> -- name value parameter pairs.</li> +		<li><tt>OrderBy</tt> -- column name and ordering pairs.</li> +		<li><tt>Condition</tt> -- parts of the WHERE SQL conditions.</li> +		<li><tt>Limit</tt> -- maximum number of records to return.</li> +		<li><tt>Offset</tt> -- record offset in the table.</li> +	</ul> +</p> + +<com:TTextHighlighter Language="php" CssClass="source"> +$criteria = new TActiveRecordCriteria; +$criteria->Condition = 'username = :name AND password = :pass'; +$criteria->Parameters[':name'] = 'admin'; +$criteria->Parameters[':pass'] = 'prado'; +$criteria->OrdersBy['level'] = 'desc'; +$criteria->OrdersBy['name'] = 'asc'; +$criteria->Limit = 10; +$criteria->Offset = 20; +</com:TTextHighlighter> + +<h3><tt>findAll()</tt></h3> +<p>Same as <tt>find()</tt> but returns an array of objects.</p> + +<h3><tt>findBy*()</tt> and <tt>findAllBy*()</tt></h3> +<p>Dynamic find method using parts of method name as search criteria. +Method names starting with <tt>findBy</tt> return 1 record only. +Method names starting with <tt>findAllBy</tt> return an array of records. +The condition is taken as part of the method name after <tt>findBy</tt> or <tt>findAllBy</tt>. + +The following blocks of code are equivalent: + +<com:TTextHighlighter Language="php" CssClass="source"> +$finder->findByName($name) +$finder->find('Name = ?', $name); +</com:TTextHighlighter> + +<com:TTextHighlighter Language="php" CssClass="source"> +$finder->findByUsernameAndPassword($name,$pass); +$finder->findBy_Username_And_Password($name,$pass); +$finder->find('Username = ? AND Password = ?', $name, $pass); +</com:TTextHighlighter> + +<com:TTextHighlighter Language="php" CssClass="source"> +$finder->findAllByAge($age); +$finder->findAll('Age = ?', $age); +</com:TTextHighlighter> +</p>	 +	 +<h3><tt>findBySql()</tt></h3> +<p>Finds records using full SQL, returns corresponding array of record objects.</p> + +<h3><tt>count()</tt></h3> +<p>Find the number of matchings records.</p> + +<h2>Inserting and updating records</h2> +<p> +Add a new record using TActiveRecord is very simple, just create a new Active +Record object and call the <tt>save()</tt> method. E.g. +<com:TTextHighlighter Language="php" CssClass="source"> +$user1 = new UserRecord(); +$user1->username = "admin" +$user1->email = "admin@example.com"; +$user1->save(); //insert a new record + +$data = array('username'=>'admin', 'email'=>'admin@example.com'); +$user2 = new UserRecord($data); //create by passing some existing data +$user2->save(); //insert a new record +</com:TTextHighlighter> +<div class="tip"><b class="note">Tip:</b> +The objects are update with the primary key of those the tables that contains  +definitions that automatically creates a primary key for the newly insert records. +For example, if you insert a new record into a MySQL table that has columns +defined with "autoincrement", the Active Record objects will be updated with the new  +incremented values.</div> +</p> + +<p> +To update a record in the database, just change one or more properties of  +the Active Record object that has been loaded from the database and then +call the <tt>save()</tt> method.	 + +<com:TTextHighlighter Language="php" CssClass="source"> +$user = UserRecord::finder()->findByName('admin'); +$user->email="test@example.com"; //change property +$user->save(); //update it. +</com:TTextHighlighter> +</p> + +<p> +Active Record objects have a simple life-cycle illustrated in the following diagram. +<img src=<%~ object_states.png %> alt="Active Records Life Cycle" id="fig:cycle.png" class="figure"/> +</p> + +<h2>Deleting existing records</h2> +<p> +	To delete an existing record that is already loaded, just call the <tt>delete()</tt> method. +	You can also delete records in the database by primary keys without +	loading any records using the <tt>deleteByPk()</tt> method.  +	For example, to delete one or records with tables having a scalar primary key. +<com:TTextHighlighter Language="php" CssClass="source"> +$finder->deleteByPk($primaryKey); //delete 1 record +$finder->deleteByPk($key1,$key2,...); //delete multiple records +$finder->deleteByPk(array($key1,$key2,...)); //delete multiple records +</com:TTextHighlighter> +</p> + +<p> +For composite primary keys (determined automatically from the table definitions): +<com:TTextHighlighter Language="php" CssClass="source"> +$finder->deleteByPk(array($key1,$key2)); //delete 1 record + +//delete multiple records +$finder->deleteByPk(array($key1,$key2), array($key3,$key4),...); + +//delete multiple records +$finder->deleteByPk(array( array($key1,$key2), array($key3,$key4), .. )); +</com:TTextHighlighter> +</p> + +<h2>Transactions</h2> +<p>All Active Record objects contains the property <tt>DbConnection</tt> +	that can be used to obtain a transaction object. +<com:TTextHighlighter Language="php" CssClass="source"> +$finder = UserRecord::finder(); + +$transaction = $finder->DbConnection->beginTransaction(); +try +{ +    $user = $finder->findByPk('admin'); +    $user->email = 'test@example.com'; //alter the $user object +    $user->save(); +    $transaction->commit(); +} +catch(Exception $e) // an exception is raised if a query fails will be raised +{ +    $transaction->rollBack(); +} +</com:TTextHighlighter> + +<h2>References</h2> +<ul> +	<li>Fowler et. al. <i>Patterns of Enterprise Application Architecture</i>, +	Addison Wesley, 2002.</li> +</ul> + +</com:TContent>
\ No newline at end of file diff --git a/demos/quickstart/protected/pages/Database/SqlMap.page b/demos/quickstart/protected/pages/Database/SqlMap.page new file mode 100644 index 00000000..2b70e8f5 --- /dev/null +++ b/demos/quickstart/protected/pages/Database/SqlMap.page @@ -0,0 +1,265 @@ +<com:TContent ID="body"> +<!-- $Id $ --> + +<h1>Data Mapper</h1> +<p>Data Mappers moves data between objects and a database while keeping them  +	independent of each other and the mapper itself. If you started with +	<a href="?page=Database.ActiveRecord">Active Records</a>, you may eventually +	 faced with more complex business +	objects as your project progresses. When you build an object model with a +	 lot of business logic it's valuable to use these mechanisms to better organize +	 the data and the behavior that goes with it. Doing so leads to variant schemas; +	 that is, the object schema and the relational schema don't match up. +</p> + +<p>The Data Mapper separates the in-memory objects from the database. Its responsibility  +	is to transfer data between the two and also to isolate them from each other.  +	With Data Mapper the in-memory objects needn't know even that there's a database  +	present; they need no SQL interface code, and certainly no knowledge of the +	 database schema. (The database schema is always ignorant of the objects that use it.)  +</p> + +<h2>When to Use It</h2> +<p>The primary occasion for using Data Mapper is when you want the database schema +	 and the object model to evolve independently. Data Mapper's primary benefit is +	 that when working on the business (or domain) objects you can ignore the database, both in +	 design and in the build and testing process. The domain objects have no idea +	 what the database structure is, because all the correspondence is done by the mappers. +</p> + +<p>This helps you in the code because you can understand and work with the domain objects  +	without having to understand how they're stored in the database. You can modify the  +	business models or the database without having to alter either. With complicated +	mappings, particularly those involving <b>existing databases</b>, this is very valuable. +</p> + +<p>The price, of course, is the extra layer that you don't get with  +	<a href="?page=Database.ActiveRecord">Active Record</a>,  +	so the test for using these patterns is the complexity of the business logic.  +	If you have fairly simple business logic, an <a href="?page=Database.ActiveRecord">Active Record</a>  +	will probably work.  +	For more complicated logic a Data Mapper may be more suitable. +</p> + +<h2>SqlMap Data Mapper</h2> +<p>The SqlMap DataMapper framework makes it easier to use a database with a PHP application.  +	SqlMap DataMapper couples objects with stored procedures or SQL statements using  +	a XML descriptor. Simplicity is the biggest advantage of the SqlMap DataMapper over  +	object relational mapping tools. To use SqlMap DataMapper you rely on your own objects,  +	XML, and SQL. There is little to learn that you don't already know.  +	With SqlMap DataMapper you have the full power of both SQL and stored procedures at +	your fingertip +</p> + +<p> +	<img src=<%~ diagram.png %> alt="SqlMap Data Mapper Overview" id="fig:sqlmap.png" class="figure"/> +	 +	Here's a high level description of the work flow illustrated in the figure abov.  +	Provide a parameter, either as an object or a primitive type. The parameter can be +	 used to set runtime values in your SQL statement or stored procedure. If a runtime value  +	is not needed, the parameter can be omitted. +</p> +<p>Execute the mapping by passing the parameter and the name you gave the statement or  +	procedure in your XML descriptor. This step is where the magic happens. The framework +	 will prepare the SQL statement or stored procedure, set any runtime values using your  +	parameter, execute the procedure or statement, and return the result. +</p> + +<p>In the case of an update, the number of rows affected is returned. In the case of a +	 query, a single object, or a collection of objects is returned. Like the parameter,  +	the result object, or collection of objects, can be a plain-old object or a primitive PHP type. +</p> + +<h2>Setting up a database connection and initializing the SqlMap</h2> +<p> +	A database connection for SqlMap can be set as follows. +	See <a href="?page=Database.DAO">Establishing Database Connection</a> for +	futher details regarding creation of database connection in general. +<com:TTextHighlighter Language="php" CssClass="source"> +//create a connection and give it to the SqlMap manager. +$dsn = 'pgsql:host=localhost;dbname=test'; //Postgres SQL +$conn = new TDbConnection($dsn, 'dbuser','dbpass'); +$manager = new TSqlMapManager($conn); +$manager->configureXml('my-sqlmap.xml'); +$sqlmap = $manager->getSqlMapGateway();  +</com:TTextHighlighter>	 +</p> + +<p> +	The <tt>TSqlMapManager</tt> is responsible for setting up the database connection +	and configuring the SqlMap with given XML file(s). The <tt>configureXml()</tt> +	method accepts a string that points to a SqlMap XML configuration file. Once +	configured, call the <tt>getSqlMapGateway()</tt> method to obtain an instance +	of the SqlMap gateway interface (use this object to insert/delete/find records). +</p> + +<p> +	SqlMap database connection can also be configured using a <tt><module></tt> +	tag in the <a href="?page=Configurations.AppConfig">application.xml</a>  +	or <a href="?page=Configurations.PageConfig">config.xml</a> as follows. +<com:TTextHighlighter Language="xml" CssClass="source"> +<modules> +  <module id="my-sqlmap" class="System.Data.SqlMap.TSqlMapConfig"  +        EnableCache="true" ConfigFile="my-sqlmap.xml" > +    <database ConnectionString="pgsql:host=localhost;dbname=test" +        Username="dbuser" Password="dbpass" /> +  </module> +</modules>	 +</com:TTextHighlighter>	 +</p> + +<p> +	The <tt>ConfigFile</tt> attribute should point to a SqlMap configuration file +	(to be detailed later) either using absolute path, relative path or the  +	Prado's namespace dot notation path (must omit the ".xml" extension). +	 +	<div class="tip"><b class="note">Tip:</b> +		The <tt>EnableCache</tt> attribute when set to "true" will cache the +		parsed configuration. You must clear or disable the cache if you +		make chanages your configuration file.  +		A <a href="?page=Advanced.Performance#6402">cache +		module</a> must also be defined for the cache to function. +	</div>	 +</p> + +<p>To obtain the SqlMap gateway interface from the <module> configuration, simply +	do, for example, +<com:TTextHighlighter Language="php" CssClass="source"> +class MyPage extends TPage +{ +	public function onLoad($param) +	{ +		parent::onLoad($param); +		$sqlmap = $this->Application->Modules['my-sqlmap']->Client; +		$sqlmap->queryForObject(...); //query for some object +	} +} +</com:TTextHighlighter> +</p> + +<h2>A quick example</h2> +<p>Let us +	consider the following "users" table that contains two columns named "username" and "email",  +	where "username" is also the primary key.  +<com:TTextHighlighter Language="sql" CssClass="source"> +CREATE TABLE users +( +	username VARCHAR( 20 ) NOT NULL , +	email VARCHAR( 200 ) , +	PRIMARY KEY ( username ) +); +</com:TTextHighlighter> +</p> +<p>Next we define our plain <tt>User</tt> class as follows. Notice that +	the <tt>User</tt> is very simple. +<com:TTextHighlighter Language="php" CssClass="source"> +class User +{ +	public $username; +	public $email; +} +</com:TTextHighlighter> +</p> +</p> + +<p>Next, we need to define a SqlMap XMl configuration file, lets name +	the file as <tt>my-sqlmap.xml</tt> +<com:TTextHighlighter Language="xml" CssClass="source"> +<?xml version="1.0" encoding="utf-8" ?> +<sqlMapConfig> +    <select id="SelectUsers" resultClass="User"> +        SELECT username, email FROM users +    </select> +</sqlMapConfig> +</com:TTextHighlighter> +</p> +<p>The <select> tag returns defines an SQL statement. The <tt>id</tt> +	attribute will be used as the identifier for the query. The <tt>resultClass</tt> +	attribute value is the name of the class the the objects to be returned. +	We can now query the objects as follows: +	 +<com:TTextHighlighter Language="php" CssClass="source"> +//assume that $sqlmap is an TSqlMapGateway instance +$userList = $sqlmap->queryForList("SelectUsers"); + +//Or just one, if that's all you need: +$user = $sqlmap->queryForObject("SelectUsers"); +</com:TTextHighlighter> +</p> + +<p>The above example shows demonstrates only a fraction of the capabilities +	of the SqlMap Data Mapper. Further details can be found in the  +	<a href="http://www.pradosoft.com/demo/sqlamp/">SqlMap Manual</a>. +</p> + +<h2>Combining SqlMap with Active Records</h2> +<p>The above example may seem trival and it also seems that there is +	alot work just to retrieve some data. However, notice that the <tt>User</tt> +	class is totally unware of been stored in the database, and the database is +	unware of the <tt>User</tt> class.  +</p> +<p> +	One of advantages of SqlMap is the +	ability to map complex object relationship, collections from an existing +	database. On the other hand, <a href="?page=Database.ActiveRecord">Active Record</a>  +	provide a very simple way +	to interact with the underlying database but unable to do more complicated +	relationship or collections. A good compromise is to use SqlMap to retrieve +	complicated relationships and collections as Active Record objects and then using +	these Active Records to do the updates, inserts and deletes. +</p> +<p>Continuing with the previous example, we change the definition of the +	<tt>User</tt> class to become an Active Record. +<com:TTextHighlighter Language="php" CssClass="source"> +class UserRecord extends TActiveRecord +{ +	public $username; //the column named "username" in the "users" table +	public $email; +	 +	private static $_tablename='users'; //table name  +	 +	/** +	 * @return TActiveRecord active record finder instance +	 */ +	public static function finder() +	{ +		return self::getRecordFinder('UserRecord'); +	} +} +</com:TTextHighlighter> +</p> + +<p>We also need to change the definition of the SqlMap XML configuration. We +	just need to change the value of <tt>resultClass</tt> attribute to <tt>UserRecord</tt>. +<com:TTextHighlighter Language="xml" CssClass="source"> +<?xml version="1.0" encoding="utf-8" ?> +<sqlMapConfig> +    <select id="SelectUsers" resultClass="UserRecord"> +        SELECT username, email FROM users +    </select> +</sqlMapConfig> +</com:TTextHighlighter> +</p> + + +<p>The PHP code for retrieving the users remains the same, but SqlMap +	returns Active Records instead, and we can take advantage of the Active Record methods. +	 +<com:TTextHighlighter Language="php" CssClass="source"> +//assume that $sqlmap is an TSqlMapGateway instance +$user = $sqlmap->queryForObject("SelectUsers"); + +$user->email = 'test@example.com'; //change data +$user->save(); //save it using Active Record +</com:TTextHighlighter> +</p> + +<h2>References</h2> +<ul> +	<li>Fowler et. al. <i>Patterns of Enterprise Application Architecture</i>, +	Addison Wesley, 2002.</li> +	<li>xxxx. <i>iBatis Data Mapper</i>,  +	<a href="http://www.apache.org/ibatis">http://www.apache.org/ibatis</a>.</li> +</ul> + +</com:TContent>
\ No newline at end of file diff --git a/demos/quickstart/protected/pages/Database/diagram.png b/demos/quickstart/protected/pages/Database/diagram.png Binary files differnew file mode 100644 index 00000000..0a0ca73d --- /dev/null +++ b/demos/quickstart/protected/pages/Database/diagram.png diff --git a/demos/quickstart/protected/pages/Database/object_states.png b/demos/quickstart/protected/pages/Database/object_states.png Binary files differnew file mode 100755 index 00000000..db194783 --- /dev/null +++ b/demos/quickstart/protected/pages/Database/object_states.png diff --git a/demos/quickstart/protected/pages/Database/sqlmap_active_record.png b/demos/quickstart/protected/pages/Database/sqlmap_active_record.png Binary files differnew file mode 100755 index 00000000..6d958d33 --- /dev/null +++ b/demos/quickstart/protected/pages/Database/sqlmap_active_record.png  | 
