From 2c221ea67d0512961beea8fbcb30b23865c16bb0 Mon Sep 17 00:00:00 2001 From: wei <> Date: Wed, 20 Dec 2006 03:15:04 +0000 Subject: Add quickstart docs for Active Record and SqlMap --- .gitattributes | 6 + buildscripts/texbuilder/quickstart/pages.php | 5 + buildscripts/texbuilder/quickstart/quickstart.tex | 10 + demos/quickstart/protected/controls/TopicList.tpl | 8 +- .../protected/pages/Database/ActiveRecord.page | 360 +++++++++++++ .../protected/pages/Database/SqlMap.page | 265 ++++++++++ .../protected/pages/Database/diagram.png | Bin 0 -> 30320 bytes .../protected/pages/Database/object_states.png | Bin 0 -> 9596 bytes .../pages/Database/sqlmap_active_record.png | Bin 0 -> 17351 bytes .../protected/App_Code/Dao/ReportsDao.php | 1 + .../protected/App_Data/MySQL4/projects.xml | 9 +- .../App_Data/MySQL4/time-tracker-mysql.sql | 317 ++++++++---- .../protected/App_Data/mysql4-sqlmap.xml | 36 +- demos/time-tracker/protected/application.xml | 2 +- .../protected/pages/TimeTracker/ProjectList.php | 4 +- demos/time-tracker/tests/unit/BaseTestCase.php | 4 +- .../time-tracker/tests/unit/ProjectDaoTestCase.php | 6 +- demos/time-tracker/tests/unit/UserDaoTestCase.php | 6 +- .../Data/SqlMap/Configuration/TParameterMap.php | 19 +- framework/Data/SqlMap/TSqlMapManager.php | 16 - .../simple_unit/SqlMap/ActiveRecordSqlMapTest.php | 6 +- tests/simple_unit/SqlMap/InheritanceTest.php | 22 +- tests/simple_unit/SqlMap/StatementTest.php | 5 +- tests/simple_unit/SqlMap/common.php | 14 +- tests/simple_unit/SqlMap/maps/MySql/Account.xml | 8 +- .../simple_unit/SqlMap/maps/MySql/ActiveRecord.xml | 16 + .../SqlMap/maps/MySql/DynamicAccount.xml | 3 +- tests/simple_unit/SqlMap/maps/MySql/Order.xml | 18 +- tests/simple_unit/SqlMap/mysql.xml | 24 +- .../simple_unit/SqlMap/scripts/mysql/DataBase.sql | 570 +++++++++++++-------- .../SqlMap/scripts/mysql/account-init.sql | 15 +- .../SqlMap/scripts/mysql/account-procedure.sql | 1 - .../SqlMap/scripts/mysql/category-init.sql | 13 +- .../SqlMap/scripts/mysql/category-procedure.sql | 2 - .../SqlMap/scripts/mysql/documents-init.sql | 14 +- .../SqlMap/scripts/mysql/enumeration-init.sql | 14 +- .../SqlMap/scripts/mysql/line-item-init.sql | 15 +- .../SqlMap/scripts/mysql/order-init.sql | 17 +- .../SqlMap/scripts/mysql/other-init.sql | 87 +--- .../SqlMap/scripts/mysql/swap-procedure.sql | 2 - .../simple_unit/SqlMap/scripts/mysql/user-init.sql | 14 +- 41 files changed, 1336 insertions(+), 618 deletions(-) create mode 100644 demos/quickstart/protected/pages/Database/ActiveRecord.page create mode 100644 demos/quickstart/protected/pages/Database/SqlMap.page create mode 100644 demos/quickstart/protected/pages/Database/diagram.png create mode 100755 demos/quickstart/protected/pages/Database/object_states.png create mode 100755 demos/quickstart/protected/pages/Database/sqlmap_active_record.png create mode 100644 tests/simple_unit/SqlMap/maps/MySql/ActiveRecord.xml diff --git a/.gitattributes b/.gitattributes index d8e0942d..e9fe5dc6 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1072,7 +1072,12 @@ demos/quickstart/protected/pages/Controls/TextHighlighter.page -text demos/quickstart/protected/pages/Controls/Validation.page -text demos/quickstart/protected/pages/Controls/Wizard.page -text demos/quickstart/protected/pages/Controls/wizard.gif -text +demos/quickstart/protected/pages/Database/ActiveRecord.page -text demos/quickstart/protected/pages/Database/DAO.page -text +demos/quickstart/protected/pages/Database/SqlMap.page -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 demos/quickstart/protected/pages/Fundamentals/Applications.page -text demos/quickstart/protected/pages/Fundamentals/Architecture.page -text demos/quickstart/protected/pages/Fundamentals/Components.page -text @@ -2353,6 +2358,7 @@ tests/simple_unit/SqlMap/domain/Search.php -text tests/simple_unit/SqlMap/domain/User.php -text tests/simple_unit/SqlMap/gen.php -text tests/simple_unit/SqlMap/maps/MySql/Account.xml -text +tests/simple_unit/SqlMap/maps/MySql/ActiveRecord.xml -text tests/simple_unit/SqlMap/maps/MySql/Category.xml -text tests/simple_unit/SqlMap/maps/MySql/Complex.xml -text tests/simple_unit/SqlMap/maps/MySql/Document.xml -text diff --git a/buildscripts/texbuilder/quickstart/pages.php b/buildscripts/texbuilder/quickstart/pages.php index c237a179..91dba08d 100644 --- a/buildscripts/texbuilder/quickstart/pages.php +++ b/buildscripts/texbuilder/quickstart/pages.php @@ -86,6 +86,11 @@ $pages['Active Control Overview'] = array( $pages['Write New Controls'] = array( 'Controls/NewControl.page'); +$pages['Working with Databases'] = array( + 'Database/DAO.page', + 'Database/ActiveRecord.page', + 'Database/SqlMap.page'); + $pages['Advanced Topics'] = array( 'Advanced/Collections.page', 'Advanced/Auth.page', diff --git a/buildscripts/texbuilder/quickstart/quickstart.tex b/buildscripts/texbuilder/quickstart/quickstart.tex index 264f9c85..d84383b8 100644 --- a/buildscripts/texbuilder/quickstart/quickstart.tex +++ b/buildscripts/texbuilder/quickstart/quickstart.tex @@ -40,6 +40,16 @@ \fancyhead[RE]{\nouppercase{\scshape\leftmark}} %----------------------------------------------------------------------- +% boxes + +\newsavebox{\fmboxb} +\newenvironment{mybox} + {\vspace{-2mm}\begin{center}\begin{lrbox}{\fmboxb}\hspace{2mm} + \begin{minipage}{0.85\textwidth} \vspace{2mm}\small} + { \vspace{2mm} \end{minipage} + \hspace{2mm}\end{lrbox}\fbox{\usebox{\fmboxb}}\end{center}} + + %----------------- TITLE -------------- \title{\Huge \bfseries PRADO v3.1 Quickstart Tutorial diff --git a/demos/quickstart/protected/controls/TopicList.tpl b/demos/quickstart/protected/controls/TopicList.tpl index ddf88c2c..e2554a52 100644 --- a/demos/quickstart/protected/controls/TopicList.tpl +++ b/demos/quickstart/protected/controls/TopicList.tpl @@ -52,12 +52,14 @@
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 + Products 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.
+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.
+ +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.
+ +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 + SqlMap Data Mapper. + 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 SqlMap is illustrated in the + following diagram. More details regarding the SqlMap Data Mapper can be found in + the SqlMap Manual. + alt="Active Records and SqlMap DataMapper" id="fig:diagram.png" class="figure"/> +
+ ++ The Active Record class has methods that do the following: +
Let us
+ consider the following "users" table that contains two columns named "username" and "email",
+ where "username" is also the primary key.
+
Next we define our Active Record class that corresponds to the "users" table.
+
Each property of the UserRecord class must correspond to a + column with the same name in the "users" table. The static class variable + $_tablename is optional when the class name is the same as + the table name in the database, otherwise $_tablename must + specify the table name that corresponds to your Active Record class. +
+ ++ The static method finder() returns an UserRecord 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 TActiveRecord::getRecordFinder() + static method takes the name of the current Active Record class as parameter. +
+ +
+ A default database connection for Active Record can be set as follows.
+ See Establishing Database Connection for
+ futher details regarding creation of database connection in general.
+
+ The default database connection can also be configured using a <module>
+ tag in the application.xml
+ or config.xml as follows.
+
A ConnectionID property can be specified with value corresponding
+ to another TDataSourceConfig module configuration's ID value. This allows
+ the same database connection to be used in other modules such as SqlMap.
+
+ The TActiveRecord class provides many convenient methods to find
+ records from the database. The simplest is finding records by matching primary keys.
+ See the
Finds one record using only the primary key or composite primary keys.
+
Finds one single record that matches the criteria. The criteria
+ can be a partial SQL string or a TActiveRecordCriteria object.
+
The TActiveRecordCriteria class has the following properties: +
Same as find() but returns an array of objects.
+ +Dynamic find method using parts of method name as search criteria.
+Method names starting with findBy return 1 record only.
+Method names starting with findAllBy return an array of records.
+The condition is taken as part of the method name after findBy or findAllBy.
+
+The following blocks of code are equivalent:
+
+
Finds records using full SQL, returns corresponding array of record objects.
+ +Find the number of matchings records.
+ +
+Add a new record using TActiveRecord is very simple, just create a new Active
+Record object and call the save() method. E.g.
+
+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 save() method.
+
+
+Active Record objects have a simple life-cycle illustrated in the following diagram. + alt="Active Records Life Cycle" id="fig:cycle.png" class="figure"/> +
+ +
+ To delete an existing record that is already loaded, just call the delete() method.
+ You can also delete records in the database by primary keys without
+ loading any records using the deleteByPk() method.
+ For example, to delete one or records with tables having a scalar primary key.
+
+For composite primary keys (determined automatically from the table definitions):
+
All Active Record objects contains the property DbConnection
+ that can be used to obtain a transaction object.
+
Data Mappers moves data between objects and a database while keeping them + independent of each other and the mapper itself. If you started with + Active Records, 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. +
+ +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.) +
+ +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. +
+ +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 existing databases, this is very valuable. +
+ +The price, of course, is the extra layer that you don't get with + Active Record, + so the test for using these patterns is the complexity of the business logic. + If you have fairly simple business logic, an Active Record + will probably work. + For more complicated logic a Data Mapper may be more suitable. +
+ +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 +
+ ++ 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. +
+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. +
+ +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. +
+ +
+ A database connection for SqlMap can be set as follows.
+ See Establishing Database Connection for
+ futher details regarding creation of database connection in general.
+
+ The TSqlMapManager is responsible for setting up the database connection + and configuring the SqlMap with given XML file(s). The configureXml() + method accepts a string that points to a SqlMap XML configuration file. Once + configured, call the getSqlMapGateway() method to obtain an instance + of the SqlMap gateway interface (use this object to insert/delete/find records). +
+ +
+ SqlMap database connection can also be configured using a <module>
+ tag in the application.xml
+ or config.xml as follows.
+
+ The ConfigFile 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). + +
To obtain the SqlMap gateway interface from the <module> configuration, simply
+ do, for example,
+
Let us
+ consider the following "users" table that contains two columns named "username" and "email",
+ where "username" is also the primary key.
+
Next we define our plain User class as follows. Notice that
+ the User is very simple.
+
Next, we need to define a SqlMap XMl configuration file, lets name
+ the file as my-sqlmap.xml
+
The <select> tag returns defines an SQL statement. The id
+ attribute will be used as the identifier for the query. The resultClass
+ attribute value is the name of the class the the objects to be returned.
+ We can now query the objects as follows:
+
+
The above example shows demonstrates only a fraction of the capabilities + of the SqlMap Data Mapper. Further details can be found in the + SqlMap Manual. +
+ +The above example may seem trival and it also seems that there is + alot work just to retrieve some data. However, notice that the User + class is totally unware of been stored in the database, and the database is + unware of the User class. +
++ One of advantages of SqlMap is the + ability to map complex object relationship, collections from an existing + database. On the other hand, Active Record + 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. +
+Continuing with the previous example, we change the definition of the
+ User class to become an Active Record.
+
We also need to change the definition of the SqlMap XML configuration. We
+ just need to change the value of resultClass attribute to UserRecord.
+
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.
+
+