Skip to content

Commit d623886

Browse files
committed
Added ChoiceWidget
1 parent 65649f3 commit d623886

31 files changed

+561
-68
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
PHP class for form handling abstraction inspired in Django Framework forms.
77

88
## Instalation
9-
109
Library can be installed using Composer like so:
1110

1211
```shell

src/Fields/BooleanField.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@
55
namespace PHPForm\Fields;
66

77
use PHPForm\Exceptions\ValidationError;
8+
use PHPForm\Widgets\CheckboxInput;
89

910
class BooleanField extends Field
1011
{
1112
const FALSE_STRING_VALUES = ['false', '0'];
1213

14+
protected $widget = CheckboxInput::class;
15+
1316
/*
1417
* Tranforms $value into a native php object type
1518
*/

src/Fields/BoundField.php

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,20 @@ public function __get($name)
5252
return $this->field->isRequired();
5353
}
5454

55+
if ($name == 'value') {
56+
return $this->getValue();
57+
}
58+
5559
return parent::__get($name);
5660
}
5761

58-
protected function asWidget($widget = null, array $attrs = null)
62+
protected function asWidget($widget = null, array $attrs = array())
5963
{
6064
$widget = is_null($widget) ? $this->field->getWidget() : $widget;
6165

62-
$value = $this->form->getInitialForField($this->field, $this->name);
63-
64-
if ($this->form->isBound()) {
65-
$value = $widget->valueFromData($this->form->data, $this->form->files, $this->html_name);
66-
}
66+
$widget->setCssClasses($this->form->getCssClasses());
6767

68-
return $widget->render($this->html_name, $value, $attrs);
68+
return $widget->render($this->html_name, $this->getValue(), $attrs);
6969
}
7070

7171
public function labelTag($contents = null, array $attrs = null)
@@ -85,4 +85,16 @@ public function labelTag($contents = null, array $attrs = null)
8585
"required" => $this->field->isRequired() ? $this::LABEL_REQUIRED : null
8686
));
8787
}
88+
89+
protected function getValue()
90+
{
91+
if ($this->form->isBound() && !$this->field->isDisabled()) {
92+
$widget = $this->field->getWidget();
93+
$value = $widget->valueFromData($this->form->data, $this->form->files, $this->html_name);
94+
} else {
95+
$value = $this->form->getInitialForField($this->field, $this->name);
96+
}
97+
98+
return $value;
99+
}
88100
}

src/Fields/DateField.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@
66

77
use PHPForm\Widgets\DateInput;
88

9-
class DateField extends Field
9+
class DateField extends TemporalField
1010
{
11+
const FORMAT = "!d-m-Y";
12+
1113
protected $widget = DateInput::class;
14+
15+
protected $error_messages = array(
16+
'invalid' => 'Enter a valid date.'
17+
);
1218
}

src/Fields/DateTimeField.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@
66

77
use PHPForm\Widgets\DateTimeInput;
88

9-
class DateTimeField extends Field
9+
class DateTimeField extends TemporalField
1010
{
11+
const FORMAT = "!d-m-Y H:i";
12+
1113
protected $widget = DateTimeInput::class;
14+
15+
protected $error_messages = array(
16+
'invalid' => 'Enter a valid date/time.'
17+
);
1218
}

src/Fields/Field.php

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

34+
/**
35+
* @var bool Mark field as disabled.
36+
*/
37+
protected $disabled = false;
38+
3439
/**
3540
* Initial value if no data bounded.
3641
* @var mixed
@@ -67,15 +72,23 @@ public function __construct(array $args = array())
6772
$this->label = array_key_exists('label', $args) ? $args['label'] : $this->label;
6873
$this->help_text = array_key_exists('help_text', $args) ? $args['help_text'] : $this->help_text;
6974
$this->required = array_key_exists('required', $args) ? $args['required'] : $this->required;
75+
$this->disabled = array_key_exists('disabled', $args) ? $args['disabled'] : $this->disabled;
7076
$this->initial = array_key_exists('initial', $args) ? $args['initial'] : $this->initial;
7177
$this->validators = array_key_exists('validators', $args) ? $args['validators'] : $this->validators;
7278
$this->error_messages = array_key_exists('error_messages', $args) ?
7379
$args['error_messages'] : $this->error_messages;
7480

7581
if (!is_null($this->widget)) {
76-
$this->widget = new $this->widget;
77-
$this->widget->setRequired($this->required);
78-
$this->widget->setAttrs($this->widgetAttrs($this->widget));
82+
// instantiate widget class if string is passed like so: Widget::class
83+
if (is_string($this->widget)) {
84+
$widget = new $this->widget;
85+
}
86+
87+
$widget->setRequired($this->required);
88+
$widget->setDisabled($this->disabled);
89+
$widget->setAttrs($this->widgetAttrs($widget));
90+
91+
$this->widget = $widget;
7992
}
8093

8194
$this->error_messages = array_merge($this->getErrorMessages(), $this->error_messages);
@@ -111,6 +124,16 @@ public function isRequired()
111124
return $this->required;
112125
}
113126

127+
/**
128+
* Return defined if this field is disabled or not.
129+
*
130+
* @return bool
131+
*/
132+
public function isDisabled()
133+
{
134+
return $this->disabled;
135+
}
136+
114137
/**
115138
* Return defined label or construct one based on the field name.
116139
*

src/Fields/TemporalField.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
/**
3+
* DateTimeField Class
4+
*/
5+
namespace PHPForm\Fields;
6+
7+
use PHPForm\Exceptions\ValidationError;
8+
9+
class TemporalField extends Field
10+
{
11+
/**
12+
* The constructor.
13+
*/
14+
public function __construct(array $args = array())
15+
{
16+
$this->format = array_key_exists('format', $args) ? $args['format'] : static::FORMAT;
17+
18+
parent::__construct($args);
19+
}
20+
21+
public function toNative($value)
22+
{
23+
if (empty($value)) {
24+
return null;
25+
}
26+
27+
$date = date_create_from_format($this->format, $value);
28+
29+
if (!$date) {
30+
throw new ValidationError($this->error_messages['invalid'], 'invalid');
31+
}
32+
33+
return $date;
34+
}
35+
}

src/Forms/Form.php

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ abstract class Form implements ArrayAccess, Iterator, Countable
4343
*/
4444
protected $prefix = null;
4545

46+
/**
47+
* Cass classes to be added to all widgets.
48+
* @var array
49+
*/
50+
protected $css_classes = array();
51+
4652
/**
4753
* Fields declared to this form.
4854
* @var array
@@ -65,6 +71,7 @@ public function __construct(array $args = array())
6571
$this->files = array_key_exists('files', $args) ? $args['files'] : null;
6672
$this->prefix = array_key_exists('prefix', $args) ? $args['prefix'] : $this->prefix;
6773
$this->initial = array_key_exists('initial', $args) ? $args['initial'] : array();
74+
$this->css_classes = array_key_exists('css_classes', $args) ? $args['css_classes'] : $this->css_classes;
6875

6976
$this->is_bound = !is_null($this->data) or !is_null($this->files);
7077
$this->fields = $this::setFields();
@@ -88,6 +95,15 @@ public function isBound()
8895
return $this->is_bound;
8996
}
9097

98+
/**
99+
* Return css classes to be added to each widget.
100+
* @return array
101+
*/
102+
public function getCssClasses()
103+
{
104+
return $this->css_classes;
105+
}
106+
91107
/**
92108
* Special method to make errors accessible as a attribute.
93109
* @param string $name Attribute name.
@@ -165,8 +181,11 @@ private function fullClean()
165181
private function cleanFields()
166182
{
167183
foreach ($this->fields as $field_name => $field) {
168-
$widget = $field->getWidget();
169-
$value = $widget->valueFromData($this->data, $this->files, $this->addPrefix($field_name));
184+
if ($field->isDisabled()) {
185+
$value = $this->getInitialForField($field, $field_name);
186+
} else {
187+
$value = $field->getWidget()->valueFromData($this->data, $this->files, $this->addPrefix($field_name));
188+
}
170189

171190
try {
172191
$this->cleaned_data[$field_name] = $field->clean($value);

src/Widgets/CheckboxInput.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
/**
3+
* Abstract class for Widgets
4+
*/
5+
namespace PHPForm\Widgets;
6+
7+
class CheckboxInput extends Input
8+
{
9+
protected $input_type = 'checkbox';
10+
11+
public function getContext(string $name, $value, array $attrs = null)
12+
{
13+
if ($value) {
14+
$attrs = is_null($attrs) ? array() : $attrs;
15+
$attrs["checked"] = "checked";
16+
}
17+
18+
return parent::getContext($name, $value, $attrs);
19+
}
20+
21+
public function valueFromData($data, $files, string $name)
22+
{
23+
return array_key_exists($name, $data) ? bool($data[$name]) : false;
24+
}
25+
}

src/Widgets/ChoiceWidget.php

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?php
2+
/**
3+
* Abstract class for Widgets
4+
*/
5+
namespace PHPForm\Widgets;
6+
7+
use Fleshgrinder\Core\Formatter;
8+
9+
use PHPForm\Utils\Attributes;
10+
11+
abstract class ChoiceWidget extends Widget
12+
{
13+
const TEMPLATE_OPTION = '';
14+
15+
protected $allow_multiple_selected = false;
16+
protected $input_type = null;
17+
protected $option_inherits_attrs = true;
18+
protected $choices;
19+
20+
/**
21+
* The constructor.
22+
*/
23+
public function __construct(array $choices = array(), array $css_classes = null, array $attrs = null)
24+
{
25+
parent::__construct($css_classes, $attrs);
26+
27+
$this->choices = $choices;
28+
}
29+
30+
public function setChoices(array $value)
31+
{
32+
$this->choices = $choices;
33+
}
34+
35+
public function getContext(string $name, $value, array $attrs = null)
36+
{
37+
$context = parent::getContext($name, $value, $attrs);
38+
$context["options"] = $this->renderOptions($context['name'], $context['value'], $attrs);
39+
40+
return $context;
41+
}
42+
43+
public function renderOptions(string $name, $value, array $attrs = null)
44+
{
45+
$options = '';
46+
$has_selected = false;
47+
48+
foreach ($this->choices as $choice_value => $choice_label) {
49+
$selected = false;
50+
51+
if (!$has_selected || $this->allow_multiple_selected) {
52+
$selected = in_array($choice_value, $value);
53+
$has_selected = true;
54+
}
55+
56+
$context = $this->getOptionContext($name, $choice_value, $choice_label, $selected, $attrs);
57+
58+
$options .= Formatter::format(static::TEMPLATE_OPTION, $context);
59+
}
60+
61+
return $options;
62+
}
63+
64+
public function getOptionContext(string $name, $value, string $label, bool $is_selected, array $attrs = null)
65+
{
66+
if (!$this->option_inherits_attrs || is_null($attrs)) {
67+
$attrs = array();
68+
}
69+
70+
if ($is_selected) {
71+
$attrs["selected"] = "selected";
72+
}
73+
74+
return array(
75+
"type" => $this->input_type,
76+
"name" => $name,
77+
"value" => htmlentities($value),
78+
"label" => htmlentities($label),
79+
"attrs" => Attributes::flatatt($attrs)
80+
);
81+
}
82+
83+
protected function formatValue($value)
84+
{
85+
if (is_array($value)) {
86+
$values = [];
87+
88+
foreach ($value as $v) {
89+
$values[] = parent::formatValue($v);
90+
}
91+
} else {
92+
$values = [parent::formatValue($value)];
93+
}
94+
95+
return $values;
96+
}
97+
}

0 commit comments

Comments
 (0)