]> git.andy128k.dev Git - ipf.git/commitdiff
rework form errors. add set field and set table widget.
authorAndrey Kutejko <andy128k@gmail.com>
Sun, 7 Sep 2014 20:36:04 +0000 (23:36 +0300)
committerAndrey Kutejko <andy128k@gmail.com>
Sun, 7 Sep 2014 20:36:04 +0000 (23:36 +0300)
ipf/admin/formlayout.php
ipf/form/boundfield.php
ipf/form/error.php [new file with mode: 0644]
ipf/form/exception.php
ipf/form/field/set.php [new file with mode: 0644]
ipf/form/form.php
ipf/form/layout.php
ipf/form/widget.php
ipf/form/widget/settable.php [new file with mode: 0644]

index 47690e6e7a8203d5b6bb60048239cddbe629748e..67dad3345cc27ce08e11812ea3d0b21a8c7dbb20 100644 (file)
@@ -19,7 +19,7 @@ class IPF_Admin_Form_Layout extends IPF_Form_LayoutAdapter
             return '';
     }
 
-    public function field($boundField)
+    public function field($boundField, $errors)
     {
         if ($boundField->help_text) {
             $help_text = Tag::p(array('class' => 'help'))
@@ -28,13 +28,20 @@ class IPF_Admin_Form_Layout extends IPF_Form_LayoutAdapter
             $help_text = '';
         }
 
-        return $this->errors($boundField->errors) .
-            Tag::div(array('class' => 'form-row'),
+        if ($boundField->field instanceof IPF_Form_Field_Set) {
+            return $this->errors($errors) .
                 Tag::div()
-                    ->raw($boundField->renderLabel())
-                    ->raw(' ')
                     ->raw($boundField->renderWidget())
-                    ->append($help_text));
+                    ->append($help_text);
+        } else {
+            return $this->errors($errors) .
+                Tag::div(array('class' => 'form-row'),
+                    Tag::div()
+                        ->raw($boundField->renderLabel())
+                        ->raw(' ')
+                        ->raw($boundField->renderWidget())
+                        ->append($help_text));
+        }
     }
 
     private function errors($errors)
index ce18a799ec53fa453857eacb79db9095e139882e..f997d3d2a0c438a34bbc6d5a6de2633183536f76 100644 (file)
@@ -51,9 +51,20 @@ class IPF_Form_BoundField
             $extra_attrs['id'] = $this->autoId();
         }
 
+        if ($widget->embeds_errors)
+            $extra_attrs['errors'] = $this->errors;
+
         return $widget->render($this->html_name, $this->value(), $extra_attrs);
     }
 
+    public function renderErrors()
+    {
+        if ($this->field->widget->embeds_errors)
+            return '';
+
+        return IPF_Form::renderErrorsAsHTML($this->errors);
+    }
+
     public function renderLabel()
     {
         $label = Tag::label()
@@ -70,7 +81,12 @@ class IPF_Form_BoundField
         return $label->html();
     }
 
-    public function autoId()
+    public function __toString()
+    {
+        return (string)$this->renderWidget();
+    }
+
+    private function autoId()
     {
         $id_fields = $this->form->id_fields;
         if (false !== strpos($id_fields, '%s')) {
@@ -80,39 +96,6 @@ class IPF_Form_BoundField
         }
         return '';
     }
