From 5858ebfda3c8c45e697ad0f37b8ad80764c40c94 Mon Sep 17 00:00:00 2001 From: garyttierney Date: Fri, 13 Jun 2014 15:38:05 +0100 Subject: [PATCH 1/5] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index ac7a005..98228f4 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "ppi/form", + "name": "garyttierney/form", "autoload": { "psr-4": { "PPI\\Form\\": "src/" } }, From 38148e4583c98768747545058a4e4bfe67e89105 Mon Sep 17 00:00:00 2001 From: garyttierney Date: Fri, 13 Jun 2014 15:41:56 +0100 Subject: [PATCH 2/5] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 98228f4..ac7a005 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "garyttierney/form", + "name": "ppi/form", "autoload": { "psr-4": { "PPI\\Form\\": "src/" } }, From df16d570c91dbe521d76f10da3cac505b8c9adce Mon Sep 17 00:00:00 2001 From: Gary Tierney Date: Fri, 13 Jun 2014 15:42:59 +0100 Subject: [PATCH 3/5] Fixing Form.php --- src/Form.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Form.php b/src/Form.php index de4e7bb..8d980ae 100644 --- a/src/Form.php +++ b/src/Form.php @@ -221,8 +221,8 @@ public function add($elementType, $name, array $options = array()) // @todo - keep a list of "labels to process" so if you add an element at a later date, // it will find previously added labels and populate them // Setup the for="" for this label to pull from the element's ID matching $name - if (isset($this->elements[$name]) && $this->elements[$name]->hasAttribute('id')) { - $element->setAttribute('for', $this->elements[$name]->getAttribute('id')); + if (isset($this->elements[$name]) && $this->elements[$name]->hasAttr('id')) { + $element->setAttr('for', $this->elements[$name]->getAttr('id')); } } From 1d77ce1681ab82707f7c4e41af4027d1a7740ccc Mon Sep 17 00:00:00 2001 From: Gary Tierney Date: Mon, 16 Jun 2014 11:21:03 +0100 Subject: [PATCH 4/5] Adding Label methods to Element --- src/Element/Element.php | 14 ++++++++++++++ src/Element/Label.php | 3 ++- src/Form.php | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Element/Element.php b/src/Element/Element.php index f65372c..080cf42 100644 --- a/src/Element/Element.php +++ b/src/Element/Element.php @@ -13,6 +13,7 @@ abstract class Element implements ElementInterface { + protected $label; /** * The constructor @@ -274,5 +275,18 @@ public function getType() return $this->type; } + public function getLabel() + { + return $this->label; + } + public function setLabel($label) + { + $this->label = $label; + } + + public function hasLabel() + { + return isset($this->label); + } } diff --git a/src/Element/Label.php b/src/Element/Label.php index bd28da3..d4cd563 100644 --- a/src/Element/Label.php +++ b/src/Element/Label.php @@ -13,6 +13,8 @@ class Label extends BaseElement { + + protected $type = 'label'; /** @@ -22,7 +24,6 @@ class Label extends BaseElement */ function render() { - // Auto-set the 'for' attribute of this to match the ID of the 'name' $html = ''; diff --git a/src/Form.php b/src/Form.php index 8d980ae..ec06a0a 100644 --- a/src/Form.php +++ b/src/Form.php @@ -280,6 +280,7 @@ public function hasElement($name) return isset($this->elements[$name]); } + /** * Apply some bind data to this form. * From cc22c57c5a7fe0eaffb7c025034fde6d239ae202 Mon Sep 17 00:00:00 2001 From: Gary Tierney Date: Thu, 19 Jun 2014 16:44:10 +0100 Subject: [PATCH 5/5] Added virtual Element - TODO: clean all this up --- composer.json | 3 +- src/Element/Element.php | 11 +++--- src/Element/Virtual.php | 54 +++++++++++++++++++++++++++++ src/Form.php | 77 ++++++++++++++++++++++++++++++++++++----- 4 files changed, 132 insertions(+), 13 deletions(-) create mode 100644 src/Element/Virtual.php diff --git a/composer.json b/composer.json index 6158e56..266bae9 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,7 @@ }, "require": { - "symfony/Validator" : "*" + "symfony/Validator" : "2.2.*" }, "require-dev": { @@ -14,3 +14,4 @@ "minimum-stability": "dev" } + diff --git a/src/Element/Element.php b/src/Element/Element.php index 080cf42..60bfa4f 100644 --- a/src/Element/Element.php +++ b/src/Element/Element.php @@ -9,7 +9,7 @@ namespace PPI\Form\Element; use Symfony\Component\Validator\Constraint; -use Symfony\Component\Validator\Validator\ValidatorInterface; +use Symfony\Component\Validator\ValidatorInterface; abstract class Element implements ElementInterface { @@ -175,12 +175,13 @@ public function getConstraints() * @todo garyttierney - do we want the Form to handle validation for every input or should that be left to an element? * if the latter, we should share a single Validator object between all of them. * + * @param $input string|array * @return ElementValidationResult */ - public function validate(ValidatorInterface $validator) + public function validate($input, ValidatorInterface $validator) { // @todo - note, this will be removed in Symfony 3.0 and there's currently no way around that - $validatorResult = $validator->validateValue($this->getValue(), $this->getConstraints()); + $validatorResult = $validator->validateValue($input, $this->getConstraints()); if(count($validatorResult) === 0) { return new ElementValidationResult(true); } else { @@ -192,10 +193,12 @@ public function validate(ValidatorInterface $validator) } } - /** + /* + * * Set element options * * @param array $options + * */ public function setOptions($options) { diff --git a/src/Element/Virtual.php b/src/Element/Virtual.php new file mode 100644 index 0000000..dc58744 --- /dev/null +++ b/src/Element/Virtual.php @@ -0,0 +1,54 @@ +elements[] = $element; + } + + public function __construct($options = array()) + { + parent::__construct($options); + } + + /** + * Render the tag + * + * @return string + */ + public function render() + { + $parts = array(); + + foreach($this->elements as $element) { + $parts[] = $element->render(); + } + + return implode("\n", $parts); + } + + /** + * Takes an array of input and returns a string. Should be used when you have 2 elements which should be concatenated into 1 variable. + * + * For example, if you want a TimeElement which allows the user to choose hours via a select element, and minutes via a select element + * then you should get the input as an array (input_x[0] -- hours, input_x[1] -- minutes) and implement this method + * to join both of those inputs. + * + * @param array $data + * @return string + */ + public abstract function transformInput(array $data); +} \ No newline at end of file diff --git a/src/Form.php b/src/Form.php index ec06a0a..c900187 100644 --- a/src/Form.php +++ b/src/Form.php @@ -10,6 +10,8 @@ use PPI\Form\Element\Element; use PPI\Form\Element\ElementInterface; +use PPI\Form\Element\Label; +use PPI\Form\Element\Virtual; use Symfony\Component\Validator\Validation; class Form @@ -132,7 +134,7 @@ public function submit($name, $value = 'Submit', array $options = array()) */ public function hidden($name, array $options = array()) { - return $this->add($name, 'hidden', $options); + return $this->add('hidden', 'hidden', $options); } /** @@ -342,13 +344,16 @@ public function end() return ''; } + /** * Validates all input elements within a form with their constraints. Returns a hashtable of element names mapped * to an array of their error messages as described in {@link ElementValidationResult::getErrorMessages} * - * @return array + * @param array $input The clients POST data + * + * @return array An array of error messages keyed by element name */ - public function validate() + public function validate(array $input) { $validator = Validation::createValidator(); $errors = array(); @@ -356,14 +361,70 @@ public function validate() /** * @var $element Element */ - foreach($this->elements as $elementName => $element) { - $validationResult = $element->validate($validator); - if($validationResult->isSuccessful()) { + foreach($this->elements as $key => $element) { + if($element instanceof Label) { continue; } - $errors[$elementName] = $validationResult->getErrorMessages(); + if(substr($key, -2) == "[]") { + $elementName = substr($key, 0, -2); + } else { + $elementName = $key; + } + + if(count($element->getConstraints()) > 0) { + $value = array_key_exists($elementName, $input) ? $input[$elementName] : null; // map elements with no value to null + if(is_array($value) && $element instanceof Virtual) { + $value = $element->transformData($value); + } + + $validationResult = $element->validate($value, $validator); + + if ($validationResult->isSuccessful()) { + continue; + } + + $errors[$elementName] = $validationResult->getErrorMessages(); + } } - return $errors; + + $this->errorMessages = $errors; + + return count($errors) == 0; + } + + protected $errorMessages = array(); + + public function getErrorsForElement(Element $element) + { + return $this->errorMessages[$element->getName()]; + } + + public function hasErrorsForElement(Element $element) + { + return array_key_exists($element->getName(), $this->errorMessages); + } + + /** + * Takes an $input an array and checks if the input key corresponds to a Virtual element, if it does then it calls + * the {@see Virtual::transformInput} function to make the input value good for validation. + * + * @param array $input An array of $_POSTed variables. + */ + public function transformInput(array $input) + { + $output = array(); + + foreach($input as $key => $val) { + if(array_key_exists($key, $this->elements) && $this->elements[$key] instanceof Virtual) { + if (is_array($input)) { + $output[$key] = $this->elements[$key]->transformInput($val); + } + } else { + $output[$key] = $val; + } + } + + return $output; } } \ No newline at end of file