Skip to content

Commit f19129b

Browse files
author
Sandro Rodrigues
committed
Improvements and bug fixes
1 parent a972b18 commit f19129b

File tree

5 files changed

+176
-61
lines changed

5 files changed

+176
-61
lines changed

src/Fields/BoundField.php

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77

88
class BoundField
99
{
10-
const LABEL_TEMPLATE = '<label for="{for}" [{attrs}?]>{contents}</label>';
10+
const LABEL_TEMPLATE = '<label for="{for}"[ {attrs}?]>{contents}[ {required}?]</label>';
11+
const LABEL_REQUIRED = '<span class="required">*</span>';
1112

1213
private $form;
1314
private $field;
@@ -39,14 +40,26 @@ public function __get($name)
3940
return $this->form->getFieldErrors($this->name);
4041
}
4142

43+
if ($name == 'has_errors') {
44+
return $this->form->hasErrors($this->name);
45+
}
46+
47+
if ($name == 'label_tag') {
48+
return $this->labelTag();
49+
}
50+
51+
if ($name == 'is_required') {
52+
return $this->field->isRequired();
53+
}
54+
4255
return parent::__get($name);
4356
}
4457

4558
protected function asWidget($widget = null, array $attrs = null)
4659
{
4760
$widget = is_null($widget) ? $this->field->getWidget() : $widget;
4861

49-
$value = null;
62+
$value = $this->form->getInitialForField($this->field, $this->name);
5063

5164
if ($this->form->isBound()) {
5265
$value = $widget->valueFromData($this->form->data, $this->form->files, $this->html_name);
@@ -68,7 +81,8 @@ public function labelTag($contents = null, array $attrs = null)
6881
return Formatter::format(self::LABEL_TEMPLATE, array(
6982
"for" => $widget->buildAutoId($this->html_name),
7083
"attrs" => $attrs,
71-
"contents" => $contents
84+
"contents" => $contents,
85+
"required" => $this->field->isRequired() ? $this::LABEL_REQUIRED : null
7286
));
7387
}
7488
}

src/Fields/Field.php

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ abstract class Field
3131
*/
3232
protected $required = false;
3333

34+
/**
35+
* Initial value if no data bounded.
36+
* @var mixed
37+
*/
38+
protected $initial = null;
39+
3440
/**
3541
* @var array Array of user validators.
3642
*/
@@ -57,7 +63,14 @@ abstract class Field
5763
*/
5864
public function __construct(array $args = array())
5965
{
60-
$this->computeArgs($args);
66+
$this->widget = array_key_exists('widget', $args) ? $args['widget'] : $this->widget;
67+
$this->label = array_key_exists('label', $args) ? $args['label'] : $this->label;
68+
$this->help_text = array_key_exists('help_text', $args) ? $args['help_text'] : $this->help_text;
69+
$this->required = array_key_exists('required', $args) ? $args['required'] : $this->required;
70+
$this->initial = array_key_exists('initial', $args) ? $args['initial'] : $this->initial;
71+
$this->validators = array_key_exists('validators', $args) ? $args['validators'] : $this->validators;
72+
$this->error_messages = array_key_exists('error_messages', $args) ?
73+
$args['error_messages'] : $this->error_messages;
6174

6275
if (!is_null($this->widget)) {
6376
$this->widget = new $this->widget;
@@ -78,6 +91,26 @@ public function getWidget()
7891
return $this->widget;
7992
}
8093

94+
/**
95+
* Return defined initial value.
96+
*
97+
* @return mixed
98+
*/
99+
public function getInitial()
100+
{
101+
return $this->initial;
102+
}
103+
104+
/**
105+
* Return defined if this field is required or not.
106+
*
107+
* @return bool
108+
*/
109+
public function isRequired()
110+
{
111+
return $this->required;
112+
}
113+
81114
/**
82115
* Return defined label or construct one based on the field name.
83116
*
@@ -210,25 +243,4 @@ private function getErrorMessages($class = null)
210243

211244
return $error_messages;
212245
}
213-
214-
/**
215-
* Init object arguments, but only valid class arguments, otherwise throw an exception.
216-
*
217-
* @param array $args Arguments to be setted.
218-
* @param array $exclude Fields to be excluded when trying to set value.
219-
*
220-
* @throws InvalidArgumentException
221-
*/
222-
private function computeArgs(array $args = array(), array $exclude = array())
223-
{
224-
foreach ($args as $name => $value) {
225-
if (property_exists(get_class($this), $name)) {
226-
if (!in_array($name, $exclude)) {
227-
$this->$name = $value;
228-
}
229-
} else {
230-
throw new InvalidArgumentException("Unexpected argument: '$name'");
231-
}
232-
}
233-
}
234246
}

