From 3d3f8d3832921f99daf8ce1953304763c2e76c62 Mon Sep 17 00:00:00 2001 From: wei <> Date: Fri, 14 Apr 2006 06:22:09 +0000 Subject: Importing SQLMap + sample + docs. --- .../SQLMap/Configuration/TConfigDeserialize.php | 171 ++++ .../SQLMap/Configuration/TDiscriminator.php | 86 ++ .../SQLMap/Configuration/TDomSqlMapBuilder.php | 481 ++++++++++ .../Configuration/TInlineParameterMapParser.php | 50 ++ .../SQLMap/Configuration/TParameterMap.php | 95 ++ .../SQLMap/Configuration/TParameterProperty.php | 68 ++ .../DataAccess/SQLMap/Configuration/TResultMap.php | 63 ++ .../SQLMap/Configuration/TResultProperty.php | 164 ++++ .../SQLMap/Configuration/TSqlMapCacheModel.php | 131 +++ .../SQLMap/Configuration/TSqlMapDelete.php | 7 + .../SQLMap/Configuration/TSqlMapInsert.php | 15 + .../SQLMap/Configuration/TSqlMapSelect.php | 11 + .../SQLMap/Configuration/TSqlMapSelectKey.php | 26 + .../SQLMap/Configuration/TSqlMapStatement.php | 109 +++ .../SQLMap/Configuration/TSqlMapUpdate.php | 11 + .../SQLMap/DataMapper/TDataMapperException.php | 65 ++ .../DataAccess/SQLMap/DataMapper/TLazyLoadList.php | 87 ++ .../SQLMap/DataMapper/TPropertyAccess.php | 91 ++ .../DataAccess/SQLMap/DataMapper/TSqlMapCache.php | 165 ++++ .../SQLMap/DataMapper/TSqlMapPagedList.php | 156 ++++ .../SQLMap/DataMapper/TTypeHandlerFactory.php | 134 +++ .../DataAccess/SQLMap/DataMapper/messages.txt | 61 ++ .../SQLMap/Statements/IMappedStatement.php | 71 ++ .../SQLMap/Statements/TCachingStatement.php | 102 +++ .../SQLMap/Statements/TDeleteMappedStatement.php | 7 + .../SQLMap/Statements/TInsertMappedStatement.php | 32 + .../SQLMap/Statements/TMappedStatement.php | 988 +++++++++++++++++++++ .../SQLMap/Statements/TPreparedCommand.php | 60 ++ .../SQLMap/Statements/TPreparedStatement.php | 26 + .../Statements/TPreparedStatementFactory.php | 47 + .../SQLMap/Statements/TSelectMappedStatement.php | 19 + .../DataAccess/SQLMap/Statements/TStaticSql.php | 19 + .../SQLMap/Statements/TUpdateMappedStatement.php | 32 + framework/DataAccess/SQLMap/TMapper.php | 60 ++ framework/DataAccess/SQLMap/TSqlMapClient.php | 81 ++ framework/DataAccess/SQLMap/TSqlMapper.php | 543 +++++++++++ 36 files changed, 4334 insertions(+) create mode 100644 framework/DataAccess/SQLMap/Configuration/TConfigDeserialize.php create mode 100644 framework/DataAccess/SQLMap/Configuration/TDiscriminator.php create mode 100644 framework/DataAccess/SQLMap/Configuration/TDomSqlMapBuilder.php create mode 100644 framework/DataAccess/SQLMap/Configuration/TInlineParameterMapParser.php create mode 100644 framework/DataAccess/SQLMap/Configuration/TParameterMap.php create mode 100644 framework/DataAccess/SQLMap/Configuration/TParameterProperty.php create mode 100644 framework/DataAccess/SQLMap/Configuration/TResultMap.php create mode 100644 framework/DataAccess/SQLMap/Configuration/TResultProperty.php create mode 100644 framework/DataAccess/SQLMap/Configuration/TSqlMapCacheModel.php create mode 100644 framework/DataAccess/SQLMap/Configuration/TSqlMapDelete.php create mode 100644 framework/DataAccess/SQLMap/Configuration/TSqlMapInsert.php create mode 100644 framework/DataAccess/SQLMap/Configuration/TSqlMapSelect.php create mode 100644 framework/DataAccess/SQLMap/Configuration/TSqlMapSelectKey.php create mode 100644 framework/DataAccess/SQLMap/Configuration/TSqlMapStatement.php create mode 100644 framework/DataAccess/SQLMap/Configuration/TSqlMapUpdate.php create mode 100644 framework/DataAccess/SQLMap/DataMapper/TDataMapperException.php create mode 100644 framework/DataAccess/SQLMap/DataMapper/TLazyLoadList.php create mode 100644 framework/DataAccess/SQLMap/DataMapper/TPropertyAccess.php create mode 100644 framework/DataAccess/SQLMap/DataMapper/TSqlMapCache.php create mode 100644 framework/DataAccess/SQLMap/DataMapper/TSqlMapPagedList.php create mode 100644 framework/DataAccess/SQLMap/DataMapper/TTypeHandlerFactory.php create mode 100644 framework/DataAccess/SQLMap/DataMapper/messages.txt create mode 100644 framework/DataAccess/SQLMap/Statements/IMappedStatement.php create mode 100644 framework/DataAccess/SQLMap/Statements/TCachingStatement.php create mode 100644 framework/DataAccess/SQLMap/Statements/TDeleteMappedStatement.php create mode 100644 framework/DataAccess/SQLMap/Statements/TInsertMappedStatement.php create mode 100644 framework/DataAccess/SQLMap/Statements/TMappedStatement.php create mode 100644 framework/DataAccess/SQLMap/Statements/TPreparedCommand.php create mode 100644 framework/DataAccess/SQLMap/Statements/TPreparedStatement.php create mode 100644 framework/DataAccess/SQLMap/Statements/TPreparedStatementFactory.php create mode 100644 framework/DataAccess/SQLMap/Statements/TSelectMappedStatement.php create mode 100644 framework/DataAccess/SQLMap/Statements/TStaticSql.php create mode 100644 framework/DataAccess/SQLMap/Statements/TUpdateMappedStatement.php create mode 100644 framework/DataAccess/SQLMap/TMapper.php create mode 100644 framework/DataAccess/SQLMap/TSqlMapClient.php create mode 100644 framework/DataAccess/SQLMap/TSqlMapper.php (limited to 'framework/DataAccess/SQLMap') diff --git a/framework/DataAccess/SQLMap/Configuration/TConfigDeserialize.php b/framework/DataAccess/SQLMap/Configuration/TConfigDeserialize.php new file mode 100644 index 00000000..250133b9 --- /dev/null +++ b/framework/DataAccess/SQLMap/Configuration/TConfigDeserialize.php @@ -0,0 +1,171 @@ +_properties = $properties; + } + + public function loadConfiguration($object, $node, $file) + { + foreach($node->attributes() as $k=>$v) + { + if($object->canSetProperty($k)) + $object->{'set'.$k}($this->replaceProperties((string)($v))); + else + throw new TUndefinedAttributeException($k,$node,$object,$file); + } + } + + public function replaceProperties($string) + { + foreach($this->_properties as $find => $replace) + $string = str_replace('${'.$find.'}', $replace, $string); + return $string; + } + + public function parameterMap($node, $sqlMap, $file) + { + $parameterMap = new TParameterMap; + $this->loadConfiguration($parameterMap, $node, $file); + foreach($node->parameter as $parameter) + { + $property = $this->parameterProperty($parameter, $sqlMap, $file); + $property->initialize($sqlMap); + $parameterMap->addParameterProperty($property); + } + return $parameterMap; + } + + public function parameterProperty($node, $sqlMap, $file) + { + $property = new TParameterProperty; + $this->loadConfiguration($property, $node, $file); + return $property; + } + + public function select($node, $sqlMap, $file) + { + $select = new TSqlMapSelect; + $this->loadConfiguration($select, $node, $file); + $select->initialize($sqlMap); + return $select; + } + + public function update($node, $sqlMap, $file) + { + $update = new TSqlMapUpdate; + $this->loadConfiguration($update, $node, $file); + $update->initialize($sqlMap); + return $update; + } + + public function delete($node, $sqlMap, $file) + { + $delete = new TSqlMapDelete; + $this->loadConfiguration($delete, $node, $file); + $delete->initialize($sqlMap); + return $delete; + } + + + public function insert($node, $sqlMap, $file) + { + $insert = new TSqlMapInsert; + $this->loadConfiguration($insert, $node, $file); + if(isset($node->selectKey)) + { + $selectKey = new TSqlMapSelectKey; + $this->loadConfiguration($selectKey, $node->selectKey, $file); + $type = $selectKey->getType(); + $selectKey->setType(strtolower($type) == 'post' ? 'post' : 'pre'); + $insert->setSelectKey($selectKey); + } + if(isset($node->generate)) + { + var_dump("add generate"); + } + + $insert->initialize($sqlMap); + return $insert; + } + + public function statement($node, $sqlMap, $file) + { + $statement = new TSqlMapStatement; + $this->loadConfiguration($statement, $node, $file); + $statement->initialize($sqlMap); + return $statement; + } + + public function resultMap($node, $sqlMap, $file) + { + $resultMap = new TResultMap; + $this->loadConfiguration($resultMap, $node, $file); + foreach($node->result as $result) + { + $property = $this->resultProperty($result, $sqlMap, $file); + $property->initialize($sqlMap, $resultMap); + $resultMap->addResultProperty($property); + } + $discriminator = null; + if(isset($node->discriminator)) + { + $discriminator = new TDiscriminator; + $this->loadConfiguration($discriminator, $node->discriminator, $file); + $discriminator->initMapping($sqlMap, $resultMap); + } + foreach($node->subMap as $subMapNode) + { + if(is_null($discriminator)) + throw new TSqlMapConfigurationException( + 'sqlmap_undefined_discriminator', $resultMap->getID(), $file); + $subMap = new TSubMap; + $this->loadConfiguration($subMap, $subMapNode, $file); + $discriminator->add($subMap); + } + if(!is_null($discriminator)) + $resultMap->setDiscriminator($discriminator); + + return $resultMap; + } + + public function resultProperty($node, $sqlMap, $file) + { + $resultProperty = new TResultProperty; + $this->loadConfiguration($resultProperty, $node, $file); + return $resultProperty; + } + + public function cacheModel($node, $sqlMap, $file) + { + $cacheModel = new TSqlMapCacheModel; + $this->loadConfiguration($cacheModel, $node, $file); + if(isset($node->flushInterval)) + { + $interval = $node->flushInterval; + $span = 0; //span in seconds + if(isset($interval['hours'])) + $span += intval($interval['hours'])*60*60; + if(isset($interval['minutes'])) + $span += intval($interval['minutes'])*60; + if(isset($interval['seconds'])) + $span += intval($interval['seconds']); + if($span > 0) + $cacheModel->setFlushInterval($span); + } + if(isset($node->property)) + { + foreach($node->property as $property) + $cacheModel->addProperty((string)$property['name'], + (string)$property['value']); + } + return $cacheModel; + } +} + + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Configuration/TDiscriminator.php b/framework/DataAccess/SQLMap/Configuration/TDiscriminator.php new file mode 100644 index 00000000..0ee34e3e --- /dev/null +++ b/framework/DataAccess/SQLMap/Configuration/TDiscriminator.php @@ -0,0 +1,86 @@ +_column; } + public function setColumn($value){ $this->_column = $value; } + + public function getType(){ return $this->_type; } + public function setType($value){ $this->_type = $value; } + + public function getTypeHandler(){ return $this->_typeHandler; } + public function setTypeHandler($value){ $this->_typeHandler = $value; } + + public function getDbType(){ return $this->_dbType; } + public function setDbType($value){ $this->_dbType = $value; } + + public function getColumnIndex(){ return $this->_columnIndex; } + public function setColumnIndex($value){ $this->_columnIndex = $value; } + + public function getNullValue(){ return $this->_nullValue; } + public function setNullValue($value){ $this->_nullValue = $value; } + + public function getMapping(){ return $this->_mapping; } + + public function getResultMaps(){ return $this->_resultMaps; } + public function setResultMaps($value){ $this->_resultMaps = $value; } + + public function add($subMap) + { + $this->_subMaps[] = $subMap; + } + + public function getSubMap($value) + { + if(isset($this->_resultMaps[$value])) + return $this->_resultMaps[$value]; + else + return null; + } + + public function initMapping($sqlMap, $resultMap) + { + $this->_mapping = new TResultProperty; + $this->_mapping->setColumn($this->getColumn()); + $this->_mapping->setColumnIndex($this->getColumnIndex()); + $this->_mapping->setType($this->getType()); + $this->_mapping->setTypeHandler($this->getTypeHandler()); + $this->_mapping->setDbType($this->getDbType()); + $this->_mapping->setNullValue($this->getNullValue()); + $this->_mapping->initialize($sqlMap, $resultMap); + } + + public function initialize($sqlMap) + { + foreach($this->_subMaps as $subMap) + { + $this->_resultMaps[$subMap->getValue()] = + $sqlMap->getResultMap($subMap->getResultMapping()); + } + } +} + + +class TSubMap extends TComponent +{ + private $_value=''; + private $_resultMapping=''; + + public function getValue(){ return $this->_value; } + public function setValue($value){ $this->_value = $value; } + + public function getResultMapping(){ return $this->_resultMapping; } + public function setResultMapping($value){ $this->_resultMapping = $value; } +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Configuration/TDomSqlMapBuilder.php b/framework/DataAccess/SQLMap/Configuration/TDomSqlMapBuilder.php new file mode 100644 index 00000000..d3059dd7 --- /dev/null +++ b/framework/DataAccess/SQLMap/Configuration/TDomSqlMapBuilder.php @@ -0,0 +1,481 @@ +_properties = new TMap; + $this->_deserialize = new TConfigDeserialize($this->_properties); + } + + public function configure($resource=null) + { + if($resource instanceof SimpleXMLElement) + return $this->build($resource); + + if(!is_string($resource)) + $resource = self::DEFAULT_CONFIG_FILE; + + $this->_configFile = $resource; + if(!is_file($resource)) + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_config', $resource); + + return $this->build($this->getConfigAsXmlDocument($resource)); + } + + protected function getConfigAsXmlDocument($file) + { + return simplexml_load_file($file); + } + + public function build(SimpleXMLElement $document) + { + $this->_document = $document; + $this->initialize($document); + return $this->_sqlMapper; + } + + protected function initialize($document) + { + $this->_sqlMapper = new TSqlMapper(new TTypeHandlerFactory); + + if(isset($document->properties)) + { + $this->loadGlobalProperties($document->properties); + } + + if(isset($document->settings) && isset($document->settings->setting)) + $this->configureSettings($document->settings); + + //load database provider + if(isset($document->provider) && isset($document->provider->datasource)) + { + $this->loadProvider($document->provider, + $document->provider->datasource, $document, $this->_configFile); + } + else + { + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_db_config', $this->_configFile); + } + + if(isset($document->sqlMaps) && isset($document->sqlMaps->sqlMap)) + $this->loadSqlMappingFiles($document->sqlMaps); + + if($this->_sqlMapper->getIsCacheModelsEnabled()) + $this->attachCacheModel(); + } + + protected function configureSettings($node) + { + foreach($node->setting as $setting) + { + if(isset($setting['useStatementNamespaces'])) + { + $this->_useNamespaces = + TPropertyValue::ensureBoolean( + (string)$setting['useStatementNamespaces']); + } + + if(isset($setting['cacheModelsEnabled'])) + { + $this->_sqlMapper->setCacheModelsEnabled( + TPropertyValue::ensureBoolean( + (string)$setting['cacheModelsEnabled'])); + } + } + } + + /** + * Attach CacheModel to statement and register trigger statements for + * cache models + */ + protected function attachCacheModel() + { + foreach($this->_sqlMapper->getStatements() as $mappedStatement) + { + if(strlen($model = $mappedStatement->getStatement()->getCacheModel()) > 0) + { + $cache = $this->_sqlMapper->getCache($model); + //var_dump($model); + $mappedStatement->getStatement()->setCache($cache); + } + } + + foreach($this->_FlushOnExecuteStatements as $cacheID => $statementIDs) + { + if(count($statementIDs) > 0) + { + foreach($statementIDs as $statementID) + { + $cacheModel = $this->_sqlMapper->getCache($cacheID); + $statement = $this->_sqlMapper->getMappedStatement($statementID); + $cacheModel->registerTriggerStatement($statement); + } + } + } + } + + protected function loadGlobalProperties($node) + { + if(isset($node['resource'])) + $this->loadPropertyResource($node); + + foreach($node->children() as $child) + { + if(isset($child['resource'])) + $this->loadPropertyResource($child); + $this->_properties[(string)$child['key']] = (string)$child['value']; + } + } + + protected function loadPropertyResource($node) + { + $resource = $this->getResourceFromPath((string)$node['resource']); + $properties = $this->getConfigAsXmlDocument($resource); + $this->loadGlobalProperties($properties); + } + + protected function loadProvider($providerNode, $node, $document, $file) + { + //$id = (string)$node['id']; + $class = (string)$providerNode['class']; + if(strlen($class) > 0 && class_exists($class,false)) + { + $provider = new $class; + $this->_deserialize->loadConfiguration($provider, $node,$file); + $this->_sqlMapper->setDataProvider($provider); + } + else + { + throw new TSqlMapConfigurationException( + 'sqlmap_unable_find_provider_class', $file); + } + //var_dump($node); + } +/* + protected function loadTypeHandlers() + { + } +*/ + protected function loadSqlMappingFiles($sqlmappings) + { + foreach($sqlmappings->sqlMap as $node) + { + $resource = $this->getResourceFromPath((string)$node['resource']); + $sqlmap = $this->getConfigAsXmlDocument($resource); + $this->configureSqlMap($sqlmap,$resource); + } + + $this->resolveResultMapping(); + } + + protected function getResourceFromPath($resource) + { + $basedir = dirname($this->_configFile); + $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); + } + + protected function configureSqlMap($document,$file) + { + // if(isset($document->typeAlias)) + // foreach($document->typeAlias as $node) + // TTypeAliasDeSerializer::Deserialize($node, $this->_sqlMapper); + if(isset($document->resultMap)) + foreach($document->resultMap as $node) + $this->loadResultMap($node,$document,$file); + if(isset($document->parameterMap)) + foreach($document->parameterMap as $node) + $this->loadParameterMap($node, $document, $file); + if(isset($document->statement)) + foreach($document->statement as $node) + $this->loadStatementTag($node, $document,$file); + if(isset($document->select)) + foreach($document->select as $node) + $this->loadSelectTag($node, $document, $file); + if(isset($document->insert)) + foreach($document->insert as $node) + $this->loadInsertTag($node, $document, $file); + if(isset($document->update)) + foreach($document->update as $node) + $this->loadUpdateTag($node, $document, $file); + if(isset($document->delete)) + foreach($document->delete as $node) + $this->loadDeleteTag($node, $document, $file); +/* if(isset($document->procedure)) + foreach($document->procedure as $node) + $this->loadProcedureTag($node); +*/ + if($this->_sqlMapper->getIsCacheModelsEnabled()) + { + if(isset($document->cacheModel)) + foreach($document->cacheModel as $node) + $this->loadCacheModel($node, $document, $file); + } + } + + protected function loadCacheModel($node, $document, $file) + { + $cacheModel = $this->_deserialize->cacheModel($node, $this->_sqlMapper, $file); + if(isset($node->flushOnExecute)) + { + foreach($node->flushOnExecute as $flush) + { + $id = $cacheModel->getID(); + if(!isset($this->_FlushOnExecuteStatements[$id])) + $this->_FlushOnExecuteStatements[$id] = array(); + + $this->_FlushOnExecuteStatements[$id][] = (string)$flush['statement']; + } + } + //var_dump($cacheModel); + $cacheModel->initialize($this->_sqlMapper); + $this->_sqlMapper->addCache($cacheModel); + } + + protected function loadUpdateTag($node, $document, $file) + { + $update = $this->_deserialize->insert($node, $this->_sqlMapper, $file); + if(!is_null($update->getGenerate())) + { + var_dump('generate update'); + } + else + { + $this->processSqlStatement($update, $document, $node, $file); + } + $mappedStatement = new TUpdateMappedStatement($this->_sqlMapper, $update); + $this->_sqlMapper->addMappedStatement($mappedStatement); + } + + protected function loadDeleteTag($node, $document, $file) + { + $delete = $this->_deserialize->delete($node, $this->_sqlMapper, $file); + if(!is_null($delete->getGenerate())) + { + var_dump('generate delete'); + } + else + { + $this->processSqlStatement($delete, $document, $node, $file); + } + $mappedStatement = new TDeleteMappedStatement($this->_sqlMapper, $delete); + $this->_sqlMapper->addMappedStatement($mappedStatement); + } + + + protected function loadParameterMap($node, $document, $file) + { + $id = (string)$node['id']; + if($this->_sqlMapper->getParameterMaps()->contains($id)) + return; + $parameterMap = $this->_deserialize->parameterMap($node, $this->_sqlMapper, $file); + $extendMap = $parameterMap->getExtends(); + if(strlen($extendMap) > 0) + { + if($this->_sqlMapper->getParameterMaps()->contains($extendMap) == false) + { + $nodes = $document->xpath("//parameterMap[@id='{$extendMap}']"); + if(isset($nodes[0])) + $this->loadParameterMap($nodes[0],$document,$file); + else + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_parent_parameter_map', $extendMap, $file); + } + $superMap = $this->_sqlMapper->getParameterMap($extendMap); + $index = 0; + foreach($superMap->getPropertyNames() as $propertyName) + { + $parameterMap->insertParameterProperty($index++, + $superMap->getProperty($propertyName)); + } + } + $this->_sqlMapper->addParameterMap($parameterMap); + } + + protected function loadInsertTag($node, $document, $file) + { + $insert = $this->_deserialize->insert($node, $this->_sqlMapper, $file); + if(!is_null($insert->getGenerate())) + { + var_dump("generate insert"); + } + else + { + $this->processSqlStatement($insert, $document, $node, $file); + } + + $mappedStatement = new TInsertMappedStatement($this->_sqlMapper, $insert); + $this->_sqlMapper->addMappedStatement($mappedStatement); + if(!is_null($insert->getSelectKey())) + { + $selectKey = $insert->getSelectKey(); + $selectKey->setID($insert->getID()); + $selectKey->initialize($this->_sqlMapper); + $selectKey->setID($insert->getID().'.SelectKey'); + $this->processSqlStatement($selectKey, + $document, $node->selectKey, $file); + $mappedStatement = new TMappedStatement($this->_sqlMapper, $selectKey); + $this->_sqlMapper->addMappedStatement($mappedStatement); + } + } + + protected function processSqlStatement($statement, $document, $node, $file) + { + $commandText = (string)$node; + if(strlen($extend = $statement->getExtends()) > 0) + { + $superNodes = $document->xpath("//*[@id='{$extend}']"); + if(isset($superNodes[0])) + $commandText = (string)$superNodes[0] . $commandText; + else + throw TSqlMapConfigurationException( + 'sqlmap_unable_to_find_parent_sql', $extend, $file); + } + + //$sql = new TStaticSql(); + //$sql->buildPreparedStatement($statement, (string)$node); + $commandText = $this->_deserialize->replaceProperties($commandText); + $this->applyInlineParameterMap($statement, $commandText, $node, $file); + //$statement->setSql($sql); + } + + protected function applyInlineParameterMap($statement, $sqlStatement, $node, $file) + { + if($statement->parameterMap() == null) + { + $scope['statement'] = $statement->getID(); + $scope['file'] = $file; + + // Build a Parametermap with the inline parameters. + // if they exist. Then delete inline infos from sqltext. + $parameterParser = new TInlineParameterMapParser; + $sqlText = $parameterParser->parseInlineParameterMap( + $this->_sqlMapper, $statement, $sqlStatement, $scope); + if(count($sqlText['parameters']) > 0) + { + $map = new TParameterMap(); + $map->setID($statement->getID().'-InLineParameterMap'); + $statement->setInlineParameterMap($map); + foreach($sqlText['parameters'] as $property) + $map->addParameterProperty($property); + } + $sqlStatement = $sqlText['sql']; + } + $sql = new TStaticSql(); + $sql->buildPreparedStatement($statement, $sqlStatement); + $statement->setSql($sql); + } + + protected function resolveResultMapping() + { + $maps = $this->_sqlMapper->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->_sqlMapper); + } + } + } + + protected function loadSelectTag($node, $document, $file) + { + $select = $this->_deserialize->select($node, $this->_sqlMapper, $file); + if(!is_null($select->getGenerate())) + { + var_dump("generate select"); + } + else + { + $this->processSqlStatement($select, $document, $node, $file); + /*$sql = new TStaticSql(); + $sql->buildPreparedStatement($select, (string)$node); + $select->setSql($sql);*/ + } + + $mappedStatement = new TMappedStatement($this->_sqlMapper, $select); + if($this->_sqlMapper->getIsCacheModelsEnabled() && + strlen($select->getCacheModel()) > 0) + { + $mappedStatement = new TCachingStatement($mappedStatement); + } + + $this->_sqlMapper->addMappedStatement($mappedStatement); + } + + protected function loadResultMap($node,$document,$file) + { + $resultMap = $this->_deserialize->resultMap($node, $this->_sqlMapper,$file); + $extendMap = $resultMap->getExtends(); + if(strlen($extendMap) > 0) + { + if(!$this->_sqlMapper->getResultMaps()->contains($extendMap)) + { + $nodes = $document->xpath("//resultMap[@id='{$extendMap}']"); + if(isset($nodes[0])) + $this->loadResultMap($nodes[0],$document,$file); + else + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_parent_result_map', $extendMap, $file); + } + $superMap = $this->_sqlMapper->getResultMap($extendMap); + $resultMap->getColumns()->mergeWith($superMap->getColumns()); + } + if(!$this->_sqlMapper->getResultMaps()->contains($resultMap->getID())) + $this->_sqlMapper->addResultMap($resultMap); + } + + + protected function loadStatementTag($node, $document, $file) + { + $statement = $this->_deserialize->statement($node, $this->_sqlMapper, $file); + + /*$sql = new TStaticSql(); + $sql->buildPreparedStatement($statement, (string)$node); + $statement->setSql($sql);*/ + $this->processSqlStatement($statement, $document, $node, $file); + + $mappedStatement = new TMappedStatement($this->_sqlMapper, $statement); + $this->_sqlMapper->addMappedStatement($mappedStatement); + } +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Configuration/TInlineParameterMapParser.php b/framework/DataAccess/SQLMap/Configuration/TInlineParameterMapParser.php new file mode 100644 index 00000000..8b1ecf04 --- /dev/null +++ b/framework/DataAccess/SQLMap/Configuration/TInlineParameterMapParser.php @@ -0,0 +1,50 @@ +getParameterClass() : null; + $matches = array(); + $mappings = array(); + preg_match_all($this->PARAMETER_TOKEN_REGEXP, $sqlText, $matches); + + for($i = 0, $k=count($matches[1]); $i<$k; $i++) + { + $mappings[] = $this->parseMapping($matches[1][$i], + $parameterClass, $sqlMap, $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# + */ + protected function parseMapping($token, $parameterClass, $sqlMap, $scope) + { + $mapping = new TParameterProperty; + $properties = explode(',', $token); + $mapping->setProperty(trim(array_shift($properties))); + //var_dump($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['statement'], $scope['file']); + } + $mapping->initialize($sqlMap); + return $mapping; + } +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Configuration/TParameterMap.php b/framework/DataAccess/SQLMap/Configuration/TParameterMap.php new file mode 100644 index 00000000..8e1c757d --- /dev/null +++ b/framework/DataAccess/SQLMap/Configuration/TParameterMap.php @@ -0,0 +1,95 @@ +_properties = new TList; + $this->_propertyMap = new TMap; + } + + public function getProperties(){ return $this->_properties; } + + public function getID(){ return $this->_ID; } + public function setID($value){ $this->_ID = $value; } + + public function getExtends(){ return $this->_extend; } + public function setExtends($value){ $this->_extend = $value; } + + 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 TDataMapperException( + 'sqlmap_index_must_be_string_or_int', $index); + } + + public function addParameterProperty(TParameterProperty $property) + { + $this->_propertyMap->add($property->getProperty(), $property); + $this->_properties->add($property); + } + + public function insertParameterProperty($index, TParameterProperty $property) + { + $this->_propertyMap->add($property->getProperty(), $property); + $this->_properties->insertAt($index, $property); + } + + public function getPropertyNames() + { + return $this->_propertyMap->getKeys(); + } + + public function getParameter($mapping, $parameterValue, $statement) + { + $value = $parameterValue; + $typeHandler = $mapping->getTypeHandler(); + try + { + $value = TPropertyAccess::get($parameterValue, $mapping->getProperty()); + } + catch (TInvalidPropertyException $e) + { + throw new TSqlMapExecutionException( + 'sqlmap_unable_to_get_property_for_parameter',$this->getID(), + $mapping->getProperty(), get_class($parameterValue), + $e->getMessage(), $statement->getID()); + } + + if(!is_null($typeHandler)) + { + try + { + $value = $typeHandler->getParameter($value); + } + catch (Exception $e) + { + throw new TSqlMapExecutionException( + 'sqlmap_error_in_parameter_from_handler',$this->getID(), + $value, get_class($typeHandler), $e->getMessage()); + } + } + + if(!is_null($nullValue = $mapping->getNullValue())) + { + if($nullValue === $value) + $value = null; + } + + if(!is_null($type = $mapping->getType())) + $value = TTypeHandlerFactory::convertToType($type, $value); + + return $value; + } +} +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Configuration/TParameterProperty.php b/framework/DataAccess/SQLMap/Configuration/TParameterProperty.php new file mode 100644 index 00000000..2ff55f73 --- /dev/null +++ b/framework/DataAccess/SQLMap/Configuration/TParameterProperty.php @@ -0,0 +1,68 @@ +_typeHandlerFactory)) return null; + if(!is_null($this->_typeHandler)) + return $this->_typeHandlerFactory->getTypeHandler($this->_typeHandler); + else if(!is_null($this->getType())) + return $this->_typeHandlerFactory->getTypeHandler($this->getType()); + else + return null; + } + public function setTypeHandler($value){ $this->_typeHandler = $value; } + + public function getType(){ return $this->_type; } + public function setType($value){ $this->_type = $value; } + + public function getColumn(){ return $this->_column; } + public function setColumn($value){ $this->_column = $value; } + + public function getDbType(){ return $this->_dbType; } + public function setDbType($value){ $this->_dbType = $value; } + + public function getProperty(){ return $this->_property; } + public function setProperty($value){ $this->_property = $value; } + + public function getNullValue(){ return $this->_nullValue; } + public function setNullValue($value){ $this->_nullValue = $value; } + + public function getSize(){ return $this->_size; } + public function setSize($value){ $this->_size = $value; } + + public function getPrecision(){ return $this->_precision; } + public function setPrecision($value){ $this->_precision = $value; } + + public function getScale(){ return $this->_scale; } + public function setScale($value){ $this->_scale = $value; } + + + public function getDirection(){ return $this->_direction; } + public function setDirection($value){ $this->_direction = $value; } + + public function initialize($sqlMap) + { + $this->_typeHandlerFactory = $sqlMap->getTypeHandlerFactory(); + // $type = !is_null($this->_typeHandler) ? $this->_typeHandler: $this->_type; + // $this->setTypeHandler($sqlMap->getTypeHandlerFactory()->getTypeHandler($type)); + // if(!is_null($type)) + // var_dump($sqlMap->getTypeHandlerFactory()); + } +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Configuration/TResultMap.php b/framework/DataAccess/SQLMap/Configuration/TResultMap.php new file mode 100644 index 00000000..0f09a1ba --- /dev/null +++ b/framework/DataAccess/SQLMap/Configuration/TResultMap.php @@ -0,0 +1,63 @@ +_columns = new TMap; + } + + public function getID(){ return $this->_ID; } + public function setID($value){ $this->_ID = $value; } + + public function getClass(){ return $this->_className; } + public function setClass($value){ $this->_className = $value; } + + public function getColumns(){ return $this->_columns; } + public function setColumns($value){ $this->_columns = $value; } + + public function getExtends(){ return $this->_extendMap; } + public function setExtends($value){ $this->_extendMap = $value; } + + public function getGroupBy(){ return $this->_groupBy; } + public function setGroupBy($value){ $this->_groupBy = $value; } + + public function getDiscriminator(){ return $this->_discriminator; } + public function setDiscriminator($value){ $this->_discriminator = $value; } + + public function addResultProperty(TResultProperty $property) + { + $this->_columns->add($property->getProperty(), $property); + } + + public function createInstanceOfResult() + { + return TTypeHandlerFactory::createInstanceOf($this->getClass()); + } + + public function resolveSubMap($row) + { + $subMap = $this; + if(!is_null($disc = $this->getDiscriminator())) + { + $mapping = $disc->getMapping(); + $dataValue = $mapping->getDatabaseValue($row); + $subMap = $disc->getSubMap((string)$dataValue); + + if(is_null($subMap)) + $subMap = $this; + else if($subMap !== $this) + $subMap = $subMap->resolveSubMap($row); + } + return $subMap; + } +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Configuration/TResultProperty.php b/framework/DataAccess/SQLMap/Configuration/TResultProperty.php new file mode 100644 index 00000000..9cc0df3a --- /dev/null +++ b/framework/DataAccess/SQLMap/Configuration/TResultProperty.php @@ -0,0 +1,164 @@ +_nullValue; } + public function setNullValue($value){ $this->_nullValue = $value; } + + public function getProperty(){ return $this->_propertyName; } + public function setProperty($value){ $this->_propertyName = $value; } + + public function getColumn(){ return $this->_columnName; } + public function setColumn($value){ $this->_columnName = $value; } + + public function getColumnIndex(){ return $this->_columnIndex; } + public function setColumnIndex($value){ $this->_columnIndex = TPropertyValue::ensureInteger($value,-1); } + + public function getResultMapping(){ return $this->_nestedResultMapName; } + public function setResultMapping($value){ $this->_nestedResultMapName = $value; } + + public function getNestedResultMap(){ return $this->_nestedResultMap; } + public function setNestedResultMap($value){ $this->_nestedResultMap = $value; } + + public function getType(){ return $this->_valueType; } + public function setType($value) { $this->_valueType = $value; } + + public function getTypeHandler() + { + if(is_null($this->_typeHandlerFactory)) return null; + if(!is_null($this->_typeHandler)) + return $this->_typeHandlerFactory->getTypeHandler($this->_typeHandler); + else if(!is_null($this->getType())) + return $this->_typeHandlerFactory->getTypeHandler($this->getType()); + else + return null; + } + public function setTypeHandler($value) { $this->_typeHandler = $value; } + + public function getSelect(){ return $this->_select; } + public function setSelect($value){ $this->_select = $value; } + + public function getLazyLoad(){ return $this->_isLazyLoad; } + public function setLazyLoad($value){ $this->_isLazyLoad = TPropertyValue::ensureBoolean($value,false); } + + public function getDbType(){ return $this->_dbType; } + public function setDbType($value){ $this->_dbType = $value; } + + public function initialize($sqlMap, $resultMap=null) + { + $this->_typeHandlerFactory = $sqlMap->getTypeHandlerFactory(); + if(!is_null($resultMap)) + $this->_hostResultMapID = $resultMap->getID(); +// $type = !is_null($this->_typeHandler) ? $this->_typeHandler: $this->_valueType; +// $this->setTypeHandler($sqlMap->getTypeHandlerFactory()->getTypeHandler($type)); + } + + public function getDatabaseValue($row,$forceType=true) + { + $value = null; + if($this->_columnIndex > 0 && isset($row[$this->_columnIndex])) + $value = $this->getTypedValue($row[$this->_columnIndex], $forceType); + else if(isset($row[$this->_columnName])) + $value = $this->getTypedValue($row[$this->_columnName],$forceType); + if(is_null($value) && !is_null($this->_nullValue)) + $value = $this->getTypedValue($this->_nullValue,$forceType); + return $value; + } + + public function getOrdinalValue($row) + { + return $this->getDatabaseValue($row,false); + } + + private function getTypedValue($value, $forceType=true) + { + if(!$forceType) return $value; + if(is_null($this->getTypeHandler())) + { + return TTypeHandlerFactory::convertToType($this->_valueType, $value); + } + else + { + try + { + return $this->getTypeHandler()->getResult($value); + } + catch (Exception $e) + { + throw new TSqlMapExecutionException( + 'sqlmap_error_in_result_property_from_handler',$this->_hostResultMapID, + $value, get_class($this->getTypeHandler()), $e->getMessage()); + } + } + } + + + public function getPropertyType($type=null) + { + if(is_null($type)) + $type = $this->getType(); + if(class_exists($type, 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; + return self::OBJECT_TYPE; + } + + public function isListType($target) + { + if(is_null($this->getType())) + { + $prop = TPropertyAccess::get($target,$this->getProperty()); + return $prop instanceof TList; + } + return $this->getPropertyType() == self::LIST_TYPE; + } + + public function isArrayType($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->getPropertyType() == self::ARRAY_TYPE; + } + + public function isObjectType($target) + { + if(is_null($this->getType())) + { + $prop = TPropertyAccess::get($target,$this->getProperty()); + return is_object($prop); + } + return $this->getPropertyType() == self::OBJECT_TYPE; + } +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Configuration/TSqlMapCacheModel.php b/framework/DataAccess/SQLMap/Configuration/TSqlMapCacheModel.php new file mode 100644 index 00000000..6c945155 --- /dev/null +++ b/framework/DataAccess/SQLMap/Configuration/TSqlMapCacheModel.php @@ -0,0 +1,131 @@ +_id; } + public function setID($value){ $this->_id = $value; } + + public function getImplementation(){ return $this->_implementation; } + public function setImplementation($value){ $this->_implementation = $value; } + + public function getFlushInterval(){ return $this->_flushInterval; } + public function setFlushInterval($value){ $this->_flushInterval = $value; } + + public function initialize($sqlMap) + { + $implementation = $this->getImplementationClass( + $sqlMap->getTypeHandlerFactory()); + $this->_cache = new $implementation; + $this->_cache->configure($this->_properties); + } + + protected function getImplementationClass($typeFactory) + { + switch(strtolower($this->_implementation)) + { + case 'fifo': return 'TSqlMapFifoCache'; + case 'lru' : return 'TSqlMapLruCache'; + } + + if(class_exists($this->_implementation, false)) + $class = $this->_implementation; + else + $class = $typeFactory->getTypeHandler($this->_implementation); + if(!is_null($class)) + return $class; + else + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_implemenation', $this->_implementation); + } + + public function addProperty($name, $value) + { + $this->_properties[strtolower($name)] = $value; + } + + public function registerTriggerStatement($mappedStatement) + { + $mappedStatement->attachEventHandler('OnExecuteQuery', + array($this, 'flushHandler')); + } + + protected function flushHandler($sender, $param) + { + $this->flush(); + } + + public function flush() + { + var_dump("flush!"); + $this->_cache->flush(); + } + + 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; + } + + public function set($key, $value) + { + if($key instanceof TSqlMapCacheKey) + $key = $key->getHash(); + + if(!is_null($value)) + $this->_cache->set($key, $value); + } + + public function getHitRatio() + { + if($this->_requests != 0) + return $this->_hits / $this->_requests; + else + return 0; + } +} + + +class TSqlMapCacheKey +{ + private $_key; + + public function __construct($object) + { + $this->_key = $this->generateKey(serialize($object)); + } + + protected function generateKey($string) + { + return sprintf('%x',crc32($string)); + } + + public function getHash() + { + return $this->_key; + } + + public function __toString() + { + return $this->getHash(); + } +} + + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Configuration/TSqlMapDelete.php b/framework/DataAccess/SQLMap/Configuration/TSqlMapDelete.php new file mode 100644 index 00000000..9ca3843a --- /dev/null +++ b/framework/DataAccess/SQLMap/Configuration/TSqlMapDelete.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Configuration/TSqlMapInsert.php b/framework/DataAccess/SQLMap/Configuration/TSqlMapInsert.php new file mode 100644 index 00000000..c8d798c5 --- /dev/null +++ b/framework/DataAccess/SQLMap/Configuration/TSqlMapInsert.php @@ -0,0 +1,15 @@ +_selectKey; } + public function setSelectKey($value){ $this->_selectKey = $value; } + + public function getGenerate(){ return $this->_generate; } + public function setGenerate($value){ $this->_generate = $value; } +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Configuration/TSqlMapSelect.php b/framework/DataAccess/SQLMap/Configuration/TSqlMapSelect.php new file mode 100644 index 00000000..b33bc27e --- /dev/null +++ b/framework/DataAccess/SQLMap/Configuration/TSqlMapSelect.php @@ -0,0 +1,11 @@ +_generate; } + public function setGenerate($value){ $this->_generate = $value; } +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Configuration/TSqlMapSelectKey.php b/framework/DataAccess/SQLMap/Configuration/TSqlMapSelectKey.php new file mode 100644 index 00000000..b73d4ef0 --- /dev/null +++ b/framework/DataAccess/SQLMap/Configuration/TSqlMapSelectKey.php @@ -0,0 +1,26 @@ +_type; } + public function setType($value){ $this->_type = $value; } + + public function getProperty(){ return $this->_property; } + public function setProperty($value){ $this->_property = $value; } + + public function setExtends($value) + { + throw new TSqlMapConfigurationException( + 'sqlmap_can_not_extend_select_key'); + } + + public function getIsAfter() + { + return $this->_type == 'post'; + } +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Configuration/TSqlMapStatement.php b/framework/DataAccess/SQLMap/Configuration/TSqlMapStatement.php new file mode 100644 index 00000000..228b37d6 --- /dev/null +++ b/framework/DataAccess/SQLMap/Configuration/TSqlMapStatement.php @@ -0,0 +1,109 @@ +_ID; } + public function setID($value){ $this->_ID = $value; } + + public function getParameterMap(){ return $this->_parameterMapName; } + public function setParameterMap($value){ $this->_parameterMapName = $value; } + + public function getParameterClass(){ return $this->_parameterClassName; } + public function setParameterClass($value){ $this->_parameterClassName = $value; } + + public function getResultMap(){ return $this->_resultMapName; } + public function setResultMap($value){ $this->_resultMapName = $value; } + + public function getResultClass(){ return $this->_resultClassName; } + public function setResultClass($value){ $this->_resultClassName = $value; } + + public function getCacheModel(){ return $this->_cacheModelName; } + public function setCacheModel($value){ $this->_cacheModelName = $value; } + + public function getCache(){ return $this->_cache; } + public function setCache($value){ $this->_cache = $value; } + + public function getRemapResults(){ return $this->_remapResults; } + public function setRemapResults($value){ $this->_remapResults = TPropertyValue::ensureBoolean($value,false); } + + public function getSQL(){ return $this->_SQL; } + public function setSQL($value){ $this->_SQL = $value; } + + public function getListClass(){ return $this->_listClass; } + public function setListClass($value){ $this->_listClass = $value; } + + public function getExtends(){ return $this->_extendStatement; } + public function setExtends($value){ $this->_extendStatement = $value; } + + public function resultMap(){ return $this->_resultMap; } + public function parameterMap(){ return $this->_parameterMap; } + + public function setInlineParameterMap($map) + { + $this->_parameterMap = $map; + } +// public function parameterClass(){ return $this->_parameterClass; } +// public function resultClass(){ return $this->_resultClass; } + + public function initialize($sqlMap) + { + $this->_typeHandler = $sqlMap->getTypeHandlerFactory(); + if(strlen($this->_resultMapName) > 0) + $this->_resultMap = $sqlMap->getResultMap($this->_resultMapName); + if(strlen($this->_parameterMapName) > 0) + $this->_parameterMap = $sqlMap->getParameterMap($this->_parameterMapName); + } + + + public function createInstanceOfListClass() + { + if(strlen($type = $this->getListClass()) > 0) + return $this->createInstanceOf($type); + return array(); //new TList; + } + + protected function createInstanceOf($type) + { + $handler = $this->_typeHandler->getTypeHandler($type); + + try + { + if(!is_null($handler)) + return $handler->createNewInstance(); + else + return TTypeHandlerFactory::createInstanceOf($type); + } + catch (TDataMapperException $e) + { + throw new TSqlMapExecutionException( + 'sqlmap_unable_to_create_new_instance', + $type, get_class($handler), $this->getID()); + } + + } + + public function createInstanceOfResultClass() + { + if(strlen($type= $this->getResultClass()) > 0) + return $this->createInstanceOf($type); + } +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Configuration/TSqlMapUpdate.php b/framework/DataAccess/SQLMap/Configuration/TSqlMapUpdate.php new file mode 100644 index 00000000..26d00e39 --- /dev/null +++ b/framework/DataAccess/SQLMap/Configuration/TSqlMapUpdate.php @@ -0,0 +1,11 @@ +_generate; } + public function setGenerate($value){ $this->_generate = $value; } +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/DataMapper/TDataMapperException.php b/framework/DataAccess/SQLMap/DataMapper/TDataMapperException.php new file mode 100644 index 00000000..7cb74244 --- /dev/null +++ b/framework/DataAccess/SQLMap/DataMapper/TDataMapperException.php @@ -0,0 +1,65 @@ +asXml()),$file); + } +} + +class TSqlMapExecutionException extends TDataMapperException +{ +} + +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 TDataMapperException +{ + +} + +class TSqlMapDuplicateException extends TDataMapperException +{ +} + +class TSqlMapConnectionException extends TDataMapperException +{ +} + +class TInvalidPropertyException extends TDataMapperException +{ + +} +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/DataMapper/TLazyLoadList.php b/framework/DataAccess/SQLMap/DataMapper/TLazyLoadList.php new file mode 100644 index 00000000..465dcaac --- /dev/null +++ b/framework/DataAccess/SQLMap/DataMapper/TLazyLoadList.php @@ -0,0 +1,87 @@ +_param = $param; + $this->_target = $target; + $this->_statementName = $mappedStatement->getID(); + $this->_sqlMap = $mappedStatement->getSqlMap(); + $this->_propertyName = $propertyName; + } + + public static function newInstance($mappedStatement, $param, $target, $propertyName) + { + $handler = new self($mappedStatement, $param, $target, $propertyName); + $statement = $mappedStatement->getStatement(); + $list = $statement->createInstanceOfListClass(); + if(!is_object($list)) + throw new TSqlMapExecutionException('sqlmap_invalid_lazyload_list', + $statement->getID()); + return new TObjectProxy($handler, $list); + } + + public function intercept($method, $arguments) + { + return call_user_func_array(array($this->_innerList, $method), $arguments); + } + + protected function fetchListData() + { + + if($this->_loaded == false) + { + $this->_innerList = $this->_sqlMap->queryForList( + $this->_statementName, $this->_param); + $this->_loaded = true; + //replace the target property with real list + TPropertyAccess::set($this->_target, + $this->_propertyName, $this->_innerList); + } + } + + public function hasMethod($method) + { + $this->fetchListData(); + if(is_object($this->_innerList)) + return in_array($method, get_class_methods($this->_innerList)); + return false; + } +} + +interface IInterceptor +{ + public function intercept($method, $params); + public function hasMethod($method); +} + +class TObjectProxy +{ + private $_object; + private $_handler; + + public function __construct(IInterceptor $handler, $object) + { + $this->_handler = $handler; + $this->_object = $object; + } + + 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/DataAccess/SQLMap/DataMapper/TPropertyAccess.php b/framework/DataAccess/SQLMap/DataMapper/TPropertyAccess.php new file mode 100644 index 00000000..8680601e --- /dev/null +++ b/framework/DataAccess/SQLMap/DataMapper/TPropertyAccess.php @@ -0,0 +1,91 @@ +_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(isset($object[$prop])) + $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 set($object, $path, $value) + { + $properties = explode('.', $path); + $prop = array_pop($properties); + if(count($properties) > 0) + $object = self::get($object, implode('.',$properties)); + if(is_object($object)) + { + $setter = 'set'.$prop; + if(is_callable(array($object, $setter))) + $object->{$setter}($value); + else + $object->{$prop} = $value; + } + else + throw new TInvalidPropertyException('sqlmap_invalid_property_type',$path); + } + +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/DataMapper/TSqlMapCache.php b/framework/DataAccess/SQLMap/DataMapper/TSqlMapCache.php new file mode 100644 index 00000000..8571d46d --- /dev/null +++ b/framework/DataAccess/SQLMap/DataMapper/TSqlMapCache.php @@ -0,0 +1,165 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System.DataAccess.SQLMap + */ + +interface ISqLMapCache +{ + public function remove($key); + + public function flush(); + + public function get($key); + + public function set($key, $value); + + public function configure($properties); +} + +/** + * Allow different implementation of caching strategy. See TSqlMapFifoCache + * for a first-in-first-out implementation. See TSqlMapLruCache for + * a least-recently-used cache implementation. + * + * @author Wei Zhuo + * @version $Revision: $ $Date: $ + * @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; + } + + /** + * Configures the Cache Size. + * @param array list of properties + */ + public function configure($properties) + { + if(isset($properties['size'])) + $this->_cacheSize = intval($properties['size']); + } + + /** + * @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 + * @version $Revision: $ $Date: $ + * @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 + * @version $Revision: $ $Date: $ + * @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); + } + } +} + + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/DataMapper/TSqlMapPagedList.php b/framework/DataAccess/SQLMap/DataMapper/TSqlMapPagedList.php new file mode 100644 index 00000000..cded4b32 --- /dev/null +++ b/framework/DataAccess/SQLMap/DataMapper/TSqlMapPagedList.php @@ -0,0 +1,156 @@ + + * @version $Revision: $ $Date: $ + * @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->getSqlMap()->openConnection(); + $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/DataAccess/SQLMap/DataMapper/TTypeHandlerFactory.php b/framework/DataAccess/SQLMap/DataMapper/TTypeHandlerFactory.php new file mode 100644 index 00000000..fcadea28 --- /dev/null +++ b/framework/DataAccess/SQLMap/DataMapper/TTypeHandlerFactory.php @@ -0,0 +1,134 @@ +_typeHandlerMap = new TMap; + } + + public function getTypeHandler($type, $dbType=null) + { + $dbTypeHandlerMap = $this->_typeHandlerMap[$type]; + $handler = null; + if(!is_null($dbTypeHandlerMap)) + { + if(is_null($dbType)) + $handler = $dbTypeHandlerMap[self::NullDbType]; + else + { + $handler = $dbTypeHandlerMap[$dbType]; + if(is_null($handler)) + $handler = $dbTypeHandlerMap[self::NullDbType]; + } + } + return $handler; + } + + public function register($type, $handler, $dbType=null) + { + $map = $this->_typeHandlerMap[$type]; + if(is_null($map)) + { + $map = new TMap; + $this->_typeHandlerMap->add($type, $map); + } + if(is_null($dbType)) + $map->add(self::NullDbType, $handler); + else + $map->add($dbType, $handler); + } + + public static 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($type, false)) //NO auto loading + return new $type; + else + throw new TDataMapperException('sqlmap_unable_to_find_class', $type); + } + return null; + } + + public static 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. + */ +interface ITypeHandlerCallback +{ + /** + * 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 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 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 string nullValue. + * @return mixed + */ + public function createNewInstance(); +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/DataMapper/messages.txt b/framework/DataAccess/SQLMap/DataMapper/messages.txt new file mode 100644 index 00000000..79c80ad5 --- /dev/null +++ b/framework/DataAccess/SQLMap/DataMapper/messages.txt @@ -0,0 +1,61 @@ +component_property_undefined = Component property '{0}.{1}' is not defined. +component_property_readonly = Component property '{0}.{1}' is read-only. +component_event_undefined = Component event '{0}.{1}' is not defined. +component_eventhandler_invalid = Component event '{0}.{1}' is attached with an invalid event handler. +component_expression_invalid = Component '{0}' is evaluating an invalid expression '{1}' : {2}. +component_statements_invalid = Component '{0}' is evaluating invalid PHP statements '{1}' : {2}. + +propertyvalue_enumvalue_invalid = Value '{0}' is a not valid enumeration value ({1}). + +list_index_invalid = Index '{0}' is out of range. +list_item_inexistent = The item cannot be found in the list. +list_data_not_iterable = Data must be either an array or an object implementing Traversable interface. +list_readonly = {0} is read-only. + +map_addition_disallowed = The new item cannot be added to the map. +map_item_unremovable = The item cannot be removed from the map. +map_data_not_iterable = Data must be either an array or an object implementing Traversable interface. +map_readonly = {0} is read-only. + +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_connection_already_exists = SqlMap could not invoke OpenConnection(). A connection is already started. Call CloseConnection first. +sqlmap_unable_to_close_null_connection = SqlMap could not invoke CloseConnection(). No connection was started. Call OpenConnection() first. +sqlmap_undefined_attribute = {0} attribute '{1}' is not defined for {2} in file {3}. +sqlmap_unable_find_provider_class = Unable to find a database provider in SQLMap configuration file {0}. +sqlmap_unable_to_find_parent_parameter_map = Unable to find parent parameter map extension '{0}' in file {1}. +sqlmap_unable_to_find_parent_sql = Unable to find parent sql statement 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_unable_to_find_parent_result_map = Unable to find parent SQLMap result map '{0}' in file {1}. +sqlmap_undefined_property_inline_map = Invalid attribute '{0}' for inline parameter in statement '{1}' in file {2}. +sqlmap_index_must_be_string_or_int = Invalid index '{0}', must be an integes or string to get a SQLMap parameter map property. +sqlmap_undefined_input_property = Undefined array index '{0}' in retrieving property in SQLMap parameter map '{1}'. +sqlmap_unable_to_find_class = Unable to find result class '{0}' in TResultMap::createInstanceOfResult(). +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. +sqlmap_cannot_execute_update = SQLMap statement class {0} can not execute update query. +sqlmap_cannot_execute_insert = SQLMap statement class {0} can not execute insert. +sqlmap_cannot_execute_query_for_list = SQLMap statement class {0} can not query for list. +sqlmap_cannot_execute_query_for_object = SQLMap statement class {0} can not query for object +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 = Invalid property getter path '{0}'. +sqlmap_invalid_property_type = Invalid object type, must be 'Object', unable to set property in path '{0}'. +sqlmap_unable_to_get_property_for_parameter = Unable to find property '{1}' in object '{2}' for parameter map '{0}' while executing statement '{4}': '{3}'. +sqlmap_error_in_parameter_from_handler = For parameter map '{0}', error in getting parameter from type handler '{2}' with value '{1}': '{3}'. +sqlmap_error_in_result_property_from_handler = For result map '{0}', error in getting result from type handler '{2}', with value '{1}': '{3}'.======= +sqlmap_unable_to_find_implemenation = Unable to find SQLMap cache implementation named '{0}'. +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. +sqlmap_unable_to_find_db_config = Unable to find database connection settings <provider> and <datasource> in configuration file '{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_undefined_discriminator = The discriminator is null, but somehow a subMap was reached in ResultMap '{0}' in file '{1}'. +sqlmap_invalid_delegate = Invalid callback row delegate '{1}' in mapped statement '{0}'. \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Statements/IMappedStatement.php b/framework/DataAccess/SQLMap/Statements/IMappedStatement.php new file mode 100644 index 00000000..6a4d76db --- /dev/null +++ b/framework/DataAccess/SQLMap/Statements/IMappedStatement.php @@ -0,0 +1,71 @@ +$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 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 $keyProperty. + */ + 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, $skip=-1, $max=-1); + + + /** + * Executes an SQL statement that returns a single row as an object + * of the type of the $result 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); +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Statements/TCachingStatement.php b/framework/DataAccess/SQLMap/Statements/TCachingStatement.php new file mode 100644 index 00000000..8e4e2a3d --- /dev/null +++ b/framework/DataAccess/SQLMap/Statements/TCachingStatement.php @@ -0,0 +1,102 @@ + + * @version $Revision: $ $Date: $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +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 getSqlMap() + { + return $this->_mappedStatement->getSqlMap(); + } + + 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, $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) + { + $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( + $connection, $this->getStatement(), $parameter); + } +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Statements/TDeleteMappedStatement.php b/framework/DataAccess/SQLMap/Statements/TDeleteMappedStatement.php new file mode 100644 index 00000000..9a1c8fae --- /dev/null +++ b/framework/DataAccess/SQLMap/Statements/TDeleteMappedStatement.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Statements/TInsertMappedStatement.php b/framework/DataAccess/SQLMap/Statements/TInsertMappedStatement.php new file mode 100644 index 00000000..7a444c89 --- /dev/null +++ b/framework/DataAccess/SQLMap/Statements/TInsertMappedStatement.php @@ -0,0 +1,32 @@ + \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Statements/TMappedStatement.php b/framework/DataAccess/SQLMap/Statements/TMappedStatement.php new file mode 100644 index 00000000..483a315a --- /dev/null +++ b/framework/DataAccess/SQLMap/Statements/TMappedStatement.php @@ -0,0 +1,988 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System.DataAccess.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 + * @version $Revision: $ $Date: $ + * @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 $_sqlMap; + + /** + * @var TPostSelectBinding[] post select statement queue. + */ + private $_selectQueque=array(); + + /** + * @var boolean true when data is mapped to a particular row. + */ + private $_IsRowDataFound = false; + + /** + * @var array group by result data cache. + */ + private $_groupBy=array(); + + /** + * @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 getSqlMap() + { + return $this->_sqlMap; + } + + /** + * @return TPreparedCommand command to prepare SQL statements. + */ + public function getCommand() + { + return $this->_command; + } + + /** + * @return TResultMapGroupBy[] list of results obtained from group by result maps. + */ + protected function getGroupByResults() + { + return $this->_groupBy; + } + + /** + * Empty the group by results cache. + */ + protected function clearGroupByResults() + { + $this->_groupBy = array(); + } + + /** + * Creates a new mapped statement. + * @param TSqlMapper an sqlmap. + * @param TSqlMapStatement An SQL statement. + */ + public function __construct(TSqlMapper $sqlMap, TSqlMapStatement $statement) + { + $this->_sqlMap = $sqlMap; + $this->_statement = $statement; + $this->_command = new TPreparedCommand(); + } + + /** + * 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, $sql, $max, $skip) + { + 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, $skip=-1, $max=-1, $delegate=null) + { + $sql = $this->_command->create($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 + * executeQueryForList() 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) + { + $list = $result instanceof ArrayAccess ? $result : + $this->_statement->createInstanceOfListClass(); + $recordSet = $this->executeSQLQueryLimit($connection, $sql, $max, $skip); + + if(!is_null($delegate)) + { + while($row = $recordSet->fetchRow()) + { + $obj = $this->applyResultMap($row); + $param = new TResultSetListItemParameter($obj, $parameter, $list); + $this->raiseRowDelegate($delegate, $param); + } + } + else + { + while($row = $recordSet->fetchRow()) + { + $obj = $this->applyResultMap($row); + if(!is_null($obj)) + $list[] = $obj; + } + } + + //get the groupings + foreach($this->getGroupbyResults() as $group) + $list[] = $group->updateProperties(); + $this->clearGroupByResults(); + + $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($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 + * executeQueryForMap() 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, $sql, $keyProperty, $valueProperty=null, $delegate=null) + { + $map = array(); + $recordSet = $this->executeSQLQuery($connection, $sql); + if(!is_null($delegate)) + { + while($row = $recordSet->fetchRow()) + { + $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()) + { + $obj = $this->applyResultMap($row); + $key = TPropertyAccess::get($obj, $keyProperty); + $map[$key] = is_null($valueProperty) ? $obj : + TPropertyAccess::get($obj, $valueProperty); + } + } + $this->onExecuteQuery($sql); + 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 $result 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) + { + $sql = $this->_command->create($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 $result passed in as a parameter. + * + * This method should only be called by internal developers, consider using + * executeQueryForObject() 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, $sql, $result) + { + $recordSet = $this->executeSQLQuery($connection, $sql); + $object = $this->applyResultMap($recordSet->fetchRow(), $result); + + //get group by result + foreach($this->getGroupbyResults() as $group) + $object = $group->updateProperties(); + $this->clearGroupByResults(); + + $this->executePostSelect($connection); + $this->onExecuteQuery($sql); + 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); + + $sql = $this->_command->create($connection, $this->_statement, $parameter); + + $this->executeSQLQuery($connection, $sql); + + if(is_null($generatedKey)) + $generatedKey = $this->getPostGeneratedSelectKey($connection, $parameter); + + $this->executePostSelect($connection); + $this->onExecuteQuery($sql); + 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->sqlMap->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($connection, $this->_statement, $parameter); + $this->executeSQLQuery($connection, $sql); + $this->executePostSelect($connection); + $this->onExecuteQuery($sql); + return $connection->Affected_Rows(); + } + + /** + * 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(); + + if($this->_sqlMap->getResultMaps()->contains($resultMapName)) + return $this->fillResultMap($resultMapName, $row, $resultObject); + else if(strlen($resultClass) > 0) + return $this->fillResultClass($resultClass, $row, $resultObject); + else + return $this->fillDefaultResultMap(null, $row, $resultObject); + } + + /** + * 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)) + $resultObject = $this->_statement->createInstanceOfResultClass(); + + 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; + foreach($row as $k=>$v) + { + $property = new TResultProperty; + $property->initialize($this->_sqlMap); + if(is_string($k) && strlen($k) > 0) + $property->setColumn($k); + $property->setColumnIndex(++$index); + $type = gettype(TPropertyAccess::get($resultObject,$k)); + $property->setType($type); + $value = $property->getDatabaseValue($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, $resultObject) + { + $resultMap = $this->_sqlMap->getResultMap($resultMapName); + $resultMap = $resultMap->resolveSubMap($row); + + if(is_null($resultObject)) + $resultObject = $resultMap->createInstanceOfResult(); + + if(is_object($resultObject)) + { + if(strlen($resultMap->getGroupBy()) > 0) + return $this->addResultMapGroupBy($resultMap, $row, $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 results temporarily, retrieve it later using + * TMappedStatement::getGroupByResults() and TResultMapGroupBy::updateProperties() + * @param TResultMap result mapping details. + * @param array a result set row retrieved from the database + * @param object the result object + * @return null, always returns null, use getGroupByResults() to obtain the result object list. + * @see getGroupByResults() + * @see runQueryForList() + */ + protected function addResultMapGroupBy($resultMap, $row, $resultObject) + { + $group = $this->getResultMapGroupKey($resultMap, $row, $resultObject); + foreach($resultMap->getColumns() as $property) + { + $this->setObjectProperty($resultMap, $property, $row, $resultObject); + if(strlen($property->getResultMapping()) > 0) + { + $key = $property->getProperty(); + $value = TPropertyAccess::get($resultObject, $key); + $this->_groupBy[$group]->addValue($key, $value); + } + } + return null; + } + + /** + * 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 + * @param object the result object + * @return string groupping key. + * @throws TSqlMapExecutionException if unable to find grouping key. + */ + protected function getResultMapGroupKey($resultMap, $row, $resultObject) + { + $groupBy = $resultMap->getGroupBy(); + if(isset($row[$groupBy])) + { + $group = $row[$groupBy]; + if(!isset($this->_groupBy[$group])) + $this->_groupBy[$group] = new TResultMapGroupBy($group, $resultObject); + return $group; + } + else + { + throw new TSqlMapExecutionException('sqlmap_unable_to_find_groupby', + $groupBy, $resultMap->getID()); + } + } + + /** + * Fill the result map using default settings. If $resultMap is null + * the result object returned will be guessed from $resultObject. + * @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(); + foreach($resultMap->getColumns() as $column) + { + if(is_null($column->getType()) + && !is_null($resultObject) && !is_object($resultObject)) + $column->setType(gettype($resultObject)); + $result[$column->getProperty()] = $column->getDatabaseValue($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(); + + if(strlen($select) == 0 && is_null($nested)) + { + $value = $property->getDatabaseValue($row); + TPropertyAccess::set($resultObject, $key, $value); + $this->_IsRowDataFound = $this->_IsRowDataFound || ($value != null); + } + else if(!is_null($nested)) + { + $obj = $nested->createInstanceOfResult(); + 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->_sqlMap->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->isListType($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->isArrayType($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 + { + return $property->getOrdinalValue($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; } +} + +class TResultMapGroupBy +{ + private $_ID=''; + private $_object=''; + private $_values = array(); + + public function __construct($id, $object) + { + $this->setID($id); + $this->setObject($object); + } + + public function getID(){ return $this->_ID; } + public function setID($value){ $this->_ID = $value; } + + public function getObject(){ return $this->_object; } + public function setObject($value){ $this->_object = $value; } + + public function addValue($property, $value) + { + $this->_values[$property][] = $value; + } + + public function getProperties() + { + return array_keys($this->_values); + } + + public function updateProperties() + { + foreach($this->_values as $property => $value) + { + TPropertyAccess::set($this->getObject(), $property, $value); + } + return $this->getObject(); + } +} + +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/DataAccess/SQLMap/Statements/TPreparedCommand.php b/framework/DataAccess/SQLMap/Statements/TPreparedCommand.php new file mode 100644 index 00000000..1c0e5ad7 --- /dev/null +++ b/framework/DataAccess/SQLMap/Statements/TPreparedCommand.php @@ -0,0 +1,60 @@ +getSQL()->getPreparedStatement(); + $parameters = $this->applyParameterMap($connection, + $prepared, $statement, $parameterObject); + return array('sql'=>$prepared->getPreparedSql(), + 'parameters'=>$parameters); + } + + protected function applyParameterMap($connection, + $prepared, $statement, $parameterObject) + { + $properties = $prepared->getParameterNames(); + $parameters = $prepared->getParameterValues(); + $values = array(); + for($i = 0, $k=$properties->getCount(); $i<$k; $i++) + { + $property = $statement->parameterMap()->getProperty($i); + $values[] = $statement->parameterMap()->getParameter( + $property, $parameterObject, $statement); + } + return count($values) > 0 ? $values : false; + } + +/* protected function applyParameterClass($connection, $statement, $parameter) + { + $type=$statement->getParameterClass(); + if(strlen($type) < 1) return; + $prepared = $statement->getSql()->getPreparedStatement(); + $names = $prepared->getParameterNames(); + $values = $prepared->getParameterValues(); + switch (strtolower($type)) + { + case 'integer': + case 'int': + $values[$names[0]] = $connection->quote(intval($parameter)); + break; + case 'array': + foreach($names as $name) + { + $key = substr(substr($name,0,-1),1); + if(isset($parameter[$key])) + $values->add($name,$connection->quote($parameter[$key])); + else + throw new TDataMapperException('unable_to_find_parameter', $key); + } + break; + default: + var_dump("todo for other parameter classes"); + } + } +*/ +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Statements/TPreparedStatement.php b/framework/DataAccess/SQLMap/Statements/TPreparedStatement.php new file mode 100644 index 00000000..e534f532 --- /dev/null +++ b/framework/DataAccess/SQLMap/Statements/TPreparedStatement.php @@ -0,0 +1,26 @@ +_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/DataAccess/SQLMap/Statements/TPreparedStatementFactory.php b/framework/DataAccess/SQLMap/Statements/TPreparedStatementFactory.php new file mode 100644 index 00000000..41059934 --- /dev/null +++ b/framework/DataAccess/SQLMap/Statements/TPreparedStatementFactory.php @@ -0,0 +1,47 @@ +_statement = $statement; + $this->_commandText = $sqlString; + // $this->_statement = new TPreparedStatement(); +// $this->_statement->setSqlString($sqlString); + } + + public function prepare() + { + //$this->createParametersFromTextCommand(); + //return $this->_statement; + $this->_preparedStatement = new TPreparedStatement(); + $this->_preparedStatement->setPreparedSql($this->_commandText); + if(!is_null($this->_statement->parameterMap())) + { + $this->createParametersForTextCommand(); + //$this->evaluateParameterMap(); + } + //var_dump($this->_preparedStatement); + return $this->_preparedStatement; + } + + protected function createParametersForTextCommand() + { + /*$matches = array(); + $string = $this->_statement->getSqlString(); + preg_match_all('/#([a-zA-Z0-9._]+)#/', $string, $matches); + $this->_statement->getParameterNames()->copyFrom($matches[0]);*/ + //var_dump($this->_statement); + foreach($this->_statement->ParameterMap()->getProperties() as $prop) + { + $this->_preparedStatement->getParameterNames()->add($prop->getProperty()); + } + } +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Statements/TSelectMappedStatement.php b/framework/DataAccess/SQLMap/Statements/TSelectMappedStatement.php new file mode 100644 index 00000000..1171e28f --- /dev/null +++ b/framework/DataAccess/SQLMap/Statements/TSelectMappedStatement.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Statements/TStaticSql.php b/framework/DataAccess/SQLMap/Statements/TStaticSql.php new file mode 100644 index 00000000..bf6e4a18 --- /dev/null +++ b/framework/DataAccess/SQLMap/Statements/TStaticSql.php @@ -0,0 +1,19 @@ +_preparedStatement = $factory->prepare(); + } + + public function getPreparedStatement() + { + return $this->_preparedStatement; + } +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/Statements/TUpdateMappedStatement.php b/framework/DataAccess/SQLMap/Statements/TUpdateMappedStatement.php new file mode 100644 index 00000000..cf16ee52 --- /dev/null +++ b/framework/DataAccess/SQLMap/Statements/TUpdateMappedStatement.php @@ -0,0 +1,32 @@ + \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/TMapper.php b/framework/DataAccess/SQLMap/TMapper.php new file mode 100644 index 00000000..4427c012 --- /dev/null +++ b/framework/DataAccess/SQLMap/TMapper.php @@ -0,0 +1,60 @@ + + * TMapper::configure($configFile); + * $object = TMapper::instance()->queryForObject('statementName'); + * + * + * If your configuration file is named 'sqlmap.config' you may skip the + * configure() call. + * + * @author Wei Zhuo + * @version $Revision: $ $Date: $ + * @package System.DataAccess.SQLMap + * @since 3.0 + */ +class TMapper +{ + /** + * Data mapper singleton + * @var TSqlMapper + */ + private static $_mapper; + + /** + * Configure the data mapper singleton instance. + * @param string configuration file + * @param boolean true to load configuration from cache. + * @return TSqlMapper data mapper instance. + */ + public static function configure($configFile,$loadCachedConfig=false) + { + if(is_null(self::$_mapper)) + { + $sqlmap = new TSQLMapClient; + self::$_mapper = $sqlmap->configure($configFile,$loadCachedConfig); + } + return self::$_mapper; + } + + /** + * Gets the data mapper singleton instance. Default configuration file is + * 'sqlmap.config'. + * @return TSqlMapper singleton instance. + */ + public static function instance() + { + if(is_null(self::$_mapper)) + self::configure('sqlmap.xml'); + return self::$_mapper; + } +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/TSqlMapClient.php b/framework/DataAccess/SQLMap/TSqlMapClient.php new file mode 100644 index 00000000..7662d83e --- /dev/null +++ b/framework/DataAccess/SQLMap/TSqlMapClient.php @@ -0,0 +1,81 @@ + + * $client = new TSqlMapClient; + * $sqlmap = $client->configure($configFile, true); //load from cache. + * $products = $sqlMap->queryForList('statementName'); + * + * + * To save the TSqlMapper instance to cache for later usage, call + * cacheConfiguration(). + * + * @author Wei Zhuo + * @version $Revision: $ $Date: $ + * @package System.DataAccess.SQLMap + * @since 3.0 + */ +class TSqlMapClient +{ + private $_mapper; + private $_cache; + + public function configure($configFile, $loadFromCache=false) + { + if(is_null($this->_mapper)) + $this->initMapper($configFile, $loadFromCache); + return $this->_mapper; + } + + public function getInstance() + { + return $this->_mapper; + } + + public function cacheConfiguration() + { + if(!is_null($this->_mapper) && $this->_cache !== false) + { + if(!is_file($this->_cache)) + { + var_dump('saving cache to file', $this->_cache); + file_put_contents($this->_cache,serialize($this->_mapper)); + return true; + } + } + return false; + } + + protected function initMapper($file=null,$loadFromCache=false) + { + $this->_cache = $this->getCacheFile($file); + if($loadFromCache && $this->_cache !== false && is_file($this->_cache)) + { + var_dump('loading from cache: '.$this->_cache); + $this->_mapper = unserialize(file_get_contents($this->_cache)); + } + else + { +// var_dump('build from *.xml'); + $builder = new TDomSqlMapBuilder(); + $this->_mapper = $builder->configure($file); + } + } + + protected function getCacheFile($file) + { + $path = realpath($file); + if($path !== false) + return substr($path,0, strrpos($path,'.')).'.cache'; + else + return false; + } +} + +?> \ No newline at end of file diff --git a/framework/DataAccess/SQLMap/TSqlMapper.php b/framework/DataAccess/SQLMap/TSqlMapper.php new file mode 100644 index 00000000..602b83ea --- /dev/null +++ b/framework/DataAccess/SQLMap/TSqlMapper.php @@ -0,0 +1,543 @@ + + * $builder = new TDomSqlMapBuilder(); + * $mapper = $builder->configure($configFile); + * + * + * Otherwise use convient classes TMapper or TSqlMap to obtain singleton + * instances. + * + * @author Wei Zhuo + * @version $Revision: $ $Date: $ + * @package System.DataAccess.SQLMap + * @since 3.0 + */ +class TSqlMapper extends TComponent +{ + private $_connection; + private $_mappedStatements; + private $_provider; + private $_resultMaps; + private $_parameterMaps; + private $_typeHandlerFactory; + private $_cacheModelsEnabled = false; + private $_cacheMaps; + + /** + * Create a new SqlMap. + * @param TTypeHandlerFactory + */ + public function __construct($typeHandlerFactory=null) + { + $this->_mappedStatements = new TMap; + $this->_resultMaps = new TMap; + $this->_parameterMaps = new TMap; + $this->_typeHandlerFactory = $typeHandlerFactory; + $this->_cacheMaps = new TMap; + } + + /** + * Cleanup work before serializing. + * This is a PHP defined magic method. + * @return array the names of instance-variables to serialize. + */ + public function __sleep() + { + if(!is_null($this->_connection) && !$this->_connection->getIsClosed()) + $this->closeConnection(); + $this->_connection = null; + return array_keys(get_object_vars($this)); + } + + /** + * This method will be automatically called when unserialization happens. + * This is a PHP defined magic method. + */ + public function __wake() + { + + } + + /** + * Set the falg to tell us if cache models were enabled or not. + * This should only be called during configuration parsing. + * It does not disable the cache after the configuration phase. + * @param boolean enables cache. + */ + public function setCacheModelsEnabled($value) + { + $this->_cacheModelsEnabled = $value; + } + + /** + * @return boolean true if cache models were enabled when this SqlMap was + * built. + */ + public function getIsCacheModelsEnabled() + { + return $this->_cacheModelsEnabled; + } + + /** + * @return TTypeHandlerFactory The TypeHandlerFactory + */ + public function getTypeHandlerFactory() + { + return $this->_typeHandlerFactory; + } + + /** + * @return TMap mapped statements collection. + */ + public function getStatements() + { + return $this->_mappedStatements; + } + + /** + * @return TMap result maps collection. + */ + public function getResultMaps() + { + return $this->_resultMaps; + } + + /** + * Adds a named cache. + * @param TSqlMapCacheModel the cache to add. + * @throws TSqlMapConfigurationException + */ + public function addCache(TSqlMapCacheModel $cacheModel) + { + if($this->_cacheMaps->contains($cacheModel->getID())) + throw new TSqlMapConfigurationException( + 'sqlmap_cache_model_already_exists', $cacheModel->getID()); + else + $this->_cacheMaps->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 getCache($name) + { + if(!$this->_cacheMaps->contains($name)) + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_cache_model', $name); + return $this->_cacheMaps[$name]; + } + + /** + * Flushes all cached objects that belong to this SqlMap + */ + public function flushCaches() + { + foreach($this->_cacheMaps as $cache) + $cache->flush(); + } + + /** + * @return TMap parameter maps collection. + */ + public function getParameterMaps() + { + return $this->_parameterMaps; + } + + /** + * 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); + } + + /** + * 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); + } + + /** + * @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); + } + + /** + * @param TDatabaseProvider changes the database provider. + */ + public function setDataProvider($provider) + { + $this->_provider = $provider; + } + + /** + * @return TDatabaseProvider database provider. + */ + public function getDataProvider() + { + return $this->_provider; + } + + /** + * Get the current connection, opens the connection if necessary. + * @return TDbConnection database connection. + */ + protected function getConnection() + { + if(is_null($this->_connection)) + $this->_connection = $this->getDataProvider()->getConnection(); + $this->_connection->open(); + return $this->_connection; + } + + /** + * Open a connection, on the specified connection string if provided. + * @param string The connection DSN string + * @return TDbConnection database connection. + */ + public function openConnection($connectionString=null) + { + if(!is_null($connectionString)) + { + if(!is_null($this->_connection)) + throw new TSqlMapConnectionException( + 'sqlmap_connection_already_exists'); + $this->getDataProvider()->setConnectionString($connectionString); + } + return $this->getConnection(); + } + + /** + * Close the current database connection. + */ + public function closeConnection() + { + if(is_null($this->_connection)) + throw new TSqlMapConnectionException( + 'sqlmap_unable_to_close_null_connection'); + $this->_connection->close(); + } + + /** + * 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->getMappedStatement($statementName); + $connection = $this->getConnection(); + return $statement->executeQueryForObject($connection, + $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->getMappedStatement($statementName); + $connection = $this->getConnection(); + return $statement->executeQueryForList($connection, + $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->getMappedStatement($statementName); + $connection = $this->getConnection(); + return $statement->executeQueryForList($connection, + $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->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->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->getMappedStatement($statementName); + $connection = $this->getConnection(); + return $statement->executeQueryForMap($connection, + $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->getMappedStatement($statementName); + $connection = $this->getConnection(); + return $statement->executeQueryForMap($connection, + $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->getMappedStatement($statementName); + $connection = $this->getConnection(); + $generatedKey = $statement->executeInsert($connection, $parameter); + return $generatedKey; + } + + /** + * 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->getMappedStatement($statementName); + $connection = $this->getConnection(); + return $statement->executeUpdate($connection, $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); + } + + + /** + * Begins a database transaction on the currect session. + * Some databases will always return false if transaction support is not + * available + * @return boolean true if successful, false otherwise. + */ + public function beginTransaction() + { + return $this->getConnection()->beginTransaction(); + } + + /** + * End a transaction successfully. If the database does not support + * transactions, will return true also as data is always committed. + * @return boolean true if successful, false otherwise. + */ + public function commitTransaction() + { + return $this->getConnection()->commit(); + } + + /** + * End a transaction, rollback all changes. If the database does not + * support transactions, will return false as data is never rollbacked. + * @return boolean true if successful, false otherwise. + */ + public function rollbackTransaction() + { + return $this->getConnection()->rollback(); + } +} + +?> \ No newline at end of file -- cgit v1.2.3