From fd4b8d9f45d1707035021bc19b8d5bc17ede66ce Mon Sep 17 00:00:00 2001 From: wei <> Date: Mon, 12 Feb 2007 12:46:11 +0000 Subject: Add IBM DB2 driver for active record. --- .gitattributes | 6 + HISTORY | 1 + buildscripts/classtree/build.php | 7 +- .../protected/pages/Database/ActiveRecord.page | 24 +-- framework/3rdParty/WsdlGen/WsdlMessage.php | 14 +- framework/3rdParty/WsdlGen/WsdlOperation.php | 38 ++--- .../Data/ActiveRecord/Exceptions/messages.txt | 3 +- .../Scaffold/InputBuilder/TMysqlScaffoldInput.php | 2 +- .../Scaffold/InputBuilder/TPgsqlScaffoldInput.php | 2 +- .../Data/ActiveRecord/Scaffold/TScaffoldBase.php | 115 ++++++++++++-- .../ActiveRecord/Scaffold/TScaffoldEditView.php | 168 ++++++++++++++++++--- .../ActiveRecord/Scaffold/TScaffoldEditView.tpl | 6 +- .../ActiveRecord/Scaffold/TScaffoldListView.php | 85 ++++++++--- .../ActiveRecord/Scaffold/TScaffoldListView.tpl | 19 +-- .../Data/ActiveRecord/Scaffold/TScaffoldSearch.php | 89 +++++++++++ .../Data/ActiveRecord/Scaffold/TScaffoldSearch.tpl | 4 + .../Data/ActiveRecord/Scaffold/TScaffoldView.php | 10 ++ .../Data/ActiveRecord/Scaffold/TScaffoldView.tpl | 10 +- framework/Data/ActiveRecord/Scaffold/style.css | 3 +- framework/Data/ActiveRecord/TActiveRecord.php | 46 +++++- .../Data/ActiveRecord/TActiveRecordManager.php | 3 + framework/Data/ActiveRecord/Vendor/TDbMetaData.php | 38 ++++- .../Data/ActiveRecord/Vendor/TDbMetaDataCommon.php | 2 +- .../ActiveRecord/Vendor/TIbmColumnMetaData.php | 149 ++++++++++++++++++ .../Data/ActiveRecord/Vendor/TIbmMetaData.php | 80 ++++++++++ .../ActiveRecord/Vendor/TIbmMetaDataInspector.php | 113 ++++++++++++++ .../Data/ActiveRecord/Vendor/TMysqlMetaData.php | 22 ++- .../Vendor/TMysqlMetaDataInspector.php | 7 +- .../ActiveRecord/Vendor/TPgsqlColumnMetaData.php | 19 ++- .../Data/ActiveRecord/Vendor/TPgsqlMetaData.php | 26 +++- .../Vendor/TPgsqlMetaDataInspector.php | 19 ++- .../Data/ActiveRecord/Vendor/TSqliteMetaData.php | 19 ++- .../Vendor/TSqliteMetaDataInspector.php | 5 +- framework/I18N/core/ChoiceFormat.php | 67 +++++--- framework/Web/Services/TSoapService.php | 121 +++++++-------- index.html | 1 + .../ActiveRecordDynamicCallTestCase.php | 8 +- tests/simple_unit/I18N/ChoiceFormatTest.php | 98 ++++++++++++ tests/simple_unit/SqlMap/sqlite/tests.db | Bin 24576 -> 24576 bytes 39 files changed, 1207 insertions(+), 242 deletions(-) create mode 100644 framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php create mode 100644 framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.tpl create mode 100644 framework/Data/ActiveRecord/Vendor/TIbmColumnMetaData.php create mode 100644 framework/Data/ActiveRecord/Vendor/TIbmMetaData.php create mode 100644 framework/Data/ActiveRecord/Vendor/TIbmMetaDataInspector.php create mode 100644 tests/simple_unit/I18N/ChoiceFormatTest.php diff --git a/.gitattributes b/.gitattributes index dfb8291c..9bc1f135 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1468,6 +1468,8 @@ framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php -text framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.tpl -text framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php -text framework/Data/ActiveRecord/Scaffold/TScaffoldListView.tpl -text +framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php -text +framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.tpl -text framework/Data/ActiveRecord/Scaffold/TScaffoldView.php -text framework/Data/ActiveRecord/Scaffold/TScaffoldView.tpl -text framework/Data/ActiveRecord/Scaffold/style.css -text @@ -1480,6 +1482,9 @@ framework/Data/ActiveRecord/TActiveRecordStateRegistry.php -text framework/Data/ActiveRecord/Vendor/TDbMetaData.php -text framework/Data/ActiveRecord/Vendor/TDbMetaDataCommon.php -text framework/Data/ActiveRecord/Vendor/TDbMetaDataInspector.php -text +framework/Data/ActiveRecord/Vendor/TIbmColumnMetaData.php -text +framework/Data/ActiveRecord/Vendor/TIbmMetaData.php -text +framework/Data/ActiveRecord/Vendor/TIbmMetaDataInspector.php -text framework/Data/ActiveRecord/Vendor/TMysqlColumnMetaData.php -text framework/Data/ActiveRecord/Vendor/TMysqlMetaData.php -text framework/Data/ActiveRecord/Vendor/TMysqlMetaDataInspector.php -text @@ -2454,6 +2459,7 @@ tests/simple_unit/ActiveRecord/records/DepartmentRecord.php -text tests/simple_unit/ActiveRecord/records/SimpleUser.php -text tests/simple_unit/ActiveRecord/records/SqliteUsers.php -text tests/simple_unit/ActiveRecord/records/UserRecord.php -text +tests/simple_unit/I18N/ChoiceFormatTest.php -text tests/simple_unit/I18N/CultureInfoTest.php -text tests/simple_unit/Soap/ContactManager.php -text tests/simple_unit/Soap/SoapTestCase.php -text diff --git a/HISTORY b/HISTORY index 6b68238c..e0aba77e 100644 --- a/HISTORY +++ b/HISTORY @@ -11,6 +11,7 @@ ENH: Added Display property to TWebControl (Wei) ENH: Added TUser.getState() and setState() for storing user session data (Qiang) ENH: Added renderer feature to TRepeater, TDataList and TDataGrid (Qiang) NEW: TShellApplication (Qiang) +NEW: Active Record driver for IBM DB2 (Cesar Ramos) Version 3.1.0 alpha January 15, 2007 ==================================== diff --git a/buildscripts/classtree/build.php b/buildscripts/classtree/build.php index be2e3a2f..87636e44 100644 --- a/buildscripts/classtree/build.php +++ b/buildscripts/classtree/build.php @@ -18,7 +18,12 @@ $exclusions=array( '/Web/Security', '/Configuration', '/Web/Services/TFeedService.php', - '/Web/Services/IFeedContentProvider.php' + '/Web/Services/IFeedContentProvider.php', + '/Data/ActiveRecord/Vendor', + '/Data/ActiveRecord/Scaffold/InputBuilder', + '/Data/SqlMap/Configuration', + '/Data/SqlMap/DataMapper', + '/Data/SqlMap/Statements' ); $a=new ClassTreeBuilder($frameworkPath,$exclusions); $a->buildTree(); diff --git a/demos/quickstart/protected/pages/Database/ActiveRecord.page b/demos/quickstart/protected/pages/Database/ActiveRecord.page index 406a39fe..2562f674 100644 --- a/demos/quickstart/protected/pages/Database/ActiveRecord.page +++ b/demos/quickstart/protected/pages/Database/ActiveRecord.page @@ -100,10 +100,11 @@ You may need to quote (specific to your database) the value of the $_tablena E.g. MySQL uses back-ticks, $_tablename = "`database1`.`table1`" -
Tip: +

Since TActiveRecord extends TComponent, setter and getter methods can be defined to allow control over how variables are set and returned. For example, adding a $level property to the UserRecord class: +

