[Solar-svn] Revision 2818

pmjones at solarphp.com pmjones at solarphp.com
Sat Oct 6 10:43:25 CDT 2007


Solar_Form
----------

* [CHG] In populate(), do not set values for "submit", "button", or "reset" 
  elements.  This keeps values from previous submit buttons from overwriting 
  those on new forms.

* [BRK] Many BC breaks related to using the new Filter mechanism, where
  sanitizing and validation are wrapped into the same class.

* [BRK] Element-specific messages are now keyed on 'invalid' (not 'feedback'),
  although overall form-level messages are still called feedback.

* [ADD] New config key 'filter' is a Solar_Filter dependency injection.

* [BRK] Methods addFilter() and addFilters() are now related to the new
  Solar_Filter class, and so handle both validation and sanitizing. Removed
  addValid() method entirely.

* [BRK] Methods addInvalid() and addInvalids() to invalidation messages to one
  or more elements. This replaces addFeedback(), which has been removed.

* [ADD] Methods setValue() and setValues() to more reliably set the value on
  one or more elements manually.

* [CHG] Method validate() now uses the Solar_Filter filter-chain instead of a
  custom internal sanitize/validate routine.

* [BRK] Renamed method values() to getValues(), to be more consistent with
  coding standards.

* [FIX] Added default 'filters' attribute.

* [CHG] Method addInvald() no longer throws an exception when the element does not exist; instead, ignores it.

* [CHG] Method setValue() no longer throws an exception when the element does not exist; instead, ignores it.



