Browse Source

Form Helper maxlength attribute uses validation

Gergely Tamás 7 years ago
parent
commit
3d3d77f46e

+ 27 - 0
src/View/Form/ArrayContext.php

@@ -230,6 +230,33 @@ class ArrayContext implements ContextInterface
     }
 
     /**
+     * Get field length from validation
+     *
+     * In this context class, this is simply defined by the 'length' array.
+     *
+     * @param string $field A dot separated path to check required-ness for.
+     * @return int|null
+     */
+    public function getMaxLength($field)
+    {
+        if (!is_array($this->_context['schema'])) {
+            return null;
+        }
+
+        $fieldSchema = Hash::get($this->_context['schema'], $field);
+
+        if (!$fieldSchema) {
+            return null;
+        }
+
+        if (is_array($fieldSchema)) {
+            return Hash::get($fieldSchema, 'length');
+        }
+
+        return null;
+    }
+
+    /**
      * {@inheritDoc}
      */
     public function fieldNames()

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

@@ -18,6 +18,7 @@ namespace Cake\View\Form;
  * Interface for FormHelper context implementations.
  *
  * @method string|null getRequiredMessage($field) Gets the default "required" error message for a field
+ * @method int|null getMaxLength($field) Get maximum length of a field from model validation
  */
 interface ContextInterface
 {

+ 23 - 0
src/View/Form/EntityContext.php

@@ -455,6 +455,29 @@ class EntityContext implements ContextInterface
     }
 
     /**
+     * Get field length from validation
+     *
+     * @param string $field The dot separated path to the field you want to check.
+     * @return int|null
+     */
+    public function getMaxLength($field)
+    {
+        $parts = explode('.', $field);
+        $validator = $this->_getValidator($parts);
+        $fieldName = array_pop($parts);
+        if (!$validator->hasField($fieldName)) {
+            return null;
+        }
+        foreach ($validator->field($fieldName)->rules() as $rule) {
+            if ($rule->get('rule') === 'maxLength') {
+                return $rule->get('pass')[0];
+            }
+        }
+
+        return null;
+    }
+
+    /**
      * Get the field names from the top level entity.
      *
      * If the context is for an array of entities, the 0th index will be used.

+ 18 - 1
src/View/Form/FormContext.php

@@ -110,7 +110,6 @@ class FormContext implements ContextInterface
      * Get default value from form schema for given field.
      *
      * @param string $field Field name.
-
      * @return mixed
      */
     protected function _schemaDefault($field)
@@ -170,6 +169,24 @@ class FormContext implements ContextInterface
     /**
      * {@inheritDoc}
      */