-
-
-    public function labelTag()
-    {
-        return $this->form->unescape($this->renderLabel());
-    }
-
-    public function render_w()
-    {
-        return $this->form->unescape($this->renderWidget());
-    }
-
-    public function fieldErrors($raw=false)
-    {
-        $errors = IPF_Form::renderErrorsAsHTML($this->errors);
-        if ($raw)
-            return $errors;
-        else
-            return $this->form->unescape($errors);
-    }
-
-    public function __get($prop)
-    {
-        if (!in_array($prop, array('labelTag', 'fieldErrors', 'render_w'))) {
-            return $this->$prop;
-        }
-        return $this->$prop();
-    }
-
-    public function __toString()
-    {
-        return (string)$this->render_w();
-    }
 }
 
 if (!function_exists('mb_ucfirst')) {
diff --git a/ipf/form/error.php b/ipf/form/error.php
new file mode 100644 (file)
index 0000000..85d4617
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+
+class IPF_Form_Error
+{
+    public $message, $meta;
+
+    public function __construct($message, $meta=null)
+    {
+        $this->message = $message;
+        $this->meta = $meta;
+    }
+}
+
index ab8a8ec63a73f85cc4171f94330e4aabd1ac8e49..7e487277576fa47c088ddc7a79e55bb83c699bea 100644 (file)
@@ -1,3 +1,20 @@
 <?php
 
-class IPF_Exception_Form extends IPF_Exception{}
+// Form validation exception
+
+class IPF_Exception_Form extends IPF_Exception
+{
+    private $meta;
+
+    function __construct($message, $meta=null)
+    {
+        parent::__construct($message);
+        $this->meta = $meta;
+    }
+
+    function getError()
+    {
+        return new IPF_Form_Error($this->getMessage(), $this->meta);
+    }
+}
+
diff --git a/ipf/form/field/set.php b/ipf/form/field/set.php
new file mode 100644 (file)
index 0000000..cbd9e7a
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+
+class IPF_Form_Field_Set extends IPF_Form_Field
+{
+    public $widget = 'IPF_Form_Widget_SetTable';
+    public $fields = array();
+    public $addCount = 3;
+
+    public function clean($value)
+    {
+        if (!$value)
+            return $value;
+
+        $errors = new \PFF\MultidimensionalArray;
+
+        $newValue = array();
+        foreach ($value as $index => $row) {
+            if (!array_filter(\PFF\Arr::flatten($row))) // recursively empty
+                continue;
+
+            foreach ($this->fields as $name => $field) {
+                try {
+                    $value = $field->clean(\PFF\Arr::get($row, $name));
+                } catch (IPF_Exception_Form $ex) {
+                    $errors->pushToKey(array($index, $name), $ex->getError());
+                }
+                $row[$name] = $value;
+            }
+            $newValue[$index] = $row;
+        }
+
+        if ($errors->count())
+            throw new IPF_Exception_Form('', $errors);
+
+        return $newValue;
+    }
+
+    protected function createWidget($args)
+    {
+        $widgets = array();
+        foreach ($this->fields as $name => $field) {
+            $widgets[] = array(
+                'name'    => $name,
+                'widget'  => $field->widget,
+                'label'   => $field->label ? $field->label : $name,
+            );
+        }
+        $args['widgets'] = $widgets;
+        $args['addCount'] = $this->addCount;
+        return parent::createWidget($args);
+    }
+}
+
index a79e6c5db6551fe2e1f552e12d2913911f6e9287..3f6074fe6d6dd808544c83be31a4807cb1c0d47c 100644 (file)
@@ -62,13 +62,8 @@ abstract class IPF_Form implements Iterator
                     $this->cleaned_data[$name] = $value;
                 }
             } catch (IPF_Exception_Form $e) {
-                if (!isset($this->errors[$name]))
-                    $this->errors[$name] = array();
-                $this->errors[$name][] = $e->getMessage();
-
-                if (isset($this->cleaned_data[$name])) {
-                    unset($this->cleaned_data[$name]);
-                }
+                $this->addError($name, $e->getError());
+                unset($this->cleaned_data[$name]);
             }
         }
 
@@ -76,8 +71,7 @@ abstract class IPF_Form implements Iterator
             try {
                 $this->cleaned_data = $this->clean();
             } catch (IPF_Exception_Form $e) {
-                if (!isset($this->errors['__all__'])) $this->errors['__all__'] = array();
-                $this->errors['__all__'][] = $e->getMessage();
+                $this->addError(null, $e->getError());
             }
         }
 
@@ -92,6 +86,13 @@ abstract class IPF_Form implements Iterator
         }
     }
 
+    public function addError($field, $error)
+    {
+        if (!$field)
+            $field = '__all__';
+        \PFF\Arr::pushToKey($this->errors, $field, $error);
+    }
+
     public function clean()
     {
         return $this->cleaned_data;
@@ -111,18 +112,14 @@ abstract class IPF_Form implements Iterator
         return $hidden;
     }
 
-    public function render_top_errors($raw=false)
+    public function errors()
     {
-        $errors = IPF_Form::renderErrorsAsHTML($this->get_top_errors());
-        if ($raw)
-            return $errors;
-        else
-            return $this->unescape($errors);
+        return \PFF\Arr::get($this->errors, '__all__', array());
     }
 
-    public function get_top_errors()
+    public function renderErrors()
     {
-        return \PFF\Arr::get($this->errors, '__all__', array());
+        IPF_Form::renderErrorsAsHTML($this->errors());
     }
 
     public function renderLayout($layout, $raw=false)