Copied: trunk/Solar/Form/Load/Model.php (from rev 2801, branches/orm/Solar/Form/Load/Model.php)
===================================================================
--- trunk/Solar/Form/Load/Model.php	                        (rev 0)
+++ trunk/Solar/Form/Load/Model.php	2007-10-06 15:43:25 UTC (rev 2818)
@@ -0,0 +1,256 @@
+<?php
+/**
+ * 
+ * Class for loading form definitions from Solar_Sql_Model columns.
+ * 
+ * @category Solar
+ * 
+ * @package Solar_Form
+ * 
+ * @author Paul M. Jones <pmjones at solarphp.com>
+ * 
+ * @license http://opensource.org/licenses/bsd-license.php BSD
+ * 
+ * @version $Id: Table.php 2646 2007-07-29 15:49:30Z pmjones $
+ * 
+ */
+
+/**
+ * 
+ * Class for loading form definitions from Solar_Sql_Model columns.
+ * 
+ * @category Solar
+ * 
+ * @package Solar_Form
+ * 
+ */
+class Solar_Form_Load_Model extends Solar_Base {
+    
+    /**
+     * 
+     * Loads Solar_Form elements based on Solar_Sql_Table columns.
+     * 
+     * @param Solar_Sql_Table $model Load form elements from this model object.
+     * 
+     * @param array $list Which model columns to load as form elements, default '*'.
+     * 
+     * @param string $array_name Load the model columns as elements of this
+     * array-name within the form.
+     * 
+     * @return Solar_Form|false Solar_Form object, or boolean false on error.
+     * 
+     */
+    public function fetch($model, $list = '*', $array_name = null)
+    {
+        // make sure it's a Model
+        if (! $model instanceof Solar_Sql_Model) {
+            throw $this->_exception('ERR_NOT_MODEL_OBJECT');
+        }
+        
+        // if not specified, set the array_name to the model name
+        if (empty($array_name)) {
+            $array_name = $model->model_name;
+        }
+        
+        // table columns in the model
+        $cols = $model->table_cols;
+        
+        // special condition: if looking for '*' columns,
+        // set the list to all the model columns.
+        if ($list == '*') {
+            $list = array_keys($cols);
+        } else {
+            settype($list, 'array');
+        }
+        
+        // default values
+        $default = $model->fetchNew();
+        
+        // loop through the list of requested columns and collect elements
+        $elements = array();
+        foreach ($list as $name => $info) {
+            
+            // if $name is integer, $info is just a column name,
+            // and there is no added element info.
+            if (is_int($name)) {
+                $name = $info;
+                $info = array();
+            } else {
+                settype($info, 'array');
+            }
+            
+            // is the column name in the model table?
+            if (empty($cols[$name])) {
+                // not in the table, fake some elements
+                $cols[$name] = array(
+                    'primary' => false,
+                    'require' => false,
+                    'type'    => 'text',
+                    'size'    => false,
+                );
+            }
+            
+            // initial set of element info based on the model column
+            $base = array(
+                'name'    => $array_name . '[' . $name . ']',
+                'type'    => null,
+                'label'   => $model->locale(strtoupper("LABEL_$name")),
+                'descr'   => $model->locale(strtoupper("DESCR_$name")),
+                'value'   => $default[$name],
+                'require' => $cols[$name]['require'],
+                'disable' => $cols[$name]['primary'],
+                'options' => array(),
+                'attribs' => array(),
+                'filters' => array(),
+                'invalid' => array(),
+            );
+            
+            $info = array_merge($base, $info);
+            
+            // use the filters here
+            if (! empty($model->filters[$name])) {
+                $filters = $model->filters[$name];
+            } else {
+                $filters = array();
+            }
+            
+            // make primary keys hidden and disabled
+            if ($cols[$name]['primary']) {
+                $info['type'] = 'hidden';
+                $info['disable'] = true;
+            }
+            
+            // pick an element type based on the column type
+            if (empty($info['type'])) {
+                // base the element type on the column type.
+                switch ($cols[$name]['type']) {
+                
+                case 'bool':
+                    $info['type'] = 'checkbox';
+                    break;
+                    
+                case 'clob':
+                    $info['type'] = 'textarea';
+                    break;
+                    
+                case 'date':
+                case 'time':
+                case 'timestamp':
+                    $info['type'] = $cols[$name]['type'];
+                    break;
+                    
+                default:
+                    
+                    // make 'id' and '*_id' columns hidden
+                    if ($name == 'id' || substr($name, -3) == '_id') {
+                        $info['type'] = 'hidden';
+                    }
+                    
+                    // if there is a filter to 'validateInList' or 'validateInKeys',
+                    // make this a select element.
+                    foreach ($filters as $v) {
+                        if ($v[0] == 'validateInKeys' || $v[0] == 'validateInList') {
+                            $info['type'] = 'select';
+                            break;
+                        }
+                    }
+                    
+                    // if type is still empty, make it text.
+                    if (empty($info['type'])) {
+                        $info['type'] = 'text';
+                    }
+                    break;
+                }
+            }
+            
+            // set up options for checkboxes if none specified
+            if ($info['type'] == 'checkbox' && empty($info['options'])) {
+                // look for 'validateInKeys' or 'validateInList' validation.
+                foreach ($filters as $v) {
+                    if ($v[0] == 'validateInKeys' || $v[0] == 'validateInList') {
+                        $info['options'] = $this->_autoOptions($v[0], $v[2]);
+                        break;
+                    }
+                }
+                // if still empty, set to 1,0
+                if (empty($info['options'])) {
+                    $info['options'] = array(1,0);
+                }
+            }
+            
+            // set up options for select and radio if none specified
+            if (($info['type'] == 'select' || $info['type'] == 'radio') &&
+                empty($info['options'])) {
+                // look for 'validateInKeys' or 'validateInList'
+                foreach ($filters as $v) {
+                    if ($v[0] == 'validateInKeys' || $v[0] == 'validateInList') {
+                        $info['options'] = $this->_autoOptions($v[0], $v[2]);
+                        break;
+                    }
+                }
+            }
+            
+            // for text elements, set up maxlength if none specified
+            if ($info['type'] == 'text' &&
+                empty($info['attribs']['maxlength']) && 
+                $cols[$name]['size'] > 0) {
+                /** @todo Add +1 or +2 to 'size' for numeric types? */
+                $info['attribs']['maxlength'] = $cols[$name]['size'];
+            }
+            
+            // if no label specified, set up based on element name
+            if (empty($info['label'])) {
+                $info['label'] = $info['name'];
+            }
+            
+            // if there is a validateNotBlank filter, mark to require
+            foreach ($filters as $v) {
+                if ($v[0] == 'validateNotBlank') {
+                    $info['require'] = true;
+                    break;
+                }
+            }
+            
+            // keep the element
+            $elements[$info['name']] = $info;
+        }
+        
+        // done!
+        $result = array(
+            'attribs'  => array(),
+            'elements' => $elements
+        );
+        
+        return $result;
+    }
+    
+    /**
+     * 
+     * Builds an option list from validateInKeys and validateInList values.
+     * 
+     * The 'validateInKeys' options are not changed.
+     * 
+     * The 'validateInList' options are generally sequential, so the label
+     * and the value are made to be identical (based on the label).
+     * 
+     * @param string $type The validation type, 'validateInKeys' or 'validateInList'.
+     * 
+     * @param array $opts The options provided by the validation.
+     * 
+     * @return array
+     * 
+     */
+    protected function _autoOptions($type, $opts)
+    {
+        // leave the labels and values alone
+        if ($type == 'validateInKeys') {
+            return $opts;
+        }
+        
+        // make the form display the labels as both labels and values
+        if ($type == 'validateInList') {
+            $vals = array_values($opts);
+            return array_combine($vals, $vals);
+        }
+    }
+}

