From 4d31ac65a04c29bd1e4a6a7c818ecd9bc1c4c8ba Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Mon, 29 Aug 2011 14:31:03 +0200 Subject: [PATCH] ENHANCEMENT Allowing array values in ListboxField->setValue(), serialising into comma-separated list of values (AIR-35) --- forms/DropdownField.php | 2 +- forms/ListboxField.php | 58 +++++++++++++ tests/forms/ListboxFieldTest.php | 145 +++++++++++++++++++++++++++++++ 3 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 tests/forms/ListboxFieldTest.php diff --git a/forms/DropdownField.php b/forms/DropdownField.php index c87a95cfc..5f8f066dc 100644 --- a/forms/DropdownField.php +++ b/forms/DropdownField.php @@ -122,7 +122,7 @@ class DropdownField extends FormField { * Argument is deprecated in 2.3, please use {@link setHasEmptyDefault()} and {@link setEmptyString()} instead. */ function __construct($name, $title = null, $source = array(), $value = "", $form = null, $emptyString = null) { - $this->source = $source; + $this->setSource($source); if($emptyString) $this->setHasEmptyDefault(true); if(is_string($emptyString)) $this->setEmptyString($emptyString); diff --git a/forms/ListboxField.php b/forms/ListboxField.php index 8d45b1b8d..3f79e5415 100644 --- a/forms/ListboxField.php +++ b/forms/ListboxField.php @@ -116,5 +116,63 @@ class ListboxField extends DropdownField { $this->multiple = $bool; } + function setSource($source) { + if($source) { + $hasCommas = array_filter(array_keys($source), create_function('$key', 'return strpos($key, ",") !== FALSE;')); + if($hasCommas) { + throw new InvalidArgumentException('No commas allowed in $source keys'); + } + } + + parent::setSource($source); + } + + /** + * @return String + */ + function dataValue() { + if($this->value && $this->multiple && is_array($this->value)) { + return implode(',', $this->value); + } else { + return parent::dataValue(); + } + } + + function setValue($val) { + if($val) { + if(!$this->multiple && is_array($val)) { + throw new InvalidArgumentException('No array values allowed with multiple=false'); + } + + if($this->multiple) { + $parts = (is_array($val)) ? $val : preg_split("/ *, */", trim($val)); + if(ArrayLib::is_associative($parts)) { + throw new InvalidArgumentException('No associative arrays allowed multiple=true'); + } + + if($diff = array_diff($parts, array_keys($this->source))) { + throw new InvalidArgumentException(sprintf( + 'Invalid keys "%s" in value array for multiple=true', + Convert::raw2xml(implode(',', $diff)) + )); + } + + parent::setValue($parts); + } else { + if(!in_array($val, array_keys($this->source))) { + throw new InvalidArgumentException(sprintf( + 'Invalid value "%s" for multiple=true', + Convert::raw2xml($val) + )); + } + + parent::setValue($val); + } + } else { + parent::setValue($val); + } + + } + } ?> \ No newline at end of file diff --git a/tests/forms/ListboxFieldTest.php b/tests/forms/ListboxFieldTest.php new file mode 100644 index 000000000..b73843722 --- /dev/null +++ b/tests/forms/ListboxFieldTest.php @@ -0,0 +1,145 @@ + 'a value', 'b' => 'b value','c' => 'c value'); + $field = new ListboxField('Choices', 'Choices', $choices); + $field->multiple = true; + + $obj = new ListboxFieldTest_DataObject(); + $field->setValue('a'); + $field->saveInto($obj); + $field->setValue(null); + $field->saveInto($obj); + $this->assertNull($obj->Choices); + } + + function testSaveIntoNullValueWithMultipleOn() { + $choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value'); + $field = new ListboxField('Choices', 'Choices', $choices); + $field->multiple = true; + + $obj = new ListboxFieldTest_DataObject(); + $field->setValue('a,c'); + $field->saveInto($obj); + $field->setValue(''); + $field->saveInto($obj); + $this->assertEquals('', $obj->Choices); + } + + function testSaveInto() { + $choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value'); + $field = new ListboxField('Choices', 'Choices', $choices); + $field->multiple = false; + + $obj = new ListboxFieldTest_DataObject(); + $field->setValue('a'); + $field->saveInto($obj); + $this->assertEquals('a', $obj->Choices); + } + + function testSaveIntoMultiple() { + $choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value'); + $field = new ListboxField('Choices', 'Choices', $choices); + $field->multiple = true; + + // As array + $obj1 = new ListboxFieldTest_DataObject(); + $field->setValue(array('a', 'c')); + $field->saveInto($obj1); + $this->assertEquals('a,c', $obj1->Choices); + + // As string + $obj2 = new ListboxFieldTest_DataObject(); + $field->setValue('a,c'); + $field->saveInto($obj2); + $this->assertEquals('a,c', $obj2->Choices); + } + + /** + * @expectedException InvalidArgumentException + */ + function testSetValueFailsOnArrayIfMultipleIsOff() { + $choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value'); + $field = new ListboxField('Choices', 'Choices', $choices); + $field->multiple = false; + + // As array (type error) + $failsOnArray = false; + $obj = new ListboxFieldTest_DataObject(); + $field->setValue(array('a', 'c')); + } + + /** + * @expectedException InvalidArgumentException + */ + function testSetValueFailsOnStringIfChoiceInvalidAndMultipleIsOff() { + $choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value'); + $field = new ListboxField('Choices', 'Choices', $choices); + $field->multiple = false; + + // As string (invalid choice as comma is regarded literal) + $obj = new ListboxFieldTest_DataObject(); + $field->setValue('invalid'); + } + + /** + * @expectedException InvalidArgumentException + */ + function testSetValueFailsOnInvalidArrayKeyIfChoiceInvalidAndMultipleIsOn() { + $choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value'); + $field = new ListboxField('Choices', 'Choices', $choices); + $field->multiple = true; + + $obj = new ListboxFieldTest_DataObject(); + $field->setValue(array('a', 'invalid')); + } + + function testFieldRenderingMultipleOff() { + $choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value'); + $field = new ListboxField('Choices', 'Choices', $choices); + $field->multiple = true; + $field->setValue('a'); + $parser = new CSSContentParser($field->Field()); + $optEls = $parser->getBySelector('option'); + $this->assertEquals(3, count($optEls)); + $this->assertEquals('selected', (string)$optEls[0]['selected']); + $this->assertEquals('', (string)$optEls[1]['selected']); + $this->assertEquals('', (string)$optEls[2]['selected']); + } + + function testFieldRenderingMultipleOn() { + $choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value'); + $field = new ListboxField('Choices', 'Choices', $choices); + $field->multiple = true; + $field->setValue('a,c'); + $parser = new CSSContentParser($field->Field()); + $optEls = $parser->getBySelector('option'); + $this->assertEquals(3, count($optEls)); + $this->assertEquals('selected', (string)$optEls[0]['selected']); + $this->assertEquals('', (string)$optEls[1]['selected']); + $this->assertEquals('selected', (string)$optEls[2]['selected']); + } + + /** + * @expectedException InvalidArgumentException + */ + function testCommasInSourceKeys() { + $choices = array('a' => 'a value', 'b,with,comma' => 'b value,with,comma',); + $field = new ListboxField('Choices', 'Choices', $choices); + } + +} + +class ListboxFieldTest_DataObject extends DataObject implements TestOnly { + static $db = array( + 'Choices' => 'Text' + ); +} \ No newline at end of file