diff options
33 files changed, 5770 insertions, 0 deletions
| diff --git a/.gitattributes b/.gitattributes index b2dcbe05..99c3a335 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1420,6 +1420,38 @@ framework/Data/ActiveRecord/Vendor/TPgsqlMetaDataInspector.php -text  framework/Data/ActiveRecord/Vendor/TSqliteColumnMetaData.php -text  framework/Data/ActiveRecord/Vendor/TSqliteMetaData.php -text  framework/Data/ActiveRecord/Vendor/TSqliteMetaDataInspector.php -text +framework/Data/SqlMap/Configuration/TDiscriminator.php -text +framework/Data/SqlMap/Configuration/TInlineParameterMapParser.php -text +framework/Data/SqlMap/Configuration/TParameterMap.php -text +framework/Data/SqlMap/Configuration/TParameterProperty.php -text +framework/Data/SqlMap/Configuration/TResultMap.php -text +framework/Data/SqlMap/Configuration/TResultProperty.php -text +framework/Data/SqlMap/Configuration/TSimpleDynamicParser.php -text +framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php -text +framework/Data/SqlMap/Configuration/TSqlMapStatement.php -text +framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php -text +framework/Data/SqlMap/DataMapper/TLazyLoadList.php -text +framework/Data/SqlMap/DataMapper/TPropertyAccess.php -text +framework/Data/SqlMap/DataMapper/TSqlMapCache.php -text +framework/Data/SqlMap/DataMapper/TSqlMapException.php -text +framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php -text +framework/Data/SqlMap/DataMapper/TSqlMapTypeHandlerRegistry.php -text +framework/Data/SqlMap/DataMapper/messages.txt -text +framework/Data/SqlMap/Statements/IMappedStatement.php -text +framework/Data/SqlMap/Statements/TCachingStatement.php -text +framework/Data/SqlMap/Statements/TDeleteMappedStatement.php -text +framework/Data/SqlMap/Statements/TInsertMappedStatement.php -text +framework/Data/SqlMap/Statements/TMappedStatement.php -text +framework/Data/SqlMap/Statements/TPreparedCommand.php -text +framework/Data/SqlMap/Statements/TPreparedStatement.php -text +framework/Data/SqlMap/Statements/TPreparedStatementFactory.php -text +framework/Data/SqlMap/Statements/TSelectMappedStatement.php -text +framework/Data/SqlMap/Statements/TSimpleDynamicSql.php -text +framework/Data/SqlMap/Statements/TSqlMapSelect.php -text +framework/Data/SqlMap/Statements/TStaticSql.php -text +framework/Data/SqlMap/Statements/TUpdateMappedStatement.php -text +framework/Data/SqlMap/TSqlMapGateway.php -text +framework/Data/SqlMap/TSqlMapManager.php -text  framework/Data/TDbCommand.php -text  framework/Data/TDbConnection.php -text  framework/Data/TDbDataReader.php -text 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 | 