Modified: trunk/Solar/Form/Load/Table.php
===================================================================
--- trunk/Solar/Form/Load/Table.php	2007-10-06 14:41:17 UTC (rev 2817)
+++ trunk/Solar/Form/Load/Table.php	2007-10-06 15:43:25 UTC (rev 2818)
@@ -94,7 +94,7 @@
                 'disable'  => $table->col[$name]['primary'],
                 'options'  => array(),
                 'attribs'  => array(),
-                'feedback' => array(),
+                'invalid' => array(),
                 'valid'    => array(),
             );
             $info = array_merge($base, $info);

Modified: trunk/Solar/Form/Load/Xml.php
===================================================================
--- trunk/Solar/Form/Load/Xml.php	2007-10-06 14:41:17 UTC (rev 2817)
+++ trunk/Solar/Form/Load/Xml.php	2007-10-06 15:43:25 UTC (rev 2818)
@@ -41,7 +41,7 @@
         'require',
         'disable',
     );
-
+    
     /**
      * 
      * Loads a Solar_Form definition from an XML file.
@@ -63,11 +63,11 @@
         $args     = func_get_args();
         $filename = array_shift($args);
         $param    = array_shift($args);
-
+        
         if (! file_exists($filename)) {
             throw $this->_exception('ERR_FILE_NOT_FOUND');
         }
-
+        
         // load the XML file data
         $xml = simplexml_load_file($filename);
         if (false === $xml) {
@@ -92,14 +92,14 @@
             // ... otherwise, get element name and initialize array
             $name = (string) $element['name'];
             $elementInfo = array();
-
+            
             // Get element attributes
             foreach ($this->elementAttribs as $attrib) {
                 if (isset($element[$attrib])) {
                     $elementInfo[$attrib] = (string) $element[$attrib];
                 }
             }
-
+            
             // Get element label/description, if present
             if (!empty($element->label)) {
                 $elementInfo['label'] = (string) $element->label;
@@ -107,7 +107,7 @@
             if (!empty($element->descr)) {
                 $elementInfo['descr'] = (string) $element->descr;
             }
-
+            
             // Get attribs and options
             foreach (array('attribs', 'options') as $opt) {
                 if (!empty($element->$opt)) {
@@ -119,7 +119,7 @@
                     $elementInfo[$opt] = $info;
                 }
             }
-
+            
             // Get element filters
             if (! empty($element->filters)) {
                 $filters = array();
@@ -127,10 +127,10 @@
                     
                     if (empty($filter['method'])) continue;
                     $method = (string) $filter['method'];
-
+                    
                     $params    = array();
                     $tmpFilter = array($method);
-
+                    
                     // Were any arguments passed?
                     if (!empty($filter->params)) {
                         $params = $this->getParams($filter->params->param);
@@ -140,7 +140,7 @@
                     foreach ($params as $param) {
                         array_push($tmpFilter, $param);
                     }
-
+                    
                     // Add filter to element
                     $filters[] = $tmpFilter;
                 }
@@ -163,28 +163,28 @@
                     
                     $params  = array();
                     $tmpRule = array($method, $message);
-
+                    
                     // Were any arguments passed?
                     if (! empty($rule->args)) {
                         $params = $this->getParams($rule->args->arg);
                     }
-
+                    
                     // Add arguments to validation array
                     foreach ($params as $param) {
                         array_push($tmpRule, $param);
                     }
-
+                    
                     // Add validation to element
                     $validate[] = $tmpRule;
                 }
-
+                
                 $elementInfo['valid'] = $validate;
             }
             
             // Add element to formElements array
             $elements[$name] = $elementInfo;
         }
-
+        
         return array(
             'attribs'  => array(),
             'elements' => $elements
@@ -211,7 +211,7 @@
             if (!empty($param['type'])) {
                 $argType = (string) $param['type'];
             }
-
+            
             if ('array' == $argType) {
                 if (empty($param->item)) {
                     $param = (array) $param;
@@ -234,10 +234,10 @@
                     $param = (string) $param->item;
                 }
             }
-
+            
             array_push($final, $param);
         }
-
+        
         return $final;
     }
 }

Modified: trunk/Solar/Form.php
===================================================================
--- trunk/Solar/Form.php	2007-10-06 14:41:17 UTC (rev 2817)
+++ trunk/Solar/Form.php	2007-10-06 15:43:25 UTC (rev 2818)
@@ -9,7 +9,7 @@
  * 
  * @author Paul M. Jones <pmjones at solarphp.com>
  * 
- * @author Contributions from Matthew Weier O'Phinney <mweierophinney at gmail.com>
+ * @author Matthew Weier O'Phinney <mweierophinney at gmail.com>
  * 
  * @license http://opensource.org/licenses/bsd-license.php BSD
  * 
@@ -50,18 +50,19 @@
      * : (string) The overall "failure" message when validating form
      * input. Default is Solar locale key FAILURE_FORM.
      * 
+     * `filter`
+     * : (dependency) A Solar_Filter dependency injection; default is empty,
+     *   which creates a standard Solar_Filter object on the fly.
+     * 
      * @var array
      * 
      */
     protected $_Solar_Form = array(
         'request' => 'request',
-        'attribs' => array(
-            'action'  => null,
-            'method'  => 'post',
-            'enctype' => 'multipart/form-data',
-        ),
+        'filter'  => null,
         'success' => null,
         'failure' => null,
+        'attribs' => array(),
     );
     
     /**
@@ -70,11 +71,25 @@
      * 
      * @var bool Null if validation has not occurred yet, true if
      * valid, false if not valid.
+     * 
      */
     protected $_status = null;
     
     /**
      * 
+     * Default <form> tag attributes.
+     * 
+     * @var array
+     * 
+     */
+    protected $_default_attribs = array(
+        'action'  => null,
+        'method'  => 'post',
+        'enctype' => 'multipart/form-data',
+    );
+    
+    /**
+     * 
      * Attributes for the form tag itself.
      * 
      * The `$attribs` array holds HTML attributes for the
@@ -93,8 +108,8 @@
      * The array of elements in this form.
      * 
      * The `$elements` array contains all elements in the form,
-     * including their names, types, values, any feedback messages,
-     * validation and filter callbacks, and so on. 
+     * including their names, types, values, any invalidation messages,
+     * filter callbacks, and so on. 
      * 
      * In general, you should not try to set $elements yourself;
      * instead, Solar_Form::setElement() and Solar_Form::setElements().
@@ -115,8 +130,8 @@
      * 
      * Note that the $feedback property pertains to the form as a
      * whole, not the individual elements.  This is as opposed to
-     * the 'feedback' key in each of the elements, which contains
-     * feedback specific to that element.
+     * the 'invalid' key in each of the elements, which contains
+     * invalidation messages specific to that element.
      * 
      * @var array
      * 
@@ -125,24 +140,15 @@
     
     /**
      * 
-     * The array of pre-filters for the form elements.
+     * The array of filters for the form elements.
      * 
      * @var array 
      * 
      */