class UserRecord extends TActiveRecord { ... //existing definitions as above @@ -117,9 +118,8 @@ class UserRecord extends TActiveRecord { } } -
-
Note: +
Info: TActiveRecord can also work with database views by specifying the value $_tablename corresponding to the view name. However, objects returned from views are read-only, calling the save() or delete() method @@ -275,7 +275,7 @@ Method names starting with findAllBy return an array of records. The condition is taken as part of the method name after findBy or findAllBy. The following blocks of code are equivalent: - +

$finder->findByName($name) $finder->find('Name = ?', $name); @@ -291,7 +291,10 @@ $finder->find('Username = ? AND Password = ?', $name, $pass); $finder->findAllByAge($age); $finder->findAll('Age = ?', $age); -

+ +
Tip: +You may also use OR as a condition in the dynamic methods. +

findBySql()

Finds records using full SQL, returns corresponding array of record objects.

@@ -303,6 +306,7 @@ $finder->findAll('Age = ?', $age);

Add a new record using TActiveRecord is very simple, just create a new Active Record object and call the save() method. E.g. +

$user1 = new UserRecord(); $user1->username = "admin" @@ -319,7 +323,6 @@ definitions that automatically creates a primary key for the newly insert record For example, if you insert a new record into a MySQL table that has columns defined with "autoincrement", the Active Record objects will be updated with the new incremented values.
-

To update a record in the database, just change one or more properties of @@ -353,15 +356,16 @@ ends the object life-cycle, no futher actions can be performed on the object. You can also delete records in the database by primary keys without loading any records using the deleteByPk() method. For example, to delete one or records with tables having a scalar primary key. +

$finder->deleteByPk($primaryKey); //delete 1 record $finder->deleteByPk($key1,$key2,...); //delete multiple records $finder->deleteByPk(array($key1,$key2,...)); //delete multiple records -

For composite primary keys (determined automatically from the table definitions): +

$finder->deleteByPk(array($key1,$key2)); //delete 1 record @@ -371,7 +375,6 @@ $finder->deleteByPk(array($key1,$key2), array($key3,$key4),...); //delete multiple records $finder->deleteByPk(array( array($key1,$key2), array($key3,$key4), .. )); -

deleteAll() and deleteBy*()

@@ -387,16 +390,13 @@ $finder->deleteByName($name); //delete by username and password $finder->deleteBy_Username_And_Password($name,$pass); -

- -

Transactions

All Active Record objects contains the property DbConnection that can be used to obtain a transaction object. $finder = UserRecord::finder(); - +$finder->DbConnection->Active=true; //open if necessary $transaction = $finder->DbConnection->beginTransaction(); try { diff --git a/framework/3rdParty/WsdlGen/WsdlMessage.php b/framework/3rdParty/WsdlGen/WsdlMessage.php index 164d81f1..2ba2cef6 100644 --- a/framework/3rdParty/WsdlGen/WsdlMessage.php +++ b/framework/3rdParty/WsdlGen/WsdlMessage.php @@ -30,13 +30,13 @@ class WsdlMessage * @var string */ private $name; - + /** * Represents the parameters for this message * @var array */ private $parts; - + /** * Creates a new message * @param string $messageName The name of the message @@ -46,9 +46,9 @@ class WsdlMessage { $this->name = $messageName; $this->parts = $parts; - + } - + /** * Gets the name of this message * @return string The name @@ -57,7 +57,7 @@ class WsdlMessage { return $this->name; } - + /** * Return the message as a DOM element * @param DOMDocument $wsdl The wsdl document the messages will be children of @@ -66,7 +66,7 @@ class WsdlMessage { $message = $dom->createElementNS('http://schemas.xmlsoap.org/wsdl/', 'wsdl:message'); $message->setAttribute('name', $this->name); - + foreach ($this->parts as $part) { if (isset($part['name'])) { $partElement = $dom->createElementNS('http://schemas.xmlsoap.org/wsdl/', 'wsdl:part'); @@ -75,7 +75,7 @@ class WsdlMessage $message->appendChild($partElement); } } - + return $message; } } diff --git a/framework/3rdParty/WsdlGen/WsdlOperation.php b/framework/3rdParty/WsdlGen/WsdlOperation.php index 58690a23..75e21308 100644 --- a/framework/3rdParty/WsdlGen/WsdlOperation.php +++ b/framework/3rdParty/WsdlGen/WsdlOperation.php @@ -29,38 +29,38 @@ class WsdlOperation * The name of the operation */ private $operationName; - + /** * Documentation for the operation */ private $documentation; - + /** * The input wsdl message */ private $inputMessage; - + /** * The output wsdl message */ private $outputMessage; - + public function __construct($name, $doc='') { $this->operationName = $name; $this->documentation = $doc; } - + public function setInputMessage(WsdlMessage $msg) { $this->inputMessage = $msg; } - + public function setOutputMessage(WsdlMessage $msg) { $this->outputMessage = $msg; } - + /** * Sets the message elements for this operation into the wsdl document * @param DOMElement $wsdl The parent domelement for the messages @@ -68,14 +68,14 @@ class WsdlOperation */ public function setMessageElements(DOMElement $wsdl, DOMDocument $dom) { - + $input = $this->inputMessage->getMessageElement($dom); $output = $this->outputMessage->getMessageElement($dom); - + $wsdl->appendChild($input); $wsdl->appendChild($output); } - + /** * Get the port operations for this operation * @param DomDocument $dom The dom document to create the messages as children of @@ -85,20 +85,20 @@ class WsdlOperation { $operation = $dom->createElementNS('http://schemas.xmlsoap.org/wsdl/', 'wsdl:operation'); $operation->setAttribute('name', $this->operationName); - + $documentation = $dom->createElementNS('http://schemas.xmlsoap.org/wsdl/', 'wsdl:documentation', htmlentities($this->documentation)); $input = $dom->createElementNS('http://schemas.xmlsoap.org/wsdl/', 'wsdl:input'); $input->setAttribute('message', 'tns:'.$this->inputMessage->getName()); $output = $dom->createElementNS('http://schemas.xmlsoap.org/wsdl/', 'wsdl:output'); $output->setAttribute('message', 'tns:'.$this->outputMessage->getName()); - + $operation->appendChild($documentation); $operation->appendChild($input); $operation->appendChild($output); - + return $operation; } - + /** * Build the binding operations. * TODO: Still quite incomplete with all the things being stuck in, I don't understand @@ -111,26 +111,26 @@ class WsdlOperation { $operation = $dom->createElementNS('http://schemas.xmlsoap.org/wsdl/', 'wsdl:operation'); $operation->setAttribute('name', $this->operationName); - + $soapOperation = $dom->createElementNS('http://schemas.xmlsoap.org/wsdl/soap/', 'soap:operation'); $method = $this->operationName; $soapOperation->setAttribute('soapAction', $namespace.'#'.$method); $soapOperation->setAttribute('style', $style); - + $input = $dom->createElementNS('http://schemas.xmlsoap.org/wsdl/', 'wsdl:input'); $output = $dom->createElementNS('http://schemas.xmlsoap.org/wsdl/', 'wsdl:output'); - + $soapBody = $dom->createElementNS('http://schemas.xmlsoap.org/wsdl/soap/', 'soap:body'); $soapBody->setAttribute('use', 'encoded'); $soapBody->setAttribute('namespace', $namespace); $soapBody->setAttribute('encodingStyle', 'http://schemas.xmlsoap.org/soap/encoding/'); $input->appendChild($soapBody); $output->appendChild(clone $soapBody); - + $operation->appendChild($soapOperation); $operation->appendChild($input); $operation->appendChild($output); - + return $operation; } } diff --git a/framework/Data/ActiveRecord/Exceptions/messages.txt b/framework/Data/ActiveRecord/Exceptions/messages.txt index 53d8f4e3..2af62bb5 100644 --- a/framework/Data/ActiveRecord/Exceptions/messages.txt +++ b/framework/Data/ActiveRecord/Exceptions/messages.txt @@ -13,4 +13,5 @@ ar_invalid_tablename_property = ActiveRecord tablename property '{0}::${1}' m ar_value_must_not_be_null = Property '{0}::${2}' must not be null as defined by column '{2}' in table '{1}'. ar_missing_pk_values = Missing primary key values in forming IN(key1, key2, ...) for table '{0}'. ar_pk_value_count_mismatch = Composite key value count mismatch in forming IN( (key1, key2, ..), (key3, key4, ..)) for table '{0}'. -ar_must_copy_from_array_or_object = $data in {0}::copyFrom($data) must be an object or an array. \ No newline at end of file +ar_must_copy_from_array_or_object = $data in {0}::copyFrom($data) must be an object or an array. +ar_mismatch_column_names = In dynamic __call() method '{0}', no matching columns were found, valid columns for table '{2}' are '{1}'. \ No newline at end of file diff --git a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TMysqlScaffoldInput.php b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TMysqlScaffoldInput.php index c6fc6902..89df0f2e 100644 --- a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TMysqlScaffoldInput.php +++ b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TMysqlScaffoldInput.php @@ -2,7 +2,7 @@ Prado::using('System.Data.ActiveRecord.Scaffold.InputBuilder.TScaffoldInputCommon'); -class MysqlScaffoldInput extends TScaffoldInputCommon +class TMysqlScaffoldInput extends TScaffoldInputCommon { protected function createControl($container, $column, $record) { diff --git a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TPgsqlScaffoldInput.php b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TPgsqlScaffoldInput.php index 40a14fbc..f5e11eae 100644 --- a/framework/Data/ActiveRecord/Scaffold/InputBuilder/TPgsqlScaffoldInput.php +++ b/framework/Data/ActiveRecord/Scaffold/InputBuilder/TPgsqlScaffoldInput.php @@ -2,7 +2,7 @@ Prado::using('System.Data.ActiveRecord.Scaffold.InputBuilder.TScaffoldInputCommon'); -class PgsqlScaffoldInput extends ScaffoldInputCommon +class TPgsqlScaffoldInput extends TScaffoldInputCommon { protected function createControl($container, $column, $record) { diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php index dc464245..b55ceedc 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php @@ -1,12 +1,49 @@ + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2007 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + */ + +/** + * Include the base Active Record class. + */ Prado::using('System.Data.ActiveRecord.TActiveRecord'); +/** + * Base class for Active Record scaffold views. + * + * Provides common properties for all scaffold views (such as, TScaffoldListView, + * TScaffoldEditView, TScaffoldListView and TScaffoldView). + * + * During the OnPrRender stage the default css style file (filename style.css) + * is published and registered. To override the default style, provide your own stylesheet + * file explicitly. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + * @since 3.1 + */ abstract class TScaffoldBase extends TTemplateControl { + /** + * @var TActiveRecord record instance (may be new or retrieved from db) + */ private $_record; + /** + * @var TDbMetaData table/view information. + */ private $_meta; + /** + * @return TDbMetaData table/view information + */ protected function getTableMetaData() { if($this->_meta===null) @@ -18,7 +55,11 @@ abstract class TScaffoldBase extends TTemplateControl return $this->_meta; } - protected function getRecordProperties($record) + /** + * @param TActiveRecord record instance + * @return array record property values + */ + protected function getRecordPropertyValues($record) { $data = array(); foreach($this->getTableMetaData()->getColumns() as $name=>$column) @@ -26,7 +67,11 @@ abstract class TScaffoldBase extends TTemplateControl return $data; } - public function getRecordObjectPk($record) + /** + * @param TActiveRecord record instance + * @return array record primary key values. + */ + protected function getRecordPkValues($record) { $pk = array(); foreach($this->getTableMetaData()->getColumns() as $name=>$column) @@ -37,76 +82,116 @@ abstract class TScaffoldBase extends TTemplateControl return $data; } + /** + * Name of the Active Record class to be viewed or scaffolded. + * @return string Active Record class name. + */ public function getRecordClass() { return $this->getViewState('RecordClass'); } + /** + * Name of the Active Record class to be viewed or scaffolded. + * @param string Active Record class name. + */ public function setRecordClass($value) { $this->setViewState('RecordClass', $value); } - public function copyFrom(TScaffoldBase $obj) + /** + * Copy the view details from another scaffold view instance. + * @param TScaffoldBase scaffold view. + */ + protected function copyFrom(TScaffoldBase $obj) { $this->_record = $obj->_record; $this->_meta = $obj->_meta; $this->setRecordClass($obj->getRecordClass()); } + /** + * Unset the current record instance and table information. + */ protected function clearRecordObject() { $this->_record=null; $this->_meta=null; } - public function getRecordObject($pk=null) + /** + * Gets the current Active Record instance. Creates new instance if the + * primary key value is null otherwise the record is fetched from the db. + * @param array primary key value + * @return TActiveRecord record instance + */ + protected function getRecordObject($pk=null) { if($this->_record===null) { if($pk!==null) + { $this->_record=$this->getRecordFinder()->findByPk($pk); + if($this->_record===null) + throw new TConfigurationException('scaffold_invalid_record_pk', + $this->getRecordClass(), $pk); + } else { $class = $this->getRecordClass(); if($class!==null) $this->_record=Prado::createComponent($class); else - throw new TConfigurationException('scaffold_invalid_record_class', $this->getID()); + { + throw new TConfigurationException('scaffold_invalid_record_class', + $this->getRecordClass(),$this->getID()); + } } } return $this->_record; } - public function getRecordFinder() + /** + * @param TActiveRecord Active Record instance. + */ + protected function setRecordObject(TActiveRecord $value) { - return TActiveRecord::getRecordFinder(get_class($this->getRecordObject())); + $this->_record=$value; } - public function setRecordObject($value) + /** + * @return TActiveRecord Active Record finder instance + */ + protected function getRecordFinder() { - if($value instanceof TActiveRecord) - $this->_record=$value; - else - throw new TConfigurationException('scaffold_object_must_be_tactiverecord', $this->getID()); + return TActiveRecord::getRecordFinder($this->getRecordClass()); } + /** + * @return string default scaffold stylesheet name + */ public function getDefaultStyle() { return $this->getViewState('DefaultStyle', 'style'); } + /** + * @param string default scaffold stylesheet name + */ public function setDefaultStyle($value) { $this->setViewState('DefaultStyle', TPropertyValue::ensureString($value), 'style'); } + /** + * Publish the default stylesheet file. + */ public function onPreRender($param) { parent::onPreRender($param); $url = $this->publishAsset($this->getDefaultStyle().'.css'); - $cs = $this->getPage()->getClientScript(); - $cs->registerStyleSheetFile($url,$url); + $this->getPage()->getClientScript()->registerStyleSheetFile($url,$url); } } diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php index a8faa6c8..a792aeb9 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php @@ -1,29 +1,82 @@ + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2007 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + */ +/** + * Load scaffold base. + */ Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase'); -Prado::using('System.Data.ActiveRecord.Scaffold.InputBuilder.TScaffoldInputBase'); +/** + * Template control for editing an Active Record instance. + * + * The default editor input controls are created based on the column types. + * + * The editor layout can be specified by a renderer. A renderer is an external + * template control that implements IScaffoldEditRenderer. + * + * The Data of the IScaffoldEditRenderer will be set as the current Active + * Record to be edited. The UpdateRecord() method of IScaffoldEditRenderer + * is called when request to save the record is requested. + * + * Validators in the custom external editor template should have the + * {@link TBaseValidator::setValidationGroup ValidationGroup} property set to the + * value of the {@link getValidationGroup} of the TScaffoldEditView instance + * (the edit view instance is the Parent of the IScaffoldEditRenderer in most + * cases. + * + * The RecordClass determines the Active Record class to be edited. + * A particular record can be edited by specifying the {@link setRecordPk RecordPk} + * value (may be an array for composite keys). + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + * @since 3.1 + */ class TScaffoldEditView extends TScaffoldBase { - private static $_builders=array(); + /** + * @var IScaffoldEditRenderer custom scaffold edit renderer + */ private $_editRenderer; + /** + * Initialize the editor form if it is Visible. + */ public function onLoad($param) { if($this->getVisible()) $this->initializeEditForm(); } + /** + * @return string the class name for scaffold editor. Defaults to empty, meaning not set. + */ public function getEditRenderer() { return $this->getViewState('EditRenderer', ''); } + /** + * @param string the class name for scaffold editor. Defaults to empty, meaning not set. + */ public function setEditRenderer($value) { $this->setViewState('EditRenderer', $value, ''); } + /** + * @param array Active Record primary key value to be edited. + */ public function setRecordPk($value) { $this->clearRecordObject(); @@ -31,16 +84,25 @@ class TScaffoldEditView extends TScaffoldBase $this->setViewState('PK', count($val) > 0 ? $val : null); } + /** + * @return array Active Record primary key value. + */ public function getRecordPk() { return $this->getViewState('PK'); } + /** + * @return TActiveRecord current Active Record instance + */ protected function getCurrentRecord() { return $this->getRecordObject($this->getRecordPk()); } + /** + * Initialize the editor form + */ public function initializeEditForm() { $record = $this->getCurrentRecord(); @@ -60,6 +122,13 @@ class TScaffoldEditView extends TScaffoldBase } } + /** + * Instantiate the external edit renderer. + * @param TActiveRecord record to be edited + * @param string external edit renderer class name. + * @throws TConfigurationException raised when renderer is not an + * instance of IScaffoldEditRenderer. + */ protected function createEditRenderer($record, $classPath) { $this->_editRenderer = Prado::createComponent($classPath); @@ -76,7 +145,10 @@ class TScaffoldEditView extends TScaffoldBase } } - protected function repeaterItemCreated($sender, $param) + /** + * Initialize the default editor using the scaffold input builder. + */ + protected function createRepeaterEditItem($sender, $param) { $type = $param->getItem()->getItemType(); if($type==TListItemType::Item || $type==TListItemType::AlternatingItem) @@ -92,14 +164,16 @@ class TScaffoldEditView extends TScaffoldBase } } + /** + * Bubble the command name event. Stops bubbling when the page validator false. + * Otherwise, the bubble event is continued. + */ public function bubbleEvent($sender, $param) { switch(strtolower($param->getCommandName())) { case 'save': - if($this->getPage()->getIsValid()) - return $this->doSave() === true ? false : true; - return true; + return $this->doSave() ? false : true; case 'clear': $this->setRecordPk(null); $this->initializeEditForm(); @@ -109,68 +183,116 @@ class TScaffoldEditView extends TScaffoldBase } } + /** + * Check the validators, then tries to save the record. + * @return boolean true if the validators are true, false otherwise. + */ protected function doSave() { - $record = $this->getCurrentRecord(); - if($this->_editRenderer===null) + if($this->getPage()->getIsValid()) { - $table = $this->getTableMetaData(); - $builder = $this->getScaffoldInputBuilder($record); - foreach($this->getInputRepeater()->getItems() as $item) + $record = $this->getCurrentRecord(); + if($this->_editRenderer===null) { - $column = $table->getColumn($item->getCustomData()); - $builder->loadScaffoldInput($this, $item, $column, $record); + $table = $this->getTableMetaData(); + $builder = $this->getScaffoldInputBuilder($record); + foreach($this->getInputRepeater()->getItems() as $item) + { + $column = $table->getColumn($item->getCustomData()); + $builder->loadScaffoldInput($this, $item, $column, $record); + } } + else + { + $this->_editRenderer->updateRecord($record); + } + $record->save(); + return true; } - else - { - $this->_editRenderer->updateRecord($record); - } - - $record->save(); - return true; + return false; } + /** + * @return TRepeater default editor input controls repeater + */ protected function getInputRepeater() { $this->ensureChildControls(); return $this->getRegisteredObject('_repeater'); } + /** + * @return TButton Button triggered to save the Active Record. + */ public function getSaveButton() { $this->ensureChildControls(); return $this->getRegisteredObject('_save'); } + /** + * @return TButton Button to clear the editor inputs. + */ public function getClearButton() { $this->ensureChildControls(); return $this->getRegisteredObject('_clear'); } + /** + * @return TButton Button to cancel the edit action (e.g. hide the edit view). + */ public function getCancelButton() { $this->ensureChildControls(); return $this->getRegisteredObject('_cancel'); } + /** + * Create the default scaffold editor control factory. + * @param TActiveRecord record instance. + * @return TScaffoldInputBase scaffold editor control factory. + */ protected function getScaffoldInputBuilder($record) { + static $_builders=array(); $class = get_class($record); - if(!isset(self::$_builders[$class])) - self::$_builders[$class] = TScaffoldInputBase::createInputBuilder($record); - return self::$_builders[$class]; + if(!isset($_builders[$class])) + { + Prado::using('System.Data.ActiveRecord.Scaffold.InputBuilder.TScaffoldInputBase'); + $_builders[$class] = TScaffoldInputBase::createInputBuilder($record); + } + return $_builders[$class]; } + /** + * @return string editor validation group name. + */ public function getValidationGroup() { return 'group_'.$this->getUniqueID(); } } +/** + * IScaffoldEditRenderer interface. + * + * IScaffoldEditRenderer defines the interface that an edit renderer + * needs to implement. Besides the {@link getData Data} property, an edit + * renderer also needs to provide {@link updateRecord updateRecord} method + * that is called before the save() method is called on the TActiveRecord. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + * @since 3.1 + */ interface IScaffoldEditRenderer extends IDataRenderer { + /** + * This method should update the record with the user input data. + * @param TActiveRecord record to be saved. + */ public function updateRecord($record); } diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.tpl b/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.tpl index 8cba7ec4..884ec2a3 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.tpl +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.tpl @@ -1,7 +1,8 @@ +

- + -
ItemIndex % 2 %> input_<%# $this->ItemIndex %> property_<%# $this->DataItem->Property %>"> @@ -16,4 +17,5 @@ ValidationGroup %>/> +
\ No newline at end of file diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php index 35c53473..2ac3fe99 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php @@ -1,7 +1,28 @@ + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2007 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + */ + +/** + * Load the scaffold base class. + */ Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase'); +/** + * TScaffoldListView displays instance of Active Record class. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + * @since 3.1 + */ class TScaffoldListView extends TScaffoldBase { public function onLoad($param) @@ -32,31 +53,26 @@ class TScaffoldListView extends TScaffoldBase public function onPreRender($param) { parent::onPreRender($param); - $this->initializeItemCount(); $this->loadRecordData(); } - protected function initializeItemCount() - { - $this->_list->setVirtualItemCount($this->getRecordFinder()->count()); - } - protected function loadRecordData() { + $this->_list->setVirtualItemCount($this->getRecordFinder()->count()); $finder = $this->getRecordFinder(); - $criteria = $this->getPagingCriteria(); + $criteria = $this->getRecordCriteria(); $this->_list->setDataSource($finder->findAll($criteria)); $this->_list->dataBind(); } - protected function getPagingCriteria() + protected function getRecordCriteria() { $total = $this->_list->getVirtualItemCount(); $limit = $this->_list->getPageSize(); $offset = $this->_list->getCurrentPageIndex()*$limit; if($offset + $limit > $total) $limit = $total - $offset; - $criteria = new TActiveRecordCriteria; + $criteria = new TActiveRecordCriteria($this->getSearchCondition(), $this->getSearchParameters()); $criteria->setLimit($limit); $criteria->setOffset($offset); $order = explode(' ',$this->_sort->getSelectedValue(), 2); @@ -65,6 +81,26 @@ class TScaffoldListView extends TScaffoldBase return $criteria; } + public function setSearchCondition($value) + { + $this->setViewState('SearchCondition', $value); + } + + public function getSearchCondition() + { + return $this->getViewState('SearchCondition'); + } + + public function setSearchParameters($value) + { + $this->setViewState('SearchParameters', TPropertyValue::ensureArray($value),array()); + } + + public function getSearchParameters() + { + return $this->getViewState('SearchParameters', array()); + } + public function bubbleEvent($sender, $param) { switch(strtolower($param->getCommandName())) @@ -72,21 +108,26 @@ class TScaffoldListView extends TScaffoldBase case 'delete': return $this->deleteRecord($sender, $param); case 'edit': - if(($ctrl=$this->getEditViewControl())!==null) - { - if($param instanceof TRepeaterCommandEventParameter) - { - $pk = $param->getItem()->getCustomData(); - $ctrl->setRecordPk($pk); - $ctrl->initializeEditForm(); - } - } + $this->initializeEdit($sender, $param); } $this->raiseBubbleEvent($this, $param); return true; } - public function deleteRecord($sender, $param) + protected function initializeEdit($sender, $param) + { + if(($ctrl=$this->getEditViewControl())!==null) + { + if($param instanceof TRepeaterCommandEventParameter) + { + $pk = $param->getItem()->getCustomData(); + $ctrl->setRecordPk($pk); + $ctrl->initializeEditForm(); + } + } + } + + protected function deleteRecord($sender, $param) { if($param instanceof TRepeaterCommandEventParameter) { @@ -111,10 +152,10 @@ class TScaffoldListView extends TScaffoldBase $item = $param->getItem(); if(($data = $item->getData()) !== null) { - $item->setCustomData($this->getRecordObjectPk($data)); + $item->setCustomData($this->getRecordPkValues($data)); if(($prop = $item->findControl('_properties'))!==null) { - $item->_properties->setDataSource($this->getRecordProperties($data)); + $item->_properties->setDataSource($this->getRecordPropertyValues($data)); $item->_properties->dataBind(); } } diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.tpl b/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.tpl index 7b8854f0..c70e864d 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.tpl +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.tpl @@ -1,4 +1,4 @@ - +
@@ -21,7 +21,7 @@ PageSize="10">
- + @@ -29,20 +29,20 @@ - + - NamingContainer->Parent->EditViewID !== Null %> - CommandName="edit" + CommandName="edit" CssClass="edit-button" CausesValidation="false" /> - - +
@@ -53,4 +53,5 @@ ControlToPaginate="_list" PageButtonCount="10" Mode="Numeric" - OnPageIndexChanged="pageChanged" /> \ No newline at end of file + OnPageIndexChanged="pageChanged" /> +
\ No newline at end of file diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php new file mode 100644 index 00000000..a47a1a47 --- /dev/null +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php @@ -0,0 +1,89 @@ +_list===null && ($id = $this->getListViewID()) !== null) + { + $this->_list = $this->getParent()->findControl($id); + if($this->_list ===null) + throw new TConfigurationException('scaffold_unable_to_find_list_view', $id); + } + return $this->_list; + } + + public function setListView($value) + { + $this->_list = $value; + } + + public function setListViewID($value) + { + $this->setViewState('ListViewID', $value); + } + + public function getListViewID() + { + return $this->getViewState('ListViewID'); + } + + public function bubbleEvent($sender, $param) + { + if(strtolower($param->getCommandName())==='search') + { + if(($list = $this->getListView()) !== null) + { + $list->setSearchCondition($this->createSearchCondition()); + $list->setSearchParameters(array()); + return false; + } + } + $this->raiseBubbleEvent($this, $param); + return true; + } + + protected function createSearchCondition() + { + $table = $this->getTableMetaData(); + if(strlen($str=$this->getSearchText()->getText()) > 0) + return $table->getSearchRegExpCriteria($this->getFields(), $str); + } + + protected function getFields() + { + if(strlen(trim($str=$this->getSearchableFields()))>0) + $fields = preg_split('/\s*,\s*/', $str); + else + $fields = array_keys($this->getTableMetaData()->getColumns()); + return $fields; + } + + public function getSearchableFields() + { + return $this->getViewState('SearchableFields',''); + } + + public function setSearchableFields($value) + { + $this->setViewState('SearchableFields', $value, ''); + } + + public function getSearchButton() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_search'); + } + + public function getSearchText() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_textbox'); + } +} + +?> \ No newline at end of file diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.tpl b/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.tpl new file mode 100644 index 00000000..a5f56b55 --- /dev/null +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.tpl @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php index 668ede61..5401c764 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php @@ -3,6 +3,7 @@ Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase'); Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldListView'); Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldEditView'); +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldSearch'); class TScaffoldView extends TScaffoldBase { @@ -11,6 +12,7 @@ class TScaffoldView extends TScaffoldBase parent::onLoad($param); $this->getListView()->copyFrom($this); $this->getEditView()->copyFrom($this); + $this->getSearchControl()->copyFrom($this); } public function getListView() @@ -25,6 +27,12 @@ class TScaffoldView extends TScaffoldBase return $this->getRegisteredObject('_editView'); } + public function getSearchControl() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_search'); + } + public function getAddButton() { $this->ensureChildControls(); @@ -50,6 +58,7 @@ class TScaffoldView extends TScaffoldBase $this->getListView()->setVisible(false); $this->getEditView()->setVisible(true); $this->getAddButton()->setVisible(false); + $this->getSearchControl()->setVisible(false); $this->getEditView()->getCancelButton()->setVisible(true); $this->getEditView()->getClearButton()->setVisible(false); } @@ -59,6 +68,7 @@ class TScaffoldView extends TScaffoldBase $this->getListView()->setVisible(true); $this->getEditView()->setVisible(false); $this->getAddButton()->setVisible(true); + $this->getSearchControl()->setVisible(true); } protected function showAddView($sender, $param) diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldView.tpl b/framework/Data/ActiveRecord/Scaffold/TScaffoldView.tpl index 0ae8b8b0..01cceb07 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldView.tpl +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldView.tpl @@ -1,7 +1,9 @@ +
+ - -
+ -
+ - \ No newline at end of file + +
\ No newline at end of file diff --git a/framework/Data/ActiveRecord/Scaffold/style.css b/framework/Data/ActiveRecord/Scaffold/style.css index eb31e9a5..a1fc7a16 100644 --- a/framework/Data/ActiveRecord/Scaffold/style.css +++ b/framework/Data/ActiveRecord/Scaffold/style.css @@ -1,3 +1,4 @@ +/* $Id$ */ body { font-family: Cambria, Georgia, "Times New Roman", Times, serif; @@ -116,7 +117,7 @@ body font-weight: bold; padding: 0.2em; } -.edit-inputs .required-input +.edit-inputs .required-input, .edit-inputs .required-input2 { border: 1px solid red; background-color: #FFF5EE; diff --git a/framework/Data/ActiveRecord/TActiveRecord.php b/framework/Data/ActiveRecord/TActiveRecord.php index 91ae971a..bdb03596 100644 --- a/framework/Data/ActiveRecord/TActiveRecord.php +++ b/framework/Data/ActiveRecord/TActiveRecord.php @@ -384,6 +384,8 @@ abstract class TActiveRecord extends TComponent /** * Find records using full SQL, returns corresponding record object. + * The names of the column retrieved must be defined in your Active Record + * class. * @param string select SQL * @param array $parameters * @return array matching active records. @@ -439,8 +441,8 @@ abstract class TActiveRecord extends TComponent * $finder->find('Name = ?', $name); * * - * $finder->findByUsernameAndPassword($name,$pass); - * $finder->findBy_Username_And_Password($name,$pass); + * $finder->findByUsernameAndPassword($name,$pass); // OR may be used + * $finder->findBy_Username_And_Password($name,$pass); // _OR_ may be used * $finder->find('Username = ? AND Password = ?', $name, $pass); * * @@ -481,13 +483,43 @@ abstract class TActiveRecord extends TComponent */ private function createCriteriaFromString($method, $condition, $args) { - $fields = array(); - foreach(preg_split('/and|_and_/i',$condition) as $field) - $fields[] = $field.' = ?'; + $fields = $this->extractMatchingConditions($method, $condition); $args=count($args) === 1 && is_array($args[0]) ? $args[0] : $args; if(count($fields)>count($args)) - throw new TActiveRecordException('ar_mismatch_args_exception',$method,count($fields),count($args)); - return new TActiveRecordCriteria(implode(' AND ',$fields),$args); + { + throw new TActiveRecordException('ar_mismatch_args_exception', + $method,count($fields),count($args)); + } + return new TActiveRecordCriteria(implode(' ',$fields),$args); + } + + /** + * Calculates the AND/OR condition from dynamic method substrings using + * table meta data, allows for any AND-OR combinations. + * @param string dynamic method name + * @param string dynamic method search criteria + * @return array search condition substrings + */ + private function extractMatchingConditions($method, $condition) + { + $meta = $this->getRecordManager()->getRecordGateway()->getMetaData($this); + $search = implode('|', $meta->getColumnNames()); + $regexp = '/('.$search.')(and|_and_|or|_or_)?/i'; + $matches = array(); + if(!preg_match_all($regexp, strtolower($condition), $matches,PREG_SET_ORDER)) + { + throw new TActiveRecordException('ar_mismatch_column_names', + $method, implode(', ', $meta->getColumnNames()), $meta->getTableName()); + } + $fields = array(); + foreach($matches as $match) + { + $sql = $meta->getColumn($match[1])->getName() . ' = ? '; + if(count($match) > 2) + $sql .= strtoupper(str_replace('_', '', $match[2])); + $fields[] = $sql; + } + return $fields; } } ?> \ No newline at end of file diff --git a/framework/Data/ActiveRecord/TActiveRecordManager.php b/framework/Data/ActiveRecord/TActiveRecordManager.php index 7f239a34..bd672c3b 100644 --- a/framework/Data/ActiveRecord/TActiveRecordManager.php +++ b/framework/Data/ActiveRecord/TActiveRecordManager.php @@ -164,6 +164,9 @@ class TActiveRecordManager extends TComponent case 'sqlite2': //sqlite 2 Prado::using('System.Data.ActiveRecord.Vendor.TSqliteMetaDataInspector'); return new TSqliteMetaDataInspector($conn); + case 'ibm': + Prado::using('System.Data.ActiveRecord.Vendor.TIbmMetaDataInspector'); + return new TIbmMetaDataInspector($conn); default: throw new TActiveRecordConfigurationException( 'ar_invalid_database_driver',$driver); diff --git a/framework/Data/ActiveRecord/Vendor/TDbMetaData.php b/framework/Data/ActiveRecord/Vendor/TDbMetaData.php index 38a82aef..3a959ba4 100644 --- a/framework/Data/ActiveRecord/Vendor/TDbMetaData.php +++ b/framework/Data/ActiveRecord/Vendor/TDbMetaData.php @@ -20,8 +20,7 @@ * @version $Id$ * @package System.Data.ActiveRecord.Vendor * @since 3.1 - */ - + */ abstract class TDbMetaData extends TComponent { private $_primaryKeys=array(); @@ -91,6 +90,11 @@ abstract class TDbMetaData extends TComponent return $this->_columns[$name]; } + public function getColumnNames() + { + return array_keys($this->_columns); + } + /** * Post process the rows after returning from a 1 row query. * @param mixed row data, may be null. @@ -187,7 +191,7 @@ abstract class TDbMetaData extends TComponent foreach($keys as $i => $key) { $value = isset($values[$i]) ? $values[$i] : $values[$key]; - $command->bindValue(':'.$key, $value); + $this->bindValue($command, ':'.$key, $value); } $command->prepare(); } @@ -249,7 +253,7 @@ abstract class TDbMetaData extends TComponent /** * Gets a comma delimited string of name parameters for update. -x * @param array name value pairs of columns for update. + * @param array name value pairs of columns for update. * @return string update named parameter string. */ protected function getUpdateBindings($columns) @@ -294,19 +298,27 @@ x * @param array name value pairs of columns for update. if($criteria->getIsNamedParameters()) { foreach($criteria->getParameters() as $name=>$value) - $command->bindValue($name,$value); + $this->bindValue($command, $name, $value); } else { $index=1; foreach($criteria->getParameters() as $value) - $command->bindValue($index++,$value); + $this->bindValue($command, $index++,$value); } } $command->prepare(); return $command; } + protected function bindValue($command, $name, $value) + { + if(is_bool($value)) + $command->bindValue($name,$value, PDO::PARAM_BOOL); + else + $command->bindValue($name,$value); + } + /** * Bind parameter values. */ @@ -316,9 +328,9 @@ x * @param array name value pairs of columns for update. foreach($parameters as $key=>$value) { if(is_string($key)) - $command->bindValue($key,$value); + $this->bindValue($command,$key,$value); else - $command->bindValue($index++,$value); + $this->bindValue($command, $index++,$value); } $command->prepare(); } @@ -345,5 +357,15 @@ x * @param array name value pairs of columns for update. return implode(', ', $fields); } + /** + * @param string ordering column name + * @param string ordering direction + * @return string DESC or ASC + */ + protected function getOrdering($by, $direction) + { + $dir = strtolower($direction) == 'desc' ? 'DESC' : 'ASC'; + return $this->getColumn($by)->getName(). ' '.$dir; + } } ?> \ No newline at end of file diff --git a/framework/Data/ActiveRecord/Vendor/TDbMetaDataCommon.php b/framework/Data/ActiveRecord/Vendor/TDbMetaDataCommon.php index 826654dc..a41e87ad 100644 --- a/framework/Data/ActiveRecord/Vendor/TDbMetaDataCommon.php +++ b/framework/Data/ActiveRecord/Vendor/TDbMetaDataCommon.php @@ -186,7 +186,7 @@ abstract class TDbMetaDataCommon extends TDbMetaData return $command; } - + /** * SQL command to delete records by criteria * @param TDbConnection database connection. diff --git a/framework/Data/ActiveRecord/Vendor/TIbmColumnMetaData.php b/framework/Data/ActiveRecord/Vendor/TIbmColumnMetaData.php new file mode 100644 index 00000000..65e40a59 --- /dev/null +++ b/framework/Data/ActiveRecord/Vendor/TIbmColumnMetaData.php @@ -0,0 +1,149 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2007 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Vendor + */ + +/** + * TIbmColumnMetaData class. + * + * Column details for IBM DB2 database. Using php_pdo_ibm.dll extension. + * + * @author Cesar Ramos + * @version $Id$ + * @package System.Data.ActiveRecord.Vendor + * @since 3.1 + */ +class TIbmColumnMetaData extends TComponent +{ + private $_name; + private $_type; + private $_length; + private $_autoIncrement; + private $_default; + private $_notNull=true; + + private $_isPrimary=null; + + private $_property; + + /** + * Initialize column meta data. + * + * @param string column name. + * @param string column data type. + * @param string column data length. + * @param boolean column can not be null. + * @param string serial name. + * @param string default value. + */ + public function __construct($name,$type,$length,$notNull,$autoIncrement,$default,$primary) + { + $this->_property = $name; + $this->_name=$name; + $this->_type=$type; + $this->_length=$length; + $this->_notNull=$notNull; + $this->_autoIncrement=$autoIncrement; + $this->_default=$default; + $this->_isPrimary=$primary; + } + + /** + * @return string quoted column name. + */ + public function getName() + { + return $this->_name; + } + + /** + * @return string active record property name + */ + public function getProperty() + { + return $this->_property; + } + + /** + * @return boolean true if column is a sequence, false otherwise. + */ + public function hasSequence() + { + return $this->_autoIncrement; + } + + /** + * @return null no sequence name. + */ + public function getSequenceName() + { + return null; + } + + /** + * @return boolean true if the column is a primary key, or part of a composite primary key. + */ + public function getIsPrimaryKey() + { + return $this->_isPrimary; + } + + /** + * @return string column type + */ + public function getType() + { + return $this->_type; + } + + + /** + * @return boolean false if column can be null, true otherwise. + */ + public function getNotNull() + { + return $this->_notNull; + } + + /** + * @return boolean true if column has default value, false otherwise. + */ + public function hasDefault() + { + return $this->_default !== null; + } + + /** + * @return string default column value. + */ + public function getDefaultValue() + { + return $this->_default; + } + + /** + * @return string PHP primative type derived from the column type. + */ + public function getPHPType() + { + switch(strtolower($this->_type)) + { + case 'smallint': case 'integer': + return 'integer'; + case 'real': case 'float': case 'double': case 'decimal': case 'bigint': + return 'float'; + default: + return 'string'; + } + } + +} + +?> \ No newline at end of file diff --git a/framework/Data/ActiveRecord/Vendor/TIbmMetaData.php b/framework/Data/ActiveRecord/Vendor/TIbmMetaData.php new file mode 100644 index 00000000..4f923406 --- /dev/null +++ b/framework/Data/ActiveRecord/Vendor/TIbmMetaData.php @@ -0,0 +1,80 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2007 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Vendor + */ +Prado::using('System.Data.ActiveRecord.Vendor.TDbMetaDataCommon'); + +/** + * TIbmMetaData class. + * + * Column details for IBM DB2 database. Using php_pdo_ibm.dll extension. + * + * Does not support LIMIT and OFFSET criterias. + * + * @author Cesar Ramos + * @version $Id$ + * @package System.Data.ActiveRecord.Vendor + * @since 3.1 + */ +class TIbmMetaData extends TDbMetaDataCommon +{ + /** + * Build the SQL search string from the criteria object for IBM DB2 database. + * @param TDbConnection database connection. + * @param TActiveRecordCriteria search criteria. + * @return string SQL search. + */ + protected function getSqlFromCriteria($conn, $criteria) + { + if($criteria===null) return ''; + $sql = ''; + if(($condition = $criteria->getCondition())!==null) + $sql .= ' WHERE '.$condition; + $orders=array(); + foreach($criteria->getOrdersBy() as $by=>$ordering) + $orders[] = $this->getOrdering($by, $ordering); + if(count($orders) > 0) + $sql .= ' ORDER BY '.implode(', ', $orders); + //if(($limit = $criteria->getLimit())!==null) + //{ + // $sql .= ' FETCH FIRST '.intval($limit).' ROWS ONLY'; + //} + return strlen($sql) > 0 ? $sql : ''; + } + + /** + * Lowercase the data keys, IBM DB2 returns uppercase column names + * @param mixed record row + * @return array record row + */ + public function postQueryRow($row) + { + if(!is_array($row)) return $row; + $result=array(); + foreach($row as $k=>$v) + $result[strtolower($k)]=$v; + return $result; + } + + /** + * Lowercase the data keys, IBM DB2 returns uppercase column names + * @param mixed record row + * @return array record row + */ + public function postQuery($rows) + { + $data = array(); + foreach($rows as $k=>$v) + $data[$k] = $this->postQueryRow($v); + return $data; + } +} +?> \ No newline at end of file diff --git a/framework/Data/ActiveRecord/Vendor/TIbmMetaDataInspector.php b/framework/Data/ActiveRecord/Vendor/TIbmMetaDataInspector.php new file mode 100644 index 00000000..0ccc05ae --- /dev/null +++ b/framework/Data/ActiveRecord/Vendor/TIbmMetaDataInspector.php @@ -0,0 +1,113 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2007 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Vendor + */ +Prado::using('System.Data.ActiveRecord.Vendor.TDbMetaDataInspector'); +Prado::using('System.Data.ActiveRecord.Vendor.TIbmColumnMetaData'); +Prado::using('System.Data.ActiveRecord.Vendor.TIbmMetaData'); + +/** + * TIbmMetaDataInspector class. + * + * Column details for IBM DB2 database. Using php_pdo_ibm.dll extension. + * + * @author Cesar Ramos + * @version $Id$ + * @package System.Data.ActiveRecord.Vendor + * @since 3.1 + */ +class TIbmMetaDataInspector extends TDbMetaDataInspector +{ + private $_schema; + + /** + * @param string default schema. + */ + public function setSchema($schema) + { + $this->_schema=$schema; + } + + /** + * @return string default schema. + */ + public function getSchema() + { + return $this->_schema; + } + /** + * Get the column definitions for given table. + * @param string table name. + * @return array column name value pairs of column meta data. + */ + protected function getColumnDefinitions($table) + { + if(count($parts= explode('.', $table)) > 1) + { + $tablename = $parts[1]; + $schema = $parts[0]; + } + else + { + $tablename = $parts[0]; + $schema = $this->getSchema(); + } + $sql="SELECT * FROM SYSCAT.COLUMNS WHERE TABNAME='".strtoupper($tablename)."'"; + if ($schema) + $sql=$sql." AND TABSCHEMA='".strtoupper($schema)."'"; + + $conn = $this->getDbConnection(); + $conn->setActive(true); + $command = $conn->createCommand($sql); + $command->prepare(); + $result=$command->query($sql); + foreach ($result as $col) + $cols[strtolower($col['COLNAME'])] = $this->getColumnMetaData($col); + return $cols; + } + + protected function getColumnMetaData($col) + { + $name = strtolower($col['COLNAME']); + $type = $col['TYPENAME']; + $length = $col['LENGTH']; + $notNull = $col['NULLS']==='N'; + $autoIncrement=$col['IDENTITY']==='N'; + $default = $col['DEFAULT']; + $primaryKey = $col['KEYSEQ']?1:0; + return new TIbmColumnMetaData($name,$type,$length,$notNull,$autoIncrement,$default,$primaryKey); + } + + /** + * Not implemented, IBM does not always have foreign key constraints. + */ + protected function getConstraintKeys($table) + { + return array('primary'=>array(), 'foreign'=>array()); + } + + /** + * Create a new instance of meta data. + * @param string table name + * @param array column meta data + * @param array primary key meta data + * @param array foreign key meta data. + * @return TDbMetaData table meta data. + */ + protected function createMetaData($table, $columns, $primary, $foreign) + { + $pks = array(); + foreach($columns as $name=>$column) + if($column->getIsPrimaryKey()) + $pks[] = $name; + return new TIbmMetaData($table,$columns,$pks); + } +} +?> \ No newline at end of file diff --git a/framework/Data/ActiveRecord/Vendor/TMysqlMetaData.php b/framework/Data/ActiveRecord/Vendor/TMysqlMetaData.php index 0cbd0b7c..03aba53f 100644 --- a/framework/Data/ActiveRecord/Vendor/TMysqlMetaData.php +++ b/framework/Data/ActiveRecord/Vendor/TMysqlMetaData.php @@ -1,4 +1,4 @@ - 0 ? $sql : ''; } - protected function getOrdering($by, $direction) + public function getSearchRegExpCriteria($fields, $keywords) { - $dir = strtolower($direction) == 'desc' ? 'DESC' : 'ASC'; - return $this->getColumn($by)->getName(). ' '.$dir; + if(strlen(trim($keywords)) == 0) return ''; + $words = preg_split('/\s/', preg_quote($keywords, '\'')); + $result = array(); + foreach($fields as $field) + { + $column = $this->getColumn($field); + $result[] = $this->getRegexpCriteriaStr($column->getName(), $words); + } + return '('.implode(' OR ', $result).')'; + } + + protected function getRegexpCriteriaStr($column, $words) + { + $regexp = implode('|', $words); + return "({$column} REGEXP '{$regexp}')"; } + } ?> \ No newline at end of file diff --git a/framework/Data/ActiveRecord/Vendor/TMysqlMetaDataInspector.php b/framework/Data/ActiveRecord/Vendor/TMysqlMetaDataInspector.php index 5e438e37..23c483d1 100644 --- a/framework/Data/ActiveRecord/Vendor/TMysqlMetaDataInspector.php +++ b/framework/Data/ActiveRecord/Vendor/TMysqlMetaDataInspector.php @@ -1,4 +1,4 @@ -createCommand($sql); $command->prepare(); foreach($command->query() as $col) - $cols[$col['Field']] = $this->getColumnMetaData($col); + $cols[strtolower($col['Field'])] = $this->getColumnMetaData($col); return $cols; } @@ -51,7 +51,8 @@ class TMysqlMetaDataInspector extends TDbMetaDataInspector $autoIncrement=is_int(strpos(strtolower($col['Extra']), 'auto_increment')); $default = $col['Default']; $primaryKey = $col['Key']==='PRI'; - return new TMysqlColumnMetaData($col['Field'],$name,$type,$notNull,$autoIncrement,$default,$primaryKey); + return new TMysqlColumnMetaData(strtolower($col['Field']),$name,$type, + $notNull,$autoIncrement,$default,$primaryKey); } /** diff --git a/framework/Data/ActiveRecord/Vendor/TPgsqlColumnMetaData.php b/framework/Data/ActiveRecord/Vendor/TPgsqlColumnMetaData.php index a51c435d..2774bb54 100644 --- a/framework/Data/ActiveRecord/Vendor/TPgsqlColumnMetaData.php +++ b/framework/Data/ActiveRecord/Vendor/TPgsqlColumnMetaData.php @@ -44,13 +44,30 @@ class TPgsqlColumnMetaData extends TComponent { $this->_property=$property; $this->_name=$name; - $this->_type=$type; $this->_length=$length; + $this->processType($type); $this->_notNull=$notNull; $this->_sequenceName=$serial; $this->_default=$default; } + protected function processType($type) + { + if(is_int($pos=strpos($type, '('))) + { + $match=array(); + if(preg_match('/\((.*)\)/', $type, $match)) + { + $this->_length=floatval($match[1]); + $this->_type = substr($type,0,$pos); + } + else + $this->_type = $type; + } + else + $this->_type = $type; + } + /** * @return string quoted column name. */ diff --git a/framework/Data/ActiveRecord/Vendor/TPgsqlMetaData.php b/framework/Data/ActiveRecord/Vendor/TPgsqlMetaData.php index d968267a..45e9c7e4 100644 --- a/framework/Data/ActiveRecord/Vendor/TPgsqlMetaData.php +++ b/framework/Data/ActiveRecord/Vendor/TPgsqlMetaData.php @@ -48,11 +48,31 @@ class TPgsqlMetaData extends TDbMetaDataCommon return strlen($sql) > 0 ? $sql : ''; } - protected function getOrdering($by, $direction) + public function getSearchRegExpCriteria($fields, $keywords) { - $dir = strtolower($direction) == 'desc' ? 'DESC' : 'ASC'; - return $this->getColumn($by)->getName(). ' '.$dir; + if(strlen(trim($keywords)) == 0) return ''; + $words = preg_split('/\s/', preg_quote($keywords, '\'')); + $result = array(); + foreach($fields as $field) + { + $column = $this->getColumn($field); + if($this->isSearchableColumn($column)) + $result[] = $this->getRegexpCriteriaStr($column->getName(), $words); + } + return '('.implode(' OR ', $result).')'; } + + protected function isSearchableColumn($column) + { + $type = strtolower($column->getType()); + return $type === 'character varying' || $type === 'varchar' || + $type === 'character' || $type === 'char' || $type === 'text'; + } + protected function getRegexpCriteriaStr($column, $words) + { + $regexp = implode('|', $words); + return "({$column} ~ '{$regexp}')"; + } } ?> \ No newline at end of file diff --git a/framework/Data/ActiveRecord/Vendor/TPgsqlMetaDataInspector.php b/framework/Data/ActiveRecord/Vendor/TPgsqlMetaDataInspector.php index 2f7202cf..16326353 100644 --- a/framework/Data/ActiveRecord/Vendor/TPgsqlMetaDataInspector.php +++ b/framework/Data/ActiveRecord/Vendor/TPgsqlMetaDataInspector.php @@ -130,7 +130,7 @@ EOD; $command->bindValue(':schema', $schema); $cols = array(); foreach($command->query() as $col) - $cols[$col['attname']] = $this->getColumnMetaData($schema,$col); + $cols[strtolower($col['attname'])] = $this->getColumnMetaData($schema,$col); return $cols; } @@ -148,19 +148,26 @@ EOD; // A specific constant in the 7.0 source, the length is offset by 4. $length = $col['atttypmod'] > 0 ? $col['atttypmod'] - 4 : null; $notNull = $col['attnotnull']; - $serial = $col['attisserial'] ? $schema.'.'.$this->getSerialName($col['adsrc']) : null; + $nextval_serial = substr($col['adsrc'],0,8) === 'nextval('; + $serial = $col['attisserial'] || $nextval_serial ? $this->getSerialName($schema,$col['adsrc']) : null; $default = $serial === null && $col['atthasdef'] ? $col['adsrc'] : null; - return new TPgsqlColumnMetaData($col['attname'],$name,$type,$length,$notNull,$serial,$default); + return new TPgsqlColumnMetaData(strtolower($col['attname']),$name, + $type,$length,$notNull,$serial,$default); } /** * @return string serial name if found, null otherwise. */ - protected function getSerialName($src) + protected function getSerialName($schema,$src) { $matches = array(); - if(preg_match('/nextval\(\'([^\']+)\'::regclass\)/i',$src,$matches)) - return $matches[1]; + if(preg_match('/nextval\([^\']*\'([^\']+)\'[^\)]*\)/i',$src,$matches)) + { + if(is_int(strpos($matches[1], '.'))) + return $matches[1]; + else + return $schema.'.'.$matches[1]; + } } /** diff --git a/framework/Data/ActiveRecord/Vendor/TSqliteMetaData.php b/framework/Data/ActiveRecord/Vendor/TSqliteMetaData.php index c82a99ad..c44d73cb 100644 --- a/framework/Data/ActiveRecord/Vendor/TSqliteMetaData.php +++ b/framework/Data/ActiveRecord/Vendor/TSqliteMetaData.php @@ -48,10 +48,23 @@ class TSqliteMetaData extends TDbMetaDataCommon return strlen($sql) > 0 ? $sql : ''; } - protected function getOrdering($by, $direction) + public function getSearchRegExpCriteria($fields, $keywords) { - $dir = strtolower($direction) == 'desc' ? 'DESC' : 'ASC'; - return $this->getColumn($by)->getName(). ' '.$dir; + if(strlen(trim($keywords)) == 0) return ''; + $words = array(); + preg_match_all('/([a-zA-Z0-9-+]+)/', $keywords, $words); + $result = array(); + foreach($fields as $field) + $result[] = $this->getLikeCriteriaStr($this->getColumn($field)->getName(), $words[0]); + return '('.implode(' OR ', $result).')'; + } + + protected function getLikeCriteriaStr($column, $words) + { + $result=array(); + foreach($words as $word) + $result[] = "({$column} LIKE \"%{$word}%\")"; + return '('.implode(' AND ', $result).')'; } /** diff --git a/framework/Data/ActiveRecord/Vendor/TSqliteMetaDataInspector.php b/framework/Data/ActiveRecord/Vendor/TSqliteMetaDataInspector.php index 3621c666..1d4599a8 100644 --- a/framework/Data/ActiveRecord/Vendor/TSqliteMetaDataInspector.php +++ b/framework/Data/ActiveRecord/Vendor/TSqliteMetaDataInspector.php @@ -55,7 +55,7 @@ class TSqliteMetaDataInspector extends TDbMetaDataInspector $command->prepare(); $cols = array(); foreach($command->query() as $col) - $cols[$col['name']] = $this->getColumnMetaData($col); + $cols[strtolower($col['name'])] = $this->getColumnMetaData($col); return $cols; } @@ -73,7 +73,8 @@ class TSqliteMetaDataInspector extends TDbMetaDataInspector $primary = $col['pk']==='1'; $autoIncrement = strtolower($type)==='integer' && $primary; $default = $col['dflt_value']; - return new TSqliteColumnMetaData($col['name'],$name,$type,$notNull,$autoIncrement,$default,$primary); + return new TSqliteColumnMetaData(strtolower($col['name']),$name,$type, + $notNull,$autoIncrement,$default,$primary); } /** diff --git a/framework/I18N/core/ChoiceFormat.php b/framework/I18N/core/ChoiceFormat.php index 29391135..d36ab57b 100644 --- a/framework/I18N/core/ChoiceFormat.php +++ b/framework/I18N/core/ChoiceFormat.php @@ -20,18 +20,18 @@ /** * ChoiceFormat class. - * - * ChoiceFormat converts between ranges of numeric values and string + * + * ChoiceFormat converts between ranges of numeric values and string * names for those ranges. * - * A ChoiceFormat splits the real number line -Inf to +Inf into two or - * more contiguous ranges. Each range is mapped to a string. - * ChoiceFormat is generally used in a MessageFormat for displaying + * A ChoiceFormat splits the real number line -Inf to +Inf into two or + * more contiguous ranges. Each range is mapped to a string. + * ChoiceFormat is generally used in a MessageFormat for displaying * grammatically correct plurals such as "There are 2 files." * * * $string = '[0] are no files |[1] is one file |(1,Inf] are {number} files'; - * + * * $formatter = new MessageFormat(...); //init for a source * $translated = $formatter->format($string); * @@ -44,7 +44,7 @@ * # [1,2] -- accepts values between 1 and 2, inclusive. * # (1,2) -- accepts values between 1 and 2, excluding 1 and 2. * # {1,2,3,4} -- only values defined in the set are accepted. - * # [-Inf,0) -- accepts value greater or equal to negative infinity + * # [-Inf,0) -- accepts value greater or equal to negative infinity * and strictly less than 0 * Any non-empty combinations of the delimiters of square and round brackets * are acceptable. @@ -57,26 +57,26 @@ class ChoiceFormat { /** * The pattern to validate a set notation - * @var string + * @var string */ - protected $validate = '/[\(\[\{]|[-Inf\d]+|,|[\+Inf\d]+|[\)\]\}]/ms'; + protected $validate = '/[\(\[\{]|[-Inf\d:\s]+|,|[\+Inf\d\s:\?\-=!><%\|&\(\)]+|[\)\]\}]/ms'; /** * The pattern to parse the formatting string. - * @var string + * @var string */ - protected $parse = '/\s?\|?([\(\[\{]([-Inf\d]+,?[\+Inf\d]*)+[\)\]\}])\s?/'; + protected $parse = '/\s*\|?([\(\[\{]([-Inf\d:\s]+,?[\+Inf\d\s:\?\-=!><%\|&\(\)]*)+[\)\]\}])\s*/'; /** * The value for positive infinity. - * @var float + * @var float */ protected $inf; /** * Constructor. - */ + */ function __construct() { $this->inf = -log(0); @@ -88,19 +88,23 @@ class ChoiceFormat * @param float the number to test. * @param string the set, in set notation. * @return boolean true if number is in the set, false otherwise. - */ + */ function isValid($number, $set) { $n = preg_match_all($this->validate,$set,$matches,PREG_SET_ORDER); - + if($n < 3) throw new Exception("Invalid set \"{$set}\""); - + + if(preg_match('/\{\s*n:([^\}]+)\}/', $set, $def)) + { + return $this->isValidSetNotation($number, $def[1]); + } + $leftBracket = $matches[0][0]; $rightBracket = $matches[$n-1][0]; $i = 0; $elements = array(); - foreach($matches as $match) { $string = $match[0]; @@ -132,19 +136,32 @@ class ChoiceFormat $right = $number <= $elements[$total-1]; else if($rightBracket == ')') $right = $number < $elements[$total-1]; - - if($left && $right) return true; + + if($left && $right) return true; return false; - } + } + protected function isValidSetNotation($number, $set) + { + $str = '$result = '.str_replace('n', '$number', $set).';'; + try + { + eval($str); + return $result; + } + catch(Exception $e) + { + return false; + } + } /** - * Parse a choice string and get a list of sets and a list of strings + * Parse a choice string and get a list of sets and a list of strings * corresponding to the sets. * @param string the string containing the choices * @return array array($sets, $strings) - */ + */ function parse($string) { $n = preg_match_all($this->parse,$string,$matches, PREG_OFFSET_CAPTURE); @@ -157,19 +174,19 @@ class ChoiceFormat { $len = strlen($offset[$i][0]); $begin = $i == 0? $len : $offset[$i][1] + $len; - $end = $i == $n-1 ? strlen($string) : $offset[$i+1][1]; + $end = $i == $n-1 ? strlen($string) : $offset[$i+1][1]; $strings[] = substr($string, $begin, $end - $begin); } return array($sets, $strings); } /** - * For the choice string, and a number, find and return the + * For the choice string, and a number, find and return the * string that satisfied the set within the choices. * @param string the choices string. * @param float the number to test. * @return string the choosen string. - */ + */ public function format($string, $number) { list($sets, $strings) = $this->parse($string); diff --git a/framework/Web/Services/TSoapService.php b/framework/Web/Services/TSoapService.php index 087b5664..e598d796 100644 --- a/framework/Web/Services/TSoapService.php +++ b/framework/Web/Services/TSoapService.php @@ -287,10 +287,10 @@ class TSoapServer extends TApplicationComponent private $_uri=''; private $_classMap; private $_persistent=false; - private $_wsdlUri=''; - - private $_requestedMethod; - + private $_wsdlUri=''; + + private $_requestedMethod; + private $_server; /** @@ -321,7 +321,7 @@ class TSoapServer extends TApplicationComponent { Prado::using($provider); $providerClass=($pos=strrpos($provider,'.'))!==false?substr($provider,$pos+1):$provider; - $this->guessMethodCallRequested($providerClass); + $this->guessMethodCallRequested($providerClass); $server=$this->createServer(); $server->setClass($providerClass, $this); if($this->_persistent) @@ -330,72 +330,73 @@ class TSoapServer extends TApplicationComponent else $server=$this->createServer(); try - { + { $server->handle(); } catch (Exception $e) - { - if($this->getApplication()->getMode()===TApplicationMode::Debug) - $this->fault($e->getMessage(), $e->__toString()); - else + { + if($this->getApplication()->getMode()===TApplicationMode::Debug) + $this->fault($e->getMessage(), $e->__toString()); + else $this->fault($e->getMessage()); } - } - - /** - * Generate a SOAP fault message. - * @param string message title - * @param mixed message details - * @param string message code, defalt is 'SERVER'. - * @param string actors - * @param string message name - */ - public function fault($title, $details='', $code='SERVER', $actor='', $name='') - { - $this->_server->fault($code, $title, $actor, $details, $name); - } - - /** - * Guess the SOAP method request from the actual SOAP message - * - * @param string $class current handler class. - */ - protected function guessMethodCallRequested($class) - { - $namespace = $class.'wsdl'; - $message = file_get_contents("php://input"); - $matches= array(); - if(preg_match('/xmlns:([^=]+)="urn:'.$namespace.'"/', $message, $matches)) - { - if(preg_match('/<'.$matches[1].':([a-zA-Z_]+[a-zA-Z0-9_]+)/', $message, $method)) - { - $this->_requestedMethod = $method[1]; - } - } - } - - /** - * Soap method guessed from the SOAP message received. - * @return string soap method request, null if not found. - */ - public function getRequestedMethod() - { - return $this->_requestedMethod; - } - - /** - * Creates the SoapServer instance. + } + + /** + * Generate a SOAP fault message. + * @param string message title + * @param mixed message details + * @param string message code, defalt is 'SERVER'. + * @param string actors + * @param string message name + */ + public function fault($title, $details='', $code='SERVER', $actor='', $name='') + { + Prado::trace('SOAP-Fault '.$code. ' '.$title.' : '.$details, 'System.Web.Services.TSoapService'); + $this->_server->fault($code, $title, $actor, $details, $name); + } + + /** + * Guess the SOAP method request from the actual SOAP message + * + * @param string $class current handler class. + */ + protected function guessMethodCallRequested($class) + { + $namespace = $class.'wsdl'; + $message = file_get_contents("php://input"); + $matches= array(); + if(preg_match('/xmlns:([^=]+)="urn:'.$namespace.'"/', $message, $matches)) + { + if(preg_match('/<'.$matches[1].':([a-zA-Z_]+[a-zA-Z0-9_]+)/', $message, $method)) + { + $this->_requestedMethod = $method[1]; + } + } + } + + /** + * Soap method guessed from the SOAP message received. + * @return string soap method request, null if not found. + */ + public function getRequestedMethod() + { + return $this->_requestedMethod; + } + + /** + * Creates the SoapServer instance. * @return SoapServer */ protected function createServer() - { - if($this->_server===null) - { + { + if($this->_server===null) + { if($this->getApplication()->getMode()===TApplicationMode::Debug) ini_set("soap.wsdl_cache_enabled",0); - $this->_server = new SoapServer($this->getWsdlUri(),$this->getOptions()); + $this->_server = new SoapServer($this->getWsdlUri(),$this->getOptions()); } - return $this->_server; + return $this->_server; } /** diff --git a/index.html b/index.html index 9a4234f9..4eb6a0a5 100644 --- a/index.html +++ b/index.html @@ -110,6 +110,7 @@ PRADO component tags when you use it to edit PRADO templates.
  • Stanislav Yordanov - the script of generating Dreameweaver extension for PRADO
  • Andrés Adolfo Testi - original concept and naming for Active Controls.
  • Jack Slocum - inspiration for the quickstart commenting system.
  • +
  • Cesar Ramos - Active Record driver for IBM DB2.
  • All PRADO users - great suggestions, feedback and support
  • ASP.NET 2.0 for its great inspiration and reference
  • All third-party work used in PRADO
  • diff --git a/tests/simple_unit/ActiveRecord/ActiveRecordDynamicCallTestCase.php b/tests/simple_unit/ActiveRecord/ActiveRecordDynamicCallTestCase.php index 75bbb2a4..85f1c944 100644 --- a/tests/simple_unit/ActiveRecord/ActiveRecordDynamicCallTestCase.php +++ b/tests/simple_unit/ActiveRecord/ActiveRecordDynamicCallTestCase.php @@ -11,6 +11,13 @@ class ActiveRecordDynamicCallTestCase extends UnitTestCase TActiveRecordManager::getInstance()->setDbConnection($conn); } + function test_multiple_field_and_or() + { + $finder = DepartmentRecord::finder(); + $r2 = $finder->findAllByName_And_Description_Or_Active_Or_Order('Facilities', null, false, 1); + $this->assertNotNull($r2); + } + function test_dynamic_call() { $finder = DepartmentRecord::finder(); @@ -57,7 +64,6 @@ class ActiveRecordDynamicCallTestCase extends UnitTestCase { var_dump($param); } - } ?> \ No newline at end of file diff --git a/tests/simple_unit/I18N/ChoiceFormatTest.php b/tests/simple_unit/I18N/ChoiceFormatTest.php new file mode 100644 index 00000000..3a95b5a8 --- /dev/null +++ b/tests/simple_unit/I18N/ChoiceFormatTest.php @@ -0,0 +1,98 @@ +assertEqual($want, $choice->format($string, 0)); + + $want = 'is one file'; + $this->assertEqual($want, $choice->format($string, 1)); + + $want = 'are {number} files'; + $this->assertEqual($want, $choice->format($string, 5)); + + $this->assertFalse($choice->format($string, -1)); + + $string = '{1,2} one two |{3,4} three four |[2,5] two to five inclusive'; + $this->assertEqual($choice->format($string,1),'one two'); + $this->assertEqual($choice->format($string,2.1),'two to five inclusive'); + $this->assertEqual($choice->format($string,3),'three four'); + } + + function test_set_notation() + { + $choice = new ChoiceFormat(); + $string = '{n: n%2 == 0} are even numbers |{n: n >= 5} are not even and greater than or equal to 5'; + + $want = 'are even numbers'; + $this->assertEqual($want, $choice->format($string, 0)); + $this->assertEqual($want, $choice->format($string, 2)); + $this->assertEqual($want, $choice->format($string, 4)); + $this->assertNotEqual($want, $choice->format($string, 1)); + + $want = 'are not even and greater than or equal to 5'; + $this->assertEqual($want, $choice->format($string, 5)); + } + + function test_polish() + { + $choice = new ChoiceFormat(); + $string = '[1] plik |{2,3,4} pliki + |[5,21] pliko\'w |{n: n % 10 > 1 && n %10 < 5} pliki |{n: n%10 >= 5 || n%10 <=1} pliko\'w'; + + $wants = array( 'plik' => array(1), + 'pliki' => array(2,3,4,22,23,24), + 'pliko\'w' => array(5,6,7,11,12,15,17,20,21,25,26,30)); + foreach($wants as $want => $numbers) + { + foreach($numbers as $n) + $this->assertEqual($want, $choice->format($string, $n)); + } + } + + function test_russian() + { + $choice = new ChoiceFormat(); + $string = ' + {n: n % 10 == 1 && n % 100 != 11} test1 + |{n: n % 10 >= 2 && n % 10 <= 4 && ( n % 100 < 10 || n % 100 >= 20 )} test2 + |{n: 2} test3'; + + $wants = array('test1' => array(1,21,31,41), + 'test2' => array(2,4, 22, 24, 32, 34), + 'test3' => array(0, 5,6,7,8,9,10,11,12,13,14, 20,25,26,30) + ); + foreach($wants as $want => $numbers) + { + foreach($numbers as $n) + $this->assertEqual($want, $choice->format($string, $n)); + } + } + + function test_english() + { + $choice = new ChoiceFormat(); + $string = '[0] none |{n: n % 10 == 1} 1st |{n: n % 10 == 2} 2nd |{n: n % 10 == 3} 3rd |{n:n} th'; + + $wants = array('none' => array(0), + '1st' => array(1,11,21), + '2nd' => array(2,12,22), + '3rd' => array(3,13,23), + 'th' => array(4,5,6,7,14,15) + ); + foreach($wants as $want => $numbers) + { + foreach($numbers as $n) + $this->assertEqual($want, $choice->format($string, $n)); + } + } +} + +?> \ No newline at end of file diff --git a/tests/simple_unit/SqlMap/sqlite/tests.db b/tests/simple_unit/SqlMap/sqlite/tests.db index 290c8899..fce534dc 100644 Binary files a/tests/simple_unit/SqlMap/sqlite/tests.db and b/tests/simple_unit/SqlMap/sqlite/tests.db differ -- cgit v1.2.3