Browse Source

Merge pull request #9127 from ionas/formhelper-values

3.x FormHelper:: Allow to specify the source of injected form field values
Mark Story 9 years ago
parent
commit
6a509da5f5
2 changed files with 483 additions and 2 deletions
  1. 67 2
      src/View/Helper/FormHelper.php
  2. 416 0
      tests/TestCase/View/Helper/FormHelperTest.php

+ 67 - 2
src/View/Helper/FormHelper.php

@@ -206,6 +206,13 @@ class FormHelper extends Helper
     protected $_lastAction = '';
 
     /**
+     * The values sources to be used to retrieve prefilled input values from.
+     *
+     * @var array
+     */
+    protected $_valuesSources = ['context'];
+
+    /**
      * Construct the widgets and binds the default context providers
      *
      * @param \Cake\View\View $View The View this helper is being attached to.
@@ -359,12 +366,16 @@ class FormHelper extends Helper
             'encoding' => strtolower(Configure::read('App.encoding')),
             'templates' => null,
             'idPrefix' => null,
+            'valuesSources' => $this->getValuesSources(),
         ];
 
         if (isset($options['action'])) {
             trigger_error('Using key `action` is deprecated, use `url` directly instead.', E_USER_DEPRECATED);
         }
 
+        $this->setValuesSources($options['valuesSources']);
+        unset($options['valuesSources']);
+
         if ($options['idPrefix'] !== null) {
             $this->_idPrefix = $options['idPrefix'];
         }
@@ -472,7 +483,7 @@ class FormHelper extends Helper
 
         $pk = $context->primaryKey();
         if (count($pk)) {
-            $id = $context->val($pk[0]);
+            $id = $this->getSourceValue($pk[0]);
         }
         if (empty($action[0]) && isset($id)) {
             $action[0] = $id;
@@ -523,6 +534,8 @@ class FormHelper extends Helper
      * Closes an HTML form, cleans up values set by FormHelper::create(), and writes hidden
      * input fields where appropriate.
      *
+     * Resets some parts of the state, shared among multiple FormHelper::create() calls, to defaults.
+     *
      * @param array $secureAttributes Secure attributes which will be passed as HTML attributes
      *   into the hidden input elements generated for the Security Component.
      * @return string A closing FORM tag.
@@ -544,6 +557,7 @@ class FormHelper extends Helper
         $this->templater()->pop();
         $this->requestType = null;
         $this->_context = null;
+        $this->_valuesSources = ['context'];
         $this->_idPrefix = $this->config('idPrefix');
 
         return $out;
@@ -2473,7 +2487,7 @@ class FormHelper extends Helper
                 'default' => isset($options['default']) ? $options['default'] : null,
                 'schemaDefault' => isset($options['schemaDefault']) ? $options['schemaDefault'] : true,
             ];
-            $options['val'] = $context->val($field, $valOptions);
+            $options['val'] = $this->getSourceValue($field, $valOptions);
         }
         if (!isset($options['val']) && isset($options['default'])) {
             $options['val'] = $options['default'];
@@ -2679,4 +2693,55 @@ class FormHelper extends Helper
     {
         return [];
     }
+
+    /**
+     * Gets the value sources.
+     *
+     * Returns a list, but at least one item, of valid sources, such as: `'context'`, `'data'` and `'query'`.
+     *
+     * @return array List of value sources.
+     */
+    public function getValuesSources()
+    {
+        return $this->_valuesSources;
+    }
+
+    /**
+     * Sets the value sources.
+     *
+     * Valid values are `'context'`, `'data'` and `'query'`.
+     * You need to supply one valid context or multiple, as a list of strings. Order sets priority.
+     *
+     * @param string|array $sources A string or a list of strings identifying a source.
+     * @return $this
+     */
+    public function setValuesSources($sources)
+    {
+        $this->_valuesSources = array_values(array_intersect((array)$sources, ['context', 'data', 'query']));
+
+        return $this;
+    }
+
+    /**
+     * Gets a single field value from the sources available.
+     *
+     * @param string $fieldname The fieldname to fetch the value for.
+     * @param array|null $options The options containing default values.
+     * @return string|null Field value derived from sources or defaults.
+     */
+    public function getSourceValue($fieldname, $options = [])
+    {
+        foreach ($this->getValuesSources() as $valuesSource) {
+            if ($valuesSource === 'context') {
+                $val = $this->_getContext()->val($fieldname, $options);
+                if ($val !== null) {
+                    return $val;
+                }
+            } elseif ($this->request->{$valuesSource}($fieldname) !== null) {
+                return $this->request->{$valuesSource}($fieldname);
+            }
+        }
+
+        return null;
+    }
 }

