Browse Source

Merge pull request #8735 from cakephp/3.3-context-schema-default

Read schema defaults when generating field using EntityContext
Mark Story 9 years ago
parent
commit
2b3b365b53

+ 14 - 1
src/View/Form/ArrayContext.php

@@ -149,14 +149,27 @@ class ArrayContext implements ContextInterface
      *
      * @param string $field A dot separated path to the field a value
      *   is needed for.
+     * @param array $options Options:
+     *   - `default`: Default value to return if no value found in request
+     *     data or context record.
+     *   - `schemaDefault`: Boolean indicating whether default value from
+     *      context's schema should be used if it's not explicitly provided.
      * @return mixed
      */
-    public function val($field)
+    public function val($field, $options = [])
     {
+        $options += [
+            'default' => null,
+            'schemaDefault' => true
+        ];
+
         $val = $this->_request->data($field);
         if ($val !== null) {
             return $val;
         }
+        if ($options['default'] !== null || !$options['schemaDefault']) {
+            return $options['default'];
+        }
         if (empty($this->_context['defaults']) || !is_array($this->_context['defaults'])) {
             return null;
         }

+ 8 - 0
src/View/Form/ContextInterface.php

@@ -46,6 +46,14 @@ interface ContextInterface
     /**
      * Get the current value for a given field.
      *
+     * Classes implementing this method can optionally have a second argument
+     * `$options`. Valid key for `$options` array are:
+     *
+     *   - `default`: Default value to return if no value found in request
+     *     data or context record.
+     *   - `schemaDefault`: Boolen indicating whether default value from
+     *      context's schema should be used if it's not explicitly provided.
+     *
      * @param string $field A dot separated path to the field a value
      *   is needed for.
      * @return mixed

+ 44 - 3
src/View/Form/EntityContext.php

@@ -207,16 +207,26 @@ class EntityContext implements ContextInterface
      * Traverses the entity data and finds the value for $path.
      *
      * @param string $field The dot separated path to the value.
+     * @param array $options Options:
+     *   - `default`: Default value to return if no value found in request
+     *     data or entity.
+     *   - `schemaDefault`: Boolen indicating whether default value from table
+     *     schema should be used if it's not explicitly provided.
      * @return mixed The value of the field or null on a miss.
      */
-    public function val($field)
+    public function val($field, $options = [])
     {
+        $options += [
+            'default' => null,
+            'schemaDefault' => true
+        ];
+
         $val = $this->_request->data($field);
         if ($val !== null) {
             return $val;
         }
         if (empty($this->_context['entity'])) {
-            return null;
+            return $options['default'];
         }
         $parts = explode('.', $field);
         $entity = $this->entity($parts);
@@ -226,7 +236,18 @@ class EntityContext implements ContextInterface
         }
 
         if ($entity instanceof EntityInterface) {
-            return $entity->get(array_pop($parts));
+            $part = array_pop($parts);
+            $val = $entity->get($part);
+            if ($val !== null) {
+                return $val;
+            }
+            if ($options['default'] !== null
+                || !$options['schemaDefault']
+                || !$entity->isNew()
+            ) {
+                return $options['default'];
+            }
+            return $this->_schemaDefault($part, $entity);
         }
         if (is_array($entity)) {
             $key = array_pop($parts);
@@ -236,6 +257,26 @@ class EntityContext implements ContextInterface
     }
 
     /**
+     * Get default value from table schema for given entity field.
+     *
+     * @param string $field Field name.
+     * @param \Cake\Datasource\EntityInterface $entity The entity.
+     * @return mixed
+     */
+    protected function _schemaDefault($field, $entity)
+    {
+        $table = $this->_getTable($entity);
+        if ($table === false) {
+            return null;
+        }
+        $defaults = $table->schema()->defaultValues();
+        if (!array_key_exists($field, $defaults)) {
+            return null;
+        }
+        return $defaults[$field];
+    }
+
+    /**
      * Helper method used to extract all the primary key values out of an array, The
      * primary key column is guessed out of the provided $path array
      *

+ 11 - 2
src/View/Form/FormContext.php

@@ -75,9 +75,18 @@ class FormContext implements ContextInterface
     /**
      * {@inheritDoc}
      */
-    public function val($field)
+    public function val($field, $options = [])
     {
-        return $this->_request->data($field);
+        $options += [
+            'default' => null,
+            'schemaDefault' => true
+        ];
+
+        $val = $this->_request->data($field);
+        if ($val !== null) {
+            return $val;
+        }
+        return $options['default'];
     }
 
     /**

+ 5 - 1
src/View/Helper/FormHelper.php

@@ -2412,7 +2412,11 @@ class FormHelper extends Helper
             unset($options['value']);
         }
         if (!isset($options['val'])) {
-            $options['val'] = $context->val($field);
+            $valOptions = [
+                'default' => isset($options['default']) ? $options['default'] : null,
+                'schemaDefault' => isset($options['schemaDefault']) ? $options['schemaDefault'] : true,
+            ];
+            $options['val'] = $context->val($field, $valOptions);
         }
         if (!isset($options['val']) && isset($options['default'])) {
             $options['val'] = $options['default'];

+ 18 - 0
tests/TestCase/View/Form/ArrayContextTest.php

@@ -167,6 +167,24 @@ class ArrayContextTest extends TestCase
     }
 
     /**
+     * Test getting default value
+     *
+     * @return void
+     */
+    public function testValDefault()
+    {
+        $context = new ArrayContext($this->request, [
+            'defaults' => [
+                'title' => 'Default value',
+            ]
+        ]);
+
+        $this->assertEquals('Default value', $context->val('title'));
+        $result = $context->val('title', ['default' => 'explicit default']);
+        $this->assertEquals('explicit default', $result);
+    }
+
+    /**
      * Test isRequired
      *
      * @return void

+ 20 - 0
tests/TestCase/View/Form/EntityContextTest.php

@@ -655,6 +655,26 @@ class EntityContextTest extends TestCase
     }
 
     /**
+     * Test getting default value from table schema.
+     *
+     * @return void
+     */
+    public function testValSchemaDefault()
+    {
+        $table = TableRegistry::get('Articles');
+        $column = $table->schema()->column('title');
+        $table->schema()->addColumn('title', ['default' => 'default title'] + $column);
+        $row = $table->newEntity();
+
+        $context = new EntityContext($this->request, [
+            'entity' => $row,
+            'table' => 'Articles',
+        ]);
+        $result = $context->val('title');
+        $this->assertEquals('default title', $result);
+    }
+
+    /**
      * Test validator for boolean fields.
      *
      * @return void

+ 13 - 0
tests/TestCase/View/Form/FormContextTest.php

@@ -99,6 +99,19 @@ class FormContextTest extends TestCase
     }
 
     /**
+     * Test getting default value
+     *
+     * @return void
+     */
+    public function testValDefault()
+    {
+        $context = new FormContext($this->request, ['entity' => new Form()]);
+
+        $result = $context->val('title', ['default' => 'default default']);
+        $this->assertEquals('default default', $result);
+    }
+
+    /**
      * Test isRequired
      *
      * @return void

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

@@ -4107,6 +4107,37 @@ class FormHelperTest extends TestCase
         $result = $this->Form->text('Model.field', ['default' => 'default value']);
         $expected = ['input' => ['type' => 'text', 'name' => 'Model[field]', 'value' => 'default value']];
         $this->assertHtml($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);
+
+        // Get default value from schema
+        $result = $this->Form->text('title');
+        $expected = ['input' => ['type' => 'text', 'name' => 'title', 'value' => 'default title']];
+        $this->assertHtml($expected, $result);
+
+        // Don't get value from schema
+        $result = $this->Form->text('title', ['schemaDefault' => false]);
+        $expected = ['input' => ['type' => 'text', 'name' => 'title']];
+        $this->assertHtml($expected, $result);
+
+        // Custom default value overrides default value from schema
+        $result = $this->Form->text('title', ['default' => 'override default']);
+        $expected = ['input' => ['type' => 'text', 'name' => 'title', 'value' => 'override default']];
+        $this->assertHtml($expected, $result);
+
+        // Default value from schema is used only for new entities.
+        $entity->isNew(false);
+        $result = $this->Form->text('title');
+        $expected = ['input' => ['type' => 'text', 'name' => 'title']];
+        $this->assertHtml($expected, $result);
     }
 
     /**
@@ -4317,6 +4348,37 @@ class FormHelperTest extends TestCase
     }
 
     /**
+     * Test default value setting on radio() method
+     *
+     * @return void
+     */
+    public function testRadioDefaultValue()
+    {
+        $Articles = TableRegistry::get('Articles');
+        $title = $Articles->schema()->column('title');
+        $Articles->schema()->addColumn(
+            'title',
+            ['default' => '1'] + $title
+        );
+
+        $this->Form->create($Articles->newEntity());
+
+        $result = $this->Form->radio('title', ['option A', 'option B']);
+        $expected = [
+            ['input' => ['type' => 'hidden', 'name' => 'title', 'value' => '']],
+            ['label' => ['for' => 'title-0']],
+            ['input' => ['type' => 'radio', 'name' => 'title', 'value' => '0', 'id' => 'title-0']],
+            'option A',
+            '/label',
+            ['label' => ['for' => 'title-1']],
+            ['input' => ['type' => 'radio', 'name' => 'title', 'value' => '1', 'id' => 'title-1', 'checked' => 'checked']],
+            'option B',
+            '/label',
+        ];
+        $this->assertHtml($expected, $result);
+    }
+
+    /**
      * Test that input works with radio types
      *
      * @return void
@@ -5385,6 +5447,17 @@ class FormHelperTest extends TestCase
         $result = $this->Form->checkbox('Model.field', ['default' => false, 'hiddenField' => false]);
         $expected = ['input' => ['type' => 'checkbox', 'name' => 'Model[field]', 'value' => '1']];
         $this->assertHtml($expected, $result);
+
+        $Articles = TableRegistry::get('Articles');
+        $Articles->schema()->addColumn(
+            'published',
+            ['type' => 'boolean', 'null' => false, 'default' => true]
+        );
+
+        $this->Form->create($Articles->newEntity());
+        $result = $this->Form->checkbox('published', ['hiddenField' => false]);
+        $expected = ['input' => ['type' => 'checkbox', 'name' => 'published', 'value' => '1', 'checked' => 'checked']];
+        $this->assertHtml($expected, $result);
     }
 
     /**