-    protected $_filter = array();
+    protected $_filters = array();
     
     /**
      * 
-     * The array of validations for the form elements.
-     * 
-     * @var array
-     * 
-     */
-    protected $_valid = array();
-    
-    /**
-     * 
      * Array of submitted values.
      * 
      * Populated on the first call to [[_populate()]], which itself uses
@@ -175,13 +181,13 @@
      * : (string) The default or selected value(s) for the element.
      * 
      * `descr`
-     * : (string) A longer description of the element, for example a tooltip
+     * : (string) A longer description of the element, such as a tooltip
      *   or help text.
      * 
      * `status`
-     * : (bool) Whether or not the particular elements has
-     *   passed or failed validation (true or false), or null if there
-     *   has been no attempt at validation.
+     * : (bool) Whether or not the particular element has passed or failed
+     *   validation (true or false), or null if there has been no attempt at
+     *   validation.
      * 
      * `require`
      * : (bool) Whether or not the element is required.
@@ -198,47 +204,38 @@
      * : (array) Additional XHTML attributes for the element in the
      *   form (attribute => value).
      * 
-     * `feedback`
-     * : (array) An array of feedback messages for this element,
-     *   generally based on validation of previous user input.
+     * `invalid`
+     * : (array) An array of messages if the value is invalid.
      * 
      * @var array
      * 
      */
