summaryrefslogtreecommitdiff
path: root/framework/Data/SqlMap
diff options
context:
space:
mode:
authorwei <>2006-12-04 00:02:23 +0000
committerwei <>2006-12-04 00:02:23 +0000
commit18ea316c553f7ccfc18b73f0c987de007f11b275 (patch)
treeb8fa0d14de75582b48c3e4d12050c84f07805920 /framework/Data/SqlMap
parentb69ca04f50ffd538239342f3bfd1e77ffc6156c0 (diff)
Fixed #469
Diffstat (limited to 'framework/Data/SqlMap')
-rw-r--r--framework/Data/SqlMap/Configuration/TDiscriminator.php233
-rw-r--r--framework/Data/SqlMap/Configuration/TInlineParameterMapParser.php80
-rw-r--r--framework/Data/SqlMap/Configuration/TParameterMap.php191
-rw-r--r--framework/Data/SqlMap/Configuration/TParameterProperty.php139
-rw-r--r--framework/Data/SqlMap/Configuration/TResultMap.php201
-rw-r--r--framework/Data/SqlMap/Configuration/TResultProperty.php327
-rw-r--r--framework/Data/SqlMap/Configuration/TSimpleDynamicParser.php46
-rw-r--r--framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php217
-rw-r--r--framework/Data/SqlMap/Configuration/TSqlMapStatement.php427
-rw-r--r--framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php720
-rw-r--r--framework/Data/SqlMap/DataMapper/TLazyLoadList.php145
-rw-r--r--framework/Data/SqlMap/DataMapper/TPropertyAccess.php130
-rw-r--r--framework/Data/SqlMap/DataMapper/TSqlMapCache.php230
-rw-r--r--framework/Data/SqlMap/DataMapper/TSqlMapException.php99
-rw-r--r--framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php158
-rw-r--r--framework/Data/SqlMap/DataMapper/TSqlMapTypeHandlerRegistry.php183
-rw-r--r--framework/Data/SqlMap/DataMapper/messages.txt66
-rw-r--r--framework/Data/SqlMap/Statements/IMappedStatement.php83
-rw-r--r--framework/Data/SqlMap/Statements/TCachingStatement.php99
-rw-r--r--framework/Data/SqlMap/Statements/TDeleteMappedStatement.php7
-rw-r--r--framework/Data/SqlMap/Statements/TInsertMappedStatement.php32
-rw-r--r--framework/Data/SqlMap/Statements/TMappedStatement.php1187
-rw-r--r--framework/Data/SqlMap/Statements/TPreparedCommand.php29
-rw-r--r--framework/Data/SqlMap/Statements/TPreparedStatement.php26
-rw-r--r--framework/Data/SqlMap/Statements/TPreparedStatementFactory.php32
-rw-r--r--framework/Data/SqlMap/Statements/TSelectMappedStatement.php19
-rw-r--r--framework/Data/SqlMap/Statements/TSimpleDynamicSql.php32
-rw-r--r--framework/Data/SqlMap/Statements/TSqlMapSelect.php11
-rw-r--r--framework/Data/SqlMap/Statements/TStaticSql.php19
-rw-r--r--framework/Data/SqlMap/Statements/TUpdateMappedStatement.php32
-rw-r--r--framework/Data/SqlMap/TSqlMapGateway.php268
-rw-r--r--framework/Data/SqlMap/TSqlMapManager.php270
32 files changed, 5738 insertions, 0 deletions
diff --git a/framework/Data/SqlMap/Configuration/TDiscriminator.php b/framework/Data/SqlMap/Configuration/TDiscriminator.php
new file mode 100644
index 00000000..cf77c10b
--- /dev/null
+++ b/framework/Data/SqlMap/Configuration/TDiscriminator.php
@@ -0,0 +1,233 @@
+<?php
+/**
+ * TDiscriminator and TSubMap classes file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ */
+
+/**
+ * The TDiscriminator corresponds to the <discriminator> tag within a <resultMap>.
+ *
+ * TDiscriminator allows inheritance logic in SqlMap result mappings.
+ * SqlMap compares the data found in the discriminator column to the different
+ * <submap> values using the column value's string equivalence. When the string values
+ * matches a particular <submap>, SqlMap will use the <resultMap> defined by
+ * {@link resultMapping TSubMap::setResultMapping()} property for loading
+ * the object data.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TDiscriminator extends TComponent
+{
+ private $_column;
+ private $_type;
+ private $_typeHandler=null;
+ private $_columnIndex;
+ private $_nullValue;
+ private $_mapping;
+ private $_resultMaps=array();
+ private $_subMaps=array();
+
+ /**
+ * @return string the name of the column in the result set from which the
+ * value will be used to populate the property.
+ */
+ public function getColumn()
+ {
+ return $this->_column;
+ }
+
+ /**
+ * @param string the name of the column in the result set from which the
+ * value will be used to populate the property.
+ */
+ public function setColumn($value)
+ {
+ $this->_column = $value;
+ }
+
+ /**
+ * @param string property type of the parameter to be set.
+ */
+ public function getType()
+ {
+ return $this->_type;
+ }
+
+ /**
+ * The type attribute is used to explicitly specify the property type of the
+ * parameter to be set. If the attribute type is not set and the framework
+ * cannot otherwise determine the type, the type is assumed from the default
+ * value of the property.
+ * @return string property type of the parameter to be set.
+ */
+ public function setType($value)
+ {
+ $this->_type = $value;
+ }
+
+ /**
+ * @return string custom type handler class name (may use namespace).
+ */
+ public function getTypeHandler()
+ {
+ return $this->_typeHandler;
+ }
+
+ /**
+ * @param string custom type handler class name (may use namespace).
+ */
+ public function setTypeHandler($value)
+ {
+ $this->_typeHandler = $value;
+ }
+
+ /**
+ * @return int index of the column in the ResultSet
+ */
+ public function getColumnIndex()
+ {
+ return $this->_columnIndex;
+ }
+
+ /**
+ * The columnIndex attribute value is the index of the column in the
+ * ResultSet from which the value will be used to populate the object property.
+ * @param int index of the column in the ResultSet
+ */
+ public function setColumnIndex($value)
+ {
+ $this->_columnIndex = TPropertyValue::ensureInteger($value);
+ }
+
+ /**
+ * @return mixed outgoing null value replacement.
+ */
+ public function getNullValue()
+ {
+ return $this->_nullValue;
+ }
+
+ /**
+ * @param mixed outgoing null value replacement.
+ */
+ public function setNullValue($value)
+ {
+ $this->_nullValue = $value;
+ }
+
+ /**
+ * @return TResultProperty result property for the discriminator column.
+ */
+ public function getMapping()
+ {
+ return $this->_mapping;
+ }
+
+ /**
+ * @param TSubMap add new sub mapping.
+ */
+ public function addSubMap($subMap)
+ {
+ $this->_subMaps[] = $subMap;
+ }
+
+ /**
+ * @param string database value
+ * @return TResultMap result mapping.
+ */
+ public function getSubMap($value)
+ {
+ if(isset($this->_resultMaps[$value]))
+ return $this->_resultMaps[$value];
+ }
+
+ /**
+ * Copies the discriminator properties to a new TResultProperty.
+ * @param TResultMap result map holding the discriminator.
+ */
+ public function initMapping($resultMap)
+ {
+ $this->_mapping = new TResultProperty($resultMap);
+ $this->_mapping->setColumn($this->getColumn());
+ $this->_mapping->setColumnIndex($this->getColumnIndex());
+ $this->_mapping->setType($this->getType());
+ $this->_mapping->setTypeHandler($this->getTypeHandler());
+ $this->_mapping->setNullValue($this->getNullValue());
+ }
+
+ /**
+ * Set the result maps for particular sub-mapping values.
+ * @param TSqlMapManager sql map manager instance.
+ */
+ public function initialize($manager)
+ {
+ foreach($this->_subMaps as $subMap)
+ {
+ $this->_resultMaps[$subMap->getValue()] =
+ $manager->getResultMap($subMap->getResultMapping());
+ }
+ }
+}
+
+/**
+ * TSubMap class defines a submapping value and the corresponding <resultMap>
+ *
+ * The {@link Value setValue()} property is used for comparison with the
+ * discriminator column value. When the {@link Value setValue()} matches
+ * that of the discriminator column value, the corresponding {@link ResultMapping setResultMapping}
+ * is used inplace of the current result map.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSubMap extends TComponent
+{
+ private $_value;
+ private $_resultMapping;
+
+ /**
+ * @return string value for comparison with discriminator column value.
+ */
+ public function getValue()
+ {
+ return $this->_value;
+ }
+
+ /**
+ * @param string value for comparison with discriminator column value.
+ */
+ public function setValue($value)
+ {
+ $this->_value = $value;
+ }
+
+ /**
+ * The result map to use when the Value matches the discriminator column value.
+ * @return string ID of a result map
+ */
+ public function getResultMapping()
+ {
+ return $this->_resultMapping;
+ }
+
+ /**
+ * @param string ID of a result map
+ */
+ public function setResultMapping($value)
+ {
+ $this->_resultMapping = $value;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/Configuration/TInlineParameterMapParser.php b/framework/Data/SqlMap/Configuration/TInlineParameterMapParser.php
new file mode 100644
index 00000000..99f98522
--- /dev/null
+++ b/framework/Data/SqlMap/Configuration/TInlineParameterMapParser.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * TInlineParameterMapParser class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ */
+
+/**
+ * TInlineParameterMapParser class.
+ *
+ * The inline parameter map syntax lets you embed the property name,
+ * the property type, the column type, and a null value replacement into a
+ * parametrized SQL statement.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TInlineParameterMapParser
+{
+ /**
+ * Regular expression for parsing inline parameter maps.
+ */
+ const PARAMETER_TOKEN_REGEXP = '/#(#?[^#]+#?)#/';
+
+ /**
+ * Parse the sql text for inline parameters.
+ * @param string sql text
+ * @param array file and node details for exception message.
+ * @return array 'sql' and 'parameters' name value pairs.
+ */
+ public function parse($sqlText, $scope)
+ {
+ $matches = array();
+ $mappings = array();
+ preg_match_all(self::PARAMETER_TOKEN_REGEXP, $sqlText, $matches);
+
+ for($i = 0, $k=count($matches[1]); $i<$k; $i++)
+ {
+ $mappings[] = $this->parseMapping($matches[1][$i], $scope);
+ $sqlText = str_replace($matches[0][$i], '?', $sqlText);
+ }
+ return array('sql'=>$sqlText, 'parameters'=>$mappings);
+ }
+
+ /**
+ * Parse inline parameter with syntax as
+ * #propertyName,type=string,dbype=Varchar,nullValue=N/A,handler=string#
+ * @param string parameter token
+ * @param array file and node details for exception message.
+ */
+ protected function parseMapping($token, $scope)
+ {
+ $mapping = new TParameterProperty;
+ $properties = explode(',', $token);
+ $mapping->setProperty(trim(array_shift($properties)));
+ foreach($properties as $property)
+ {
+ $prop = explode('=',$property);
+ $name = trim($prop[0]); $value=trim($prop[1]);
+ if($mapping->canSetProperty($name))
+ $mapping->{'set'.$name}($value);
+ else
+ {
+ throw new TSqlMapUndefinedException(
+ 'sqlmap_undefined_property_inline_map',
+ $name, $scope['file'], $scope['node'], $token);
+ }
+ }
+ return $mapping;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/Configuration/TParameterMap.php b/framework/Data/SqlMap/Configuration/TParameterMap.php
new file mode 100644
index 00000000..8d09d9a9
--- /dev/null
+++ b/framework/Data/SqlMap/Configuration/TParameterMap.php
@@ -0,0 +1,191 @@
+<?php
+/**
+ * TParameterMap class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ */
+
+/**
+ * TParameterMap corresponds to the <parameterMap> element.
+ *
+ * TParameterMap holds one or more parameter child elements that map object
+ * properties to placeholders in a SQL statement.
+ *
+ * A TParameterMap defines an ordered list of values that match up with the
+ * placeholders of a parameterized query statement. While the attributes
+ * specified by the map still need to be in the correct order, each parameter
+ * is named. You can populate the underlying class in any order, and the
+ * TParameterMap ensures each value is passed in the correct order.
+ *
+ * Parameter Maps can be provided as an external element and inline.
+ * The <parameterMap> element accepts two attributes: id (required) and extends (optional).
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TParameterMap extends TComponent
+{
+ private $_extend;
+ private $_properties;
+ private $_propertyMap;
+ private $_extendMap;
+ private $_ID;
+
+ /**
+ * Initialize the properties and property map collections.
+ */
+ public function __construct()
+ {
+ $this->_properties = new TList;
+ $this->_propertyMap = new TMap;
+ }
+
+ /**
+ * @return string a unique identifier for the <parameterMap>.
+ */
+ public function getID()
+ {
+ return $this->_ID;
+ }
+
+ /**
+ * @param string a unique identifier for the <parameterMap>.
+ */
+ public function setID($value)
+ {
+ $this->_ID=$value;
+ }
+
+ /**
+ * @return TParameterProperty[] list of properties for the parameter map.
+ */
+ public function getProperties()
+ {
+ return $this->_properties;
+ }
+
+ /**
+ * @return string name of another <parameterMap> upon which to base this TParameterMap.
+ */
+ public function getExtends()
+ {
+ return $this->_extend;
+ }
+
+ /**
+ * @param string name of another <parameterMap> upon which to base this TParameterMap.
+ */
+ public function setExtends($value)
+ {
+ $this->_extend = $value;
+ }
+
+ /**
+ * @param string name of a parameter property.
+ * @return TParameterProperty parameter property.
+ * @throws TSqlMapException if index is not string nor integer.
+ */
+ public function getProperty($index)
+ {
+ if(is_string($index))
+ return $this->_propertyMap->itemAt($index);
+ else if(is_int($index))
+ return $this->_properties->itemAt($index);
+ else
+ throw new TSqlMapException('sqlmap_index_must_be_string_or_int', $index);
+ }
+
+ /**
+ * @param TParameterProperty new parameter property
+ */
+ public function addProperty(TParameterProperty $property)
+ {
+ $this->_propertyMap->add($property->getProperty(), $property);
+ $this->_properties->add($property);
+ }
+
+ /**
+ * @param int parameter property index
+ * @param TParameterProperty new parameter property.
+ */
+ public function insertProperty($index, TParameterProperty $property)
+ {
+ $this->_propertyMap->add($property->getProperty(), $property);
+ $this->_properties->insertAt($index, $property);
+ }
+
+ /**
+ * @return array list of property names.
+ */
+ public function getPropertyNames()
+ {
+ return $this->_propertyMap->getKeys();
+ }
+
+ /**
+ * Get the value of a property from the the parameter object.
+ * @param TSqlMapTypeHandlerRegistry type handler registry.
+ * @param TParameterProperty parameter proproperty.
+ * @param mixed parameter object to get the value from.
+ * @return unknown
+ */
+ public function getPropertyValue($registry, $property, $parameterValue)
+ {
+ $value = $this->getObjectValue($parameterValue,$property);
+
+ if(!is_null($handler=$registry->getTypeHandler($property->getType())))
+ $value = $handler->getParameter($value);
+
+ $value = $this->nullifyDefaultValue($property,$value);
+
+ if(!is_null($type = $property->getType()))
+ $value = $registry->convertToType($type, $value);
+
+ return $value;
+ }
+
+ /**
+ * @param mixed object to obtain the property from.
+ * @param TParameterProperty parameter property.
+ * @return mixed property value.
+ * @throws TSqlMapExecutionException if property access is invalid.
+ */
+ protected function getObjectValue($object,$property)
+ {
+ try
+ {
+ return TPropertyAccess::get($object, $property->getProperty());
+ }
+ catch (TInvalidPropertyException $e)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_unable_to_get_property_for_parameter',
+ $this->getID(), $property->getProperty(), get_class($object));
+ }
+ }
+
+ /**
+ * When the actual value matches the {@link NullValue TParameterProperty::setNullValue()},
+ * set the current value to null.
+ * @param TParameterProperty parameter property.
+ * @param mixed current property value
+ * @return mixed null if NullValue matches currrent value.
+ */
+ protected function nullifyDefaultValue($property,$value)
+ {
+ if(!is_null($nullValue = $property->getNullValue()))
+ {
+ if($nullValue === $value)
+ $value = null;
+ }
+ return $value;
+ }
+}
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/Configuration/TParameterProperty.php b/framework/Data/SqlMap/Configuration/TParameterProperty.php
new file mode 100644
index 00000000..4aecbf83
--- /dev/null
+++ b/framework/Data/SqlMap/Configuration/TParameterProperty.php
@@ -0,0 +1,139 @@
+<?php
+/**
+ * TParameterPropert class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ */
+
+/**
+ * TParameterProperty corresponds to the <property> tag and defines
+ * one object property for the <parameterMap>
+ *
+ * The {@link NullValue setNullValue()} attribute can be set to any valid
+ * value (based on property type). The {@link NullValue setNullValue()} attribute
+ * is used to specify an inbound null value replacement. What this means is
+ * that when the value is detected in the object property, a NULL will be written
+ * to the database (the opposite behavior of an inbound null value replacement).
+ * This allows you to use a magic null number in your application for types that
+ * do not support null values (such as int, double, float). When these types of
+ * properties contain a matching null value (for example, say, -9999), a NULL
+ * will be written to the database instead of the value.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TParameterProperty extends TComponent
+{
+ private $_typeHandler;
+ private $_type;
+ private $_column;
+ private $_dbType;
+ private $_property;
+ private $_nullValue;
+
+ /**
+ * @return string class name of a custom type handler.
+ */
+ public function getTypeHandler()
+ {
+ return $this->_typeHandler;
+ }
+
+ /**
+ * @param string class name of a custom type handler.
+ */
+ public function setTypeHandler($value)
+ {
+ $this->_typeHandler = $value;
+ }
+
+ /**
+ * @return string type of the parameter's property
+ */
+ public function getType()
+ {
+ return $this->_type;
+ }
+
+ /**
+ * @param string type of the parameter's property
+ */
+ public function setType($value)
+ {
+ $this->_type = $value;
+ }
+
+ /**
+ * @return string name of a parameter to be used in the SQL statement.
+ */
+ public function getColumn()
+ {
+ return $this->_column;
+ }
+
+ /**
+ * @param string name of a parameter to be used in the SQL statement.
+ */
+ public function setColumn($value)
+ {
+ $this->_column = $value;
+ }
+
+ /**
+ * @return string the database column type of the parameter to be set by this property.
+ */
+ public function getDbType()
+ {
+ return $this->_dbType;
+ }
+
+ /**
+ * @param string the database column type of the parameter to be set by this property.
+ */
+ public function setDbType($value)
+ {
+ $this->_dbType = $value;
+ }
+
+ /**
+ * @return string name of a property of the parameter object.
+ */
+ public function getProperty()
+ {
+ return $this->_property;
+ }
+
+ /**
+ * @param string name of a property of the parameter object.
+ */
+ public function setProperty($value)
+ {
+ $this->_property = $value;
+ }
+
+ /**
+ * @return mixed null value replacement
+ */
+ public function getNullValue()
+ {
+ return $this->_nullValue;
+ }
+
+ /**
+ * The nullValue attribute is used to specify an outgoing null value replacement.
+ * @param mixed null value replacement.
+ */
+ public function setNullValue($value)
+ {
+ $this->_nullValue = $value;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/Configuration/TResultMap.php b/framework/Data/SqlMap/Configuration/TResultMap.php
new file mode 100644
index 00000000..fa798dc2
--- /dev/null
+++ b/framework/Data/SqlMap/Configuration/TResultMap.php
@@ -0,0 +1,201 @@
+<?php
+/**
+ * TResultMap class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ */
+
+/**
+ * TResultMap corresponds to <resultMap> mapping tag.
+ *
+ * A TResultMap lets you control how data is extracted from the result of a
+ * query, and how the columns are mapped to object properties. A TResultMap
+ * can describe the column type, a null value replacement, and complex property
+ * mappings including Collections.
+ *
+ * The <resultMap> can contain any number of property mappings that map object
+ * properties to the columns of a result element. The property mappings are
+ * applied, and the columns are read, in the order that they are defined.
+ * Maintaining the element order ensures consistent results between different
+ * drivers and providers.
+ *
+ * The {@link Class setClass()} property must be a PHP class object or array instance.
+ *
+ * The optional {@link Extends setExtends()} attribute can be set to the ID of
+ * another <resultMap> upon which to base this <resultMap>. All properties of the
+ * "parent" <resultMap> will be included as part of this <resultMap>, and values
+ * from the "parent" <resultMap> are set before any values specified by this <resultMap>.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TResultMap extends TComponent
+{
+ private $_columns;
+ private $_class;
+ private $_extends;
+ private $_groupBy;
+ private $_discriminator;
+ private $_typeHandlers;
+ private $_ID;
+
+ /**
+ * Initialize the columns collection.
+ */
+ public function __construct()
+ {
+ $this->_columns=new TMap;
+ }
+
+ /**
+ * @return string a unique identifier for the <resultMap>.
+ */
+ public function getID()
+ {
+ return $this->_ID;
+ }
+
+ /**
+ * @param string a unique identifier for the <resultMap>.
+ */
+ public function setID($value)
+ {
+ $this->_ID=$value;
+ }
+
+ /**
+ * @return string result class name.
+ */
+ public function getClass()
+ {
+ return $this->_class;
+ }
+
+ /**
+ * @param string result class name.
+ */
+ public function setClass($value)
+ {
+ $this->_class = $value;
+ }
+
+ /**
+ * @return TMap result columns.
+ */
+ public function getColumns()
+ {
+ return $this->_columns;
+ }
+
+ /**
+ * @return string result map extends another result map.
+ */
+ public function getExtends()
+ {
+ return $this->_extends;
+ }
+
+ /**
+ * @param string result map extends another result map.
+ */
+ public function setExtends($value)
+ {
+ $this->_extends = $value;
+ }
+
+ /**
+ * @return string result map groups by.
+ */
+ public function getGroupBy()
+ {
+ return $this->_groupBy;
+ }
+
+ /**
+ * @param string result map group by
+ */
+ public function setGroupBy($value)
+ {
+ $this->_groupBy = $value;
+ }
+
+ /**
+ * @return TDiscriminator result class discriminator.
+ */
+ public function getDiscriminator()
+ {
+ return $this->_discriminator;
+ }
+
+ /**
+ * @param TDiscriminator result class discriminator.
+ */
+ public function setDiscriminator(TDiscriminator $value)
+ {
+ $this->_discriminator = $value;
+ }
+
+ /**
+ * Add a TResultProperty to result mapping.
+ * @param TResultProperty result property.
+ */
+ public function addResultProperty(TResultProperty $property)
+ {
+ $this->_columns[$property->getProperty()] = $property;
+ }
+
+ /**
+ * Create a new instance of the class of this result map.
+ * @param TSqlMapTypeHandlerRegistry type handler registry.
+ * @return mixed new result object.
+ * @throws TSqlMapExecutionException
+ */
+ public function createInstanceOfResult($registry)
+ {
+ $handler = $registry->getTypeHandler($this->getClass());
+ try
+ {
+ if(!is_null($handler))
+ return $handler->createNewInstance();
+ else
+ return $registry->createInstanceOf($this->getClass());
+ }
+ catch (TSqlMapException $e)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_unable_to_create_new_instance',
+ $this->getClass(), get_class($handler), $this->getID());
+ }
+ }
+
+ /**
+ * Result sub-mappings using the discriminiator column.
+ * @param TSqlMapTypeHandlerRegistry type handler registry
+ * @param array row data.
+ * @return TResultMap result sub-map.
+ */
+ public function resolveSubMap($registry,$row)
+ {
+ $subMap = $this;
+ if(!is_null($disc = $this->getDiscriminator()))
+ {
+ $value = $disc->getMapping()->getPropertyValue($registry,$row);
+ $subMap = $disc->getSubMap((string)$value);
+
+ if(is_null($subMap))
+ $subMap = $this;
+ else if($subMap !== $this)
+ $subMap = $subMap->resolveSubMap($registry,$row);
+ }
+ return $subMap;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/Configuration/TResultProperty.php b/framework/Data/SqlMap/Configuration/TResultProperty.php
new file mode 100644
index 00000000..e8fc5c38
--- /dev/null
+++ b/framework/Data/SqlMap/Configuration/TResultProperty.php
@@ -0,0 +1,327 @@
+<?php
+/**
+ * TResultProperty class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ */
+
+/**
+ * TResultProperty corresponds a <property> tags inside a <resultMap> tag.
+ *
+ * The {@link NullValue setNullValue()} attribute can be set to any valid
+ * value (based on property type). The {@link NullValue setNullValue()} attribute
+ * is used to specify an outgoing null value replacement. What this means is
+ * that when a null value is detected in the result, the corresponding value of
+ * the {@link NullValue getNullValue()} will be used instead.
+ *
+ * The {@link Select setSelect()} property is used to describe a relationship
+ * between objects and to automatically load complex (i.e. user defined)
+ * property types. The value of the {@link Select setSelect()} property must be
+ * the name of another mapped statement. The value of the database
+ * {@link Column setColumn()} that is defined in the same property element as
+ * this statement attribute will be passed to the related mapped statement as
+ * the parameter. The {@link LazyLoad setLayLoad()} attribute can be specified
+ * with the {@link Select setSelect()} .
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TResultProperty extends TComponent
+{
+ private $_nullValue;
+ private $_propertyName;
+ private $_columnName;
+ private $_columnIndex=-1;
+ private $_nestedResultMapName;
+ private $_nestedResultMap;
+ private $_valueType;
+ private $_typeHandler;
+ private $_isLazyLoad=false;
+ private $_select;
+
+ private $_hostResultMapID='inplicit internal mapping';
+
+ const LIST_TYPE = 0;
+ const ARRAY_TYPE = 1;
+
+ /**
+ * Gets the containing result map ID.
+ * @param TResultMap containing result map.
+ */
+ public function __construct($resultMap=null)
+ {
+ if($resultMap instanceof TResultMap)
+ $this->_hostResultMapID = $resultMap->getID();
+ }
+
+ /**
+ * @return mixed null value replacement.
+ */
+ public function getNullValue()
+ {
+ return $this->_nullValue;
+ }
+
+ /**
+ * @param mixed null value replacement.
+ */
+ public function setNullValue($value)
+ {
+ $this->_nullValue = $value;
+ }
+
+ /**
+ * @return string name of a property of the result object that will be set to.
+ */
+ public function getProperty()
+ {
+ return $this->_propertyName;
+ }
+
+ /**
+ * @param string name of a property of the result object that will be set to.
+ */
+ public function setProperty($value)
+ {
+ $this->_propertyName = $value;
+ }
+
+ /**
+ * @return string name of the column in the result set from which the value
+ * will be used to populate the property.
+ */
+ public function getColumn()
+ {
+ return $this->_columnName;
+ }
+
+ /**
+ * @param string name of the column in the result set from which the value
+ * will be used to populate the property.
+ */
+ public function setColumn($value)
+ {
+ $this->_columnName = $value;
+ }
+
+ /**
+ * @return int index of the column in the ResultSet from which the value will
+ * be used to populate the object property
+ */
+ public function getColumnIndex()
+ {
+ return $this->_columnIndex;
+ }
+
+ /**
+ * @param int index of the column in the ResultSet from which the value will
+ * be used to populate the object property
+ */
+ public function setColumnIndex($value)
+ {
+ $this->_columnIndex = TPropertyValue::ensureInteger($value);
+ }
+
+ /**
+ * @return string ID of another <resultMap> used to fill the property.
+ */
+ public function getResultMapping()
+ {
+ return $this->_nestedResultMapName;
+ }
+
+ /**
+ * @parma string ID of another <resultMap> used to fill the property.
+ */
+ public function setResultMapping($value)
+ {
+ $this->_nestedResultMapName = $value;
+ }
+
+ /**
+ * @return TResultMap nested result map.
+ */
+ public function getNestedResultMap()
+ {
+ return $this->_nestedResultMap;
+ }
+
+ /**
+ * @param TResult nested result map.
+ */
+ public function setNestedResultMap($value)
+ {
+ $this->_nestedResultMap = $value;
+ }
+
+ /**
+ * @return string property type of the object property to be set.
+ */
+ public function getType()
+ {
+ return $this->_valueType;
+ }
+
+ /**
+ * @param string property type of the object property to be set.
+ */
+ public function setType($value)
+ {
+ $this->_valueType = $value;
+ }
+
+ /**
+ * @return string custom type handler class name (may use namespace).
+ */
+ public function getTypeHandler()
+ {
+ return $this->_typeHandler;
+ }
+
+ /**
+ * @param string custom type handler class name (may use namespace).
+ */
+ public function setTypeHandler($value)
+ {
+ $this->_typeHandler = $value;
+ }
+
+ /**
+ * @return string name of another mapped statement
+ */
+ public function getSelect()
+ {
+ return $this->_select;
+ }
+
+ /**
+ * The select property is used to describe a relationship between objects
+ * and to automatically load complex (i.e. user defined) property types.
+ * @param string name of another mapped statement.
+ */
+ public function setSelect($value)
+ {
+ $this->_select = $value;
+ }
+
+ /**
+ * @return boolean indicate whether or not the select statement's results should be lazy loaded
+ */
+ public function getLazyLoad()
+ {
+ return $this->_isLazyLoad;
+ }
+
+ /**
+ * @param boolean indicate whether or not the select statement's results should be lazy loaded
+ */
+ public function setLazyLoad($value)
+ {
+ $this->_isLazyLoad = TPropertyValue::ensureBoolean($value,false);
+ }
+
+ /**
+ * Gets the value for the current property, converts to applicable type if necessary.
+ * @param TSqlMapTypeHandlerRegistry type handler registry
+ * @param array result row
+ * @return mixed property value.
+ */
+ public function getPropertyValue($registry,$row)
+ {
+ $value = null;
+ $index = $this->getColumnIndex();
+ $name = $this->getColumn();
+ if($index > 0 && isset($row[$index]))
+ $value = $this->getTypedValue($registry,$row[$index]);
+ else if(isset($row[$name]))
+ $value = $this->getTypedValue($registry,$row[$name]);
+ if(is_null($value) && !is_null($this->getNullValue()))
+ $value = $this->getTypedValue($registry,$this->getNullValue());
+ return $value;
+ }
+
+ /**
+ * @param TSqlMapTypeHandlerRegistry type handler registry
+ * @param mixed raw property value
+ * @return mixed property value casted to specific type.
+ */
+ protected function getTypedValue($registry,$value)
+ {
+ if(($handler = $this->createTypeHandler($registry))!==null)
+ return $handler->getResult($value);
+ else
+ return $registry->convertToType($this->getType(), $value);
+ }
+
+ /**
+ * Create type handler from {@link Type setType()} or {@link TypeHandler setTypeHandler}.
+ * @param TSqlMapTypeHandlerRegistry type handler registry
+ * @return TSqlMapTypeHandler type handler.
+ */
+ protected function createTypeHandler($registry)
+ {
+ $type=$this->getTypeHandler() ? $this->getTypeHandler() : $this->getType();
+ $handler=$registry->getTypeHandler($type);
+ if($handler===null && $this->getTypeHandler())
+ $handler = Prado::createComponent($type);
+ return $handler;
+ }
+
+ /**
+ * Determines if the type is an instance of ArrayAccess, TList or an array.
+ * @return int TResultProperty::LIST_TYPE or TResultProperty::ARRAY_TYPE
+ */
+ protected function getPropertyValueType()
+ {
+ if(class_exists($type = $this->getType(), false)) //NO force autoloading
+ {
+ $class = new ReflectionClass($type);
+ if($class->isSubclassOf('TList'))
+ return self::LIST_TYPE;
+ if($class->inmplementsInterface('ArrayAccess'))
+ return self::ARRAY_TYPE;
+ }
+ if(strtolower($type) == 'array')
+ return self::ARRAY_TYPE;
+ }
+
+ /**
+ * Returns true if the result property {@link Type getType()} is of TList type
+ * or that the actual result object is an instance of TList.
+ * @param object result object
+ * @return boolean true if the result object is an instance of TList
+ */
+ public function instanceOfListType($target)
+ {
+ if(is_null($this->getType()))
+ return TPropertyAccess::get($target,$this->getProperty()) instanceof TList;
+ return $this->getPropertyValueType() == self::LIST_TYPE;
+ }
+
+ /**
+ * Returns true if the result property {@link Type getType()} is of ArrayAccess
+ * or that the actual result object is an array or implements ArrayAccess
+ * @param object result object
+ * @return boolean true if the result object is an instance of ArrayAccess or is an array.
+ */
+ public function instanceOfArrayType($target)
+ {
+ if(is_null($this->getType()))
+ {
+ $prop = TPropertyAccess::get($target,$this->getProperty());
+ if(is_object($prop))
+ return $prop instanceof ArrayAccess;
+ return is_array($prop);
+ }
+ return $this->getPropertyValueType() == self::ARRAY_TYPE;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/Configuration/TSimpleDynamicParser.php b/framework/Data/SqlMap/Configuration/TSimpleDynamicParser.php
new file mode 100644
index 00000000..b67f7aae
--- /dev/null
+++ b/framework/Data/SqlMap/Configuration/TSimpleDynamicParser.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * TSimpleDynamicParser class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ */
+
+/**
+ * TSimpleDynamicParser finds place holders $name$ in the sql text and replaces
+ * it with a TSimpleDynamicParser::DYNAMIC_TOKEN.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSimpleDynamicParser
+{
+ const PARAMETER_TOKEN_REGEXP = '/\$([^\$]+)\$/';
+ const DYNAMIC_TOKEN = '`!`';
+
+ /**
+ * Parse the sql text for dynamic place holders of the form $name$.
+ * @param string Sql text.
+ * @return array name value pairs 'sql' and 'parameters'.
+ */
+ public function parse($sqlText)
+ {
+ $matches = array();
+ $mappings = array();
+ preg_match_all(self::PARAMETER_TOKEN_REGEXP, $sqlText, $matches);
+ for($i = 0, $k=count($matches[1]); $i<$k; $i++)
+ {
+ $mappings[] = $matches[1][$i];
+ $sqlText = str_replace($matches[0][$i], self::DYNAMIC_TOKEN, $sqlText);
+ }
+ return array('sql'=>$sqlText, 'parameters'=>$mappings);
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php b/framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php
new file mode 100644
index 00000000..002ebef5
--- /dev/null
+++ b/framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php
@@ -0,0 +1,217 @@
+<?php
+/**
+ * TSqlMapCacheModel, TSqlMapCacheTypes and TSqlMapCacheKey classes file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ */
+
+/**
+ * TSqlMapCacheModel corresponds to the <cacheModel> sql mapping configuration tag.
+ *
+ * The results from a query Mapped Statement can be cached simply by specifying
+ * the {@link CacheModel TSqlMapStatement::setCacheModel()} property in <statement> tag.
+ * A cache model is a configured cache that is defined within the sql map
+ * configuration file. Cache models are configured using the <cacheModel> element.
+ *
+ * The cache model uses a pluggable framework for supporting different types of
+ * caches. The choice of cache is specified by the {@link Implementation setImplementation()}
+ * property. The class name specified must be one of {@link TSqlMapCacheTypes}.
+ *
+ * The cache implementations, LRU and FIFO cache below do not persist across
+ * requests. That is, once the request is complete, all cache data is lost.
+ * These caches are useful queries that results in the same repeated data during
+ * the current request.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSqlMapCacheModel extends TComponent
+{
+ private $_cache;
+ private $_hits = 0;
+ private $_requests = 0;
+ private $_id;
+ private $_implementation='basic';
+ private $_properties = array();
+
+ /**
+ * @return string unique cache model identifier.
+ */
+ public function getID()
+ {
+ return $this->_id;
+ }
+
+ /**
+ * @param string unique cache model identifier.
+ */
+ public function setID($value)
+ {
+ $this->_id = $value;
+ }
+
+ /**
+ * @return string cache implements of TSqlMapCacheTypes, either 'Basic', 'LRU' or 'FIFO'.
+ */
+ public function getImplementation()
+ {
+ return $this->_implementation;
+ }
+
+ /**
+ * @param string cache implements of TSqlMapCacheTypes, either 'Basic', 'LRU' or 'FIFO'.
+ */
+ public function setImplementation($value)
+ {
+ $this->_implementation = TPropertyValue::ensureEnum($value,'TSqlMapCacheTypes');
+ }
+
+ /**
+ * Initialize the cache implementation, sets the actual cache contain if supplied.
+ * @param ISqLMapCache cache implementation instance.
+ */
+ public function initialize($cache=null)
+ {
+ if(is_null($cache))
+ $this->_cache= Prado::createComponent($this->getImplementationClass());
+ else
+ $this->_cache=$cache;
+ }
+
+ /**
+ * @return string cache implementation class name.
+ */
+ public function getImplementationClass()
+ {
+ switch(TPropertyValue::ensureEnum($this->_implementation,'TSqlMapCacheTypes'))
+ {
+ case TSqlMapCacheTypes::FIFO: return 'TSqlMapFifoCache';
+ case TSqlMapCacheTypes::LRU : return 'TSqlMapLruCache';
+ case TSqlMapCacheTypes::Basic : return 'TSqlMapApplicationCache';
+ }
+ }
+
+ /**
+ * Register a mapped statement that will trigger a cache flush.
+ * @param TMappedStatement mapped statement that may flush the cache.
+ */
+ public function registerTriggerStatement($mappedStatement)
+ {
+ $mappedStatement->attachEventHandler('OnExecuteQuery',array($this, 'flush'));
+ }
+
+ /**
+ * Clears the cache.
+ */
+ public function flush()
+ {
+ $this->_cache->flush();
+ }
+
+ /**
+ * @param TSqlMapCacheKey|string cache key
+ * @return mixed cached value.
+ */
+ public function get($key)
+ {
+ if($key instanceof TSqlMapCacheKey)
+ $key = $key->getHash();
+
+ //if flush ?
+ $value = $this->_cache->get($key);
+ $this->_requests++;
+ if(!is_null($value))
+ $this->_hits++;
+ return $value;
+ }
+
+ /**
+ * @param TSqlMapCacheKey|string cache key
+ * @param mixed value to be cached.
+ */
+ public function set($key, $value)
+ {
+ if($key instanceof TSqlMapCacheKey)
+ $key = $key->getHash();
+
+ if(!is_null($value))
+ $this->_cache->set($key, $value);
+ }
+
+ /**
+ * @return float cache hit ratio.
+ */
+ public function getHitRatio()
+ {
+ if($this->_requests != 0)
+ return $this->_hits / $this->_requests;
+ else
+ return 0;
+ }
+}
+
+/**
+ * TSqlMapCacheTypes enumerable class.
+ *
+ * Implemented cache are 'Basic', 'FIFO' and 'LRU'.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSqlMapCacheTypes extends TEnumerable
+{
+ const Basic='Basic';
+ const FIFO='FIFO';
+ const LRU='LRU';
+}
+
+/**
+ * TSqlMapCacheKey class.
+ *
+ * Provides a hash of the object to be cached.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSqlMapCacheKey
+{
+ private $_key;
+
+ /**
+ * @param mixed object to be cached.
+ */
+ public function __construct($object)
+ {
+ $this->_key = $this->generateKey(serialize($object));
+ }
+
+ /**
+ * @param string serialized object
+ * @return string crc32 hash of the serialized object.
+ */
+ protected function generateKey($string)
+ {
+ return sprintf('%x',crc32($string));
+ }
+
+ /**
+ * @return string object hash.
+ */
+ public function getHash()
+ {
+ return $this->_key;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/Configuration/TSqlMapStatement.php b/framework/Data/SqlMap/Configuration/TSqlMapStatement.php
new file mode 100644
index 00000000..0b4b5781
--- /dev/null
+++ b/framework/Data/SqlMap/Configuration/TSqlMapStatement.php
@@ -0,0 +1,427 @@
+<?php
+/**
+ * TSqlMapStatement, TSqlMapInsert, TSqlMapUpdate, TSqlMapDelete,
+ * TSqlMapSelect and TSqlMapSelectKey classes file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration.
+ */
+
+/**
+ * TSqlMapStatement class corresponds to <statement> element.
+ *
+ * Mapped Statements can hold any SQL statement and can use Parameter Maps
+ * and Result Maps for input and output.
+ *
+ * The <statement> element is a general "catch all" element that can be used
+ * for any type of SQL statement. Generally it is a good idea to use one of the
+ * more specific statement-type elements. The more specific elements provided
+ * better error-checking and even more functionality. (For example, the insert
+ * statement can return a database-generated key.)
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSqlMapStatement extends TComponent
+{
+ private $_parameterMapName;
+ private $_parameterMap;
+ private $_parameterClassName;
+ private $_resultMapName;
+ private $_resultMap;
+ private $_resultClassName;
+ private $_cacheModelName;
+ private $_SQL;
+ private $_listClass;
+ private $_typeHandler;
+ private $_extendStatement;
+ private $_cache;
+ private $_ID;
+
+ /**
+ * @return string name for this statement, unique to each sql map manager.
+ */
+ public function getID()
+ {
+ return $this->_ID;
+ }
+
+ /**
+ * @param string name for this statement, which must be unique for each sql map manager.
+ */
+ public function setID($value)
+ {
+ $this->_ID=$value;
+ }
+
+ /**
+ * @return string name of a parameter map.
+ */
+ public function getParameterMap()
+ {
+ return $this->_parameterMapName;
+ }
+
+ /**
+ * A Parameter Map defines an ordered list of values that match up with
+ * the "?" placeholders of a standard, parameterized query statement.
+ * @param string parameter map name.
+ */
+ public function setParameterMap($value)
+ {
+ $this->_parameterMapName = $value;
+ }
+
+ /**
+ * @return string parameter class name.
+ */
+ public function getParameterClass()
+ {
+ return $this->_parameterClassName;
+ }
+
+ /**
+ * If a {@link ParameterMap setParameterMap()} property is not specified,
+ * you may specify a ParameterClass instead and use inline parameters.
+ * The value of the parameterClass attribute can be any existing PHP class name.
+ * @param string parameter class name.
+ */
+ public function setParameterClass($value)
+ {
+ $this->_parameterClassName = $value;
+ }
+
+ /**
+ * @return string result map name.
+ */
+ public function getResultMap()
+ {
+ return $this->_resultMapName;
+ }
+
+ /**
+ * A Result Map lets you control how data is extracted from the result of a
+ * query, and how the columns are mapped to object properties.
+ * @param string result map name.
+ */
+ public function setResultMap($value)
+ {
+ $this->_resultMapName = $value;
+ }
+
+ /**
+ * @return string result class name.
+ */
+ public function getResultClass()
+ {
+ return $this->_resultClassName;
+ }
+
+ /**
+ * If a {@link ResultMap setResultMap()} is not specified, you may specify a
+ * ResultClass instead. The value of the ResultClass property can be the
+ * name of a PHP class or primitives like integer, string, or array. The
+ * class specified will be automatically mapped to the columns in the
+ * result, based on the result metadata.
+ * @param string result class name.
+ */
+ public function setResultClass($value)
+ {
+ $this->_resultClassName = $value;
+ }
+
+ /**
+ * @return string cache mode name.
+ */
+ public function getCacheModel()
+ {
+ return $this->_cacheModelName;
+ }
+
+ /**
+ * @param string cache mode name.
+ */
+ public function setCacheModel($value)
+ {
+ $this->_cacheModelName = $value;
+ }
+
+ /**
+ * @return TSqlMapCacheModel cache implementation instance for this statement.
+ */
+ public function getCache()
+ {
+ return $this->_cache;
+ }
+
+ /**
+ * @param TSqlMapCacheModel cache implementation instance for this statement.
+ */
+ public function setCache($value)
+ {
+ $this->_cache = $value;
+ }
+
+ /**
+ * @return TStaticSql sql text container.
+ */
+ public function getSqlText()
+ {
+ return $this->_SQL;
+ }
+
+ /**
+ * @param TStaticSql sql text container.
+ */
+ public function setSqlText($value)
+ {
+ $this->_SQL = $value;
+ }
+
+ /**
+ * @return string name of a PHP class that implements ArrayAccess.
+ */
+ public function getListClass()
+ {
+ return $this->_listClass;
+ }
+
+ /**
+ * An ArrayAccess class can be specified to handle the type of objects in the collection.
+ * @param string name of a PHP class that implements ArrayAccess.
+ */
+ public function setListClass($value)
+ {
+ $this->_listClass = $value;
+ }
+
+ /**
+ * @return string another statement element name.
+ */
+ public function getExtends()
+ {
+ return $this->_extendStatement;
+ }
+
+ /**
+ * @param string name of another statement element to extend.
+ */
+ public function setExtends($value)
+ {
+ $this->_extendStatement = $value;
+ }
+
+ /**
+ * @return TResultMap the result map corresponding to the
+ * {@link ResultMap getResultMap()} property.
+ */
+ public function resultMap()
+ {
+ return $this->_resultMap;
+ }
+
+ /**
+ * @return TParameterMap the parameter map corresponding to the
+ * {@link ParameterMap getParameterMap()} property.
+ */
+ public function parameterMap()
+ {
+ return $this->_parameterMap;
+ }
+
+ /**
+ * @param TInlineParameterMap parameter extracted from the sql text.
+ */
+ public function setInlineParameterMap($map)
+ {
+ $this->_parameterMap = $map;
+ }
+
+ /**
+ * @param TSqlMapManager initialize the statement, sets the result and parameter maps.
+ */
+ public function initialize($manager)
+ {
+ if(strlen($this->_resultMapName) > 0)
+ $this->_resultMap = $manager->getResultMap($this->_resultMapName);
+ if(strlen($this->_parameterMapName) > 0)
+ $this->_parameterMap = $manager->getParameterMap($this->_parameterMapName);
+ }
+
+ /**
+ * @param TSqlMapTypeHandlerRegistry type handler registry
+ * @return ArrayAccess new instance of list class.
+ */
+ public function createInstanceOfListClass($registry)
+ {
+ if(strlen($type = $this->getListClass()) > 0)
+ return $this->createInstanceOf($registry,$type);
+ return array();
+ }
+
+ /**
+ * Create a new instance of a given type.
+ * @param TSqlMapTypeHandlerRegistry type handler registry
+ * @param string result class name.
+ * @param array result data.
+ * @return mixed result object.
+ */
+ protected function createInstanceOf($registry,$type,$row=null)
+ {
+ $handler = $registry->getTypeHandler($type);
+ if(!is_null($handler))
+ return $handler->createNewInstance($row);
+ else
+ return $registry->createInstanceOf($type);
+ }
+
+ /**
+ * Create a new instance of result class.
+ * @param TSqlMapTypeHandlerRegistry type handler registry
+ * @param array result data.
+ * @return mixed result object.
+ */
+ public function createInstanceOfResultClass($registry,$row)
+ {
+ if(strlen($type= $this->getResultClass()) > 0)
+ return $this->createInstanceOf($registry,$type,$row);
+ }
+}
+
+/**
+ * TSqlMapDelete class corresponds to the <delete> element.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSqlMapDelete extends TSqlMapUpdate
+{
+}
+
+/**
+ * TSqlMapInsert class corresponds to the <insert> element.
+ *
+ * The <insert> element allows <selectKey> child elements that can be used
+ * to generate a key to be used for the insert command.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSqlMapInsert extends TSqlMapStatement
+{
+ private $_selectKey=null;
+
+ /**
+ * @return TSqlMapSelectKey select key element.
+ */
+ public function getSelectKey()
+ {
+ return $this->_selectKey;
+ }
+
+ /**
+ * @param TSqlMapSelectKey select key.
+ */
+ public function setSelectKey($value)
+ {
+ $this->_selectKey = $value;
+ }
+}
+
+/**
+ * TSqlMapSelect class corresponds to <select> element.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration.
+ * @since 3.1
+ */
+class TSqlMapSelect extends TSqlMapStatement
+{
+}
+
+/**
+ * TSqlMapUpdate class corresponds to <update> element.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSqlMapUpdate extends TSqlMapStatement
+{
+}
+
+/**
+ * TSqlMapSelect corresponds to the <selectKey> element.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSqlMapSelectKey extends TSqlMapStatement
+{
+ private $_type = 'post';
+ private $_property;
+
+ /**
+ * @return string select generated key type, 'post' or 'pre'.
+ */
+ public function getType()
+ {
+ return $this->_type;
+ }
+
+ /**
+ * @param string select generated key type, 'post' or 'pre'.
+ */
+ public function setType($value)
+ {
+ $this->_type = strtolower($value) == 'post' ? 'post' : 'pre';
+ }
+
+ /**
+ * @return string property name for the generated key.
+ */
+ public function getProperty()
+ {
+ return $this->_property;
+ }
+
+ /**
+ * @param string property name for the generated key.
+ */
+ public function setProperty($value)
+ {
+ $this->_property = $value;
+ }
+
+ /**
+ * @throws TSqlMapConfigurationException extends is unsupported.
+ */
+ public function setExtends($value)
+ {
+ throw new TSqlMapConfigurationException('sqlmap_can_not_extend_select_key');
+ }
+
+ /**
+ * @return boolean true if key is generated after insert command, false otherwise.
+ */
+ public function getIsAfter()
+ {
+ return $this->_type == 'post';
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php b/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php
new file mode 100644
index 00000000..eccf4f6e
--- /dev/null
+++ b/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php
@@ -0,0 +1,720 @@
+<?php
+/**
+ * TSqlMapXmlConfigBuilder, TSqlMapXmlConfiguration, TSqlMapXmlMappingConfiguration classes file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ */
+
+Prado::using('System.Data.SqlMap.Configuration.TSqlMapStatement');
+
+/**
+ * TSqlMapXmlConfig class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ */
+abstract class TSqlMapXmlConfigBuilder
+{
+ /**
+ * Create an instance of an object give by the attribute named 'class' in the
+ * node and set the properties on the object given by attribute names and values.
+ * @param SimpleXmlNode property node
+ * @return Object new instance of class with class name given by 'class' attribute value.
+ */
+ protected function createObjectFromNode($node)
+ {
+ if(isset($node['class']))
+ {
+ $obj = Prado::createComponent((string)$node['class']);
+ $this->setObjectPropFromNode($obj,$node,array('class'));
+ return $obj;
+ }
+ throw new TSqlMapConfigurationException(
+ 'sqlmap_node_class_undef', $node, $this->getConfigFile());
+ }
+
+ /**
+ * For each attributes (excluding attribute named in $except) set the
+ * property of the $obj given by the name of the attribute with the value
+ * of the attribute.
+ * @param Object object instance
+ * @param SimpleXmlNode property node
+ * @param array exception property name
+ */
+ protected function setObjectPropFromNode($obj,$node,$except=array())
+ {
+ foreach($node->attributes() as $name=>$value)
+ {
+ if(!in_array($name,$except))
+ {
+ if($obj->canSetProperty($name))
+ $obj->{$name} = (string)$value;
+ else
+ throw new TSqlMapConfigurationException(
+ 'sqlmap_invalid_property', $name, get_class($obj),
+ $node, $this->getConfigFile());
+ }
+ }
+ }
+
+ /**
+ * Gets the filename relative to the basefile.
+ * @param string base filename
+ * @param string relative filename
+ * @return string absolute filename.
+ */
+ protected function getAbsoluteFilePath($basefile,$resource)
+ {
+ $basedir = dirname($basefile);
+ $file = realpath($basedir.'/'.$resource);
+ if(!is_string($file) || !is_file($file))
+ $file = realpath($resource);
+ if(is_string($file) && is_file($file))
+ return $file;
+ else
+ throw new TSqlMapConfigurationException(
+ 'sqlmap_unable_to_find_resource', $resource);
+ }
+
+ /**
+ * Load document using simple xml.
+ * @param string filename.
+ * @return SimpleXmlElement xml document.
+ */
+ protected function loadXmlDocument($filename)
+ {
+ if(!is_file($filename))
+ throw new TSqlMapConfigurationException(
+ 'sqlmap_unable_to_find_config', $filename);
+ return simplexml_load_file($filename);
+ }
+
+ /**
+ * Get element node by ID value (try for attribute name ID as case insensitive).
+ * @param SimpleXmlDocument $document
+ * @param string tag name.
+ * @param string id value.
+ * @return SimpleXmlElement node if found, null otherwise.
+ */
+ protected function getElementByIdValue($document, $tag, $value)
+ {
+ //hack to allow upper case and lower case attribute names.
+ foreach(array('id','ID','Id', 'iD') as $id)
+ {
+ $xpath = "//{$tag}[@{$id}='{$value}']";
+ foreach($document->xpath($xpath) as $node)
+ return $node;
+ }
+ }
+
+ /**
+ * @return string configuration file.
+ */
+ protected abstract function getConfigFile();
+}
+
+/**
+ * TSqlMapXmlConfig class.
+ *
+ * Configures the TSqlMapManager using xml configuration file.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSqlMapXmlConfiguration extends TSqlMapXmlConfigBuilder
+{
+ /**
+ * @var TSqlMapManager manager
+ */
+ private $_manager;
+ /**
+ * @var string configuration file.
+ */
+ private $_configFile;
+ /**
+ * @var array global properties.
+ */
+ private $_properties=array();
+
+ /**
+ * @param TSqlMapManager manager instance.
+ */
+ public function __construct($manager)
+ {
+ $this->_manager=$manager;
+ }
+
+ public function getManager()
+ {
+ return $this->_manager;
+ }
+
+ protected function getConfigFile()
+ {
+ return $this->_configFile;
+ }
+
+ /**
+ * Configure the TSqlMapManager using the given xml file.
+ * @param string SqlMap configuration xml file.
+ */
+ public function configure($filename=null)
+ {
+ $this->_configFile=$filename;
+ $document = $this->loadXmlDocument($filename);
+
+ foreach($document->xpath('//property') as $property)
+ $this->loadGlobalProperty($property);
+
+ foreach($document->xpath('//typeHandler') as $handler)
+ $this->loadTypeHandler($handler);
+
+ foreach($document->xpath('//connection[last()]') as $conn)
+ $this->loadDatabaseConnection($conn);
+
+ foreach($document->xpath('//sqlMap') as $sqlmap)
+ $this->loadSqlMappingFiles($sqlmap);
+
+ $this->resolveResultMapping();
+ $this->attachCacheModels();
+ }
+
+ /**
+ * Load global replacement property.
+ * @param SimpleXmlElement property node.
+ */
+ protected function loadGlobalProperty($node)
+ {
+ $this->_properties[(string)$node['name']] = (string)$node['value'];
+ }
+
+ /**
+ * Load the type handler configurations.
+ * @param SimpleXmlElement type handler node
+ */
+ protected function loadTypeHandler($node)
+ {
+ $handler = $this->createObjectFromNode($node);
+ $this->_manager->getTypeHandlers()->registerTypeHandler($handler);
+ }
+
+ /**
+ * Load the database connection tag.
+ * @param SimpleXmlElement connection node.
+ */
+ protected function loadDatabaseConnection($node)
+ {
+ $conn = $this->createObjectFromNode($node);
+ $this->_manager->setDbConnection($conn);
+ }
+
+ /**
+ * Load SqlMap mapping configuration.
+ * @param unknown_type $node
+ */
+ protected function loadSqlMappingFiles($node)
+ {
+ $mapping = new TSqlMapXmlMappingConfiguration($this);
+ $filename = $this->getAbsoluteFilePath($this->_configFile, (string)$node['resource']);
+ $mapping->configure($filename);
+ }
+
+ /**
+ * Resolve nest result mappings.
+ */
+ protected function resolveResultMapping()
+ {
+ $maps = $this->_manager->getResultMaps();
+ foreach($maps as $entry)
+ {
+ foreach($entry->getColumns() as $item)
+ {
+ $resultMap = $item->getResultMapping();
+ if(strlen($resultMap) > 0)
+ {
+ if($maps->contains($resultMap))
+ $item->setNestedResultMap($maps[$resultMap]);
+ else
+ throw new TSqlMapConfigurationException(
+ 'sqlmap_unable_to_find_result_mapping',
+ $resultMap, $this->_configFile, $entry->getID());
+ }
+ }
+ if(!is_null($entry->getDiscriminator()))
+ $entry->getDiscriminator()->initialize($this->_manager);
+ }
+ }
+
+ /**
+ * Set the cache for each statement having a cache model property.
+ */
+ protected function attachCacheModels()
+ {
+ foreach($this->_manager->getMappedStatements() as $mappedStatement)
+ {
+ if(strlen($model = $mappedStatement->getStatement()->getCacheModel()) > 0)
+ {
+ $cache = $this->_manager->getCacheModel($model);
+ $mappedStatement->getStatement()->setCache($cache);
+ }
+ }
+ }
+
+ /**
+ * Replace the place holders ${name} in text with properties the
+ * corresponding global property value.
+ * @param string original string.
+ * @return string string with global property replacement.
+ */
+ public function replaceProperties($string)
+ {
+ foreach($this->_properties as $find => $replace)
+ $string = str_replace('${'.$find.'}', $replace, $string);
+ return $string;
+ }
+}
+
+/**
+ * Loads the statements, result maps, parameters maps from xml configuration.
+ *
+ * description
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSqlMapXmlMappingConfiguration extends TSqlMapXmlConfigBuilder
+{
+ private $_xmlConfig;
+ private $_configFile;
+ private $_manager;
+
+ private $_document;
+
+ private $_FlushOnExecuteStatements=array();
+
+ /**
+ * @param TSqlMapXmlConfiguration parent xml configuration.
+ */
+ public function __construct(TSqlMapXmlConfiguration $xmlConfig)
+ {
+ $this->_xmlConfig=$xmlConfig;
+ $this->_manager=$xmlConfig->getManager();
+ }
+
+ protected function getConfigFile()
+ {
+ return $this->_configFile;
+ }
+
+ /**
+ * Configure an XML mapping.
+ * @param string xml mapping filename.
+ */
+ public function configure($filename)
+ {
+ $this->_configFile=$filename;
+ $document = $this->loadXmlDocument($filename);
+ $this->_document=$document;
+
+ foreach($document->xpath('//resultMap') as $node)
+ $this->loadResultMap($node);
+
+ foreach($document->xpath('//parameterMap') as $node)
+ $this->loadParameterMap($node);
+
+ foreach($document->xpath('//statement') as $node)
+ $this->loadStatementTag($node);
+
+ foreach($document->xpath('//select') as $node)
+ $this->loadSelectTag($node);
+
+ foreach($document->xpath('//insert') as $node)
+ $this->loadInsertTag($node);
+
+ foreach($document->xpath('//update') as $node)
+ $this->loadUpdateTag($node);
+
+ foreach($document->xpath('//delete') as $node)
+ $this->loadDeleteTag($node);
+
+ foreach($document->xpath('//procedure') as $node)
+ $this->loadProcedureTag($node);
+
+ foreach($document->xpath('//cacheModel') as $node)
+ $this->loadCacheModel($node);
+
+ $this->registerCacheTriggers();
+ }
+
+ /**
+ * Load the result maps.
+ * @param SimpleXmlElement result map node.
+ */
+ protected function loadResultMap($node)
+ {
+ $resultMap = $this->createResultMap($node);
+
+ //find extended result map.
+ if(strlen($extendMap = $resultMap->getExtends()) > 0)
+ {
+ if(!$this->_manager->getResultMaps()->contains($extendMap))
+ {
+ $extendNode=$this->getElementByIdValue($this->_document,'resultMap',$extendMap);
+ if($extendNode!==null)
+ $this->loadResultMap($extendNode);
+ }
+
+ if(!$this->_manager->getResultMaps()->contains($extendMap))
+ throw new TSqlMapConfigurationException(
+ 'sqlmap_unable_to_find_parent_result_map', $node, $this->_configFile, $extendMap);
+
+ $superMap = $this->_manager->getResultMap($extendMap);
+ $resultMap->getColumns()->mergeWith($superMap->getColumns());
+ }
+
+ //add the result map
+ if(!$this->_manager->getResultMaps()->contains($resultMap->getID()))
+ $this->_manager->addResultMap($resultMap);
+ }
+
+ /**
+ * Create a new result map and its associated result properties,
+ * disciminiator and sub maps.
+ * @param SimpleXmlElement result map node
+ * @return TResultMap SqlMap result mapping.
+ */
+ protected function createResultMap($node)
+ {
+ $resultMap = new TResultMap();
+ $this->setObjectPropFromNode($resultMap,$node);
+
+ //result nodes
+ foreach($node->result as $result)
+ {
+ $property = new TResultProperty($resultMap);
+ $this->setObjectPropFromNode($property,$result);
+ $resultMap->addResultProperty($property);
+ }
+
+ //create the discriminator
+ $discriminator = null;
+ if(isset($node->discriminator))
+ {
+ $discriminator = new TDiscriminator();
+ $this->setObjectPropFromNode($discriminator, $node->discriminator);
+ $discriminator->initMapping($resultMap);
+ }
+
+ foreach($node->xpath('subMap') as $subMapNode)
+ {
+ if(is_null($discriminator))
+ throw new TSqlMapConfigurationException(
+ 'sqlmap_undefined_discriminator', $node, $this->_configFile,$subMapNode);
+ $subMap = new TSubMap;
+ $this->setObjectPropFromNode($subMap,$subMapNode);
+ $discriminator->addSubMap($subMap);
+ }
+
+ if(!is_null($discriminator))
+ $resultMap->setDiscriminator($discriminator);
+
+ return $resultMap;
+ }
+
+ /**
+ * Load parameter map from xml.
+ *
+ * @param SimpleXmlElement parameter map node.
+ */
+ protected function loadParameterMap($node)
+ {
+ $parameterMap = $this->createParameterMap($node);
+
+ if(strlen($extendMap = $parameterMap->getExtends()) > 0)
+ {
+ if(!$this->_manager->getParameterMaps()->contains($extendMap))
+ {
+ $extendNode=$this->getElementByIdValue($this->_document,'parameterMap',$extendMap);
+ if($extendNode!==null)
+ $this->loadParameterMap($extendNode);
+ }
+
+ if(!$this->_manager->getParameterMaps()->contains($extendMap))
+ throw new TSqlMapConfigurationException(
+ 'sqlmap_unable_to_find_parent_parameter_map', $node, $this->_configFile,$extendMap);
+ $superMap = $this->_manager->getParameterMap($extendMap);
+ $index = 0;
+ foreach($superMap->getPropertyNames() as $propertyName)
+ $parameterMap->insertProperty($index++,$superMap->getProperty($propertyName));
+ }
+ $this->_manager->addParameterMap($parameterMap);
+ }
+
+ /**
+ * Create a new parameter map from xml node.
+ * @param SimpleXmlElement parameter map node.
+ * @return TParameterMap new parameter mapping.
+ */
+ protected function createParameterMap($node)
+ {
+ $parameterMap = new TParameterMap();
+ $this->setObjectPropFromNode($parameterMap,$node);
+ foreach($node->parameter as $parameter)
+ {
+ $property = new TParameterProperty();
+ $this->setObjectPropFromNode($property,$parameter);
+ $parameterMap->addProperty($property);
+ }
+ return $parameterMap;
+ }
+
+ /**
+ * Load statement mapping from xml configuration file.
+ * @param SimpleXmlElement statement node.
+ */
+ protected function loadStatementTag($node)
+ {
+ $statement = new TSqlMapStatement();
+ $this->setObjectPropFromNode($statement,$node);
+ $this->processSqlStatement($statement, $node);
+ $mappedStatement = new TMappedStatement($this->_manager, $statement);
+ $this->_manager->addMappedStatement($mappedStatement);
+ }
+
+ /**
+ * Load extended SQL statements if application. Replaces global properties
+ * in the sql text. Extracts inline parameter maps.
+ * @param TSqlMapStatement mapped statement.
+ * @param SimpleXmlElement statement node.
+ */
+ protected function processSqlStatement($statement, $node)
+ {
+ $commandText = (string)$node;
+ if(strlen($extend = $statement->getExtends()) > 0)
+ {
+ $superNode = $this->getElementByIdValue($this->_document,'*',$extend);
+ if($superNode!==null)
+ $commandText = (string)$superNode . $commandText;
+ else
+ throw new TSqlMapConfigurationException(
+ 'sqlmap_unable_to_find_parent_sql', $extend, $this->_configFile,$node);
+ }
+ $commandText = $this->_xmlConfig->replaceProperties($commandText);
+ $statement->initialize($this->_manager);
+ $this->applyInlineParameterMap($statement, $commandText, $node);
+ }
+
+ /**
+ * Extract inline parameter maps.
+ * @param TSqlMapStatement statement object.
+ * @param string sql text
+ * @param SimpleXmlElement statement node.
+ */
+ protected function applyInlineParameterMap($statement, $sqlStatement, $node)
+ {
+ $scope['file'] = $this->_configFile;
+ $scope['node'] = $node;
+
+ if($statement->parameterMap() === null)
+ {
+ // Build a Parametermap with the inline parameters.
+ // if they exist. Then delete inline infos from sqltext.
+ $parameterParser = new TInlineParameterMapParser;
+ $sqlText = $parameterParser->parse($sqlStatement, $scope);
+ if(count($sqlText['parameters']) > 0)
+ {
+ $map = new TParameterMap();
+ $map->setID($statement->getID().'-InLineParameterMap');
+ $statement->setInlineParameterMap($map);
+ foreach($sqlText['parameters'] as $property)
+ $map->addProperty($property);
+ }
+ $sqlStatement = $sqlText['sql'];
+ }
+
+ $this->prepareSql($statement, $sqlStatement, $node);
+ }
+
+ /**
+ * Prepare the sql text (may extend to dynamic sql).
+ * @param TSqlMapStatement mapped statement.
+ * @param string sql text.
+ * @param SimpleXmlElement statement node.
+ * @todo Extend to dynamic sql.
+ */
+ protected function prepareSql($statement,$sqlStatement, $node)
+ {
+ $simpleDynamic = new TSimpleDynamicParser;
+ $dynamics = $simpleDynamic->parse($sqlStatement);
+ if(count($dynamics['parameters']) > 0)
+ {
+ $sql = new TSimpleDynamicSql($dynamics['parameters']);
+ $sqlStatement = $dynamics['sql'];
+ }
+ else
+ $sql = new TStaticSql();
+ $sql->buildPreparedStatement($statement, $sqlStatement);
+ $statement->setSqlText($sql);
+ }
+
+ /**
+ * Load select statement from xml mapping.
+ * @param SimpleXmlElement select node.
+ */
+ protected function loadSelectTag($node)
+ {
+ $select = new TSqlMapSelect;
+ $this->setObjectPropFromNode($select,$node);
+ $this->processSqlStatement($select,$node);
+ $mappedStatement = new TMappedStatement($this->_manager, $select);
+ if(strlen($select->getCacheModel()) > 0)
+ $mappedStatement = new TCachingStatement($mappedStatement);
+
+ $this->_manager->addMappedStatement($mappedStatement);
+ }
+
+ /**
+ * Load insert statement from xml mapping.
+ * @param SimpleXmlElement insert node.
+ */
+ protected function loadInsertTag($node)
+ {
+ $insert = $this->createInsertStatement($node);
+ $this->processSqlStatement($insert, $node);
+ $mappedStatement = new TInsertMappedStatement($this->_manager, $insert);
+ $this->_manager->addMappedStatement($mappedStatement);
+ }
+
+ /**
+ * Create new insert statement from xml node.
+ * @param SimpleXmlElement insert node.
+ * @return TSqlMapInsert insert statement.
+ */
+ protected function createInsertStatement($node)
+ {
+ $insert = new TSqlMapInsert;
+ $this->setObjectPropFromNode($insert,$node);
+ if(isset($node->selectKey))
+ $this->loadSelectKeyTag($insert,$node->selectKey);
+ return $insert;
+ }
+
+ /**
+ * Load the selectKey statement from xml mapping.
+ * @param SimpleXmlElement selectkey node
+ */
+ protected function loadSelectKeyTag($insert, $node)
+ {
+ $selectKey = new TSqlMapSelectKey;
+ $this->setObjectPropFromNode($selectKey,$node);
+ $selectKey->setID($insert->getID());
+ $selectKey->setID($insert->getID().'.SelectKey');
+ $this->processSqlStatement($selectKey,$node);
+ $insert->setSelectKey($selectKey);
+ $mappedStatement = new TMappedStatement($this->_manager, $selectKey);
+ $this->_manager->addMappedStatement($mappedStatement);
+ }
+
+ /**
+ * Load update statement from xml mapping.
+ * @param SimpleXmlElement update node.
+ */
+ protected function loadUpdateTag($node)
+ {
+ $update = new TSqlMapUpdate;
+ $this->setObjectPropFromNode($update,$node);
+ $this->processSqlStatement($update, $node);
+ $mappedStatement = new TUpdateMappedStatement($this->_manager, $update);
+ $this->_manager->addMappedStatement($mappedStatement);
+ }
+
+ /**
+ * Load delete statement from xml mapping.
+ * @param SimpleXmlElement delete node.
+ */
+ protected function loadDeleteTag($node)
+ {
+ $delete = new TSqlMapDelete;
+ $this->setObjectPropFromNode($delete,$node);
+ $this->processSqlStatement($delete, $node);
+ $mappedStatement = new TDeleteMappedStatement($this->_manager, $delete);
+ $this->_manager->addMappedStatement($mappedStatement);
+ }
+
+ /**
+ * Load procedure statement from xml mapping.
+ * @todo Implement loading procedure
+ * @param SimpleXmlElement procedure node
+ */
+ protected function loadProcedureTag($node)
+ {
+ //var_dump('todo: add load procedure');
+ }
+
+ /**
+ * Load cache models from xml mapping.
+ * @param SimpleXmlElement cache node.
+ */
+ protected function loadCacheModel($node)
+ {
+ $cacheModel = new TSqlMapCacheModel;
+ $properties = array('id','implementation');
+ foreach($node->attributes() as $name=>$value)
+ {
+ if(in_array(strtolower($name), $properties))
+ $cacheModel->{'set'.$name}((string)$value);
+ }
+ $cache = Prado::createComponent($cacheModel->getImplementationClass());
+ $this->setObjectPropFromNode($cache,$node,$properties);
+ $cacheModel->initialize($cache);
+ $this->_manager->addCacheModel($cacheModel);
+ foreach($node->xpath('flushOnExecute') as $flush)
+ $this->loadFlushOnCache($cacheModel,$node,$flush);
+ }
+
+ /**
+ * Load the flush on cache properties.
+ * @param TSqlMapCacheModel cache model
+ * @param SimpleXmlElement parent node.
+ * @param SimpleXmlElement flush node.
+ */
+ protected function loadFlushOnCache($cacheModel,$parent,$node)
+ {
+ $id = $cacheModel->getID();
+ if(!isset($this->_FlushOnExecuteStatements[$id]))
+ $this->_FlushOnExecuteStatements[$id] = array();
+ foreach($node->attributes() as $name=>$value)
+ {
+ if(strtolower($name)==='statement')
+ $this->_FlushOnExecuteStatements[$id][] = (string)$value;
+ }
+ }
+
+ /**
+ * Attach CacheModel to statement and register trigger statements for cache models
+ */
+ protected function registerCacheTriggers()
+ {
+ foreach($this->_FlushOnExecuteStatements as $cacheID => $statementIDs)
+ {
+ $cacheModel = $this->_manager->getCacheModel($cacheID);
+ foreach($statementIDs as $statementID)
+ {
+ $statement = $this->_manager->getMappedStatement($statementID);
+ $cacheModel->registerTriggerStatement($statement);
+ }
+ }
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/DataMapper/TLazyLoadList.php b/framework/Data/SqlMap/DataMapper/TLazyLoadList.php
new file mode 100644
index 00000000..770c217a
--- /dev/null
+++ b/framework/Data/SqlMap/DataMapper/TLazyLoadList.php
@@ -0,0 +1,145 @@
+<?php
+/**
+ * TLazyLoadList, TObjectProxy classes file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap.DataMapper
+ */
+
+/**
+ * TLazyLoadList executes mapped statements when the proxy collection is first accessed.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.DataMapper
+ * @since 3.1
+ */
+class TLazyLoadList
+{
+ private $_param;
+ private $_target;
+ private $_propertyName='';
+ private $_statement='';
+ private $_loaded=false;
+ private $_innerList;
+ private $_connection;
+
+ /**
+ * Create a new proxy list that will execute the mapped statement when any
+ * of the list's method are accessed for the first time.
+ * @param TMappedStatement statement to be executed to load the data.
+ * @param mixed parameter value for the statement.
+ * @param object result object that contains the lazy collection.
+ * @param string property of the result object to set the loaded collection.
+ */
+ protected function __construct($mappedStatement, $param, $target, $propertyName)
+ {
+ $this->_param = $param;
+ $this->_target = $target;
+ $this->_statement = $mappedStatement;
+ $this->_connection=$mappedStatement->getManager()->getDbConnection();
+ $this->_propertyName = $propertyName;
+ }
+
+ /**
+ * Create a new instance of a lazy collection.
+ * @param TMappedStatement statement to be executed to load the data.
+ * @param mixed parameter value for the statement.
+ * @param object result object that contains the lazy collection.
+ * @param string property of the result object to set the loaded collection.
+ * @return TObjectProxy proxied collection object.
+ */
+ public static function newInstance($mappedStatement, $param, $target, $propertyName)
+ {
+ $handler = new self($mappedStatement, $param, $target, $propertyName);
+ $statement = $mappedStatement->getStatement();
+ $registry=$mappedStatement->getManager()->getTypeHandlers();
+ $list = $statement->createInstanceOfListClass($registry);
+ if(!is_object($list))
+ throw new TSqlMapExecutionException('sqlmap_invalid_lazyload_list',$statement->getID());
+ return new TObjectProxy($handler, $list);
+ }
+
+ /**
+ * Relay the method call to the underlying collection.
+ * @param string method name.
+ * @param array method parameters.
+ */
+ public function intercept($method, $arguments)
+ {
+ return call_user_func_array(array($this->_innerList, $method), $arguments);
+ }
+
+ /**
+ * Load the data by executing the mapped statement.
+ */
+ protected function fetchListData()
+ {
+ if($this->_loaded == false)
+ {
+ $this->_innerList = $this->_statement->executeQueryForList($this->_connection,$this->_param);
+ $this->_loaded = true;
+ //replace the target property with real list
+ TPropertyAccess::set($this->_target, $this->_propertyName, $this->_innerList);
+ }
+ }
+
+ /**
+ * Try to fetch the data when any of the proxy collection method is called.
+ * @param string method name.
+ * @return boolean true if the underlying collection has the corresponding method name.
+ */
+ public function hasMethod($method)
+ {
+ $this->fetchListData();
+ if(is_object($this->_innerList))
+ return in_array($method, get_class_methods($this->_innerList));
+ return false;
+ }
+}
+
+/**
+ * TObjectProxy sets up a simple object that intercepts method calls to a
+ * particular object and relays the call to handler object.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.DataMapper
+ * @since 3.1
+ */
+class TObjectProxy
+{
+ private $_object;
+ private $_handler;
+
+ /**
+ * @param object handler to method calls.
+ * @param object the object to by proxied.
+ */
+ public function __construct($handler, $object)
+ {
+ $this->_handler = $handler;
+ $this->_object = $object;
+ }
+
+ /**
+ * Relay the method call to the handler object (if able to be handled), otherwise
+ * it calls the proxied object's method.
+ * @param string method name called
+ * @param array method arguments
+ * @return mixed method return value.
+ */
+ public function __call($method,$params)
+ {
+ if($this->_handler->hasMethod($method))
+ return $this->_handler->intercept($method, $params);
+ else
+ return call_user_func_array(array($this->_object, $method), $params);
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/DataMapper/TPropertyAccess.php b/framework/Data/SqlMap/DataMapper/TPropertyAccess.php
new file mode 100644
index 00000000..fea94fa4
--- /dev/null
+++ b/framework/Data/SqlMap/DataMapper/TPropertyAccess.php
@@ -0,0 +1,130 @@
+<?php
+
+class TPropertyAccess
+{
+ private $_obj;
+ private $_performance=false;
+
+ public function __construct($obj,$performance=false)
+ {
+ $this->_obj = $obj;
+ $this->_performance=$performance;
+ }
+
+ public function __get($name)
+ {
+ return self::get($this->_obj,$name,$this->_performance);
+ }
+
+ public function __set($name,$value)
+ {
+ self::set($this->_obj,$name,$value,$this->_performance);
+ }
+
+ /**
+ * Evaluates the data value at the specified field.
+ * - If the data is an array, then the field is treated as an array index
+ * and the corresponding element value is returned;
+ * - If the data is a TMap or TList object, then the field is treated as a key
+ * into the map or list, and the corresponding value is returned.
+ * - If the data is an object, the field is treated as a property or subproperty
+ * defined with getter methods. For example, if the object has a method called
+ * getMyValue(), then field 'MyValue' will retrive the result of this method call.
+ * If getMyValue() returns an object which contains a method getMySubValue(),
+ * then field 'MyValue.MySubValue' will return that method call result.
+ * @param mixed data containing the field value, can be an array, TMap, TList or object.
+ * @param mixed field value
+ * @return mixed value at the specified field
+ * @throw TInvalidDataValueException if field or data is invalid
+ */
+ public static function get($object,$path)
+ {
+ if(!is_array($object) && !is_object($object))
+ return $object;
+ $properties = explode('.', $path);
+ foreach($properties as $prop)
+ {
+ if(is_array($object) || $object instanceof ArrayAccess)
+ {
+ if(array_key_exists($prop, $object))
+ $object = $object[$prop];
+ else
+ throw new TInvalidPropertyException('sqlmap_invalid_property',$path);
+ }
+ else if(is_object($object))
+ {
+ $getter = 'get'.$prop;
+ if(is_callable(array($object,$getter)))
+ $object = $object->{$getter}();
+ else if(in_array($prop, array_keys(get_object_vars($object))))
+ $object = $object->{$prop};
+ else
+ throw new TInvalidPropertyException('sqlmap_invalid_property',$path);
+ }
+ else
+ throw new TInvalidPropertyException('sqlmap_invalid_property',$path);
+ }
+ return $object;
+ }
+
+ public static function has($object, $path)
+ {
+ if(!is_array($object) && !is_object($object))
+ return false;
+ $properties = explode('.', $path);
+ foreach($properties as $prop)
+ {
+ if(is_array($object) || $object instanceof ArrayAccess)
+ {
+ if(array_key_exists($prop, $object))
+ $object = $object[$prop];
+ else
+ return false;
+ }
+ else if(is_object($object))
+ {
+ $getter = 'get'.$prop;
+ if(is_callable(array($object,$getter)))
+ $object = $object->{$getter}();
+ else if(in_array($prop, array_keys(get_object_vars($object))))
+ $object = $object->{$prop};
+ return false;
+ }
+ else
+ return false;
+ }
+ return true;
+ }
+
+ public static function set(&$originalObject, $path, $value)
+ {
+ $properties = explode('.', $path);
+ $prop = array_pop($properties);
+ if(count($properties) > 0)
+ $object = self::get($originalObject, implode('.',$properties));
+ else
+ $object = &$originalObject;
+
+ //var_dump($object);
+ if(is_array($object) || $object instanceof ArrayAccess)
+ {
+ $object[$prop] = $value;
+ }
+ else if(is_object($object))
+ {
+ $setter = 'set'.$prop;
+ if(is_callable(array($object, $setter)))
+ {
+ if($object->{$setter}($value) === null)
+ $object->{$prop} = $value;
+ }
+ else
+ $object->{$prop} = $value;
+ }
+ else
+ throw new TInvalidPropertyException('sqlmap_invalid_property_type',$path);
+ }
+
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/DataMapper/TSqlMapCache.php b/framework/Data/SqlMap/DataMapper/TSqlMapCache.php
new file mode 100644
index 00000000..5cb9cbcb
--- /dev/null
+++ b/framework/Data/SqlMap/DataMapper/TSqlMapCache.php
@@ -0,0 +1,230 @@
+<?php
+/**
+ * TSqlMapCache class file contains FIFO, LRU, and GLOBAL cache implementations.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.DataAccess.SQLMap
+ */
+
+interface ISqLMapCache
+{
+ public function remove($key);
+
+ public function flush();
+
+ public function get($key);
+
+ public function set($key, $value);
+}
+
+/**
+ * Allow different implementation of caching strategy. See <tt>TSqlMapFifoCache</tt>
+ * for a first-in-first-out implementation. See <tt>TSqlMapLruCache</tt> for
+ * a least-recently-used cache implementation.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.DataAccess.SQLMap
+ * @since 3.0
+ */
+abstract class TSqlMapCache implements ISqlMapCache
+{
+ protected $_keyList;
+ protected $_cache;
+ protected $_cacheSize = 100;
+
+ /**
+ * Create a new cache with limited cache size.
+ * @param integer maxium number of items to cache.
+ */
+ public function __construct($cacheSize=100)
+ {
+ $this->_cache = new TMap;
+ $this->_cacheSize = intval($cacheSize);
+ $this->_keyList = new TList;
+ }
+
+ public function setCacheSize($value)
+ {
+ $this->_cacheSize=TPropertyValue::ensureInteger($value,100);
+ }
+
+ public function getCacheSize()
+ {
+ return $this->_cacheSize;
+ }
+
+ /**
+ * @return object the object removed if exists, null otherwise.
+ */
+ public function remove($key)
+ {
+ $object = $this->get($key);
+ $this->_cache->remove($key);
+ $this->_keyList->remove($key);
+ return $object;
+ }
+
+ /**
+ * Clears the cache.
+ */
+ public function flush()
+ {
+ $this->_keyList->clear();
+ $this->_cache->clear();
+ }
+
+}
+
+/**
+ * First-in-First-out cache implementation, removes
+ * object that was first added when the cache is full.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.DataAccess.SQLMap
+ * @since 3.0
+ */
+class TSqlMapFifoCache extends TSqlMapCache
+{
+ /**
+ * @return mixed Gets a cached object with the specified key.
+ */
+ public function get($key)
+ {
+ return $this->_cache->itemAt($key);
+ }
+
+ /**
+ * Adds an item with the specified key and value into cached data.
+ * @param string cache key
+ * @param mixed value to cache.
+ */
+ public function set($key, $value)
+ {
+ $this->_cache->add($key, $value);
+ $this->_keyList->add($key);
+ if($this->_keyList->getCount() > $this->_cacheSize)
+ {
+ $oldestKey = $this->_keyList->removeAt(0);
+ $this->_cache->remove($oldestKey);
+ }
+ }
+}
+
+/**
+ * Least recently used cache implementation, removes
+ * object that was accessed last when the cache is full.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.DataAccess.SQLMap
+ * @since 3.0
+ */
+class TSqlMapLruCache extends TSqlMapCache
+{
+ /**
+ * @return mixed Gets a cached object with the specified key.
+ */
+ public function get($key)
+ {
+ if($this->_keyList->contains($key))
+ {
+ $this->_keyList->remove($key);
+ $this->_keyList->add($key);
+ return $this->_cache->itemAt($key);
+ }
+ else
+ return null;
+ }
+
+ /**
+ * Adds an item with the specified key and value into cached data.
+ * @param string cache key
+ * @param mixed value to cache.
+ */
+ public function set($key, $value)
+ {
+ $this->_cache->add($key, $value);
+ $this->_keyList->add($key);
+ if($this->_keyList->getCount() > $this->_cacheSize)
+ {
+ $oldestKey = $this->_keyList->removeAt(0);
+ $this->_cache->remove($oldestKey);
+ }
+ }
+}
+
+class TSqlMapApplicationCache implements ISqlMapCache
+{
+ private $_cache;
+ private $_expiry=0;
+ private $_property=array();
+ private $_cacheModelID;
+
+ public function __sleep()
+ {
+ $this->_cache = null;
+ return array_keys(get_object_vars($this));
+ }
+
+ public function remove($key)
+ {
+ $this->getCache()->delete($key);
+ }
+
+ public function flush()
+ {
+ $this->getCache()->flush();
+ }
+
+ public function get($key)
+ {
+ $result = $this->getCache()->get($key);
+ return $result === false ? null : $result;
+ }
+
+ public function set($key, $value)
+ {
+ $this->getCache()->set($key, $value, $this->_expiry);
+ }
+
+ public function configure($model, $properties)
+ {
+ $this->_property = $properties;
+ $this->_cacheModelID = $model->getID();
+ }
+
+ protected function getCache()
+ {
+ if(is_null($this->_cache))
+ $this->initialize();
+ return $this->_cache;
+ }
+
+ protected function initialize()
+ {
+ if(isset($this->_property['expiry']))
+ $this->_expiry = intval($this->_property['expiry']);
+
+ if(isset($this->_property['cacheModule']))
+ {
+ $id = $this->_property['cacheModule'];
+ $this->_cache = Prado::getApplication()->getModule($id);
+ }
+ else
+ {
+ $this->_cache = Prado::getApplication()->getCache();
+ }
+
+ if(!($this->_cache instanceof ICache))
+ throw new TSqlMapConfigurationException(
+ 'sqlmap_invalid_prado_cache', $this->_cacheModelID);
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/DataMapper/TSqlMapException.php b/framework/Data/SqlMap/DataMapper/TSqlMapException.php
new file mode 100644
index 00000000..9a2db478
--- /dev/null
+++ b/framework/Data/SqlMap/DataMapper/TSqlMapException.php
@@ -0,0 +1,99 @@
+<?php
+
+class TSqlMapException extends TException
+{
+ /**
+ * Constructor.
+ * @param string error message. This can be a string that is listed
+ * in the message file. If so, the message in the preferred language
+ * will be used as the error message. Any rest parameters will be used
+ * to replace placeholders ({0}, {1}, {2}, etc.) in the message.
+ */
+ public function __construct($errorMessage)
+ {
+ $this->setErrorCode($errorMessage);
+ $errorMessage=$this->translateErrorMessage($errorMessage);
+ $args=func_get_args();
+ array_shift($args);
+ $n=count($args);
+ $tokens=array();
+ for($i=0;$i<$n;++$i)
+ {
+ if($args[$i] instanceof SimpleXmlElement)
+ $tokens['{'.$i.'}']=$this->implodeNode($args[$i]);
+ else
+ $tokens['{'.$i.'}']=TPropertyValue::ensureString($args[$i]);
+ }
+ parent::__construct(strtr($errorMessage,$tokens));
+ }
+
+ protected function implodeNode($node)
+ {
+ $attributes=array();
+ foreach($node->attributes() as $k=>$v)
+ $attributes[]=$k.'="'.(string)$v.'"';
+ return '<'.$node->getName().' '.implode(' ',$attributes).'>';
+ }
+
+ /**
+ * @return string path to the error message file
+ */
+ protected function getErrorMessageFile()
+ {
+ $lang=Prado::getPreferredLanguage();
+ $dir=dirname(__FILE__);
+ $msgFile=$dir.'/messages-'.$lang.'.txt';
+ if(!is_file($msgFile))
+ $msgFile=$dir.'/messages.txt';
+ return $msgFile;
+ }
+}
+
+class TSqlMapConfigurationException extends TSqlMapException
+{
+
+}
+
+class TUndefinedAttributeException extends TSqlMapConfigurationException
+{
+ public function __construct($attr, $node, $object, $file)
+ {
+ parent::__construct(
+ 'sqlmap_undefined_attribute', get_class($object), $attr,
+ htmlentities($node->asXml()),$file);
+ }
+}
+
+class TSqlMapExecutionException extends TSqlMapException
+{
+}
+
+class TSqlMapQueryExecutionException extends TSqlMapExecutionException
+{
+ protected $parent;
+ public function __construct($statement, $exception)
+ {
+ $this->parent = $exception;
+ parent::__construct('sqlmap_query_execution_error',
+ $statement->getID(), $exception->getMessage());
+ }
+}
+
+class TSqlMapUndefinedException extends TSqlMapException
+{
+
+}
+
+class TSqlMapDuplicateException extends TSqlMapException
+{
+}
+
+class TSqlMapConnectionException extends TSqlMapException
+{
+}
+
+class TInvalidPropertyException extends TSqlMapException
+{
+
+}
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php b/framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php
new file mode 100644
index 00000000..b15a1d4c
--- /dev/null
+++ b/framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php
@@ -0,0 +1,158 @@
+<?php
+
+Prado::using('System.Collections.TPagedList');
+
+/**
+ * TSQLMapPagedList
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TSqlMapPagedList extends TPagedList
+{
+ private $_statement;
+ private $_parameter;
+ private $_prevPageList;
+ private $_nextPageList;
+ private $_delegate=null;
+
+ public function __construct(IMappedStatement $statement,
+ $parameter, $pageSize, $delegate=null)
+ {
+ parent::__construct();
+ parent::setCustomPaging(true);
+ $this->initialize($statement,$parameter, $pageSize);
+ $this->_delegate=$delegate;
+ }
+
+ protected function initialize($statement, $parameter, $pageSize)
+ {
+ $this->_statement = $statement;
+ $this->_parameter = $parameter;
+ $this->setPageSize($pageSize);
+ $this->attachEventHandler('OnFetchData', array($this, 'fetchDataFromStatement'));
+ $this->gotoPage(0);
+ }
+
+ public function setCustomPaging($value)
+ {
+ throw new TDataMapperException('sqlmap_must_enable_custom_paging');
+ }
+
+ protected function fetchDataFromStatement($sender, $param)
+ {
+ $limit = $this->getOffsetAndLimit($param);
+ $connection = $this->_statement->getManager()->getDbConnection();
+ $data = $this->_statement->executeQueryForList($connection,
+ $this->_parameter, null, $limit[0], $limit[1], $this->_delegate);
+ $this->populateData($param, $data);
+ }
+
+ public function nextPage()
+ {
+ if($this->getIsNextPageAvailable())
+ return parent::nextPage();
+ else
+ return false;
+ }
+
+ public function previousPage()
+ {
+ if($this->getIsPreviousPageAvailable())
+ return parent::previousPage();
+ else
+ return false;
+ }
+
+ protected function populateData($param, $data)
+ {
+ $total = $data instanceof TList ? $data->getCount() : count($data);
+ $pageSize = $this->getPageSize();
+ if($total < 1)
+ {
+ $param->setData($data);
+ $this->_prevPageList = null;
+ $this->_nextPageList = null;
+ return;
+ }
+
+ if($param->getNewPageIndex() < 1)
+ {
+ $this->_prevPageList = null;
+ if($total <= $pageSize)
+ {
+ $param->setData($data);
+ $this->_nextPageList = null;
+ }
+ else
+ {
+ $param->setData($this->sublist($data, 0, $pageSize));
+ $this->_nextPageList = $this->sublist($data, $pageSize,$total);
+ }
+ }
+ else
+ {
+ if($total <= $pageSize)
+ {
+ $this->_prevPageList = $this->sublist($data, 0, $total);
+ $param->setData(array());
+ $this->_nextPageList = null;
+ }
+ else if($total <= $pageSize*2)
+ {
+ $this->_prevPageList = $this->sublist($data, 0, $pageSize);
+ $param->setData($this->sublist($data, $pageSize, $total));
+ $this->_nextPageList = null;
+ }
+ else
+ {
+ $this->_prevPageList = $this->sublist($data, 0, $pageSize);
+ $param->setData($this->sublist($data, $pageSize, $pageSize*2));
+ $this->_nextPageList = $this->sublist($data, $pageSize*2, $total);
+ }
+ }
+ }
+
+ protected function sublist($data, $from, $to)
+ {
+ $array = array();
+ for($i = $from; $i<$to; $i++)
+ $array[] = $data[$i];
+ return $array;
+ }
+
+ protected function getOffsetAndLimit($param)
+ {
+ $index = $param->getNewPageIndex();
+ $pageSize = $this->getPageSize();
+ if($index < 1)
+ return array($index, $pageSize*2);
+ else
+ return array(($index-1)*$pageSize, $pageSize*3);
+ }
+
+ public function getIsNextPageAvailable()
+ {
+ return !is_null($this->_nextPageList);
+ }
+
+ public function getIsPreviousPageAvailable()
+ {
+ return !is_null($this->_prevPageList);
+ }
+
+ public function getIsLastPage()
+ {
+ return is_null($this->_nextPageList)
+ || $this->_nextPageList->getCount() < 1;
+ }
+
+ public function getIsMiddlePage()
+ {
+ return !($this->getIsFirstPage() || $this->getIsLastPage());
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/DataMapper/TSqlMapTypeHandlerRegistry.php b/framework/Data/SqlMap/DataMapper/TSqlMapTypeHandlerRegistry.php
new file mode 100644
index 00000000..c26c0401
--- /dev/null
+++ b/framework/Data/SqlMap/DataMapper/TSqlMapTypeHandlerRegistry.php
@@ -0,0 +1,183 @@
+<?php
+
+/**
+ * TTypeHandlerFactory provides type handler classes to convert database field type
+ * to PHP types and vice versa.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.DataMapper
+ * @since 3.1
+ */
+class TSqlMapTypeHandlerRegistry
+{
+ private $_typeHandlers=array();
+
+ /**
+ * @param string database field type
+ * @return TSqlMapTypeHandler type handler for give database field type.
+ */
+ public function getDbTypeHandler($dbType='NULL')
+ {
+ foreach($this->_typeHandlers as $handler)
+ if($handler->getDbType()===$dbType)
+ return $handler;
+ }
+
+ /**
+ * @param string type handler class name
+ * @return TSqlMapTypeHandler type handler
+ */
+ public function getTypeHandler($class)
+ {
+ if(isset($this->_typeHandlers[$class]))
+ return $this->_typeHandlers[$class];
+ }
+
+ /**
+ * @param TSqlMapTypeHandler registers a new type handler
+ */
+ public function registerTypeHandler(TSqlMapTypeHandler $handler)
+ {
+ $this->_typeHandlers[$handler->getType()] = $handler;
+ }
+
+ /**
+ * Creates a new instance of a particular class (for PHP primative types,
+ * their corresponding default value for given type is used).
+ * @param string PHP type name
+ * @return mixed default type value, if no type is specified null is returned.
+ * @throws TSqlMapException if class name is not found.
+ */
+ public function createInstanceOf($type='')
+ {
+ if(strlen($type) > 0)
+ {
+ switch(strtolower($type))
+ {
+ case 'string': return '';
+ case 'array': return array();
+ case 'float': case 'double': case 'decimal': return 0.0;
+ case 'integer': case 'int': return 0;
+ case 'bool': case 'boolean': return false;
+ }
+
+ if(class_exists('Prado', false))
+ return Prado::createComponent($type);
+ else if(class_exists($type, false)) //NO auto loading
+ return new $type;
+ else
+ throw new TSqlMapException('sqlmap_unable_to_find_class', $type);
+ }
+ }
+
+ /**
+ * Converts the value to given type using PHP's settype() function.
+ * @param string PHP primative type.
+ * @param mixed value to be casted
+ * @return mixed type casted value.
+ */
+ public function convertToType($type, $value)
+ {
+ switch(strtolower($type))
+ {
+ case 'integer': case 'int':
+ $type = 'integer'; break;
+ case 'float': case 'double': case 'decimal':
+ $type = 'float'; break;
+ case 'boolean': case 'bool':
+ $type = 'boolean'; break;
+ case 'string' :
+ $type = 'string'; break;
+ default:
+ return $value;
+ }
+ settype($value, $type);
+ return $value;
+ }
+}
+
+/**
+ * A simple interface for implementing custom type handlers.
+ *
+ * Using this interface, you can implement a type handler that
+ * will perform customized processing before parameters are set
+ * on and after values are retrieved from the database.
+ * Using a custom type handler you can extend
+ * the framework to handle types that are not supported, or
+ * handle supported types in a different way. For example,
+ * you might use a custom type handler to implement proprietary
+ * BLOB support (e.g. Oracle), or you might use it to handle
+ * booleans using "Y" and "N" instead of the more typical 0/1.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.DataMapper
+ * @since 3.1
+ */
+abstract class TSqlMapTypeHandler extends TComponent
+{
+ private $_dbType='NULL';
+ private $_type;
+ /**
+ * @param string database field type.
+ */
+ public function setDbType($value)
+ {
+ $this->_dbType=$value;
+ }
+
+ /**
+ * @return string database field type.
+ */
+ public function getDbType()
+ {
+ return $this->_dbType;
+ }
+
+ public function getType()
+ {
+ if($this->_type===null)
+ return get_class($this);
+ else
+ return $this->_type;
+ }
+
+ public function setType($value)
+ {
+ $this->_type=$value;
+ }
+
+ /**
+ * Performs processing on a value before it is used to set
+ * the parameter of a IDbCommand.
+ * @param object The interface for setting the value.
+ * @param object The value to be set.
+ */
+ public abstract function getParameter($object);
+
+
+ /**
+ * Performs processing on a value before after it has been retrieved
+ * from a database
+ * @param object The interface for getting the value.
+ * @return mixed The processed value.
+ */
+ public abstract function getResult($string);
+
+
+ /**
+ * Casts the string representation of a value into a type recognized by
+ * this type handler. This method is used to translate nullValue values
+ * into types that can be appropriately compared. If your custom type handler
+ * cannot support nullValues, or if there is no reasonable string representation
+ * for this type (e.g. File type), you can simply return the String representation
+ * as it was passed in. It is not recommended to return null, unless null was passed
+ * in.
+ * @param array result row.
+ * @return mixed
+ */
+ public abstract function createNewInstance($row=null);
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/DataMapper/messages.txt b/framework/Data/SqlMap/DataMapper/messages.txt
new file mode 100644
index 00000000..0923d606
--- /dev/null
+++ b/framework/Data/SqlMap/DataMapper/messages.txt
@@ -0,0 +1,66 @@
+
+# TSqlMapManager.php
+sqlmap_contains_no_statement = Unable to find SQLMap statement '{0}'.
+sqlmap_already_contains_statement = Duplicate SQLMap statement found, '{0}' already exists.
+sqlmap_contains_no_result_map = Unable to find SQLMap result map '{0}'.
+sqlmap_already_contains_result_map = Duplicate SQLMap result map found, '{0}' already exists.
+sqlmap_contains_no_parameter_map = Unable to find SQLMap parameter map '{0}'.
+sqlmap_already_contains_parameter_map = Duplicate SQLMap parameter map found, '{0}' already exists.
+sqlmap_cache_model_already_exists = This SQLMap already contains cache model '{0}'.
+sqlmap_unable_to_find_cache_model = Unable to find cache model '{0}' in this SQLMap.
+
+# TTypeHandlerFactory.php
+sqlmap_dbtype_handler_not_found = Type handler for dbType='{0}' not found.
+sqlmap_type_handler_class_not_found = Type handler class '{0}' not found.
+sqlmap_unable_to_find_class = Unable to find class '{0}'.
+
+# TSqlMapXmlConfig.php
+sqlmap_node_class_undef = Missing attribute 'class' in tag '{0}' in configuration file '{1}'.
+sqlmap_unable_to_find_parent_result_map = Unable to find parent SQLMap result map named '{2}' in file {1} near '{0}'.
+sqlmap_undefined_discriminator = The <discriminator> tag not found in ResultMap '{0}' for sub-map '{2}' in file '{1}'.
+sqlmap_unable_to_find_parent_sql = Unable to find parent sql statement extension '{0}' near '{2}' in file {1}.
+sqlmap_invalid_property = Invalid property '{0}' for class '{1}' for tag '{2}' in configuration file '{3}'.
+
+
+# TInlineParameterMapParser.php
+sqlmap_undefined_property_inline_map = Invalid attribute '{0}' in '{3}' for inline parameter in statement '{2}' in file {1}.
+
+# TSqlMapCacheModel.php
+sqlmap_unable_to_find_implemenation = Unable to find cache implementation class '{0}'.
+
+# TResultProperty.php
+sqlmap_error_in_result_property_from_handler = For result map '{0}', error in getting result from type handler '{2}', with value '{1}'.
+
+# TParameterMap.php
+sqlmap_index_must_be_string_or_int = Invalid index '{0}', must be an integes or string to get a SqlMap parameter map property.
+sqlmap_unable_to_get_property_for_parameter = Unable to find property '{1}' in object '{2}' for parameter map '{0}'.
+sqlmap_error_in_parameter_from_handler = For parameter map '{0}', error in getting parameter from type handler '{2}' with value '{1}': '{3}'.
+
+# MISC
+sqlmap_type_handler_class_undef = Unable to find type handler class named '{1}' in sqlmap configuration file '{0}'.
+sqlmap_type_handler_callback_undef = Attributes 'type' and 'callback' must be defined in typeHandler tag in configuration file '{0}'.
+
+sqlmap_undefined_attribute = {0} attribute '{1}' is not defined for {2} in file {3}.
+sqlmap_unable_to_find_parent_parameter_map = Unable to find parent parameter map extension '{0}' in file {1}.
+sqlmap_unable_to_find_result_mapping = Unable to resolve SQLMap result mapping '{0}' in Result Map '{2}' using configuration file {1}.
+
+sqlmap_undefined_input_property = Undefined array index '{0}' in retrieving property in SQLMap parameter map '{1}'.
+sqlmap_can_not_instantiate = Type handler '{0}' can not create new objects.
+sqlmap_cannot_execute_query_for_map = SQLMap statement class {0} can not query for map in statement '{1}'.
+sqlmap_cannot_execute_update = SQLMap statement class {0} can not execute update query in statement '{1}'.
+sqlmap_cannot_execute_insert = SQLMap statement class {0} can not execute insert in statement '{1}'.
+sqlmap_cannot_execute_query_for_list = SQLMap statement class {0} can not query for list in statement '{1}'.
+sqlmap_cannot_execute_query_for_object = SQLMap statement class {0} can not query for object in statement '{1}'.
+sqlmap_execution_error_no_record = No record set found in executing statement '{0}': '{1}'.
+sqlmap_unable_to_create_new_instance = Unable to create a new instance of '{0}' using type hander '{1}' for SQLMap statement with ID '{2}'.
+sqlmap_invalid_property_type = Invalid object type, must be 'Object', unable to set property in path '{0}'.
+
+sqlmap_unable_to_find_config = Unable to find SQLMap configuration file '{0}'.
+sqlmap_unable_to_find_groupby = Unable to find data in result set with column '{0}' in result map with ID '{1}'.
+sqlmap_invalid_lazyload_list = Invalid type to lazy load, must specify a valid ListClass in statement '{0}'.
+sqlmap_unable_to_find_resource = 'Unable to find SQLMap configuration file '{0}'.
+sqlmap_query_execution_error = Error in executing SQLMap statement '{0}' : '{1}'.
+sqlmap_invalid_delegate = Invalid callback row delegate '{1}' in mapped statement '{0}'.
+sqlmap_invalid_prado_cache = Unable to find Prado cache module for SQLMap cache '{0}'.
+
+sqlmap_non_groupby_array_list_type = Expecting GroupBy property in result map '{0}' since {1}::{2} is an array or TList type. \ No newline at end of file
diff --git a/framework/Data/SqlMap/Statements/IMappedStatement.php b/framework/Data/SqlMap/Statements/IMappedStatement.php
new file mode 100644
index 00000000..8d948558
--- /dev/null
+++ b/framework/Data/SqlMap/Statements/IMappedStatement.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * IMappedStatement interface file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Statements
+ */
+
+/**
+ * Interface for all mapping statements.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Statements
+ * @since 3.1
+ */
+interface IMappedStatement
+{
+ /**
+ * @return string Name used to identify the MappedStatement amongst the others.
+ */
+ public function getID();
+
+ /**
+ * @return TSqlMapStatement The SQL statment used by this TMappedStatement.
+ */
+ public function getStatement();
+
+ /**
+ * @return TSqlMap The TSqlMap used by this TMappedStatement
+ */
+ public function getManager();
+
+ /**
+ * Executes the SQL and retuns all rows selected in a map that is keyed on
+ * the property named in the <tt>$keyProperty</tt> parameter. The value at
+ * each key will be the value of the property specified in the
+ * <tt>$valueProperty</tt> parameter. If <tt>$valueProperty</tt> is
+ * <tt>null</tt>, the entire result object will be entered.
+ * @param IDbConnection database connection to execute the query
+ * @param mixed The object used to set the parameters in the SQL.
+ * @param string The property of the result object to be used as the key.
+ * @param string The property of the result object to be used as the value (or null)
+ * @return TMap A map of object containing the rows keyed by <tt>$keyProperty</tt>.
+ */
+ public function executeQueryForMap($connection, $parameter, $keyProperty, $valueProperty=null);
+
+
+ /**
+ * Execute an update statement. Also used for delete statement. Return the
+ * number of row effected.
+ * @param IDbConnection database connection to execute the query
+ * @param mixed The object used to set the parameters in the SQL.
+ * @return integer The number of row effected.
+ */
+ public function executeUpdate($connection, $parameter);
+
+
+ /**
+ * Executes the SQL and retuns a subset of the rows selected.
+ * @param IDbConnection database connection to execute the query
+ * @param mixed The object used to set the parameters in the SQL.
+ * @param TList A list to populate the result with.
+ * @param integer The number of rows to skip over.
+ * @param integer The maximum number of rows to return.
+ * @return TList A TList of result objects.
+ */
+ public function executeQueryForList($connection, $parameter, $result=null, $skip=-1, $max=-1);
+
+
+ /**
+ * Executes an SQL statement that returns a single row as an object
+ * of the type of the <tt>$result</tt> passed in as a parameter.
+ * @param IDbConnection database connection to execute the query
+ * @param mixed The object used to set the parameters in the SQL.
+ * @param object The result object.
+ * @return object result.
+ */
+ public function executeQueryForObject($connection,$parameter, $result=null);
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/Statements/TCachingStatement.php b/framework/Data/SqlMap/Statements/TCachingStatement.php
new file mode 100644
index 00000000..0bcc5291
--- /dev/null
+++ b/framework/Data/SqlMap/Statements/TCachingStatement.php
@@ -0,0 +1,99 @@
+<?php
+
+/**
+ *
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.Statements
+ * @since 3.1
+ */
+class TCachingStatement implements IMappedStatement
+{
+ private $_mappedStatement;
+
+ public function __construct(TMappedStatement $statement)
+ {
+ $this->_mappedStatement = $statement;
+ }
+
+ public function getID()
+ {
+ return $this->_mappedStatement->getID();
+ }
+
+ public function getStatement()
+ {
+ return $this->_mappedStatement->getStatement();
+ }
+
+ public function getManager()
+ {
+ return $this->_mappedStatement->getManager();
+ }
+
+ public function executeQueryForMap($connection, $parameter,$keyProperty, $valueProperty=null, $delegate=null)
+ {
+ $sql = $this->createCommand($connection, $parameter);
+ $key = $this->getCacheKey(array($sql, $keyProperty, $valueProperty));
+ $map = $this->getStatement()->getCache()->get($key);
+ if(is_null($map))
+ {
+ $map = $this->_mappedStatement->runQueryForMap(
+ $connection, $parameter, $sql, $keyProperty, $valueProperty, $delegate);
+ $this->getStatement()->getCache()->set($key, $map);
+ }
+ return $map;
+ }
+
+ public function executeUpdate($connection, $parameter)
+ {
+ return $this->_mappedStatement->executeUpdate($connection, $parameter);
+ }
+
+ public function executeInsert($connection, $parameter)
+ {
+ return $this->executeInsert($connection, $parameter);
+ }
+
+ public function executeQueryForList($connection, $parameter, $result=null, $skip=-1, $max=-1, $delegate=null)
+ {
+ $sql = $this->createCommand($connection, $parameter);
+ $key = $this->getCacheKey(array($sql, $skip, $max));
+ $list = $this->getStatement()->getCache()->get($key);
+ if(is_null($list))
+ {
+ $list = $this->_mappedStatement->runQueryForList(
+ $connection, $parameter, $sql, $result, $skip, $max, $delegate);
+ $this->getStatement()->getCache()->set($key, $list);
+ }
+ return $list;
+ }
+
+ public function executeQueryForObject($connection, $parameter, $result=null)
+ {
+ $sql = $this->createCommand($connection, $parameter);
+ $key = $this->getCacheKey($sql);
+ $object = $this->getStatement()->getCache()->get($key);
+ if(is_null($object))
+ {
+ $object = $this->_mappedStatement->runQueryForObject($connection, $sql, $result);
+ $this->getStatement()->getCache()->set($key, $object);
+ }
+ return $object;
+ }
+
+ protected function getCacheKey($object)
+ {
+ $cacheKey = new TSqlMapCacheKey($object);
+ return $cacheKey->getHash();
+ }
+
+ protected function createCommand($connection, $parameter)
+ {
+ return $this->_mappedStatement->getCommand()->create($this->getManager(),
+ $connection, $this->getStatement(), $parameter);
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/Statements/TDeleteMappedStatement.php b/framework/Data/SqlMap/Statements/TDeleteMappedStatement.php
new file mode 100644
index 00000000..9a1c8fae
--- /dev/null
+++ b/framework/Data/SqlMap/Statements/TDeleteMappedStatement.php
@@ -0,0 +1,7 @@
+<?php
+
+class TDeleteMappedStatement extends TUpdateMappedStatement
+{
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/Statements/TInsertMappedStatement.php b/framework/Data/SqlMap/Statements/TInsertMappedStatement.php
new file mode 100644
index 00000000..46814aee
--- /dev/null
+++ b/framework/Data/SqlMap/Statements/TInsertMappedStatement.php
@@ -0,0 +1,32 @@
+<?php
+
+class TInsertMappedStatement extends TMappedStatement
+{
+ public function executeQueryForMap($connection, $parameter,
+ $keyProperty, $valueProperty=null)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_cannot_execute_query_for_map', get_class($this), $this->getID());
+ }
+
+ public function executeUpdate($connection, $parameter)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_cannot_execute_update', get_class($this), $this->getID());
+ }
+
+ public function executeQueryForList($connection, $parameter, $result=null,
+ $skip=-1, $max=-1)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_cannot_execute_query_for_list', get_class($this), $this->getID());
+ }
+
+ public function executeQueryForObject($connection, $parameter, $result=null)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_cannot_execute_query_for_object', get_class($this), $this->getID());
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/Statements/TMappedStatement.php b/framework/Data/SqlMap/Statements/TMappedStatement.php
new file mode 100644
index 00000000..6bd4eb47
--- /dev/null
+++ b/framework/Data/SqlMap/Statements/TMappedStatement.php
@@ -0,0 +1,1187 @@
+<?php
+/**
+ * TMappedStatement and related classes.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SQLMap.Statements
+ */
+
+/**
+ * TMappedStatement class executes SQL mapped statements. Mapped Statements can
+ * hold any SQL statement and use Parameter Maps and Result Maps for input and output.
+ *
+ * This class is usualy instantiated during SQLMap configuration by TSqlDomBuilder.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.DataAccess.SQLMap.Statements
+ * @since 3.0
+ */
+class TMappedStatement extends TComponent implements IMappedStatement
+{
+ /**
+ * @var TSqlMapStatement current SQL statement.
+ */
+ private $_statement;
+
+ /**
+ * @var TPreparedCommand SQL command prepareer
+ */
+ private $_command;
+
+ /**
+ * @var TSqlMapper sqlmap used by this mapper.
+ */
+ private $_manager;
+
+ /**
+ * @var TPostSelectBinding[] post select statement queue.
+ */
+ private $_selectQueque=array();
+
+ /**
+ * @var boolean true when data is mapped to a particular row.
+ */
+ private $_IsRowDataFound = false;
+
+ /**
+ * @var TSQLMapObjectCollectionTree group by object collection tree
+ */
+ private $_groupBy;
+
+ /**
+ * @var Post select is to query for list.
+ */
+ const QUERY_FOR_LIST = 0;
+
+ /**
+ * @var Post select is to query for list.
+ */
+ const QUERY_FOR_ARRAY = 1;
+
+ /**
+ * @var Post select is to query for object.
+ */
+ const QUERY_FOR_OBJECT = 2;
+
+ /**
+ * @return string Name used to identify the TMappedStatement amongst the others.
+ * This the name of the SQL statement by default.
+ */
+ public function getID()
+ {
+ return $this->_statement->ID;
+ }
+
+ /**
+ * @return TSqlMapStatement The SQL statment used by this MappedStatement
+ */
+ public function getStatement()
+ {
+ return $this->_statement;
+ }
+
+ /**
+ * @return TSqlMapper The SqlMap used by this MappedStatement
+ */
+ public function getManager()
+ {
+ return $this->_manager;
+ }
+
+ /**
+ * @return TPreparedCommand command to prepare SQL statements.
+ */
+ public function getCommand()
+ {
+ return $this->_command;
+ }
+
+ /**
+ * Empty the group by results cache.
+ */
+ protected function initialGroupByResults()
+ {
+ $this->_groupBy = new TSQLMapObjectCollectionTree();
+ }
+
+ /**
+ * Creates a new mapped statement.
+ * @param TSqlMapper an sqlmap.
+ * @param TSqlMapStatement An SQL statement.
+ */
+ public function __construct(TSqlMapManager $sqlMap, TSqlMapStatement $statement)
+ {
+ $this->_manager = $sqlMap;
+ $this->_statement = $statement;
+ $this->_command = new TPreparedCommand();
+ $this->initialGroupByResults();
+ }
+
+ /**
+ * Execute SQL Query.
+ * @param IDbConnection database connection
+ * @param array SQL statement and parameters.
+ * @return mixed record set if applicable.
+ * @throws TSqlMapExecutionException if execution error or false record set.
+ * @throws TSqlMapQueryExecutionException if any execution error
+ */
+/* protected function executeSQLQuery($connection, $sql)
+ {
+ try
+ {
+ if(!($recordSet = $connection->execute($sql['sql'],$sql['parameters'])))
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_execution_error_no_record', $this->getID(),
+ $connection->ErrorMsg());
+ }
+ return $recordSet;
+ }
+ catch (Exception $e)
+ {
+ throw new TSqlMapQueryExecutionException($this->getStatement(), $e);
+ }
+ }*/
+
+ /**
+ * Execute SQL Query with limits.
+ * @param IDbConnection database connection
+ * @param array SQL statement and parameters.
+ * @return mixed record set if applicable.
+ * @throws TSqlMapExecutionException if execution error or false record set.
+ * @throws TSqlMapQueryExecutionException if any execution error
+ */
+ protected function executeSQLQueryLimit($connection, $command, $max, $skip)
+ {
+ if($max>-1 || $skip > -1)
+ {
+ $maxStr=$max>0?' LIMIT '.$max:'';
+ $skipStr=$skip>0?' OFFSET '.$skip:'';
+ $command->setText($command->getText().$maxStr.$skipStr);
+ }
+ $connection->setActive(true);
+ return $command->query();
+
+ /*//var_dump($command);
+ try
+ {
+ $recordSet = $connection->selectLimit($sql['sql'],$max,$skip,$sql['parameters']);
+ if(!$recordSet)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_execution_error_query_for_list',
+ $connection->ErrorMsg());
+ }
+ return $recordSet;
+ }
+ catch (Exception $e)
+ {
+ throw new TSqlMapQueryExecutionException($this->getStatement(), $e);
+ }*/
+ }
+
+ /**
+ * Executes the SQL and retuns a List of result objects.
+ * @param IDbConnection database connection
+ * @param mixed The object used to set the parameters in the SQL.
+ * @param object result collection object.
+ * @param integer The number of rows to skip over.
+ * @param integer The maximum number of rows to return.
+ * @return array a list of result objects
+ * @param callback row delegate handler
+ * @see executeQueryForList()
+ */
+ public function executeQueryForList($connection, $parameter, $result=null, $skip=-1, $max=-1, $delegate=null)
+ {
+ $sql = $this->_command->create($this->_manager, $connection, $this->_statement, $parameter);
+ return $this->runQueryForList($connection, $parameter, $sql, $result, $skip, $max, $delegate);
+ }
+
+ /**
+ * Executes the SQL and retuns a List of result objects.
+ *
+ * This method should only be called by internal developers, consider using
+ * <tt>executeQueryForList()</tt> first.
+ *
+ * @param IDbConnection database connection
+ * @param mixed The object used to set the parameters in the SQL.
+ * @param array SQL string and subsititution parameters.
+ * @param object result collection object.
+ * @param integer The number of rows to skip over.
+ * @param integer The maximum number of rows to return.
+ * @param callback row delegate handler
+ * @return array a list of result objects
+ * @see executeQueryForList()
+ */
+ public function runQueryForList($connection, $parameter, $sql, $result, $skip=-1, $max=-1, $delegate=null)
+ {
+ $registry=$this->getManager()->getTypeHandlers();
+ $list = $result instanceof ArrayAccess ? $result :
+ $this->_statement->createInstanceOfListClass($registry);
+ $reader = $this->executeSQLQueryLimit($connection, $sql, $max, $skip);
+ if(!is_null($delegate))
+ {
+ foreach($reader as $row)
+ {
+ $obj = $this->applyResultMap($row);
+ $param = new TResultSetListItemParameter($obj, $parameter, $list);
+ $this->raiseRowDelegate($delegate, $param);
+ }
+ }
+ else
+ {
+ //var_dump($sql,$parameter);
+ foreach($reader as $row)
+ {
+// var_dump($row);
+ $list[] = $this->applyResultMap($row);
+ }
+ }
+
+ if(!$this->_groupBy->isEmpty())
+ {
+ $list = $this->_groupBy->collect();
+ $this->initialGroupByResults();
+ }
+
+ $this->executePostSelect($connection);
+ $this->onExecuteQuery($sql);
+
+ return $list;
+ }
+
+ /**
+ * Executes the SQL and retuns all rows selected in a map that is keyed on
+ * the property named in the keyProperty parameter. The value at each key
+ * will be the value of the property specified in the valueProperty parameter.
+ * If valueProperty is null, the entire result object will be entered.
+ * @param IDbConnection database connection
+ * @param mixed The object used to set the parameters in the SQL.
+ * @param string The property of the result object to be used as the key.
+ * @param string The property of the result object to be used as the value (or null).
+ * @param callback row delegate handler
+ * @return array An array of object containing the rows keyed by keyProperty.
+ */
+ public function executeQueryForMap($connection, $parameter, $keyProperty, $valueProperty=null, $delegate=null)
+ {
+ $sql = $this->_command->create($this->_manager, $connection, $this->_statement, $parameter);
+ return $this->runQueryForMap($connection, $parameter, $sql, $keyProperty, $valueProperty, $delegate);
+ }
+
+ /**
+ * Executes the SQL and retuns all rows selected in a map that is keyed on
+ * the property named in the keyProperty parameter. The value at each key
+ * will be the value of the property specified in the valueProperty parameter.
+ * If valueProperty is null, the entire result object will be entered.
+ *
+ * This method should only be called by internal developers, consider using
+ * <tt>executeQueryForMap()</tt> first.
+ *
+ * @param IDbConnection database connection
+ * @param mixed The object used to set the parameters in the SQL.
+ * @param array SQL string and subsititution parameters.
+ * @param string The property of the result object to be used as the key.
+ * @param string The property of the result object to be used as the value (or null).
+ * @param callback row delegate, a callback function
+ * @return array An array of object containing the rows keyed by keyProperty.
+ * @see executeQueryForMap()
+ */
+ public function runQueryForMap($connection, $parameter, $command, $keyProperty, $valueProperty=null, $delegate=null)
+ {
+ $map = array();
+ //$recordSet = $this->executeSQLQuery($connection, $sql);
+ $connection->setActive(true);
+ $reader = $command->query();
+ if(!is_null($delegate))
+ {
+ //while($row = $recordSet->fetchRow())
+ foreach($reader as $row)
+ {
+ $obj = $this->applyResultMap($row);
+ $key = TPropertyAccess::get($obj, $keyProperty);
+ $value = is_null($valueProperty) ? $obj :
+ TPropertyAccess::get($obj, $valueProperty);
+ $param = new TResultSetMapItemParameter($key, $value, $parameter, $map);
+ $this->raiseRowDelegate($delegate, $param);
+ }
+ }
+ else
+ {
+ //while($row = $recordSet->fetchRow())
+ foreach($reader as $row)
+ {
+ $obj = $this->applyResultMap($row);
+ $key = TPropertyAccess::get($obj, $keyProperty);
+ $map[$key] = is_null($valueProperty) ? $obj :
+ TPropertyAccess::get($obj, $valueProperty);
+ }
+ }
+ $this->onExecuteQuery($command);
+ return $map;
+ }
+
+ /**
+ * Raises delegate handler.
+ * This method is invoked for each new list item. It is the responsibility
+ * of the handler to add the item to the list.
+ * @param object event parameter
+ */
+ protected function raiseRowDelegate($handler, $param)
+ {
+ if(is_string($handler))
+ {
+ call_user_func($handler,$this,$param);
+ }
+ else if(is_callable($handler,true))
+ {
+ // an array: 0 - object, 1 - method name/path
+ list($object,$method)=$handler;
+ if(is_string($object)) // static method call
+ call_user_func($handler,$this,$param);
+ else
+ {
+ if(($pos=strrpos($method,'.'))!==false)
+ {
+ $object=$this->getSubProperty(substr($method,0,$pos));
+ $method=substr($method,$pos+1);
+ }
+ $object->$method($this,$param);
+ }
+ }
+ else
+ throw new TInvalidDataValueException('sqlmap_invalid_delegate', $this->getID(), $handler);
+ }
+
+ /**
+ * Executes an SQL statement that returns a single row as an object of the
+ * type of the <tt>$result</tt> passed in as a parameter.
+ * @param IDbConnection database connection
+ * @param mixed The parameter data (object, arrary, primitive) used to set the parameters in the SQL
+ * @param mixed The result object.
+ * @return ${return}
+ */
+ public function executeQueryForObject($connection, $parameter, $result=null)
+ {
+ $sql = $this->_command->create($this->_manager, $connection, $this->_statement, $parameter);
+ return $this->runQueryForObject($connection, $sql, $result);
+ }
+
+ /**
+ * Executes an SQL statement that returns a single row as an object of the
+ * type of the <tt>$result</tt> passed in as a parameter.
+ *
+ * This method should only be called by internal developers, consider using
+ * <tt>executeQueryForObject()</tt> first.
+ *
+ * @param IDbConnection database connection
+ * @param array SQL string and subsititution parameters.
+ * @param object The result object.
+ * @return object the object.
+ * @see executeQueryForObject()
+ */
+ public function runQueryForObject($connection, $command, &$result)
+ {
+ $object = null;
+ $connection->setActive(true);
+ foreach($command->query() as $row)
+ $object = $this->applyResultMap($row, $result);
+
+ if(!$this->_groupBy->isEmpty())
+ {
+ $list = $this->_groupBy->collect();
+ $this->initialGroupByResults();
+ $object = $list[0];
+ }
+
+ $this->executePostSelect($connection);
+ $this->onExecuteQuery($command);
+
+ return $object;
+ }
+
+ /**
+ * Execute an insert statement. Fill the parameter object with the ouput
+ * parameters if any, also could return the insert generated key.
+ * @param IDbConnection database connection
+ * @param mixed The parameter object used to fill the statement.
+ * @return string the insert generated key.
+ */
+ public function executeInsert($connection, $parameter)
+ {
+ $generatedKey = $this->getPreGeneratedSelectKey($connection, $parameter);
+
+ $command = $this->_command->create($this->_manager, $connection, $this->_statement, $parameter);
+// var_dump($command,$parameter);
+ $result = $command->execute();
+
+ if(is_null($generatedKey))
+ $generatedKey = $this->getPostGeneratedSelectKey($connection, $parameter);
+
+ $this->executePostSelect($connection);
+ $this->onExecuteQuery($command);
+ return $generatedKey;
+ }
+
+ /**
+ * Gets the insert generated ID before executing an insert statement.
+ * @param IDbConnection database connection
+ * @param mixed insert statement parameter.
+ * @return string new insert ID if pre-select key statement was executed, null otherwise.
+ */
+ protected function getPreGeneratedSelectKey($connection, $parameter)
+ {
+ if($this->_statement instanceof TSqlMapInsert)
+ {
+ $selectKey = $this->_statement->getSelectKey();
+ if(!is_null($selectKey) && !$selectKey->getIsAfter())
+ return $this->executeSelectKey($connection, $parameter, $selectKey);
+ }
+ }
+
+ /**
+ * Gets the inserted row ID after executing an insert statement.
+ * @param IDbConnection database connection
+ * @param mixed insert statement parameter.
+ * @return string last insert ID, null otherwise.
+ */
+ protected function getPostGeneratedSelectKey($connection, $parameter)
+ {
+ if($this->_statement instanceof TSqlMapInsert)
+ {
+ $selectKey = $this->_statement->getSelectKey();
+ if(!is_null($selectKey) && $selectKey->getIsAfter())
+ return $this->executeSelectKey($connection, $parameter, $selectKey);
+ }
+ }
+
+ /**
+ * Execute the select key statement, used to obtain last insert ID.
+ * @param IDbConnection database connection
+ * @param mixed insert statement parameter
+ * @param TSqlMapSelectKey select key statement
+ * @return string last insert ID.
+ */
+ protected function executeSelectKey($connection, $parameter, $selectKey)
+ {
+ $mappedStatement = $this->getManager()->getMappedStatement($selectKey->getID());
+ $generatedKey = $mappedStatement->executeQueryForObject(
+ $connection, $parameter, null);
+ if(strlen($prop = $selectKey->getProperty()) > 0)
+ TPropertyAccess::set($parameter, $prop, $generatedKey);
+ return $generatedKey;
+ }
+
+ /**
+ * Execute an update statement. Also used for delete statement.
+ * Return the number of rows effected.
+ * @param IDbConnection database connection
+ * @param mixed The object used to set the parameters in the SQL.
+ * @return integer The number of rows effected.
+ */
+ public function executeUpdate($connection, $parameter)
+ {
+ $sql = $this->_command->create($this->getManager(),$connection, $this->_statement, $parameter);
+ $affectedRows = $sql->execute();
+ //$this->executeSQLQuery($connection, $sql);
+ $this->executePostSelect($connection);
+ $this->onExecuteQuery($sql);
+ return $affectedRows;
+ }
+
+ /**
+ * Process 'select' result properties
+ * @param IDbConnection database connection
+ */
+ protected function executePostSelect($connection)
+ {
+ while(count($this->_selectQueque))
+ {
+ $postSelect = array_shift($this->_selectQueque);
+ $method = $postSelect->getMethod();
+ $statement = $postSelect->getStatement();
+ $property = $postSelect->getResultProperty()->getProperty();
+ $keys = $postSelect->getKeys();
+ $resultObject = $postSelect->getResultObject();
+
+ if($method == self::QUERY_FOR_LIST || $method == self::QUERY_FOR_ARRAY)
+ {
+ $values = $statement->executeQueryForList($connection, $keys, null);
+
+ if($method == self::QUERY_FOR_ARRAY)
+ $values = $values->toArray();
+ TPropertyAccess::set($resultObject, $property, $values);
+ }
+ else if($method == self::QUERY_FOR_OBJECT)
+ {
+ $value = $statement->executeQueryForObject($connection, $keys, null);
+ TPropertyAccess::set($resultObject, $property, $value);
+ }
+ }
+ }
+
+ /**
+ * Raise the execute query event.
+ * @param array prepared SQL statement and subsititution parameters
+ */
+ public function onExecuteQuery($sql)
+ {
+ $this->raiseEvent('OnExecuteQuery', $this, $sql);
+ }
+
+ /**
+ * Apply result mapping.
+ * @param array a result set row retrieved from the database
+ * @param object the result object, will create if necessary.
+ * @return object the result filled with data, null if not filled.
+ */
+ protected function applyResultMap($row, &$resultObject=null)
+ {
+ if($row === false) return null;
+
+ $resultMapName = $this->_statement->getResultMap();
+ $resultClass = $this->_statement->getResultClass();
+
+ $obj=null;
+ if($this->getManager()->getResultMaps()->contains($resultMapName))
+ $obj = $this->fillResultMap($resultMapName, $row, null, $resultObject);
+ else if(strlen($resultClass) > 0)
+ $obj = $this->fillResultClass($resultClass, $row, $resultObject);
+ else
+ $obj = $this->fillDefaultResultMap(null, $row, $resultObject);
+ if(class_exists('TActiveRecord',false) && $obj instanceof TActiveRecord)
+ TActiveRecordManager::getInstance()->getObjectStateRegistry()->registerClean($obj);
+ return $obj;
+ }
+
+ /**
+ * Fill the result using ResultClass, will creates new result object if required.
+ * @param string result object class name
+ * @param array a result set row retrieved from the database
+ * @param object the result object, will create if necessary.
+ * @return object result object filled with data
+ */
+ protected function fillResultClass($resultClass, $row, $resultObject)
+ {
+ if(is_null($resultObject))
+ {
+ $registry = $this->getManager()->getTypeHandlers();
+ $resultObject = $this->_statement->createInstanceOfResultClass($registry,$row);
+ }
+
+ if($resultObject instanceOf ArrayAccess)
+ return $this->fillResultArrayList($row, $resultObject);
+ else if(is_object($resultObject))
+ return $this->fillResultObjectProperty($row, $resultObject);
+ else
+ return $this->fillDefaultResultMap(null, $row, $resultObject);
+ }
+
+ /**
+ * Apply the result to a TList or an array.
+ * @param array a result set row retrieved from the database
+ * @param object result object, array or list
+ * @return object result filled with data.
+ */
+ protected function fillResultArrayList($row, $resultObject)
+ {
+ if($resultObject instanceof TList)
+ foreach($row as $v)
+ $resultObject[] = $v;
+ else
+ foreach($row as $k => $v)
+ $resultObject[$k] = $v;
+ return $resultObject;
+ }
+
+ /**
+ * Apply the result to an object.
+ * @param array a result set row retrieved from the database
+ * @param object result object, array or list
+ * @return object result filled with data.
+ */
+ protected function fillResultObjectProperty($row, $resultObject)
+ {
+ $index = 0;
+ $registry=$this->getManager()->getTypeHandlers();
+ foreach($row as $k=>$v)
+ {
+ $property = new TResultProperty;
+ if(is_string($k) && strlen($k) > 0)
+ $property->setColumn($k);
+ $property->setColumnIndex(++$index);
+ $type = gettype(TPropertyAccess::get($resultObject,$k));
+ $property->setType($type);
+ $value = $property->getPropertyValue($registry,$row);
+ TPropertyAccess::set($resultObject, $k,$value);
+ }
+ return $resultObject;
+ }
+
+ /**
+ * Fills the result object according to result mappings.
+ * @param string result map name.
+ * @param array a result set row retrieved from the database
+ * @param object result object to fill, will create new instances if required.
+ * @return object result object filled with data.
+ */
+ protected function fillResultMap($resultMapName, $row, $parentGroup=null, &$resultObject=null)
+ {
+ $resultMap = $this->getManager()->getResultMap($resultMapName);
+ $registry = $this->getManager()->getTypeHandlers();
+ $resultMap = $resultMap->resolveSubMap($registry,$row);
+
+ if(is_null($resultObject))
+ $resultObject = $resultMap->createInstanceOfResult($registry);
+
+ if(is_object($resultObject))
+ {
+ if(strlen($resultMap->getGroupBy()) > 0)
+ return $this->addResultMapGroupBy($resultMap, $row, $parentGroup, $resultObject);
+ else
+ foreach($resultMap->getColumns() as $property)
+ $this->setObjectProperty($resultMap, $property, $row, $resultObject);
+ }
+ else
+ {
+ $resultObject = $this->fillDefaultResultMap($resultMap, $row, $resultObject);
+ }
+ return $resultObject;
+ }
+
+ /**
+ * ResultMap with GroupBy property. Save object collection graph in a tree
+ * and collect the result later.
+ * @param TResultMap result mapping details.
+ * @param array a result set row retrieved from the database
+ * @param object the result object
+ * @return object result object.
+ */
+ protected function addResultMapGroupBy($resultMap, $row, $parent, &$resultObject)
+ {
+ $group = $this->getResultMapGroupKey($resultMap, $row);
+
+ if(empty($parent))
+ {
+ $rootObject = array('object'=>$resultObject, 'property' => null);
+ $this->_groupBy->add(null, $group, $rootObject);
+ }
+
+ foreach($resultMap->getColumns() as $property)
+ {
+ //set properties.
+ $this->setObjectProperty($resultMap, $property, $row, $resultObject);
+ $nested = $property->getResultMapping();
+
+ //nested property
+ if($this->getManager()->getResultMaps()->contains($nested))
+ {
+ $nestedMap = $this->getManager()->getResultMap($nested);
+ $groupKey = $this->getResultMapGroupKey($nestedMap, $row);
+
+ //add the node reference first
+ if(empty($parent))
+ $this->_groupBy->add($group, $groupKey, '');
+
+ //get the nested result mapping value
+ $value = $this->fillResultMap($nested, $row, $groupKey);
+
+ //add it to the object tree graph
+ $groupObject = array('object'=>$value, 'property' => $property->getProperty());
+ if(empty($parent))
+ $this->_groupBy->add($group, $groupKey, $groupObject);
+ else
+ $this->_groupBy->add($parent, $groupKey, $groupObject);
+ }
+ }
+ return $resultObject;
+ }
+
+ /**
+ * Gets the result 'group by' groupping key for each row.
+ * @param TResultMap result mapping details.
+ * @param array a result set row retrieved from the database
+ * @return string groupping key.
+ */
+ protected function getResultMapGroupKey($resultMap, $row)
+ {
+ $groupBy = $resultMap->getGroupBy();
+ if(isset($row[$groupBy]))
+ return $resultMap->getID().$row[$groupBy];
+ else
+ return $resultMap->getID().crc32(serialize($row));
+ }
+
+ /**
+ * Fill the result map using default settings. If <tt>$resultMap</tt> is null
+ * the result object returned will be guessed from <tt>$resultObject</tt>.
+ * @param TResultMap result mapping details.
+ * @param array a result set row retrieved from the database
+ * @param object the result object
+ * @return mixed the result object filled with data.
+ */
+ protected function fillDefaultResultMap($resultMap, $row, $resultObject)
+ {
+ if(is_null($resultObject))
+ $resultObject='';
+
+ if(!is_null($resultMap))
+ $result = $this->fillArrayResultMap($resultMap, $row, $resultObject);
+ else
+ $result = $row;
+
+ //if scalar result types
+ if(count($result) == 1 && ($type = gettype($resultObject))!= 'array')
+ return $this->getScalarResult($result, $type);
+ else
+ return $result;
+ }
+
+ /**
+ * Retrieve the result map as an array.
+ * @param TResultMap result mapping details.
+ * @param array a result set row retrieved from the database
+ * @param object the result object
+ * @return array array list of result objects.
+ */
+ protected function fillArrayResultMap($resultMap, $row, $resultObject)
+ {
+ $result = array();
+ $registry=$this->getManager()->getTypeHandlers();
+ foreach($resultMap->getColumns() as $column)
+ {
+ if(is_null($column->getType())
+ && !is_null($resultObject) && !is_object($resultObject))
+ $column->setType(gettype($resultObject));
+ $result[$column->getProperty()] = $column->getPropertyValue($registry,$row);
+ }
+ return $result;
+ }
+
+ /**
+ * Converts the first array value to scalar value of given type.
+ * @param array list of results
+ * @param string scalar type.
+ * @return mixed scalar value.
+ */
+ protected function getScalarResult($result, $type)
+ {
+ $scalar = array_shift($result);
+ settype($scalar, $type);
+ return $scalar;
+ }
+
+ /**
+ * Set a property of the result object with appropriate value.
+ * @param TResultMap result mapping details.
+ * @param TResultProperty the result property to fill.
+ * @param array a result set row retrieved from the database
+ * @param object the result object
+ */
+ protected function setObjectProperty($resultMap, $property, $row, &$resultObject)
+ {
+ $select = $property->getSelect();
+ $key = $property->getProperty();
+ $nested = $property->getNestedResultMap();
+ $registry=$this->getManager()->getTypeHandlers();
+ if($key === '')
+ {
+ $resultObject = $property->getPropertyValue($registry,$row);
+ }
+ else if(strlen($select) == 0 && is_null($nested))
+ {
+ $value = $property->getPropertyValue($registry,$row);
+
+ $this->_IsRowDataFound = $this->_IsRowDataFound || ($value != null);
+ if(is_array($resultObject) || is_object($resultObject))
+ TPropertyAccess::set($resultObject, $key, $value);
+ else
+ $resultObject = $value;
+ }
+ else if(!is_null($nested))
+ {
+ if($property->instanceOfListType($resultObject) || $property->instanceOfArrayType($resultObject))
+ {
+ if(strlen($resultMap->getGroupBy()) <= 0)
+ throw new TSqlMapExecutionException(
+ 'sqlmap_non_groupby_array_list_type', $resultMap->getID(),
+ get_class($resultObject), $key);
+ }
+ else
+ {
+ $obj = $nested->createInstanceOfResult($this->getManager()->getTypeHandlers());
+ if($this->fillPropertyWithResultMap($nested, $row, $obj) == false)
+ $obj = null;
+ TPropertyAccess::set($resultObject, $key, $obj);
+ }
+ }
+ else //'select' ResultProperty
+ {
+ $this->enquequePostSelect($select, $resultMap, $property, $row, $resultObject);
+ }
+ }
+
+ /**
+ * Add nested result property to post select queue.
+ * @param string post select statement ID
+ * @param TResultMap current result mapping details.
+ * @param TResultProperty current result property.
+ * @param array a result set row retrieved from the database
+ * @param object the result object
+ */
+ protected function enquequePostSelect($select, $resultMap, $property, $row, $resultObject)
+ {
+ $statement = $this->getManager()->getMappedStatement($select);
+ $key = $this->getPostSelectKeys($resultMap, $property, $row);
+ $postSelect = new TPostSelectBinding;
+ $postSelect->setStatement($statement);
+ $postSelect->setResultObject($resultObject);
+ $postSelect->setResultProperty($property);
+ $postSelect->setKeys($key);
+
+ if($property->instanceOfListType($resultObject))
+ {
+ $values = null;
+ if($property->getLazyLoad())
+ {
+ $values = TLazyLoadList::newInstance($statement, $key,
+ $resultObject, $property->getProperty());
+ TPropertyAccess::set($resultObject, $property->getProperty(), $values);
+ }
+ else
+ $postSelect->setMethod(self::QUERY_FOR_LIST);
+ }
+ else if($property->instanceOfArrayType($resultObject))
+ $postSelect->setMethod(self::QUERY_FOR_ARRAY);
+ else
+ $postSelect->setMethod(self::QUERY_FOR_OBJECT);
+
+ if(!$property->getLazyLoad())
+ array_push($this->_selectQueque, $postSelect);
+ }
+
+ /**
+ * Finds in the post select property the SQL statement primary selection keys.
+ * @param TResultMap result mapping details
+ * @param TResultProperty result property
+ * @param array current row data.
+ * @return array list of primary key values.
+ */
+ protected function getPostSelectKeys($resultMap, $property,$row)
+ {
+ $value = $property->getColumn();
+ if(is_int(strpos($value.',',0)) || is_int(strpos($value, '=',0)))
+ {
+ $keys = array();
+ foreach(explode(',', $value) as $entry)
+ {
+ $pair =explode('=',$entry);
+ $keys[trim($pair[0])] = $row[trim($pair[1])];
+ }
+ return $keys;
+ }
+ else
+ {
+ $registry=$this->getManager()->getTypeHandlers();
+ return $property->getPropertyValue($registry,$row);
+ }
+ }
+
+ /**
+ * Fills the property with result mapping results.
+ * @param TResultMap nested result mapping details.
+ * @param array a result set row retrieved from the database
+ * @param object the result object
+ * @return boolean true if the data was found, false otherwise.
+ */
+ protected function fillPropertyWithResultMap($resultMap, $row, &$resultObject)
+ {
+ $dataFound = false;
+ foreach($resultMap->getColumns() as $property)
+ {
+ $this->_IsRowDataFound = false;
+ $this->setObjectProperty($resultMap, $property, $row, $resultObject);
+ $dataFound = $dataFound || $this->_IsRowDataFound;
+ }
+ $this->_IsRowDataFound = $dataFound;
+ return $dataFound;
+ }
+}
+
+class TPostSelectBinding
+{
+ private $_statement=null;
+ private $_property=null;
+ private $_resultObject=null;
+ private $_keys=null;
+ private $_method=TMappedStatement::QUERY_FOR_LIST;
+
+ public function getStatement(){ return $this->_statement; }
+ public function setStatement($value){ $this->_statement = $value; }
+
+ public function getResultProperty(){ return $this->_property; }
+ public function setResultProperty($value){ $this->_property = $value; }
+
+ public function getResultObject(){ return $this->_resultObject; }
+ public function setResultObject($value){ $this->_resultObject = $value; }
+
+ public function getKeys(){ return $this->_keys; }
+ public function setKeys($value){ $this->_keys = $value; }
+
+ public function getMethod(){ return $this->_method; }
+ public function setMethod($value){ $this->_method = $value; }
+}
+
+/**
+ * TSQLMapObjectCollectionTree class.
+ *
+ * Maps object collection graphs as trees. Nodes in the collection can
+ * be {@link add} using parent relationships. The object collections can be
+ * build using the {@link collect} method.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.DataAccess.SQLMap.Statements
+ * @since 3.1
+ */
+class TSQLMapObjectCollectionTree
+{
+ /**
+ * @var array object graph as tree
+ */
+ private $_tree = array();
+ /**
+ * @var array tree node values
+ */
+ private $_entries = array();
+ /**
+ * @var array resulting object collection
+ */
+ private $_list = array();
+
+ /**
+ * @return boolean true if the graph is empty
+ */
+ public function isEmpty()
+ {
+ return count($this->_entries) == 0;
+ }
+
+ /**
+ * Add a new node to the object tree graph.
+ * @param string parent node id
+ * @param string new node id
+ * @param mixed node value
+ */
+ public function add($parent, $node, $object='')
+ {
+ if(isset($this->_entries[$parent]) && !is_null($this->_entries[$parent])
+ && isset($this->_entries[$node]) && !is_null($this->_entries[$node]))
+ {
+ $this->_entries[$node] = $object;
+ return;
+ }
+ $this->_entries[$node] = $object;
+ if(empty($parent))
+ {
+ if(isset($this->_entries[$node]))
+ return;
+ $this->_tree[$node] = array();
+ }
+ $found = $this->addNode($this->_tree, $parent, $node);
+ if(!$found && !empty($parent))
+ {
+ $this->_tree[$parent] = array();
+ if(!isset($this->_entries[$parent]) || $object !== '')
+ $this->_entries[$parent] = $object;
+ $this->addNode($this->_tree, $parent, $node);
+ }
+ }
+
+ /**
+ * Find the parent node and add the new node as its child.
+ * @param array list of nodes to check
+ * @param string parent node id
+ * @param string new node id
+ * @return boolean true if parent node is found.
+ */
+ protected function addNode(&$childs, $parent, $node)
+ {
+ $found = false;
+ reset($childs);
+ for($i = 0, $k = count($childs); $i < $k; $i++)
+ {
+ $key = key($childs);
+ next($childs);
+ if($key == $parent)
+ {
+ $found = true;
+ $childs[$key][$node] = array();
+ }
+ else
+ {
+ $found = $found || $this->addNode($childs[$key], $parent, $node);
+ }
+ }
+ return $found;
+ }
+
+ /**
+ * @return array object collection
+ */
+ public function collect()
+ {
+ while(count($this->_tree) > 0)
+ $this->collectChildren(null, $this->_tree);
+ return $this->getCollection();
+ }
+
+ /**
+ * @param array list of nodes to check
+ * @return boolean true if all nodes are leaf nodes, false otherwise
+ */
+ protected function hasChildren(&$nodes)
+ {
+ $hasChildren = false;
+ foreach($nodes as $node)
+ if(count($node) != 0)
+ return true;
+ return $hasChildren;
+ }
+
+ /**
+ * Visit all the child nodes and collect them by removing.
+ * @param string parent node id
+ * @param array list of child nodes.
+ */
+ protected function collectChildren($parent, &$nodes)
+ {
+ $noChildren = !$this->hasChildren($nodes);
+ $childs = array();
+ for(reset($nodes); $key = key($nodes);)
+ {
+ next($nodes);
+ if($noChildren)
+ {
+ $childs[] = $key;
+ unset($nodes[$key]);
+ }
+ else
+ $this->collectChildren($key, $nodes[$key]);
+ }
+ if(count($childs) > 0)
+ $this->onChildNodesVisited($parent, $childs);
+ }
+
+ /**
+ * Set the object properties for all the child nodes visited.
+ * @param string parent node id
+ * @param array list of child nodes visited.
+ */
+ protected function onChildNodesVisited($parent, $nodes)
+ {
+ if(empty($parent) || empty($this->_entries[$parent]))
+ return;
+
+ $parentObject = $this->_entries[$parent]['object'];
+ $property = $this->_entries[$nodes[0]]['property'];
+
+ $list = TPropertyAccess::get($parentObject, $property);
+
+ foreach($nodes as $node)
+ {
+ if($list instanceof TList)
+ $parentObject->{$property}[] = $this->_entries[$node]['object'];
+ else if(is_array($list))
+ $list[] = $this->_entries[$node]['object'];
+ else
+ throw TSqlMapExecutionException(
+ 'sqlmap_property_must_be_list');
+ }
+
+ if(is_array($list))
+ TPropertyAccess::set($parentObject, $property, $list);
+
+ if($this->_entries[$parent]['property'] === null)
+ $this->_list[] = $parentObject;
+ }
+
+ /**
+ * @return array object collection.
+ */
+ protected function getCollection()
+ {
+ return $this->_list;
+ }
+}
+
+class TResultSetListItemParameter extends TComponent
+{
+ private $_resultObject;
+ private $_parameterObject;
+ private $_list;
+
+ public function __construct($result, $parameter, &$list)
+ {
+ $this->_resultObject = $result;
+ $this->_parameterObject = $parameter;
+ $this->_list = &$list;
+ }
+
+ public function getResult()
+ {
+ return $this->_resultObject;
+ }
+
+ public function getParameter()
+ {
+ return $this->_parameterObject;
+ }
+
+ public function &getList()
+ {
+ return $this->_list;
+ }
+}
+
+class TResultSetMapItemParameter extends TComponent
+{
+ private $_key;
+ private $_value;
+ private $_parameterObject;
+ private $_map;
+
+ public function __construct($key, $value, $parameter, &$map)
+ {
+ $this->_key = $key;
+ $this->_value = $value;
+ $this->_parameterObject = $parameter;
+ $this->_map = &$map;
+ }
+
+ public function getKey()
+ {
+ return $this->_key;
+ }
+
+ public function getValue()
+ {
+ return $this->_value;
+ }
+
+ public function getParameter()
+ {
+ return $this->_parameterObject;
+ }
+
+ public function &getMap()
+ {
+ return $this->_map;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/Statements/TPreparedCommand.php b/framework/Data/SqlMap/Statements/TPreparedCommand.php
new file mode 100644
index 00000000..5a8041ae
--- /dev/null
+++ b/framework/Data/SqlMap/Statements/TPreparedCommand.php
@@ -0,0 +1,29 @@
+<?php
+
+class TPreparedCommand
+{
+
+ public function create(TSqlMapManager $manager, $connection, $statement, $parameterObject)
+ {
+ $prepared = $statement->getSQLText()->getPreparedStatement($parameterObject);
+ $connection->setActive(true);
+ $command = $connection->createCommand($prepared->getPreparedSql());
+ $this->applyParameterMap($manager, $command, $prepared, $statement, $parameterObject);
+ return $command;
+ }
+
+ protected function applyParameterMap($manager,$command,$prepared, $statement, $parameterObject)
+ {
+ $properties = $prepared->getParameterNames();
+ $parameters = $prepared->getParameterValues();
+ $registry=$manager->getTypeHandlers();
+ for($i = 0, $k=$properties->getCount(); $i<$k; $i++)
+ {
+ $property = $statement->parameterMap()->getProperty($i);
+ $value = $statement->parameterMap()->getPropertyValue($registry,$property, $parameterObject);
+ $command->bindValue($i+1,$value);
+ }
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/Statements/TPreparedStatement.php b/framework/Data/SqlMap/Statements/TPreparedStatement.php
new file mode 100644
index 00000000..e534f532
--- /dev/null
+++ b/framework/Data/SqlMap/Statements/TPreparedStatement.php
@@ -0,0 +1,26 @@
+<?php
+
+class TPreparedStatement extends TComponent
+{
+ private $_sqlString='';
+ private $_parameterNames;
+ private $_parameterValues;
+
+ public function __construct()
+ {
+ $this->_parameterNames=new TList;
+ $this->_parameterValues=new TMap;
+ }
+
+ public function getPreparedSql(){ return $this->_sqlString; }
+ public function setPreparedSql($value){ $this->_sqlString = $value; }
+
+ public function getParameterNames(){ return $this->_parameterNames; }
+ public function setParameterNames($value){ $this->_parameterNames = $value; }
+
+ public function getParameterValues(){ return $this->_parameterValues; }
+ public function setParameterValues($value){ $this->_parameterValues = $value; }
+
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/Statements/TPreparedStatementFactory.php b/framework/Data/SqlMap/Statements/TPreparedStatementFactory.php
new file mode 100644
index 00000000..52461983
--- /dev/null
+++ b/framework/Data/SqlMap/Statements/TPreparedStatementFactory.php
@@ -0,0 +1,32 @@
+<?php
+
+class TPreparedStatementFactory
+{
+ private $_statement;
+ private $_preparedStatement;
+ private $_parameterPrefix = 'param';
+ private $_commandText;
+
+ public function __construct($statement, $sqlString)
+ {
+ $this->_statement = $statement;
+ $this->_commandText = $sqlString;
+ }
+
+ public function prepare()
+ {
+ $this->_preparedStatement = new TPreparedStatement();
+ $this->_preparedStatement->setPreparedSql($this->_commandText);
+ if(!is_null($this->_statement->parameterMap()))
+ $this->createParametersForTextCommand();
+ return $this->_preparedStatement;
+ }
+
+ protected function createParametersForTextCommand()
+ {
+ foreach($this->_statement->ParameterMap()->getProperties() as $prop)
+ $this->_preparedStatement->getParameterNames()->add($prop->getProperty());
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/Statements/TSelectMappedStatement.php b/framework/Data/SqlMap/Statements/TSelectMappedStatement.php
new file mode 100644
index 00000000..b6f179c0
--- /dev/null
+++ b/framework/Data/SqlMap/Statements/TSelectMappedStatement.php
@@ -0,0 +1,19 @@
+<?php
+
+class TSelectMappedStatement extends TMappedStatement
+{
+ public function executeInsert($connection, $parameter)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_cannot_execute_insert', get_class($this), $this->getID());
+ }
+
+ public function executeUpdate($connection, $parameter)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_cannot_execute_update', get_class($this), $this->getID());
+ }
+
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/Statements/TSimpleDynamicSql.php b/framework/Data/SqlMap/Statements/TSimpleDynamicSql.php
new file mode 100644
index 00000000..1230b4f7
--- /dev/null
+++ b/framework/Data/SqlMap/Statements/TSimpleDynamicSql.php
@@ -0,0 +1,32 @@
+<?php
+
+class TSimpleDynamicSql extends TStaticSql
+{
+ private $_mappings=array();
+
+ public function __construct($mappings)
+ {
+ $this->_mappings = $mappings;
+ }
+
+ public function getPreparedStatement($parameter=null)
+ {
+ $statement = parent::getPreparedStatement($parameter);
+ if($parameter !== null)
+ $this->mapDynamicParameter($statement, $parameter);
+ return $statement;
+ }
+
+ protected function mapDynamicParameter($statement, $parameter)
+ {
+ $sql = $statement->getPreparedSql();
+ foreach($this->_mappings as $property)
+ {
+ $value = TPropertyAccess::get($parameter, $property);
+ $sql = preg_replace('/'.TSimpleDynamicParser::DYNAMIC_TOKEN.'/', $value, $sql, 1);
+ }
+ $statement->setPreparedSql($sql);
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/Statements/TSqlMapSelect.php b/framework/Data/SqlMap/Statements/TSqlMapSelect.php
new file mode 100644
index 00000000..b33bc27e
--- /dev/null
+++ b/framework/Data/SqlMap/Statements/TSqlMapSelect.php
@@ -0,0 +1,11 @@
+<?php
+
+class TSqlMapSelect extends TSqlMapStatement
+{
+ private $_generate;
+
+ public function getGenerate(){ return $this->_generate; }
+ public function setGenerate($value){ $this->_generate = $value; }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/Statements/TStaticSql.php b/framework/Data/SqlMap/Statements/TStaticSql.php
new file mode 100644
index 00000000..b99b86ed
--- /dev/null
+++ b/framework/Data/SqlMap/Statements/TStaticSql.php
@@ -0,0 +1,19 @@
+<?php
+
+class TStaticSql extends TComponent
+{
+ private $_preparedStatement;
+
+ public function buildPreparedStatement($statement, $sqlString)
+ {
+ $factory = new TPreparedStatementFactory($statement, $sqlString);
+ $this->_preparedStatement = $factory->prepare();
+ }
+
+ public function getPreparedStatement($parameter=null)
+ {
+ return $this->_preparedStatement;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/Statements/TUpdateMappedStatement.php b/framework/Data/SqlMap/Statements/TUpdateMappedStatement.php
new file mode 100644
index 00000000..46d27778
--- /dev/null
+++ b/framework/Data/SqlMap/Statements/TUpdateMappedStatement.php
@@ -0,0 +1,32 @@
+<?php
+
+class TUpdateMappedStatement extends TMappedStatement
+{
+ public function executeInsert($connection, $parameter)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_cannot_execute_insert', get_class($this), $this->getID());
+ }
+
+ public function executeQueryForMap($connection, $parameter, $keyProperty,
+ $valueProperty=null)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_cannot_execute_query_for_map', get_class($this), $this->getID());
+ }
+
+ public function executeQueryForList($connection, $parameter, $result=null,
+ $skip=-1, $max=-1)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_cannot_execute_query_for_list', get_class($this), $this->getID());
+ }
+
+ public function executeQueryForObject($connection, $parameter, $result=null)
+ {
+ throw new TSqlMapExecutionException(
+ 'sqlmap_cannot_execute_query_for_object', get_class($this), $this->getID());
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/TSqlMapGateway.php b/framework/Data/SqlMap/TSqlMapGateway.php
new file mode 100644
index 00000000..799db446
--- /dev/null
+++ b/framework/Data/SqlMap/TSqlMapGateway.php
@@ -0,0 +1,268 @@
+<?php
+/**
+ * TSqlMapGateway class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap
+ */
+
+Prado::using('System.Data.SqlMap.TSqlMapManager');
+
+/**
+ * DataMapper client, a fascade to provide access the rest of the DataMapper
+ * framework. It provides three core functions:
+ *
+ * # execute an update query (including insert and delete).
+ * # execute a select query for a single object
+ * # execute a select query for a list of objects
+ *
+ * This class should be instantiated from a TSqlMapManager instance.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap
+ * @since 3.1
+ */
+class TSqlMapGateway extends TComponent
+{
+ /**
+ * @var TSqlMapManager manager
+ */
+ private $_manager;
+
+ public function __construct($manager)
+ {
+ $this->_manager=$manager;
+ }
+
+ /**
+ * @return TSqlMapManager sqlmap manager.
+ */
+ public function getSqlMapManager()
+ {
+ return $this->_manager;
+ }
+
+ /**
+ * @return TDbConnection database connection.
+ */
+ public function getDbConnection()
+ {
+ return $this->getSqlMapManager()->getDbConnection();
+ }
+
+ /**
+ * @param TDbConnection new database connection.
+ */
+ public function setDbConnection($conn)
+ {
+ $this->getSqlMapManager()->setDbConnection($conn);
+ }
+
+ /**
+ * Executes a Sql SELECT statement that returns that returns data
+ * to populate a single object instance.
+ *
+ * The parameter object is generally used to supply the input
+ * data for the WHERE clause parameter(s) of the SELECT statement.
+ *
+ * @param string The name of the sql statement to execute.
+ * @param mixed The object used to set the parameters in the SQL.
+ * @param mixed An object of the type to be returned.
+ * @return object A single result object populated with the result set data.
+ */
+ public function queryForObject($statementName, $parameter=null, $result=null)
+ {
+ $statement = $this->getSqlMapManager()->getMappedStatement($statementName);
+ return $statement->executeQueryForObject($this->getDbConnection(), $parameter, $result);
+ }
+
+ /**
+ * Executes a Sql SELECT statement that returns data to populate a number
+ * of result objects.
+ *
+ * The parameter object is generally used to supply the input
+ * data for the WHERE clause parameter(s) of the SELECT statement.
+ *
+ * @param string The name of the sql statement to execute.
+ * @param mixed The object used to set the parameters in the SQL.
+ * @param TList An Ilist object used to hold the objects,
+ * pass in null if want to return a list instead.
+ * @param int The number of rows to skip over.
+ * @param int The maximum number of rows to return.
+ * @return TList A List of result objects.
+ */
+ public function queryForList($statementName, $parameter=null, $result=null, $skip=-1, $max=-1)
+ {
+ $statement = $this->getSqlMapManager()->getMappedStatement($statementName);
+ return $statement->executeQueryForList($this->getDbConnection(),$parameter, $result, $skip, $max);
+ }
+
+ /**
+ * Runs a query for list with a custom object that gets a chance to deal
+ * with each row as it is processed.
+ *
+ * Example: $sqlmap->queryWithRowDelegate('getAccounts', array($this, 'rowHandler'));
+ *
+ * @param string The name of the sql statement to execute.
+ * @param callback Row delegate handler, a valid callback required.
+ * @param mixed The object used to set the parameters in the SQL.
+ * @param TList An Ilist object used to hold the objects,
+ * pass in null if want to return a list instead.
+ * @param int The number of rows to skip over.
+ * @param int The maximum number of rows to return.
+ * @return TList A List of result objects.
+ */
+ public function queryWithRowDelegate($statementName, $delegate, $parameter=null, $result=null, $skip=-1, $max=-1)
+ {
+ $statement = $this->getSqlMapManager()->getMappedStatement($statementName);
+ return $statement->executeQueryForList($this->getDbConnection(), $parameter, $result, $skip, $max, $delegate);
+ }
+
+ /**
+ * Executes the SQL and retuns a subset of the results in a dynamic
+ * TPagedList that can be used to automatically scroll through results
+ * from a database table.
+ * @param string The name of the sql statement to execute.
+ * @param mixed The object used to set the parameters in the SQL.
+ * @param integer The maximum number of objects to store in each page.
+ * @return TPagedList A PaginatedList of beans containing the rows.
+ */
+ public function queryForPagedList($statementName, $parameter=null, $pageSize=10)
+ {
+ $statement = $this->getSqlMapManager()->getMappedStatement($statementName);
+ return new TSqlMapPagedList($statement, $parameter, $pageSize);
+ }
+
+ /**
+ * Executes the SQL and retuns a subset of the results in a dynamic
+ * TPagedList that can be used to automatically scroll through results
+ * from a database table.
+ *
+ * Runs paged list query with row delegate
+ * Example: $sqlmap->queryForPagedListWithRowDelegate('getAccounts', array($this, 'rowHandler'));
+ *
+ * @param string The name of the sql statement to execute.
+ * @param callback Row delegate handler, a valid callback required.
+ * @param mixed The object used to set the parameters in the SQL.
+ * @param integer The maximum number of objects to store in each page.
+ * @return TPagedList A PaginatedList of beans containing the rows.
+ */
+ public function queryForPagedListWithRowDelegate($statementName,$delegate, $parameter=null, $pageSize=10)
+ {
+ $statement = $this->getSqlMapManager()->getMappedStatement($statementName);
+ return new TSqlMapPagedList($statement, $parameter, $pageSize, $delegate);
+ }
+
+
+ /**
+ * Executes the SQL and retuns all rows selected in a map that is keyed on
+ * the property named in the keyProperty parameter. The value at each key
+ * will be the value of the property specified in the valueProperty
+ * parameter. If valueProperty is null, the entire result object will be
+ * entered.
+ * @param string The name of the sql statement to execute.
+ * @param mixed The object used to set the parameters in the SQL.
+ * @param string The property of the result object to be used as the key.
+ * @param string The property of the result object to be used as the value.
+ * @return TMap Array object containing the rows keyed by keyProperty.
+ */
+ public function queryForMap($statementName, $parameter=null, $keyProperty=null, $valueProperty=null)
+ {
+ $statement = $this->getSqlMapManager()->getMappedStatement($statementName);
+ return $statement->executeQueryForMap($this->getDbConnection(), $parameter, $keyProperty, $valueProperty);
+ }
+
+ /**
+ * Runs a query with a custom object that gets a chance to deal
+ * with each row as it is processed.
+ *
+ * Example: $sqlmap->queryForMapWithRowDelegate('getAccounts', array($this, 'rowHandler'));
+ *
+ * @param string The name of the sql statement to execute.
+ * @param callback Row delegate handler, a valid callback required.
+ * @param mixed The object used to set the parameters in the SQL.
+ * @param string The property of the result object to be used as the key.
+ * @param string The property of the result object to be used as the value.
+ * @return TMap Array object containing the rows keyed by keyProperty.
+ */
+ public function queryForMapWithRowDelegate($statementName, $delegate, $parameter=null, $keyProperty=null, $valueProperty=null)
+ {
+ $statement = $this->getSqlMapManager()->getMappedStatement($statementName);
+ return $statement->executeQueryForMap($this->getDbConnection(), $parameter, $keyProperty, $valueProperty, $delegate);
+ }
+
+ /**
+ * Executes a Sql INSERT statement.
+ *
+ * Insert is a bit different from other update methods, as it provides
+ * facilities for returning the primary key of the newly inserted row
+ * (rather than the effected rows),
+ *
+ * The parameter object is generally used to supply the input data for the
+ * INSERT values.
+ *
+ * @param string The name of the statement to execute.
+ * @param string The parameter object.
+ * @return mixed The primary key of the newly inserted row.
+ * This might be automatically generated by the RDBMS,
+ * or selected from a sequence table or other source.
+ */
+ public function insert($statementName, $parameter=null)
+ {
+ $statement = $this->getSqlMapManager()->getMappedStatement($statementName);
+ return $statement->executeInsert($this->getDbConnection(), $parameter);
+ }
+
+ /**
+ * Executes a Sql UPDATE statement.
+ *
+ * Update can also be used for any other update statement type, such as
+ * inserts and deletes. Update returns the number of rows effected.
+ *
+ * The parameter object is generally used to supply the input data for the
+ * UPDATE values as well as the WHERE clause parameter(s).
+ *
+ * @param string The name of the statement to execute.
+ * @param mixed The parameter object.
+ * @return integer The number of rows effected.
+ */
+ public function update($statementName, $parameter=null)
+ {
+ $statement = $this->getSqlMapManager()->getMappedStatement($statementName);
+ return $statement->executeUpdate($this->getDbConnection(), $parameter);
+ }
+
+ /**
+ * Executes a Sql DELETE statement. Delete returns the number of rows effected.
+ * @param string The name of the statement to execute.
+ * @param mixed The parameter object.
+ * @return integer The number of rows effected.
+ */
+ public function delete($statementName, $parameter=null)
+ {
+ return $this->update($statementName, $parameter);
+ }
+
+ /**
+ * Flushes all cached objects that belong to this SqlMap
+ */
+ public function flushCaches()
+ {
+ $this->getSqlMapManager()->flushCacheModels();
+ }
+
+ /**
+ * @param TSqlMapTypeHandler new type handler.
+ */
+ public function registerTypeHandler($typeHandler)
+ {
+ $this->getSqlMapManager()->getTypeHandlers()->registerTypeHandler($typeHandler);
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/TSqlMapManager.php b/framework/Data/SqlMap/TSqlMapManager.php
new file mode 100644
index 00000000..32656bd5
--- /dev/null
+++ b/framework/Data/SqlMap/TSqlMapManager.php
@@ -0,0 +1,270 @@
+<?php
+/**
+ * TSqlMapManager class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap
+ */
+
+Prado::using('System.Data.SqlMap.TSqlMapGateway');
+Prado::using('System.Data.SqlMap.DataMapper.TSqlMapException');
+Prado::using('System.Data.SqlMap.DataMapper.TSqlMapTypeHandlerRegistry');
+Prado::using('System.Data.SqlMap.DataMapper.TSqlMapCache');
+Prado::using('System.Data.SqlMap.DataMapper.*');
+Prado::using('System.Data.SqlMap.Configuration.*');
+Prado::using('System.Data.SqlMap.Statements.*');
+
+/**
+ * TSqlMapManager class holds the sqlmap configuation result maps, statements
+ * parameter maps and a type handler factory.
+ *
+ * Use {@link SqlMapGateway getSqlMapGateway()} property to obtain the gateway
+ * instance used for querying statements defined in the SqlMap configuration files.
+ *
+ * <code>
+ * $conn = new TDbConnection($dsn,$dbuser,$dbpass);
+ * $manager = new TSqlMapManager($conn, 'mydb-sqlmap.xml');
+ * $sqlmap = $manager->getSqlMapGateway();
+ * $result = $sqlmap->queryForObject('Products');
+ * </code>
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap
+ * @since 3.1
+ */
+class TSqlMapManager extends TComponent
+{
+ private $_mappedStatements;
+ private $_resultMaps;
+ private $_parameterMaps;
+ private $_typeHandlers;
+ private $_cacheModels;
+
+ private $_connection;
+
+ private $_configFile;
+
+ private $_gateway;
+
+ /**
+ * Constructor, create a new SqlMap manager.
+ * @param TDbConnection database connection
+ * @param string configuration file.
+ */
+ public function __construct($connection=null,$configFile=null)
+ {
+ $this->_connection=$connection;
+ $this->_configFile=$configFile;
+
+ $this->_mappedStatements=new TMap;
+ $this->_resultMaps=new TMap;
+ $this->_parameterMaps=new TMap;
+ $this->_cacheModels=new TMap;
+ }
+
+ /**
+ * @param TDbConnection default database connection
+ */
+ public function setDbConnection($conn)
+ {
+ $this->_connection=$conn;
+ }
+
+ /**
+ * @return TDbConnection default database connection
+ */
+ public function getDbConnection()
+ {
+ return $this->_connection;
+ }
+
+ /**
+ * @return TTypeHandlerFactory The TypeHandlerFactory
+ */
+ public function getTypeHandlers()
+ {
+ if($this->_typeHandlers===null)
+ $this->_typeHandlers= new TSqlMapTypeHandlerRegistry();
+ return $this->_typeHandlers;
+ }
+
+ /**
+ * @param string configuration file.
+ */
+ public function setConfigFile($file)
+ {
+ $this->_configFile=$file;
+ }
+
+ /**
+ * @return string configuration file.
+ */
+ public function getConfigFile()
+ {
+ return $this->_configFile;
+ }
+
+ /**
+ * @return TSqlMapGateway SqlMap gateway.
+ */
+ public function getSqlmapGateway()
+ {
+ if($this->_gateway===null)
+ $this->_gateway=$this->createSqlMapGateway();
+ return $this->_gateway;
+ }
+
+ /**
+ * Configures the current TSqlMapManager using the given xml configuration file
+ * defined in {@link ConfigFile setConfigFile()}.
+ * @return TSqlMapGateway create and configure a new TSqlMapGateway.
+ */
+ protected function createSqlMapGateway()
+ {
+ $config = new TSqlMapXmlConfiguration($this);
+ $config->configure($this->getConfigFile());
+ return new TSqlMapGateway($this);
+ }
+
+ /**
+ * @return TMap mapped statements collection.
+ */
+ public function getMappedStatements()
+ {
+ return $this->_mappedStatements;
+ }
+
+ /**
+ * Gets a MappedStatement by name.
+ * @param string The name of the statement.
+ * @return IMappedStatement The MappedStatement
+ * @throws TSqlMapUndefinedException
+ */
+ public function getMappedStatement($name)
+ {
+ if($this->_mappedStatements->contains($name) == false)
+ throw new TSqlMapUndefinedException('sqlmap_contains_no_statement', $name);
+ return $this->_mappedStatements[$name];
+ }
+
+ /**
+ * Adds a (named) MappedStatement.
+ * @param string The key name
+ * @param IMappedStatement The statement to add
+ * @throws TSqlMapDuplicateException
+ */
+ public function addMappedStatement(IMappedStatement $statement)
+ {
+ $key = $statement->getID();
+ if($this->_mappedStatements->contains($key) == true)
+ throw new TSqlMapDuplicateException('sqlmap_already_contains_statement', $key);
+ $this->_mappedStatements->add($key, $statement);
+ }
+
+ /**
+ * @return TMap result maps collection.
+ */
+ public function getResultMaps()
+ {
+ return $this->_resultMaps;
+ }
+
+ /**
+ * Gets a named result map
+ * @param string result name.
+ * @return TResultMap the result map.
+ * @throws TSqlMapUndefinedException
+ */
+ public function getResultMap($name)
+ {
+ if($this->_resultMaps->contains($name) == false)
+ throw new TSqlMapUndefinedException('sqlmap_contains_no_result_map', $name);
+ return $this->_resultMaps[$name];
+ }
+
+ /**
+ * @param TResultMap add a new result map to this SQLMap
+ * @throws TSqlMapDuplicateException
+ */
+ public function addResultMap(TResultMap $result)
+ {
+ $key = $result->getID();
+ if($this->_resultMaps->contains($key) == true)
+ throw new TSqlMapDuplicateException('sqlmap_already_contains_result_map', $key);
+ $this->_resultMaps->add($key, $result);
+ }
+
+ /**
+ * @return TMap parameter maps collection.
+ */
+ public function getParameterMaps()
+ {
+ return $this->_parameterMaps;
+ }
+
+ /**
+ * @param string parameter map ID name.
+ * @return TParameterMap the parameter with given ID.
+ * @throws TSqlMapUndefinedException
+ */
+ public function getParameterMap($name)
+ {
+ if($this->_parameterMaps->contains($name) == false)
+ throw new TSqlMapUndefinedException('sqlmap_contains_no_parameter_map', $name);
+ return $this->_parameterMaps[$name];
+ }
+
+ /**
+ * @param TParameterMap add a new parameter map to this SQLMap.
+ * @throws TSqlMapDuplicateException
+ */
+ public function addParameterMap(TParameterMap $parameter)
+ {
+ $key = $parameter->getID();
+ if($this->_parameterMaps->contains($key) == true)
+ throw new TSqlMapDuplicateException('sqlmap_already_contains_parameter_map', $key);
+ $this->_parameterMaps->add($key, $parameter);
+ }
+
+ /**
+ * Adds a named cache.
+ * @param TSqlMapCacheModel the cache to add.
+ * @throws TSqlMapConfigurationException
+ */
+ public function addCacheModel(TSqlMapCacheModel $cacheModel)
+ {
+ if($this->_cacheModels->contains($cacheModel->getID()))
+ throw new TSqlMapConfigurationException('sqlmap_cache_model_already_exists', $cacheModel->getID());
+ else
+ $this->_cacheModels->add($cacheModel->getID(), $cacheModel);
+ }
+
+ /**
+ * Gets a cache by name
+ * @param string the name of the cache to get.
+ * @return TSqlMapCacheModel the cache object.
+ * @throws TSqlMapConfigurationException
+ */
+ public function getCacheModel($name)
+ {
+ if(!$this->_cacheModels->contains($name))
+ throw new TSqlMapConfigurationException('sqlmap_unable_to_find_cache_model', $name);
+ return $this->_cacheModels[$name];
+ }
+
+ /**
+ * Flushes all cached objects that belong to this SqlMap
+ */
+ public function flushCacheModels()
+ {
+ foreach($this->_cacheModels as $cache)
+ $cache->flush();
+ }
+}
+
+?> \ No newline at end of file