@@ -161,7 +158,10 @@ abstract class IPF_Form implements Iterator
 
                 $bf = $this->field($name);
 
-                $output .= $layout->field($bf);
+                if ($field->widget->embeds_errors)
+                    $output .= $layout->field($bf, array());
+                else
+                    $output .= $layout->field($bf, $bf->errors);
             }
             $output .= $layout->endGroup($groupLabel);
         }
@@ -196,19 +196,6 @@ abstract class IPF_Form implements Iterator
         return $form->renderLayout(new IPF_Form_TableLayout, $raw);
     }
 
-    public function render_admin($raw=false)
-    {
-        return $form->renderLayout(new IPF_Admin_Form_Layout, $raw);
-    }
-
-    function __get($prop)
-    {
-        if (!in_array($prop, array('render_p', 'render_ul', 'render_table', 'render_top_errors', 'get_top_errors'))) {
-            return $this->$prop;
-        }
-        return $this->$prop();
-    }
-
     public function field($key)
     {
         return new IPF_Form_BoundField($this, $this->fields[$key], $key);
@@ -246,7 +233,7 @@ abstract class IPF_Form implements Iterator
             return '';
         $ul = Tag::ul(array('class' => 'errorlist'));
         foreach ($errors as $err)
-            $ul->append(Tag::li(null, $err));
+            $ul->append(Tag::li(null, $err->message));
         return $ul->html();
     }
 
index 89a6eeade54afa2702c810bc73cf61065888dc04..88e3f369eebc31f62ea31affeffe6016aca54f68 100644 (file)
@@ -4,7 +4,7 @@
 {
     public function startForm($form);
     public function startGroup($label);
-    public function field($boundField);
+    public function field($boundField, $errors);
     public function endGroup($label);
     public function endForm($form);
 }*/
