Просмотр исходного кода

[Form] ChoiceField now accepts closures in the 'choices' option

Bernhard Schussek 14 лет назад
Родитель
Сommit
ce61baf717

+ 51 - 9
src/Symfony/Component/Form/ChoiceField.php

@@ -40,6 +40,13 @@ class ChoiceField extends HybridField
      */
     protected $preferredChoices = array();
 
+    /**
+     * Stores the choices
+     * You should only access this property through getInitializedChoices()
+     * @var array
+     */
+    protected $initializedChoices = array();
+
     protected function configure()
     {
         $this->addRequiredOption('choices');
@@ -47,8 +54,12 @@ class ChoiceField extends HybridField
         $this->addOption('multiple', false);
         $this->addOption('expanded', false);
 
-        if (!is_array($this->getOption('choices'))) {
-            throw new InvalidOptionsException('The choices option must be an array', array('choices'));
+        parent::configure();
+
+        $choices = $this->getOption('choices');
+
+        if (!is_array($choices) && !$choices instanceof \Closure) {
+            throw new InvalidOptionsException('The choices option must be an array or a closure', array('choices'));
         }
 
         if (!is_array($this->getOption('preferred_choices'))) {
@@ -62,7 +73,7 @@ class ChoiceField extends HybridField
         if ($this->isExpanded()) {
             $this->setFieldMode(self::GROUP);
 
-            $choices = $this->getOption('choices');
+            $choices = $this->getInitializedChoices();
 
             foreach ($this->preferredChoices as $choice => $_) {
                 $this->add($this->newChoiceField($choice, $choices[$choice]));
@@ -76,8 +87,6 @@ class ChoiceField extends HybridField
         } else {
             $this->setFieldMode(self::FIELD);
         }
-
-        parent::configure();
     }
 
     public function getName()
@@ -95,19 +104,52 @@ class ChoiceField extends HybridField
         return $name;
     }
 
+    /**
+     * Initializes the choices
+     *
+     * If the choices were given as a closure, the closure is executed now.
+     *
+     * @return array
+     */
+    protected function initializeChoices()
+    {
+        $this->initializedChoices = $this->getOption('choices');
+
+        if ($this->initializedChoices instanceof \Closure) {
+            $this->initializedChoices = $this->initializedChoices->__invoke();
+        }
+    }
+
+    /**
+     * Returns the choices
+     *
+     * If the choices were given as a closure, the closure is executed on
+     * the first call of this method.
+     *
+     * @return array
+     */
+    protected function getInitializedChoices()
+    {
+        if (!$this->initializedChoices) {
+            $this->initializeChoices();
+        }
+
+        return $this->initializedChoices;
+    }
+
     public function getPreferredChoices()
     {
-        return array_intersect_key($this->getOption('choices'), $this->preferredChoices);
+        return array_intersect_key($this->getInitializedChoices(), $this->preferredChoices);
     }
 
     public function getOtherChoices()
     {
-        return array_diff_key($this->getOption('choices'), $this->preferredChoices);
+        return array_diff_key($this->getInitializedChoices(), $this->preferredChoices);
     }
 
     public function getLabel($choice)
     {
-        $choices = $this->getOption('choices');
+        $choices = $this->getInitializedChoices();
 
         return isset($choices[$choice]) ? $choices[$choice] : null;
     }
@@ -183,7 +225,7 @@ class ChoiceField extends HybridField
     {
         if ($this->isExpanded()) {
             $value = parent::transform($value);
-            $choices = $this->getOption('choices');
+            $choices = $this->getInitializedChoices();
 
             foreach ($choices as $choice => $_) {
                 $choices[$choice] = $this->isMultipleChoice()

+ 50 - 12
tests/Symfony/Tests/Component/Form/ChoiceFieldTest.php

@@ -67,12 +67,35 @@ class ChoiceFieldTest extends \PHPUnit_Framework_TestCase
         ));
     }
 
-    public function testBindSingleNonExpanded()
+    public function getChoicesVariants()
+    {
+        $choices = $this->choices;
+
+        return array(
+            array($choices),
+            array(function () use ($choices) { return $choices; }),
+        );
+    }
+
+    public function getNumericChoicesVariants()
+    {
+        $choices = $this->numericChoices;
+
+        return array(
+            array($choices),
+            array(function () use ($choices) { return $choices; }),
+        );
+    }
+
+    /**
+     * @dataProvider getChoicesVariants
+     */
+    public function testBindSingleNonExpanded($choices)
     {
         $field = new ChoiceField('name', array(
             'multiple' => false,
             'expanded' => false,
-            'choices' => $this->choices,
+            'choices' => $choices,
         ));
 
         $field->bind('b');
@@ -81,12 +104,15 @@ class ChoiceFieldTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals('b', $field->getDisplayedData());
     }
 
-    public function testBindMultipleNonExpanded()
+    /**
+     * @dataProvider getChoicesVariants
+     */
+    public function testBindMultipleNonExpanded($choices)
     {
         $field = new ChoiceField('name', array(
             'multiple' => true,
             'expanded' => false,
-            'choices' => $this->choices,
+            'choices' => $choices,
         ));
 
         $field->bind(array('a', 'b'));
@@ -95,12 +121,15 @@ class ChoiceFieldTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals(array('a', 'b'), $field->getDisplayedData());
     }
 
-    public function testBindSingleExpanded()
+    /**
+     * @dataProvider getChoicesVariants
+     */
+    public function testBindSingleExpanded($choices)
     {
         $field = new ChoiceField('name', array(
             'multiple' => false,
             'expanded' => true,
-            'choices' => $this->choices,
+            'choices' => $choices,
         ));
 
         $field->bind('b');
@@ -119,12 +148,15 @@ class ChoiceFieldTest extends \PHPUnit_Framework_TestCase
         $this->assertSame(array('a' => '', 'b' => '1', 'c' => '', 'd' => '', 'e' => ''), $field->getDisplayedData());
     }
 
-    public function testBindSingleExpandedNumericChoices()
+    /**
+     * @dataProvider getNumericChoicesVariants
+     */
+    public function testBindSingleExpandedNumericChoices($choices)
     {
         $field = new ChoiceField('name', array(
             'multiple' => false,
             'expanded' => true,
-            'choices' => $this->numericChoices,
+            'choices' => $choices,
         ));
 
         $field->bind('1');
@@ -143,12 +175,15 @@ class ChoiceFieldTest extends \PHPUnit_Framework_TestCase
         $this->assertSame(array(0 => '', 1 => '1', 2 => '', 3 => '', 4 => ''), $field->getDisplayedData());
     }
 
-    public function testBindMultipleExpanded()
+    /**
+     * @dataProvider getChoicesVariants
+     */
+    public function testBindMultipleExpanded($choices)
     {
         $field = new ChoiceField('name', array(
             'multiple' => true,
             'expanded' => true,
-            'choices' => $this->choices,
+            'choices' => $choices,
         ));
 
         $field->bind(array('a' => 'a', 'b' => 'b'));
@@ -167,12 +202,15 @@ class ChoiceFieldTest extends \PHPUnit_Framework_TestCase
         $this->assertSame(array('a' => '1', 'b' => '1', 'c' => '', 'd' => '', 'e' => ''), $field->getDisplayedData());
     }
 
-    public function testBindMultipleExpandedNumericChoices()
+    /**
+     * @dataProvider getNumericChoicesVariants
+     */
+    public function testBindMultipleExpandedNumericChoices($choices)
     {
         $field = new ChoiceField('name', array(
             'multiple' => true,
             'expanded' => true,
-            'choices' => $this->numericChoices,
+            'choices' => $choices,
         ));
 
         $field->bind(array(1 => 1, 2 => 2));