summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorwei <>2007-02-12 12:46:11 +0000
committerwei <>2007-02-12 12:46:11 +0000
commitfd4b8d9f45d1707035021bc19b8d5bc17ede66ce (patch)
tree093b8b3f92d4d6421f19a6d1e7c8211817f3d51d
parentf4c525abc3d4d3f3eecf1019770936e4ca39fd62 (diff)
Add IBM DB2 driver for active record.
-rw-r--r--.gitattributes6
-rw-r--r--HISTORY1
-rw-r--r--buildscripts/classtree/build.php7
-rw-r--r--demos/quickstart/protected/pages/Database/ActiveRecord.page24
-rw-r--r--framework/3rdParty/WsdlGen/WsdlMessage.php14
-rw-r--r--framework/3rdParty/WsdlGen/WsdlOperation.php38
-rw-r--r--framework/Data/ActiveRecord/Exceptions/messages.txt3
-rw-r--r--framework/Data/ActiveRecord/Scaffold/InputBuilder/TMysqlScaffoldInput.php2
-rw-r--r--framework/Data/ActiveRecord/Scaffold/InputBuilder/TPgsqlScaffoldInput.php2
-rw-r--r--framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php115
-rw-r--r--framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php168
-rw-r--r--framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.tpl6
-rw-r--r--framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php85
-rw-r--r--framework/Data/ActiveRecord/Scaffold/TScaffoldListView.tpl19
-rw-r--r--framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php89
-rw-r--r--framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.tpl4
-rw-r--r--framework/Data/ActiveRecord/Scaffold/TScaffoldView.php10
-rw-r--r--framework/Data/ActiveRecord/Scaffold/TScaffoldView.tpl10
-rw-r--r--framework/Data/ActiveRecord/Scaffold/style.css3
-rw-r--r--framework/Data/ActiveRecord/TActiveRecord.php46
-rw-r--r--framework/Data/ActiveRecord/TActiveRecordManager.php3
-rw-r--r--framework/Data/ActiveRecord/Vendor/TDbMetaData.php38
-rw-r--r--framework/Data/ActiveRecord/Vendor/TDbMetaDataCommon.php2
-rw-r--r--framework/Data/ActiveRecord/Vendor/TIbmColumnMetaData.php149
-rw-r--r--framework/Data/ActiveRecord/Vendor/TIbmMetaData.php80
-rw-r--r--framework/Data/ActiveRecord/Vendor/TIbmMetaDataInspector.php113
-rw-r--r--framework/Data/ActiveRecord/Vendor/TMysqlMetaData.php22
-rw-r--r--framework/Data/ActiveRecord/Vendor/TMysqlMetaDataInspector.php7
-rw-r--r--framework/Data/ActiveRecord/Vendor/TPgsqlColumnMetaData.php19
-rw-r--r--framework/Data/ActiveRecord/Vendor/TPgsqlMetaData.php26
-rw-r--r--framework/Data/ActiveRecord/Vendor/TPgsqlMetaDataInspector.php19
-rw-r--r--framework/Data/ActiveRecord/Vendor/TSqliteMetaData.php19
-rw-r--r--framework/Data/ActiveRecord/Vendor/TSqliteMetaDataInspector.php5
-rw-r--r--framework/I18N/core/ChoiceFormat.php67
-rw-r--r--framework/Web/Services/TSoapService.php121
-rw-r--r--index.html1
-rw-r--r--tests/simple_unit/ActiveRecord/ActiveRecordDynamicCallTestCase.php8
-rw-r--r--tests/simple_unit/I18N/ChoiceFormatTest.php98
-rw-r--r--tests/simple_unit/SqlMap/sqlite/tests.dbbin24576 -> 24576 bytes
39 files changed, 1207 insertions, 242 deletions
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 <tt>$_tablena
E.g. MySQL uses back-ticks, <tt>$_tablename = "`database1`.`table1`"</tt>
</div>
-<div class="tip"><b class="note">Tip:</b>
+<p class="block-content" id="ar_as_component">
Since <tt>TActiveRecord</tt> extends <tt>TComponent</tt>, setter and
getter methods can be defined to allow control over how variables
are set and returned. For example, adding a <tt>$level</tt> property to the UserRecord class:
+</p>
<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_690149">
class UserRecord extends TActiveRecord {
... //existing definitions as above
@@ -117,9 +118,8 @@ class UserRecord extends TActiveRecord {
}
}
</com:TTextHighlighter>
-</div>
-<div class="note"><b class="note">Note:</b>
+<div class="info"><b class="note">Info:</b>
<tt>TActiveRecord</tt> can also work with database views by specifying the value <tt>$_tablename</tt>
corresponding to the view name. However, objects returned
from views are read-only, calling the <tt>save()</tt> or <tt>delete()</tt> method
@@ -275,7 +275,7 @@ Method names starting with <tt>findAllBy</tt> return an array of records.
The condition is taken as part of the method name after <tt>findBy</tt> or <tt>findAllBy</tt>.
The following blocks of code are equivalent:
-
+</p>
<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_690158">
$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);
</com:TTextHighlighter>
-</p>
+
+<div class="tip"><b class="note">Tip:</b>
+You may also use <tt>OR</tt> as a condition in the dynamic methods.
+</div>
<h3 id="138060"><tt>findBySql()</tt></h3>
<p id="690497" class="block-content">Finds records using full SQL, returns corresponding array of record objects.</p>
@@ -303,6 +306,7 @@ $finder->findAll('Age = ?', $age);
<p id="690499" class="block-content">
Add a new record using TActiveRecord is very simple, just create a new Active
Record object and call the <tt>save()</tt> method. E.g.
+</p>
<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_690161">
$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.</div>
-</p>
<p id="690500" class="block-content">
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 <tt>deleteByPk()</tt> method.
For example, to delete one or records with tables having a scalar primary key.
+</p>
<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_690163">
$finder->deleteByPk($primaryKey); //delete 1 record
$finder->deleteByPk($key1,$key2,...); //delete multiple records
$finder->deleteByPk(array($key1,$key2,...)); //delete multiple records
</com:TTextHighlighter>
-</p>
<p id="690503" class="block-content">
For composite primary keys (determined automatically from the table definitions):
+</p>
<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_690164">
$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), .. ));
</com:TTextHighlighter>
-</p>
<h3 id="138052a"><tt>deleteAll()</tt> and <tt>deleteBy*()</tt></h3>
<p id="690502a" class="block-content">
@@ -387,16 +390,13 @@ $finder->deleteByName($name);
//delete by username and password
$finder->deleteBy_Username_And_Password($name,$pass);
</com:TTextHighlighter>
-</p>
-
-
<h2 id="138053">Transactions</h2>
<p id="690504" class="block-content">All Active Record objects contains the property <tt>DbConnection</tt>
that can be used to obtain a transaction object.
<com:TTextHighlighter Language="php" CssClass="source block-content" id="code_690165">
$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 @@
<?php
-
+/**
+ * TScaffoldBase class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.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 <weizho[at]gmail[dot]com>
+ * @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 @@
<?php
+/**
+ * TScaffoldEditView class and IScaffoldEditRenderer interface file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.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 <b>Data</b> of the IScaffoldEditRenderer will be set as the current Active
+ * Record to be edited. The <b>UpdateRecord()</b> 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 <b>Parent</b> of the IScaffoldEditRenderer in most
+ * cases.
+ *
+ * The <b>RecordClass</b> 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 <weizho[at]gmail[dot]com>
+ * @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 <weizho[at]gmail[dot]com>
+ * @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 @@
+<div class="scaffold_edit_view">
<div class="edit-inputs">
-<com:TRepeater ID="_repeater" onItemCreated="repeaterItemCreated">
+<com:TRepeater ID="_repeater" onItemCreated="createRepeaterEditItem">
<prop:ItemTemplate>
- <div class="edit-item item_<%# $this->ItemIndex % 2 %>
+ <div class="edit-item item_<%# $this->ItemIndex % 2 %>
input_<%# $this->ItemIndex %> property_<%# $this->DataItem->Property %>">
<com:TLabel ID="_label" CssClass="item-label"/>
<span class="item-input">
@@ -16,4 +17,5 @@
<com:TButton ID="_save" Text="Save" CommandName="save" ValidationGroup=<%= $this->ValidationGroup %>/>
<com:TButton ID="_clear" Text="Clear" CommandName="clear" CausesValidation="false"/>
<com:TButton ID="_cancel" Text="Cancel" CommandName="cancel" CausesValidation="false" Visible="false"/>
+</div>
</div> \ 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 @@
<?php
-
+/**
+ * TScaffoldListView class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ */
+
+/**
+ * Load the scaffold base class.
+ */
Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
+/**
+ * TScaffoldListView displays instance of Active Record class.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @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 @@
-
+<div class="scaffold_list_view">
<div class="item-header">
<com:TRepeater ID="_header">
<prop:ItemTemplate>
@@ -21,7 +21,7 @@
PageSize="10">
<prop:ItemTemplate>
<div class="item item_<%# $this->ItemIndex % 2 %>">
-
+
<com:TRepeater ID="_properties">
<prop:ItemTemplate>
<span class="field field_<%# $this->ItemIndex %>">
@@ -29,20 +29,20 @@
</span>
</prop:ItemTemplate>
</com:TRepeater>
-
+
<span class="edit-delete-buttons">
- <com:TButton Text="Edit"
+ <com:TButton Text="Edit"
Visible=<%# $this->NamingContainer->Parent->EditViewID !== Null %>
- CommandName="edit"
+ CommandName="edit"
CssClass="edit-button"
CausesValidation="false" />
- <com:TButton Text="Delete"
- CommandName="delete"
+ <com:TButton Text="Delete"
+ CommandName="delete"
CssClass="delete-button"
CausesValidation="false"
Attributes.onclick="if(!confirm('Are you sure?')) return false;" />
</span>
-
+
</div>
</prop:ItemTemplate>
</com:TRepeater>
@@ -53,4 +53,5 @@
ControlToPaginate="_list"
PageButtonCount="10"
Mode="Numeric"
- OnPageIndexChanged="pageChanged" /> \ No newline at end of file
+ OnPageIndexChanged="pageChanged" />
+</div> \ 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 @@
+<?php
+
+Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
+
+class TScaffoldSearch extends TScaffoldBase
+{
+ private $_list;
+
+ public function getListView()
+ {
+ if($this->_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 @@
+<span class="scaffold_search">
+<com:TTextBox ID="_textbox" />
+<com:TButton ID="_search" Text="Search" CommandName="search" />
+</span> \ 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 @@
+<div class="scaffold_view">
+<com:TScaffoldSearch ID="_search" ListViewID="_listView" />
<com:TScaffoldListView ID="_listView" EditViewID="_editView" />
-
-<div class="auxilary-button buttons">
+<span class="auxilary-button buttons">
<com:TButton ID="_newButton" Text="Add new record" CssClass="new-button" CommandName="new" />
-</div>
+</span>
-<com:TScaffoldEditView ID="_editView" Visible="false"/> \ No newline at end of file
+<com:TScaffoldEditView ID="_editView" Visible="false"/>
+</div> \ 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);
* </code>
* <code>
- * $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);
* </code>
* <code>
@@ -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 @@
+<?php
+/**
+ * TIbmColumnMetaData class file.
+ *
+ * @author Cesar Ramos <cramos[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Vendor
+ */
+
+/**
+ * TIbmColumnMetaData class.
+ *
+ * Column details for IBM DB2 database. Using php_pdo_ibm.dll extension.
+ *
+ * @author Cesar Ramos <cramos[at]gmail[dot]com>
+ * @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 @@
+<?php
+
+/**
+ * TIbmMetaData class file.
+ *
+ * @author Cesar Ramos <cramos[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.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 <cramos[at]gmail[dot]com>
+ * @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 @@
+<?php
+/**
+ * TIbmMetaDataInspector class file.
+ *
+ * @author Cesar Ramos <cramos[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.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 <cramos[at]gmail[dot]com>
+ * @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 @@
-<?php
+<?php
/**
* TMysqlMetaData class file.
*
@@ -48,10 +48,24 @@ class TMysqlMetaData 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);
+ $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 @@
-<?php
+<?php
/**
* TMysqlMetaDataInspector class file.
*
@@ -39,7 +39,7 @@ class TMysqlMetaDataInspector extends TDbMetaDataInspector
$command = $conn->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."
*
* <code>
* $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 @@
* # <t>[1,2]</t> -- accepts values between 1 and 2, inclusive.
* # <t>(1,2)</t> -- accepts values between 1 and 2, excluding 1 and 2.
* # <t>{1,2,3,4}</t> -- only values defined in the set are accepted.
- * # <t>[-Inf,0)</t> -- accepts value greater or equal to negative infinity
+ * # <t>[-Inf,0)</t> -- 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.
<li>Stanislav Yordanov - the script of generating Dreameweaver extension for PRADO</li>
<li>Andrés Adolfo Testi - original concept and naming for Active Controls.</li>
<li><a href="http://www.jackslocum.com/">Jack Slocum</a> - inspiration for the quickstart commenting system.</li>
+<li>Cesar Ramos - Active Record driver for IBM DB2.</li>
<li>All PRADO users - great suggestions, feedback and support</li>
<li>ASP.NET 2.0 for its great inspiration and reference</li>
<li>All <a href="framework/3rdParty/readme.html">third-party work</a> used in PRADO</li>
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 @@
+<?php
+
+Prado::using('System.I18N.core.ChoiceFormat');
+
+class ChoiceFormatTest extends UnitTestCase
+{
+ function testChoices()
+ {
+ $choice = new ChoiceFormat();
+ $string = '[0] are no files |[1] is one file |(1,Inf] are {number} files';
+
+ $want = 'are no files';
+ $this->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
--- a/tests/simple_unit/SqlMap/sqlite/tests.db
+++ b/tests/simple_unit/SqlMap/sqlite/tests.db
Binary files differ