+ 416 - 0
tests/TestCase/View/Helper/FormHelperTest.php

@@ -8165,4 +8165,420 @@ class FormHelperTest extends TestCase
         ];
         $this->assertHtml($expected, $result);
     }
+
+    /**
+     * Test the basic setters and getters for value sources
+     *
+     * @return void
+     */
+    public function testFormValuesSourcesSettersGetters()
+    {
+        $expected = ['context'];
+        $result = $this->Form->getValuesSources();
+        $this->assertEquals($expected, $result);
+
+        $expected = null;
+        $result = $this->Form->getSourceValue('id');
+        $this->assertEquals($expected, $result);
+
+        $expected = ['query', 'data', 'context'];
+        $this->Form->setValuesSources(['query', 'data', 'invalid', 'context', 'foo']);
+        $result = $this->Form->getValuesSources();
+        $this->assertEquals($expected, $result);
+
+
+        $this->Form->request->data['id'] = '1';
+        $this->Form->request->query['id'] = '2';
+
+        $this->Form->setValuesSources(['context']);
+        $expected = '1';
+        $result = $this->Form->getSourceValue('id');
+        $this->assertEquals($expected, $result);
+
+        $this->Form->setValuesSources('query');
+        $expected = ['query'];
+        $result = $this->Form->getValuesSources();
+        $this->assertEquals($expected, $result);
+
+        $expected = '2';
+        $result = $this->Form->getSourceValue('id');
+        $this->assertEquals($expected, $result);
+
+        $this->Form->setValuesSources(['data']);
+        $expected = '1';
+        $result = $this->Form->getSourceValue('id');
+        $this->assertEquals($expected, $result);
+
+        $this->Form->setValuesSources(['query', 'data']);
+        $expected = '2';
+        $result = $this->Form->getSourceValue('id');
+        $this->assertEquals($expected, $result);
+    }
+
+    /**
+     * Tests the different input rendering values based on sources values switching
+     *
+     * @return void
+     */
+    public function testFormValuesSourcesSingleSwitchRendering()
+    {
+        $this->loadFixtures('Articles');
+        $articles = TableRegistry::get('Articles');
+        $article = new Article();
+        $articles->patchEntity($article, ['id' => '3']);
+
+
+        $this->Form->create($article);
+        $this->Form->setValuesSources(['context']);
+        $result = $this->Form->input('id');
+        $expected = [
+            ['input' => ['type' => 'hidden', 'name' => 'id', 'id' => 'id', 'value' => '3']],
+        ];
+        $this->assertHtml($expected, $result);
+
+
+        $this->Form->request->query['id'] = '5';
+        $this->Form->setValuesSources(['query']);
+        $this->Form->create($article);
+        $result = $this->Form->input('id');
+        $expected = [
+            ['input' => ['type' => 'hidden', 'name' => 'id', 'id' => 'id', 'value' => '5']],
+        ];
+        $this->assertHtml($expected, $result);
+
+        $this->Form->create($article);
+        $this->Form->setValuesSources(['query']);
+        $result = $this->Form->input('id');
+        $expected = [
+            ['input' => ['type' => 'hidden', 'name' => 'id', 'id' => 'id', 'value' => '5']],
+        ];
+        $this->assertHtml($expected, $result);
+
+
+        $this->Form->request->query['id'] = '5a';
+        $this->Form->request->data['id'] = '5b';
+
+        $this->Form->setValuesSources(['context']);
+        $this->Form->create($article);
+        $result = $this->Form->input('id');
+        $expected = [
+            ['input' => ['type' => 'hidden', 'name' => 'id', 'id' => 'id', 'value' => '5b']],
+        ];
+        $this->assertHtml($expected, $result);
+
+        $this->Form->setValuesSources(['data']);
+        $this->Form->create($article);
+        $result = $this->Form->input('id');
+        $expected = [
+            ['input' => ['type' => 'hidden', 'name' => 'id', 'id' => 'id', 'value' => '5b']],
+        ];
+        $this->assertHtml($expected, $result);
+
+        $this->Form->request->data['id'] = '6';
+        $this->Form->request->query['id'] = '7';
+        $this->Form->create($article);
+        $this->Form->setValuesSources(['data']);
+        $result = $this->Form->input('id');
+        $expected = [
+            ['input' => ['type' => 'hidden', 'name' => 'id', 'id' => 'id', 'value' => '6']],
+        ];
+        $this->assertHtml($expected, $result);
+
+        $this->Form->request->data['id'] = '8';
+        $this->Form->request->query['id'] = '9';
+        $this->Form->create($article);
+        $this->Form->setValuesSources(['query']);
+        $result = $this->Form->input('id');
+        $expected = [
+            ['input' => ['type' => 'hidden', 'name' => 'id', 'id' => 'id', 'value' => '9']],
+        ];
+        $this->assertHtml($expected, $result);
+    }
+
+    /**
+     * Tests the different input rendering values based on sources values switching while supplying
+     * an entity (base context) and multiple sources (such as data, query)
+     *
+     * @return void
+     */
+    public function testFormValuesSourcesListSwitchRendering()
+    {
+        $this->loadFixtures('Articles');
+        $articles = TableRegistry::get('Articles');
+        $article = new Article();
+        $articles->patchEntity($article, ['id' => '3']);
+
+        $this->Form->request->query['id'] = '9';
+
+        $this->Form->create($article);
+        $this->Form->setValuesSources(['context']);
+        $result = $this->Form->input('id');
+        $expected = [
+            ['input' => ['type' => 'hidden', 'name' => 'id', 'id' => 'id', 'value' => '3']],
+        ];
+        $this->assertHtml($expected, $result);
+
+        $this->Form->setValuesSources(['query']);
+        $result = $this->Form->input('id');
+        $expected = [
+            ['input' => ['type' => 'hidden', 'name' => 'id', 'id' => 'id', 'value' => '9']],
+        ];
+        $this->assertHtml($expected, $result);
+
+        $this->Form->setValuesSources(['context', 'query']);
+        $result = $this->Form->input('id');
+        $expected = [
+            ['input' => ['type' => 'hidden', 'name' => 'id', 'id' => 'id', 'value' => '3']],
+        ];
+        $this->assertHtml($expected, $result);
+
+        $this->Form->setValuesSources(['query', 'context']);
+        $result = $this->Form->input('id');
+        $expected = [
+            ['input' => ['type' => 'hidden', 'name' => 'id', 'id' => 'id', 'value' => '9']],
+        ];
+        $this->assertHtml($expected, $result);
+
+        $this->Form->setValuesSources(['data', 'query', 'context']);
+        $result = $this->Form->input('id');
+        $expected = [
+            ['input' => ['type' => 'hidden', 'name' => 'id', 'id' => 'id', 'value' => '9']],
+        ];
+        $this->assertHtml($expected, $result);
+
+
+        $this->Form->request->data['id'] = '8';
+        $this->Form->request->query['id'] = '9';
+        $this->Form->setValuesSources(['data', 'query', 'context']);
+        $result = $this->Form->input('id');
+        $expected = [
+            ['input' => ['type' => 'hidden', 'name' => 'id', 'id' => 'id', 'value' => '8']],
+        ];
+        $this->assertHtml($expected, $result);
+    }
+
+    /**
+     * Test the different form input renderings based on values sources switchings through form options
+     *
+     * @return void
+     */
+    public function testFormValuesSourcesSwitchViaOptionsRendering()
+    {
+        $this->loadFixtures('Articles');
+        $articles = TableRegistry::get('Articles');
+        $article = new Article();
+        $articles->patchEntity($article, ['id' => '3']);
+
+
+        $this->Form->request->data['id'] = '4';
+        $this->Form->request->query['id'] = '5';
+
+        $this->Form->create($article, ['valuesSources' => 'query']);
+        $result = $this->Form->input('id');
+        $expected = [
+            ['input' => ['type' => 'hidden', 'name' => 'id', 'id' => 'id', 'value' => '5']],
+        ];
+        $this->assertHtml($expected, $result);
+        $result = $this->Form->getSourceValue('id');
+        $this->assertEquals('5', $result);
+
+
+        $this->Form->request->data['id'] = '6';
+        $this->Form->request->query['id'] = '7';
+
+        $this->Form->setValuesSources(['context']);
+        $this->Form->create($article, ['valuesSources' => 'query']);
+        $result = $this->Form->input('id');
+        $expected = [
+            ['input' => ['type' => 'hidden', 'name' => 'id', 'id' => 'id', 'value' => '7']],
+        ];
+        $this->assertHtml($expected, $result);
+        $result = $this->Form->getSourceValue('id');
+        $this->assertEquals('7', $result);
+
+        $this->Form->setValuesSources(['query']);
+        $this->Form->create($article, ['valuesSources' => 'data']);
+        $result = $this->Form->input('id');
+        $expected = [
+            ['input' => ['type' => 'hidden', 'name' => 'id', 'id' => 'id', 'value' => '6']],
+        ];
+        $this->assertHtml($expected, $result);
+        $result = $this->Form->getSourceValue('id');
+        $this->assertEquals('6', $result);
+
+
+        $this->Form->request->data['id'] = '8';
+        $this->Form->request->query['id'] = '9';
+
+        $this->Form->setValuesSources(['query']);
+        $this->Form->create($article, ['valuesSources' => ['context', 'data']]);
+        $result = $this->Form->input('id');
+        $expected = [
+            ['input' => ['type' => 'hidden', 'name' => 'id', 'id' => 'id', 'value' => '8']],
+        ];
+        $this->assertHtml($expected, $result);
+        $result = $this->Form->getSourceValue('id');
+        $this->assertEquals('8', $result);
+    }
+
+    /**
+     * Test the different form input renderings based on values sources switchings through form options
+     *
+     * @return void
+     */
+    public function testFormValuesSourcesSwitchViaOptionsAndSetterRendering()
+    {
+        $this->loadFixtures('Articles');
+        $articles = TableRegistry::get('Articles');
+        $article = new Article();
+        $articles->patchEntity($article, ['id' => '3']);
+
+
+        $this->Form->request->data['id'] = '10';
+        $this->Form->request->query['id'] = '11';
+
+        $this->Form->setValuesSources(['context'])->create($article, ['valuesSources' => ['query', 'data']]);
+        $result = $this->Form->input('id');
+        $expected = [
+            ['input' => ['type' => 'hidden', 'name' => 'id', 'id' => 'id', 'value' => '11']],
+        ];
+        $this->assertHtml($expected, $result);
+        $result = $this->Form->getSourceValue('id');
+        $this->assertEquals('11', $result);
+
+        unset($this->Form->request->query['id']);
+        $this->Form->setValuesSources(['context'])->create($article, ['valuesSources' => ['query', 'data']]);
+        $result = $this->Form->input('id');
+        $expected = [
+            ['input' => ['type' => 'hidden', 'name' => 'id', 'id' => 'id', 'value' => '10']],
+        ];
+        $this->assertHtml($expected, $result);
+        $result = $this->Form->getSourceValue('id');
+        $this->assertEquals('10', $result);
+    }
+
+    /**
+     * Test the different form values sources resetting through From::end();
+     *
+     * @return void
+     */
+    public function testFormValuesSourcesResetViaEnd()
+    {
+        $expected = ['context'];
+        $result = $this->Form->getValuesSources();
+        $this->assertEquals($expected, $result);
+        
+        $expected = ['query', 'context', 'data'];
+        $this->Form->setValuesSources(['query', 'context', 'data']);
+
+        $result = $this->Form->getValuesSources();
+        $this->assertEquals($expected, $result);
+        
+        $this->Form->create();
+        $result = $this->Form->getValuesSources();
+        $this->assertEquals($expected, $result);
+
+        $this->Form->end();
+        $expected = ['context'];
+        $result = $this->Form->getValuesSources();
+        $this->assertEquals($expected, $result);
+    }
+
+    /**
+     * Test sources values schema defaults handling
+     *
+     * @return void
+     */
+    public function testFormValuesSourcesSchemaDefaults()
+    {
+        $Articles = TableRegistry::get('Articles');
+        $entity = $Articles->newEntity();
+        $this->Form->create($entity);
+        $result = $this->Form->getSourceValue('title');
+        $expected = '';
+        $this->assertEquals($expected, $result);
+
+        $Articles = TableRegistry::get('Articles');
+        $title = $Articles->schema()->column('title');
+        $Articles->schema()->addColumn(
+            'title',
+            ['default' => 'default title'] + $title
+        );
+        $entity = $Articles->newEntity();
+        $this->Form->create($entity);
+        $result = $this->Form->getSourceValue('title');
+        $expected = 'default title';
+        $this->assertEquals($expected, $result);
+
+        $Articles = TableRegistry::get('Articles');
+        $title = $Articles->schema()->column('title');
+        $Articles->schema()->addColumn(
+            'title',
+            ['default' => 'default title'] + $title
+        );
+        $entity = $Articles->newEntity();
+        $this->Form->create($entity);
+        $this->Form->setValuesSources(['query']);
+        $result = $this->Form->getSourceValue('title');
+        $expected = '';
+        $this->assertEquals($expected, $result);
+        $this->Form->setValuesSources(['context']);
+        $result = $this->Form->getSourceValue('title');
+        $expected = 'default title';
+        $this->assertEquals($expected, $result);
+    }
+
+    /**
+     * Test sources values defaults handling
+     *
+     * @return void
+     */
+    public function testFormValuesSourcesDefaults()
+    {
+        $this->Form->request->query['password'] = 'open Sesame';
+        $this->Form->create();
+
+        $result = $this->Form->password('password');
+        $expected = ['input' => ['type' => 'password', 'name' => 'password']];
+        $this->assertHtml($expected, $result);
+
+        $result = $this->Form->password('password', ['default' => 'helloworld']);
+        $expected = ['input' => ['type' => 'password', 'name' => 'password', 'value' => 'helloworld']];
+        $this->assertHtml($expected, $result);
+
+        $this->Form->setValuesSources('query');
+        $result = $this->Form->password('password', ['default' => 'helloworld']);
+        $expected = ['input' => ['type' => 'password', 'name' => 'password', 'value' => 'open Sesame']];
+        $this->assertHtml($expected, $result);
+
+        $this->Form->setValuesSources('data');
+        $result = $this->Form->password('password', ['default' => 'helloworld']);
+        $expected = ['input' => ['type' => 'password', 'name' => 'password', 'value' => 'helloworld']];
+        $this->assertHtml($expected, $result);
+    }
+
+
+    /**
+     * Test sources values schema defaults handling
+     *
+     * @return void
+     */
+    public function testSourcesValueDoesntExistPassThrough()
+    {
+        $this->Form->request->query['category'] = 'sesame-cookies';
+
+        $Articles = TableRegistry::get('Articles');
+        $entity = $Articles->newEntity();
+        $this->Form->create($entity);
+
+        $this->Form->setValuesSources(['query', 'context']);
+        $result = $this->Form->getSourceValue('category');
+        $expected = 'sesame-cookies';
+        $this->assertEquals($expected, $result);
+
+        $this->Form->setValuesSources(['context', 'query']);
+        $result = $this->Form->getSourceValue('category');
+        $expected = 'sesame-cookies';
+        $this->assertEquals($expected, $result);
+    }
 }