@@ -14,13 +14,13 @@ abstract class IPF_Form_LayoutAdapter /*implements informal interface IPF_Form_L
 {
     public function startForm($form) { return ''; }
     public function startGroup($label) { return ''; }
-    public abstract function field($boundField);
+    public abstract function field($boundField, $errors);
     public function endGroup($label) { return ''; }
     public function endForm($form) { return ''; }
 
     protected function commonErrors($form)
     {
-        $commonErrors = $form->get_top_errors();
+        $commonErrors = $form->errors();
         foreach ($form->hiddenFields() as $field_name)
             foreach (\PFF\Arr::get($form->errors, $field_name, array()) as $error)
                 $commonErrors[] = sprintf(__('(Hidden field %1$s) %2$s'), $field_name, $error);
@@ -75,10 +75,10 @@ class IPF_Form_ParagraphLayout extends IPF_Form_LayoutAdapter
         return $o;
     }
 
-    public function field($boundField)
+    public function field($boundField, $errors)
     {
         return
-            $this->errorList($boundField->errors) .
+            $this->errorList($errors) .
             Tag::p()
                 ->raw($boundField->renderLabel())
                 ->raw(' ')
@@ -104,7 +104,7 @@ class IPF_Form_ListLayout extends IPF_Form_LayoutAdapter
             return '';
     }
 
-    public function field($boundField)
+    public function field($boundField, $errors)
     {
         if ($boundField->help_text)
             $help_text = '<br /><span class="helptext">'.$boundField->help_text.'</span>';
@@ -113,7 +113,7 @@ class IPF_Form_ListLayout extends IPF_Form_LayoutAdapter
 
         return Tag::li()
             ->raw($this->takeDeferred())
-            ->raw($this->errorList($boundField->errors))
+            ->raw($this->errorList($errors))
             ->raw($boundField->renderLabel())
             ->raw(' ')
             ->raw($boundField->renderWidget())
@@ -147,7 +147,7 @@ class IPF_Form_TableLayout extends IPF_Form_LayoutAdapter
             return '';
     }
 
-    public function field($boundField)
+    public function field($boundField, $errors)
     {
         if ($boundField->help_text)
             $help_text = '<br /><span class="helptext">'.$boundField->help_text.'</span>';
@@ -159,7 +159,7 @@ class IPF_Form_TableLayout extends IPF_Form_LayoutAdapter
                 ->raw($boundField->renderLabel()),
             Tag::td()
                 ->raw($this->takeDeferred())
-                ->raw($this->errorList($boundField->errors))
+                ->raw($this->errorList($errors))
                 ->raw($boundField->renderWidget())
                 ->raw($help_text));
     }
index e561d6187edbd4a513d93651564049382aca7168..a2123b4dfe6510d196a24cb2588e5ea09a08f9f9 100644 (file)
@@ -4,6 +4,7 @@ abstract class IPF_Form_Widget
 {
     public $is_hidden = false; // renders invisible
     public $needs_multipart_form = false;
+    public $embeds_errors = false;
     public $attrs = array();
 
     public function __construct($attrs=array())
diff --git a/ipf/form/widget/settable.php b/ipf/form/widget/settable.php
new file mode 100644 (file)
index 0000000..d5fb457
--- /dev/null
@@ -0,0 +1,121 @@
+<?php
+
+use \PFF\HtmlBuilder\Tag as Tag;
+
+class IPF_Form_Widget_SetTable extends IPF_Form_Widget
+{
+    public $embeds_errors = true;
+
+    public $widgets, $addCount;
+
+    public function __construct($attrs=array())
+    {
+        $this->widgets = \PFF\Arr::pop($attrs, 'widgets', array());
+        $this->addCount = \PFF\Arr::pop($attrs, 'addCount', 3);
+        parent::__construct($attrs);
+    }
+
+    private function extraRows($value)
+    {
+        if (!$value)
+            $value = array();
+
+        $addRows = $this->addCount;
+        foreach ($value as $obj)
+            if (!array_key_exists('id', $obj))
+                --$addRows;
+
+        for (; $addRows > 0; --$addRows)
+            $value[] = array();
+
+        return $value;
+    }
+
+    public function render($name, $value, $extra_attrs=array())
+    {
+        $errors = new \PFF\MultidimensionalArray;
+        foreach (\PFF\Arr::pop($extra_attrs, 'errors', array()) as $e)
+            $errors = $e->meta; // TODO: merge
+
+        $value = $this->extraRows($value);
+
+        $table = Tag::table(array('class' => 'set-widget'))
+            ->attrs($this->attrs)
+            ->attrs($extra_attrs);
+
+        $tr = Tag::tr(array('class' => 'nodrag nodrop'));
+        foreach ($this->widgets as $w)
+            $tr->append(Tag::th(null, $w['label']));
+        $tr->append(Tag::th(null, __('Delete')));
+
+        $table->append($tr);
+
+        $index = 0;
+        foreach ($value as $obj) {
+            $prefix = "{$name}[{$index}]";
+
+            $tr = $this->renderRow($prefix, $obj, $errors, $index);
+            $table->append($tr);
+
+            ++$index;
+        }
+
+        return $table->html();
+    }
+
+    protected function renderRow($prefix, $obj, $errors, $index)
+    {
+        $tr = Tag::tr();
+        foreach ($this->widgets as $w) {
+            $cellErrors = $errors->get($index, $w['name']);
+            $e = IPF_Form::renderErrorsAsHTML($cellErrors);
+
+            $val = \PFF\Arr::get($obj, $w['name']);
+
+            $tr->append(Tag::td()
+                ->raw($e)
+                ->raw($w['widget']->render($prefix.'['.$w['name'].']', $val)));
+        }
+
+        if (array_key_exists('id', $obj))
+            $tr->append(Tag::td(null,
+                Tag::input(array('type' => 'checkbox', 'name' => "{$prefix}[is_remove]")),
+                Tag::input(array('type' => 'hidden', 'name' => "{$prefix}[id]", 'value' => $obj['id']))));
+        else
+            $tr->append(Tag::td());
+
+        return $tr;
+    }
+
+    public function extra_js()
+    {
+        $js = array();
+        foreach ($this->widgets as $w)
+            $js = array_merge($js, $w['widget']->extra_js());
+        return $js;
+    }
+
+    public function valueFromFormData($name, $data)
+    {
+        if (!isset($data[$name]))
+            return null;
+
+        $value = array();
+        foreach ($data[$name] as $i => $rowData) {
+            $rowValue = array();
+
+            foreach ($this->widgets as $w) {
+                $rowValue[$w['name']] = $w['widget']->valueFromFormData($w['name'], $rowData);
+            }
+            if (array_key_exists('is_remove', $rowData))
+                $rowValue['is_remove'] = $rowData['is_remove'];
+            if (array_key_exists('id', $rowData))
+                $rowValue['id'] = $rowData['id'];
+
+            $value[$i] = $rowValue;
+        }
+
+        return $value;
+    }
+}
+