src/Forms/Form.php

Lines changed: 112 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
namespace PHPForm\Forms;
33

44
use ArrayAccess;
5-
use Iterator;
65
use Countable;
6+
use InvalidArgumentException;
7+
use Iterator;
8+
use UnexpectedValueException;
79

810
use Fleshgrinder\Core\Formatter;
911

@@ -17,40 +19,78 @@ abstract class Form implements ArrayAccess, Iterator, Countable
1719
const PREFIX_TEMPLATE = '{prefix}-{field_name}';
1820
const NON_FIELD_ERRORS = '__all__';
1921

22+
/**
23+
* List of form errors.
24+
* @var array
25+
*/
2026
private $form_errors = null;
27+
28+
/**
29+
* Array of bounded field to cache propose.
30+
* @var array
31+
*/
32+
private $bound_fields_cache = array();
33+
34+
/**
35+
* Indicates if there's data bounded to form.
36+
* @var boolean
37+
*/
2138
private $is_bound = false;
39+
40+
/**
41+
* Prefix to be used in form names.
42+
* @var string
43+
*/
44+
protected $prefix = null;
45+
46+
/**
47+
* Fields declared to this form.
48+
* @var array
49+
*/
2250
protected $fields = array();
51+
52+
/**
53+
* Cleaned values after validation.
54+
* @var array
55+
*/
2356
protected $cleaned_data = array();
2457

25-
public function __construct(array $data = null, array $files = null, string $prefix = null)
58+
/**
59+
* Constructor method
60+
* @param array $args Arguments
61+
*/
62+
public function __construct(array $args = array())
2663
{
27-
$this->is_bound = !is_null($data) or !is_null($files);
28-
$this->data = $data;
29-
$this->files = $files;
30-
$this->prefix = $prefix;
64+
$this->data = array_key_exists('data', $args) ? $args['data'] : null;
65+
$this->files = array_key_exists('files', $args) ? $args['files'] : null;
66+
$this->prefix = array_key_exists('prefix', $args) ? $args['prefix'] : $this->prefix;
67+
$this->initial = array_key_exists('initial', $args) ? $args['initial'] : array();
68+
69+
$this->is_bound = !is_null($this->data) or !is_null($this->files);
3170
$this->fields = $this::setFields();
3271
}
3372

3473
/**
3574
* Method to be redefined with form fields
36-
*
3775
* @return array Desired fields for this form.
3876
*/
3977
protected static function setFields()
4078
{
4179
return array();
4280
}
4381

82+
/**
83+
* Return if form is bounded or not.
84+
* @return boolean
85+
*/
4486
public function isBound()
4587
{
4688
return $this->is_bound;
4789
}
4890

4991
/**
5092
* Special method to make errors accessible as a attribute.
51-
*
52-
* @param string Attribute name.
53-
*
93+
* @param string $name Attribute name.
5494
* @return mixed
5595
*/
5696
public function __get(string $name)
@@ -68,9 +108,7 @@ public function __get(string $name)
68108

69109
/**
70110
* Add a prefix to a name.
71-
*
72-
* @param string Field name.
73-
*
111+
* @param string $field_name Field name.
74112
* @return string
75113
*/
76114
public function addPrefix(string $field_name)
@@ -86,8 +124,9 @@ public function addPrefix(string $field_name)
86124
}
87125