-    protected $_default = array(
-        'name'     => null,
-        'type'     => null,
-        'label'    => null,
-        'descr'    => null,
-        'value'    => null,
-        'status'   => null,
-        'require'  => false,
-        'disable'  => false,
-        'options'  => array(),
-        'attribs'  => array(),
-        'feedback' => array(),
+    protected $_default_element = array(
+        'name'    => null,
+        'type'    => null,
+        'label'   => null,
+        'descr'   => null,
+        'value'   => null,
+        'status'  => null,
+        'require' => false,
+        'disable' => false,
+        'options' => array(),
+        'attribs' => array(),
+        'filters' => array(),
+        'invalid' => array(),
     );
     
     /**
      * 
-     * A Solar_Filter object for internal filtering needs.
+     * A Solar_Filter object for filtering form values.
      * 
      * @var Solar_Filter
      * 
      */
-    protected $_obj_filter;
+    protected $_filter;
     
     /**
      * 
-     * A Solar_Valid object for internal validation needs.
-     * 
-     * @var Solar_Valid
-     * 
-     */
-    protected $_obj_valid;
-    
-    /**
-     * 
      * Request environment object.
      * 
      * @var Solar_Request
@@ -268,21 +265,24 @@
             $this->_config['request']
         );
         
-        // make sure we have an action. normally we try not to change $_config
-        // from what the user set, but in this case, we need $_config values
-        // so that reset() works properly.
-        if (empty($this->_config['attribs']['action'])) {
-            $this->_config['attribs']['action'] = $this->_request->server('REQUEST_URI');
-        }
+        // filter object
+        $this->_filter = Solar::dependency(
+            'Solar_Filter',
+            $this->_config['filter']
+        );
         
-        // retain setups, create validator/filter objects
-        $this->attribs = array_merge(
-            $this->_Solar_Form['attribs'],
+        // set the default action attribute
+        $action = $this->_request->server('REQUEST_URI');
+        $this->_default_attribs['action'] = $action;
+        
+        // now merge attribute configs
+        $this->_config['attribs'] = array_merge(
+            $this->_default_attribs,
             $this->_config['attribs']
         );
         
-        $this->_obj_filter = Solar::factory('Solar_Filter');
-        $this->_obj_valid = Solar::factory('Solar_Valid');
+        // reset all the properties based on config now
+        $this->reset();
     }
     
     // -----------------------------------------------------------------
@@ -299,7 +299,7 @@
      * $info['name'].
      * 
      * @param array $info Element information using the same keys as
-     * in Solar_Form::$_default.
+     * in Solar_Form::$_default_element.
      * 
      * @param string $array Rename the element as a key in this array.
      * 
@@ -312,48 +312,22 @@
         $name = $this->_prepareName($name, $array);
         
         // prepare the element info
-        $info = array_merge($this->_default, $info);
+        $info = array_merge($this->_default_element, $info);
         
         // forcibly cast each of the keys into the elements array
         $this->elements[$name] = array (
-            'name'     =>          $name,
-            'type'     => (string) $info['type'],
-            'label'    => (string) $info['label'],
-            'value'    =>          $info['value'], // mixed
-            'descr'    => (string) $info['descr'],
-            'require'  => (bool)   $info['require'],
-            'disable'  => (bool)   $info['disable'],
-            'options'  => (array)  $info['options'],
-            'attribs'  => (array)  $info['attribs'],
-            'feedback' => (array)  $info['feedback'],
+            'name'    => (string) $name,
+            'type'    => (string) $info['type'],
+            'label'   => (string) $info['label'],
+            'value'   =>          $info['value'], // mixed
+            'descr'   => (string) $info['descr'],
+            'require' => (bool)   $info['require'],
+            'disable' => (bool)   $info['disable'],
+            'options' => (array)  $info['options'],
+            'attribs' => (array)  $info['attribs'],
+            'filters' => (array)  $info['filters'],
+            'invalid' => (array)  $info['invalid'],
         );
-        
-        // add filters
-        if (array_key_exists('filter', $info)) {
-            foreach ( (array) $info['filter'] as $args) {
-                $this->_filter[$name][] = $args;
-            }
-        }
-        
-        // add validations
-        if (array_key_exists('valid', $info)) {
-        
-            foreach ( (array) $info['valid'] as $args) {
-            
-                // make sure $args is an array
-                settype($args, 'array');
-                
-                // shift the name onto the top of the args
-                array_unshift($args, $name);
-                
-                // add the validation to the element
-                call_user_func_array(
-                    array($this, 'addValid'),
-                    $args
-                );
-                
-            }
-        }
     }
     
     /**
@@ -361,7 +335,7 @@
      * Sets multiple elements in the form.  Appends if they do not exist.
      * 
      * @param array $list Element information as array(name => info), where
-     * each info value is an array like Solar_Form::$_default.
+     * each info value is an array like Solar_Form::$_default_element.
      * 
      * @param string $array Rename each element as a key in this array.
      * 
@@ -406,95 +380,103 @@
     
     /**
      * 
-     * Adds a Solar_Filter method callback for an element.
+     * Adds one filter to an element.
      * 
-     * All pre-filters are applied via 
-     * Solar_Filter::multiple() and should conform to the 
-     * specifications for that method.
+     * @param string $name The element name.
      * 
-     * All parameters after $method are treated as added parameters
-     * for the Solar_Filter method call.
+     * @param array|string $spec The filter specification; either a
+     * Solar_Filter method name (string), or an array where the first element
+     * is a method name and remaining elements are parameters to that method.
      * 
+     * @return void
+     * 
+     */
+    public function addFilter($name, $spec, $array) 
+    {
+        // make sure the element exists
+        $name = $this->_prepareName($name, $array);
+        if (empty($this->elements[$name])) {
+            throw $this->_exception('ERR_NO_SUCH_ELEMENT', array(
+                'name' => $name,
+            ));
+        }
+        
+        // add the filter
+        $this->elements[$name]['filters'][] = (array) $spec;
+    }
+    
+    /**
+     * 
+     * Adds many filters to one element.
+     * 
      * @param string $name The element name.
      * 
-     * @param string $method Solar_Filter method or PHP function to use
-     * for filtering.
+     * @param array|string $spec The filter specification; either a
+     * Solar_Filter method name (string), or an array where the first element
+     * is a method name and remaining elements are parameters to that method.
      * 
+     * @param string $array Rename each element as a key in this array.
+     * 
      * @return void
      * 
      */
-    public function addFilter($name, $method) 
+    public function addFilters($name, $list, $array)
     {
-        // Get the arguments, drop the element name
-        $args = func_get_args();
-        array_shift($args);
-
-        $this->_filter[$name][] = $args;
+        foreach ((array) $list as $spec) {
+            $this->addFilter($name, $spec, $array);
+        }
     }
     
     /**
      * 
-     * Adds a Solar_Valid method callback as a validation for an element.
+     * Adds one or more invalid message to an element, sets the element status
+     * to false (invalid), and sets the form status to false (invalid).
      * 
      * @param string $name The element name.
      * 
-     * @param string $method The Solar_Valid callback method.
+     * @param string|array $spec The invalidation message(s).
      * 
-     * @param string $message The feedback message to use if validation fails.
+     * @param string $array Rename each element as a key in this array.
      * 
      * @return void
      * 
      */
-    public function addValid($name, $method, $message = null)
+    public function addInvalid($name, $spec, $array = null)
     {
-        // get the arguments, drop the element name
-        $args = func_get_args();
-        $name = array_shift($args);
+        // make sure the element exists
+        $name = $this->_prepareName($name, $array);
+        if (! empty($this->elements[$name])) {
+            // add the messages
+            foreach ((array) $spec as $text) {
+                $this->elements[$name]['invalid'][] = $text;
+            }
         
-        // add a default validation message (args[0] is the method,
-        // args[1] is the message)
-        if (empty($args[1]) || trim($args[1]) == '') {
-            
-            // see if we have an method-specific validation message
-            $key = 'VALID_' . strtoupper($method);
-            $args[1] = $this->locale($key);
-            
-            // if the message is the same as the key,
-            // there was no method-specific validation
-            // message.  revert to the generic default.
-            if ($key == $args[1]) {
-                $args[1] = $this->locale('ERR_INVALID');
-            }
+            // mark the status of the element, and of the form
+            $this->elements[$name]['status'] = false;
+            $this->_status = false;
         }
-        
-        // add to the validation array
-        $this->_valid[$name][] = $args;
     }
     
     /**
      * 
-     * Adds multiple feedback messages to elements.
+     * Adds invalidation messages to multiple elements, sets their status to
+     * false (invalid) and sets the form status to false (invalid).
      * 
-     * @param array $list An associative array where the key is an element
-     * name and the value is a string or sequential array of feedback messages.
+     * @param array $list An array where the key is the element name, and the
+     * value is a string or array of invalidation messages for that element.
      * 
      * @param string $array Rename each element as a key in this array.
      * 
      * @return void
      * 
      */
-    public function addFeedback($list, $array = null)
+    public function addInvalids($list, $array = null)
     {
-        foreach ($list as $name => $feedback) {
-            $name = $this->_prepareName($name, $array);
-            settype($feedback, 'array');
-            foreach ($feedback as $text) {
-                $this->elements[$name]['feedback'][] = $text;
-            }
+        foreach ((array) $list as $name => $spec) {
+            $this->addInvalid($name, $spec, $array);
         }
     }
     
-    
     // -----------------------------------------------------------------
     // 
     // Value-management methods
@@ -503,8 +485,61 @@
     
     /**
      * 
-     * Populates form elements with specified values.
+     * Manually set the value of one element.
      * 
+     * Note that this is subtly different from [[populate()]].  This method
+     * takes full name of the element, whereas populate() takes a "natural"
+     * hierarchical array like $_POST.
+     * 
+     * @param string $name The element name.
+     * 
+     * @param mixes $value Set the element to this value.
+     * 
+     * @param string $array Rename the element as a key in this array.
+     * 
+     * @return void
+     * 
+     */
+    public function setValue($name, $value, $array = null)
+    {
+        // make sure the element exists
+        $name = $this->_prepareName($name, $array);
+        if (! empty($this->elements[$name])) {
+            $this->elements[$name]['value'] = $value;
+        }
+        
+    }
+    
+    /**
+     * 
+     * Manually set the value of several elements.
+     * 
+     * Note that this is subtly different from [[populate()]].  This method
+     * takes a flat array where the full name of the element is the key, 
+     * whereas populate() takes a "natural" hierarchical array like $_POST.
+     * 
+     * @param array $data An associative array where the key is the element
+     * name and the value is the element value to set.
+     * 
+     * @param string $array Rename each element as a key in this array.
+     * 
+     * @return void
+     * 
+     * @throws Solar_Form_Exception_NoSuchElement when the named element does
+     * not exist in the form.
+     * 
+     */
+    public function setValues($data, $array = null)
+    {
+        foreach ((array) $data as $name => $value) {
+            $this->setValue($name, $value, $array);
+        }
+    }
+    
+    /**
+     * 
+     * Automatically populates form elements with specified values.
+     * 
      * @param array $submit The source data array for populating form
      * values as array(name => value); if null, will populate from POST
      * or GET vars as determined from the Solar_Form::$attribs['method']
@@ -540,19 +575,17 @@
     
     /**
      * 
-     * Performs filtering and validation on each form element.
+     * Applies the filter chain to the form element values; in particular,
+     * checks validation and updates the 'invalid' keys for each element that
+     * fails.
      * 
-     * Updates the feedback keys for each element that fails validation.
-     * Values are either pulled from the submitted form or from the array
-     * passed in $submit.
-     * 
      * This method cycles through each element in the form, where it ...
      * 
      * 1. Applies the filters to populated user input for the element,
      * 
      * 2. Validates the filtered value against the validation rules for the element,
      * 
-     * 3. Adds feedback messages to the element if it does not pass validation.
+     * 3. Adds invalidation messages to the element if it does not pass validation.
      * 
      * If all populated values pass validation, the method returns boolean
      * true, indicating the form as a whole it valid; if even one validation on
@@ -566,74 +599,46 @@
      * please see those pages for more information on how to add filters and
      * validation to an element.
      * 
-     * @param array $submit The source data array for populating form
-     * values as array(name => info); if null, will populate from POST
-     * or GET vars as determined from the 'method' attribute.
-     * 
      * @return bool True if all elements are valid, false if not.
      * 
      */
-    public function validate($submit = null)
+    public function validate()
     {
-        // Populate the form values.
-        if (empty($this->_submitted)) {
-            $this->populate($submit);
-        }
-
-        // Loop through each element to filter
-        foreach ($this->_filter as $name => $filters) {
-            $value = $this->elements[$name]['value'];
-            $this->elements[$name]['value'] = $this->_obj_filter->multiple(
-                $value, $filters
-            );
-        }
-
-        $validated = true;
+        // reset the filter chain so we can rebuild it
+        $this->_filter->resetChain();
         
-        // loop through each element to be validated
-        foreach ($this->_valid as $name => $list) {
+        // build the filter chain and data values. note that the foreach()
+        // loop uses an info **reference**, not a copy.
+        $data = array();
+        foreach ($this->elements as $name => &$info) {
+            // keep a **reference** to the data (not a copy)
+            $data[$name] = &$info['value'];
             
-            // loop through each validation for the element
-            foreach ($list as $args) {
-                
-                // the name of the Solar_Valid method
-                $method = array_shift($args);
-                
-                // the text of the error message
-                $feedback = array_shift($args);
-                
-                // config is now the remaining arguments,
-                // put the value on top of it.
-                array_unshift($args, $this->elements[$name]['value']);
-                
-                // call the appropriate Solar_Valid method.
-                $result = call_user_func_array(
-                    array($this->_obj_valid, $method),
-                    $args
-                );
-                
-                // was it valid?
-                if (! $result) {
-                    // no, add the feedback message
-                    $validated = false;
-                    $this->elements[$name]['feedback'][] = $feedback;
-                    $this->elements[$name]['status'] = false;
-                } else {
-                    $this->elements[$name]['status'] = true;
-                }
-                
-            } // inner loop of validations
+            // set the filters and require-flag, reference not needed
+            $this->_filter->addChainFilters($name, $info['filters']);
+            $this->_filter->setChainRequire($name, $info['require']);
             
-        } // outer loop of elements
+        }
         
-        if ($validated) {
+        // apply the filter chain to the data, which will modify the 
+        // element data in place because of the references
+        $this->_status = $this->_filter->applyChain($data);
+        
+        // set the main feedback message
+        if ($this->_status) {
             $this->feedback = array($this->_config['success']);
         } else {
             $this->feedback = array($this->_config['failure']);
         }
         
-        $this->_status = $validated;
-        return $validated;
+        // retain any invalidation messages
+        $invalid = $this->_filter->getChainInvalid();
+        foreach ((array) $invalid as $key => $val) {
+            $this->addInvalid($key, $val);
+        }
+        
+        // done!
+        return $this->_status;
     }
     
     /**
@@ -647,7 +652,7 @@
      * @return array An associative array of element values.
      * 
      */
-    public function values($key = null)
+    public function getValues($key = null)
     {
         $values = array();
         foreach ($this->elements as $name => $elem) {
@@ -686,8 +691,7 @@
         $this->attribs    = $this->_config['attribs'];
         $this->elements   = array();
         $this->feedback   = null;
-        $this->_filter    = array();
-        $this->_valid     = array();
+        $this->_filters   = array();
         $this->_submitted = null;
     }
     




More information about the Solar-svn mailing list