Skip to content

Commit 7f2e848

Browse files
Fix incorrect option selection logic and improve data binding for Select and Checkable fields (#617)
1 parent 4f8f5b0 commit 7f2e848

File tree

4 files changed

+118
-10
lines changed

4 files changed

+118
-10
lines changed

src/Former/Form/Fields/Select.php

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ public function render()
9595
$this->value = (array) $this->value;
9696
}
9797

98+
$this->clearSelected();
99+
98100
// Mark selected values as selected
99101
if ($this->hasChildren() and !empty($this->value)) {
100102
foreach ($this->value as $value) {
@@ -131,9 +133,9 @@ protected function selectValue($value, $parent = null)
131133
}
132134

133135
foreach ($parent->getChildren() as $child) {
134-
// Search by value
136+
$optionValue = $child->getAttribute('value');
135137

136-
if ($child->getAttribute('value') === $value || is_numeric($value) && $child->getAttribute('value') === (int)$value ) {
138+
if ($optionValue === $value || (is_numeric($value) && is_numeric($optionValue) && (int)$optionValue === (int)$value)) {
137139
$child->selected('selected');
138140
}
139141

@@ -239,7 +241,11 @@ public function addOption($text = null, $value = null, $attributes = array())
239241
if (is_array($text)) {
240242
$this->children[$childrenKey] = Element::create('optgroup')->label($value);
241243
foreach ($text as $key => $value) {
242-
$option = Element::create('option', $value)->setAttribute('value', $key);
244+
if (is_array($value)) {
245+
$option = Element::create('option', $key)->setAttributes($value);
246+
} else {
247+
$option = Element::create('option', $value)->setAttribute('value', $key);
248+
}
243249
$this->children[$childrenKey]->nest($option);
244250
}
245251
// Else if it's a simple option
@@ -254,6 +260,29 @@ public function addOption($text = null, $value = null, $attributes = array())
254260
return $this;
255261
}
256262

263+
/**
264+
* Clear selected attribute for select options
265+
*
266+
* @param Element $parent
267+
*
268+
* @return void
269+
*/
270+
public function clearSelected($parent = null)
271+
{
272+
// If no parent element defined, use direct children
273+
if (!$parent) {
274+
$parent = $this;
275+
}
276+
277+
foreach ($parent->getChildren() as $child) {
278+
$child->removeAttribute('selected');
279+
280+
if ($child->hasChildren()) {
281+
$this->clearSelected($child);
282+
}
283+
}
284+
}
285+
257286
/**
258287
* Use the results from a Fluent/Eloquent query as options
259288
*

src/Former/Helpers.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public static function setApp(Container $app)
2929
/**
3030
* Encodes HTML
3131
*
32-
* @param string|null $value The string to encode
32+
* @param string|array|null $value The string to encode
3333
*
3434
* @return string
3535
*/
@@ -39,6 +39,10 @@ public static function encode($value)
3939
return '';
4040
}
4141

42+
if (is_array($value)) {
43+
$value = '';
44+
}
45+
4246
return htmlentities($value, ENT_QUOTES, 'UTF-8', true);
4347
}
4448

src/Former/Traits/Checkable.php

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,24 @@ public function check($checked = true)
257257
return $this;
258258
}
259259

260+
/**
261+
* Creates a series of checkable items from query with attributes
262+
*
263+
* @param mixed $query
264+
* @param mixed $text
265+
* @param mixed $attributes
266+
* @return $this
267+
*/
268+
public function fromQuery($query, $text = null, $attributes = null)
269+
{
270+
if ($this->isGrouped()) {
271+
// Remove any possible items added by the Populator.
272+
$this->items = array();
273+
}
274+
$this->items($query, $text, $attributes);
275+
276+
return $this;
277+
}
260278

261279
/**
262280
* Check if the checkables are inline
@@ -275,9 +293,11 @@ public function isInline()
275293
/**
276294
* Creates a series of checkable items
277295
*
278-
* @param array $_items Items to create
296+
* @param array|Collection $_items Items to create
297+
* @param string|function $text The value to use as text
298+
* @param string|array $attributes The data to use as attributes
279299
*/
280-
protected function items($_items)
300+
protected function items($_items, $text = null, $attributes = null)
281301
{
282302
// If passing an array
283303
if (sizeof($_items) == 1 and
@@ -288,9 +308,11 @@ protected function items($_items)
288308
}
289309

290310
// Fetch models if that's what we were passed
291-
if (isset($_items[0]) and is_object($_items[0])) {
292-
$_items = Helpers::queryToArray($_items);
293-
$_items = array_flip($_items);
311+
if ((isset($_items[0]) and is_object($_items[0])) or ($_items instanceof Collection)) {
312+
$_items = Helpers::queryToArray($_items, $text, $attributes);
313+
if (is_null($text) && is_null($attributes)) {
314+
$_items = array_flip($_items);
315+
}
294316
}
295317

296318
// Iterate through items, assign a name and a label to each
@@ -542,7 +564,7 @@ protected function isChecked($item = null, $value = null)
542564

543565
if (!is_null($post) and $post !== $this->app['former']->getOption('unchecked_value')) {
544566
$isChecked = ($post == $value);
545-
} elseif (!is_null($static)) {
567+
} elseif (!is_null($static) && !(is_array($static) && empty($static))) {
546568
$isChecked = ($static == $value);
547569
} else {
548570
$isChecked = $checked;

tests/Fields/SelectTest.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,4 +542,57 @@ public function testSelectCanPickRightOptionWithOptgroups()
542542

543543
$this->assertStringContainsString($matcher, $select->render());
544544
}
545+
546+
public function testCanClearPreviouslySelectedOptions()
547+
{
548+
$select = $this->former->select('foo')->options([
549+
'One' => ['value' => 1, 'selected' => 'selected'],
550+
'Two' => ['value' => 2],
551+
])->select(2);
552+
553+
$matcher = $this->controlGroup(
554+
'<select id="foo" name="foo">' .
555+
'<option value="1">One</option>' .
556+
'<option value="2" selected="selected">Two</option>' .
557+
'</select>'
558+
);
559+
560+
$this->assertEquals($matcher, $select->__toString());
561+
}
562+
563+
public function testOptgroupSupportsNestedOptionsWithAttributes()
564+
{
565+
$select = $this->former->select('foo')->addOption([
566+
'A' => ['value' => 1, 'data-type' => 'x'],
567+
'B' => ['value' => 2, 'data-type' => 'y'],
568+
], 'Group 1');
569+
570+
$matcher = $this->controlGroup(
571+
'<select id="foo" name="foo">' .
572+
'<optgroup label="Group 1">' .
573+
'<option value="1" data-type="x">A</option>' .
574+
'<option value="2" data-type="y">B</option>' .
575+
'</optgroup>' .
576+
'</select>'
577+
);
578+
579+
$this->assertEquals($matcher, $select->__toString());
580+
}
581+
582+
public function testSelectHandlesLooseTypeMatchingForValues()
583+
{
584+
$select = $this->former->select('foo')->options([
585+
'Zero' => ['value' => 0],
586+
'One' => ['value' => 1],
587+
])->select('0');
588+
589+
$matcher = $this->controlGroup(
590+
'<select id="foo" name="foo">' .
591+
'<option value="0" selected="selected">Zero</option>' .
592+
'<option value="1">One</option>' .
593+
'</select>'
594+
);
595+
596+
$this->assertEquals($matcher, $select->__toString());
597+
}
545598
}

0 commit comments

Comments
 (0)