+    public function getMaxLength($field)
+    {
+        $validator = $this->_form->getValidator();
+        if (!$validator->hasField($field)) {
+            return null;
+        }
+        foreach ($validator->field($field)->rules() as $rule) {
+            if ($rule->get('rule') === 'maxLength') {
+                return $rule->get('pass')[0];
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     public function fieldNames()
     {
         return $this->_form->schema()->fields();

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

@@ -94,6 +94,14 @@ class NullContext implements ContextInterface
     /**
      * {@inheritDoc}
      */
+    public function getMaxLength($field)
+    {
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     public function fieldNames()
     {
         return [];

+ 17 - 6
src/View/Helper/FormHelper.php

@@ -1502,13 +1502,24 @@ class FormHelper extends Helper
             unset($options['step']);
         }
 
-        $autoLength = !array_key_exists('maxlength', $options)
-            && !empty($fieldDef['length'])
-            && $options['type'] !== 'select';
+        $typesWithMaxLength = ['text', 'textarea', 'email', 'tel', 'url', 'search'];
+        if (in_array($options['type'], $typesWithMaxLength)) {
 
-        $allowedTypes = ['text', 'textarea', 'email', 'tel', 'url', 'search'];
-        if ($autoLength && in_array($options['type'], $allowedTypes)) {
-            $options['maxlength'] = min($fieldDef['length'], 100000);
+            if (method_exists($context, 'getMaxLength')) {
+                $maxLength = $context->getMaxLength($fieldName);
+            }
+
+            if (!empty($fieldDef['length']) && isset($maxLength)) {
+                $fieldDef['length'] = min($fieldDef['length'], $maxLength);
+            } elseif (isset($maxLength)) {
+                $fieldDef['length'] = $maxLength;
+            }
+            if (!empty($options['maxlength'])) {
+                $fieldDef['length'] = min($fieldDef['length'], $options['maxlength']);
+            }
+            if (!empty($fieldDef['length'])) {
+                $options['maxlength'] = min($fieldDef['length'], 100000);
+            }
         }
 
         if (in_array($options['type'], ['datetime', 'date', 'time', 'select'])) {

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

@@ -23,6 +23,8 @@ use Cake\ORM\Table;
 use Cake\Routing\Router;
 use Cake\TestSuite\TestCase;
 use Cake\Utility\Security;
+use Cake\Validation\Validator;
+use Cake\View\Form\EntityContext;
 use Cake\View\Helper\FormHelper;
 use Cake\View\View;
 use Cake\View\Widget\WidgetLocator;
@@ -9574,4 +9576,227 @@ class FormHelperTest extends TestCase
         ];
         $this->assertHtml($expected, $result);
     }
+
+    /**
+     * testControlMaxLengthArrayContext method
+     *
+     * Test control() with maxlength attribute in Array Context.
+     *
+     * @return void
+     */
+    public function testControlMaxLengthArrayContext()
+    {
+        $this->article['schema'] = [
+            'title' => ['length' => 10]
+        ];
+
+        $this->Form->create($this->article);
+        $result = $this->Form->control('title');
+        $expected = [
+            'div' => ['class'],
+            'label' => ['for'],
+            'Title',
+            '/label',
+            'input' => [
+                'id',
+                'name' => 'title',
+                'type' => 'text',
+                'required' => 'required',
+                'maxlength' => 10,
+            ],
+            '/div',
+
+        ];
+        $this->assertHtml($expected, $result);
+    }
+
+    /**
+     * testControlMaxLengthEntityContext method
+     *
+     * Test control() with maxlength attribute in Entity Context.
+     *
+     * @return void
+     */
+    public function testControlMaxLengthEntityContext()
+    {
+        $this->article['schema']['title']['length'] = 45;
+
+        $validator = new Validator();
+        $validator->maxLength('title', 10);
+        $article = new EntityContext(
+            new ServerRequest(),
+            [
+                'entity' => new Entity($this->article),
+                'table' => new Table([
+                    'schema' => $this->article['schema'],
+                    'validator' => $validator
+                ])
+
+            ]
+        );
+
+        $this->Form->create($article);
+        $result = $this->Form->control('title');
+        $expected = [
+            'div' => ['class'],
+            'label' => ['for'],
+            'Title',
+            '/label',
+            'input' => [
+                'id',
+                'name' => 'title',
+                'type' => 'text',
+                'required' => 'required',
+                'maxlength' => 10,
+            ],
+            '/div',
+
+        ];
+        $this->assertHtml($expected, $result);
+
+        $this->article['schema']['title']['length'] = 45;
+        $validator = new Validator();
+        $validator->maxLength('title', 55);
+        $article = new EntityContext(
+            new ServerRequest(),
+            [
+                'entity' => new Entity($this->article),
+                'table' => new Table([
+                    'schema' => $this->article['schema'],
+                    'validator' => $validator
+                ])
+
+            ]
+        );
+
+        $this->Form->create($article);
+        $result = $this->Form->control('title');
+        $expected = [
+            'div' => ['class'],
+            'label' => ['for'],
+            'Title',
+            '/label',
+            'input' => [
+                'id',
+                'name' => 'title',
+                'type' => 'text',
+                'required' => 'required',
+                'maxlength' => 45,
+            ],
+            '/div',
+
+        ];
+        $this->assertHtml($expected, $result);
+
+        $this->article['schema']['title']['length'] = 45;
+        $validator = new Validator();
+        $validator->maxLength('title', 55);
+        $article = new EntityContext(
+            new ServerRequest(),
+            [
+                'entity' => new Entity($this->article),
+                'table' => new Table([
+                    'schema' => $this->article['schema'],
+                    'validator' => $validator
+                ])
+
+            ]
+        );
+
+        $this->Form->create($article);
+        $result = $this->Form->control('title', ['maxlength' => 10]);
+        $expected = [
+            'div' => ['class'],
+            'label' => ['for'],
+            'Title',
+            '/label',
+            'input' => [
+                'id',
+                'name' => 'title',
+                'type' => 'text',
+                'required' => 'required',
+                'maxlength' => 10,
+            ],
+            '/div',
+
+        ];
+        $this->assertHtml($expected, $result);
+    }
+
+    /**
+     * testControlMinMaxLengthEntityContext method
+     *
+     * Test control() with maxlength attribute in Entity Context sets the minimum val.
+     *
+     * @return void
+     */
+    public function testControlMinMaxLengthEntityContext()
+    {
+        $validator = new Validator();
+        $validator->maxLength('title', 10);
+        $article = new EntityContext(
+            new ServerRequest(),
+            [
+                'entity' => new Entity($this->article),
+                'table' => new Table([
+                    'schema' => $this->article['schema'],
+                    'validator' => $validator
+                ])
+
+            ]
+        );
+
+        $this->Form->create($article);
+        $result = $this->Form->control('title');
+        $expected = [
+            'div' => ['class'],
+            'label' => ['for'],
+            'Title',
+            '/label',
+            'input' => [
+                'id',
+                'name' => 'title',
+                'type' => 'text',
+                'required' => 'required',
+                'maxlength' => 10,
+            ],
+            '/div',
+
+        ];
+        $this->assertHtml($expected, $result);
+    }
+
+    /**
+     * testControlMaxLengthFormContext method
+     *
+     * Test control() with maxlength attribute in Form Context.
+     *
+     * @return void
+     */
+    public function testControlMaxLengthFormContext()
+    {
+        $validator = new Validator();
+        $validator->maxLength('title', 10);
+        $form = new Form();
+        $form->setValidator('default', $validator);
+
+        $this->Form->create($form);
+        $result = $this->Form->control('title');
+        $expected = [
+            'div' => ['class'],
+            'label' => ['for'],
+            'Title',
+            '/label',
+            'input' => [
+                'id',
+                'name' => 'title',
+                'type' => 'text',
+                'required' => 'required',
+                'maxlength' => 10,
+            ],
+            '/div',
+
+        ];
+        $this->assertHtml($expected, $result);
+    }
 }