88126
/**
89-
* @param mixed
90-
* @param string
127+
* Add error to specific $field_name, if null, define to NON_FIELD_ERRORS.
128+
* @param mixed $error
129+
* @param string $field_name
91130
*/
92131
protected function addError($error, string $field_name = null)
93132
{
@@ -157,7 +196,6 @@ private function cleanForm()
157196

158197
/**
159198
* Redefine if need to validate crossfields.
160-
*
161199
* @return array Cleaned data
162200
*/
163201
protected function clean()
@@ -166,12 +204,42 @@ protected function clean()
166204
}
167205

168206
/**
169-
* @param string Field name
207+
* Return cleaned data values.
208+
* @return array Cleaned data
209+
*/
210+
public function getCleanedData()
211+
{
212+
return $this->cleaned_data;
213+
}
214+
215+
/**
216+
* Return cleaned field value.
217+
* @param string $field_name Name of field.
218+
* @return string Cleaned field value.
219+
*/
220+
public function getCleanedField(string $field_name)
221+
{
222+
return isset($this->cleaned_data[$field_name]) ? $this->cleaned_data[$field_name] : null;
223+
}
224+
225+
/**
226+
* Check if field has error on it.
227+
* @param string $field_name Name of field to check
228+
* @return boolean
229+
*/
230+
public function hasError($field_name)
231+
{
232+
return array_key_exists($field_name, $this->errors);
233+
}
234+
235+
/**
236+
* Return all errors associated to $field_name.
237+
* @param string $field_name Field name
170238
* @return ErrorList
171239
*/
172240
public function getFieldErrors(string $field_name)
173241
{
174-
if (!array_key_exists($field_name, $this->errors)) {
242+
if (!$this->hasError($field_name)) {
175243
return new ErrorList();
176244
}
177245

@@ -180,12 +248,11 @@ public function getFieldErrors(string $field_name)
180248

181249
/**
182250
* Return errors not associated with any field.
183-
*
184251
* @return ErrorList
185252
*/
186253
public function getNonFieldErrors()
187254
{
188-
if (!array_key_exists($this::NON_FIELD_ERRORS, $this->errors)) {
255+
if (!$this->hasError($this::NON_FIELD_ERRORS)) {
189256
return new ErrorList();
190257
}
191258

@@ -194,14 +261,22 @@ public function getNonFieldErrors()
194261

195262
/**
196263
* Check if form is valid.
197-
*
198264
* @return bool
199265
*/
200266
public function isValid()
201267
{
202268
return $this->is_bound and !count($this->errors);
203269
}
204270

271+
/**
272+
* Check if form is valid.
273+
* @return bool
274+
*/
275+
public function getInitialForField($field, $field_name)
276+
{
277+
return isset($this->initial[$field_name]) ? $this->initial[$field_name] : $field->getInitial();
278+
}
279+
205280
/**
206281
* Implementation of ArrayAccess interface to provide accessing objects as arrays.
207282
*/
@@ -224,10 +299,25 @@ public function offsetUnset($offset)
224299
unset($this->fields[$offset]);
225300
}
226301

302+
/**
303+
* @throws UnexpectedValueException
304+
* @return BoundField
305+
*/
227306
public function offsetGet($offset)
228307
{
229308
$field = isset($this->fields[$offset]) ? $this->fields[$offset] : null;
230-
return new BoundField($this, $field, $offset);
309+
310+
if (is_null($field)) {
311+
$choices = implode(", ", array_keys($this->fields));
312+
$class_name = get_called_class();
313+
throw new UnexpectedValueException("Field '$offset' not found in $class_name. Choices are: $choices", 1);
314+
}
315+
316+
if (!isset($this->bound_fields_cache[$offset])) {
317+
$this->bound_fields_cache[$offset] = new BoundField($this, $field, $offset);
318+
}
319+
320+
return $this->bound_fields_cache[$offset];
231321
}
232322

233323
/**

src/Validators/URLValidator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22
/**
3-
* Validator to check if $value is an valid email
3+
* Validator to check if $value is an valid url
44
*/
55
namespace PHPForm\Validators;
66

0 commit comments

Comments
 (0)