Browse Source

Merge pull request #11505 from cakephp/form-validator

Form validator
Mark Story 8 years ago
parent
commit
d45faf3d5e

+ 80 - 5
src/Form/Form.php

@@ -14,7 +14,14 @@
  */
 namespace Cake\Form;
 
+use Cake\Event\Event;
+use Cake\Event\EventDispatcherInterface;
+use Cake\Event\EventDispatcherTrait;
+use Cake\Event\EventListenerInterface;
+use Cake\Event\EventManager;
 use Cake\Validation\Validator;
+use Cake\Validation\ValidatorAwareInterface;
+use Cake\Validation\ValidatorAwareTrait;
 
 /**
  * Form abstraction used to create forms not tied to ORM backed models,
@@ -33,9 +40,26 @@ use Cake\Validation\Validator;
  *
  * Forms are conventionally placed in the `App\Form` namespace.
  */
-class Form
+class Form implements EventListenerInterface, EventDispatcherInterface, ValidatorAwareInterface
 {
 
+    use EventDispatcherTrait;
+    use ValidatorAwareTrait;
+
+    /**
+     * The alias this object is assigned to validators as.
+     *
+     * @var string
+     */
+    const VALIDATOR_PROVIDER_NAME = 'form';
+
+    /**
+     * The name of the event dispatched when a validator has been built.
+     *
+     * @var string
+     */
+    const BUILD_VALIDATOR_EVENT = 'Form.buildValidator';
+
     /**
      * The schema used by this form.
      *
@@ -58,6 +82,37 @@ class Form
     protected $_validator;
 
     /**
+     * Constructor
+     *
+     * @param \Cake\Event\EventManager|null $eventManager The event manager.
+     *  Defaults to a new instance.
+     */
+    public function __construct(EventManager $eventManager = null)
+    {
+        if ($eventManager !== null) {
+            $this->setEventManager($eventManager);
+        }
+
+        $this->getEventManager()->on($this);
+    }
+
+    /**
+     * Get the Form callbacks this form is interested in.
+     *
+     * The conventional method map is:
+     *
+     * - Form.buildValidator => buildValidator
+     *
+     * @return array
+     */
+    public function implementedEvents()
+    {
+        return [
+            'Form.buildValidator' => 'buildValidator',
+        ];
+    }
+
+    /**
      * Get/Set the schema for this form.
      *
      * This method will call `_buildSchema()` when the schema
@@ -103,17 +158,24 @@ class Form
      *
      * @param \Cake\Validation\Validator|null $validator The validator to set, or null.
      * @return \Cake\Validation\Validator the validator instance.
+     * @deprecated 3.6.0 Use Form::getValidator()/setValidator() instead.
      */
     public function validator(Validator $validator = null)
     {
+        deprecationWarning(
+            'Form::validator() is deprecated. ' .
+            'Use Form::getValidator()/setValidator() instead.'
+        );
+
         if ($validator === null && empty($this->_validator)) {
-            $validator = $this->_buildValidator(new Validator());
+            $validator = $this->_buildValidator(new $this->_validatorClass);
         }
         if ($validator) {
             $this->_validator = $validator;
+            $this->setValidator('default', $validator);
         }
 
-        return $this->_validator;
+        return $this->getValidator();
     }
 
     /**
@@ -125,6 +187,7 @@ class Form
      *
      * @param \Cake\Validation\Validator $validator The validator to customize.
      * @return \Cake\Validation\Validator The validator to use.
+     * @deprecated 3.6.0 Use Form::getValidator()/setValidator() and buildValidator() instead.
      */
     protected function _buildValidator(Validator $validator)
     {
@@ -132,6 +195,18 @@ class Form
     }
 
     /**
+     * Callback method for Form.buildValidator event.
+     *
+     * @param \Cake\Event\Event $event The Form.buildValidator event instance.
+     * @param \Cake\Validation\Validator $validator The validator to customize.
+     * @param string $name Validator name
+     * @return void
+     */
+    public function buildValidator(Event $event, Validator $validator, $name)
+    {
+    }
+
+    /**
      * Used to check if $data passes this form's validation.
      *
      * @param array $data The data to check.
@@ -139,7 +214,7 @@ class Form
      */
     public function validate(array $data)
     {
-        $validator = $this->validator();
+        $validator = $this->getValidator();
         $this->_errors = $validator->errors($data);
 
         return count($this->_errors) === 0;
@@ -224,7 +299,7 @@ class Form
         $special = [
             '_schema' => $this->schema()->__debugInfo(),
             '_errors' => $this->errors(),
-            '_validator' => $this->validator()->__debugInfo()
+            '_validator' => $this->getValidator()->__debugInfo()
         ];
 
         return $special + get_object_vars($this);

+ 2 - 1
src/Form/composer.json

@@ -22,7 +22,8 @@
     },
     "require": {
         "php": ">=5.6.0",
-        "cakephp/validation": "^3.0.0"
+        "cakephp/event": "^3.5.0",
+        "cakephp/validation": "^3.5.0"
     },
     "autoload": {
         "psr-4": {

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

@@ -123,7 +123,7 @@ class FormContext implements ContextInterface
      */
     public function isRequired($field)
     {
-        $validator = $this->_form->validator();
+        $validator = $this->_form->getValidator();
         if (!$validator->hasField($field)) {
             return false;
         }

+ 45 - 10
tests/TestCase/Form/FormTest.php

@@ -16,6 +16,7 @@ namespace Cake\Test\TestCase\Form;
 
 use Cake\Form\Form;
 use Cake\TestSuite\TestCase;
+use Cake\Validation\Validator;
 
 /**
  * Form test case.
@@ -45,18 +46,52 @@ class FormTest extends TestCase
      * Test validator()
      *
      * @return void
+     * @group deprecated
      */
     public function testValidator()
     {
-        $form = new Form();
-        $validator = $form->validator();
+        $this->deprecated(function () {
+            $form = new Form();
+            $validator = $form->validator();
+
+            $this->assertInstanceOf('Cake\Validation\Validator', $validator);
+            $this->assertSame($validator, $form->validator(), 'Same instance each time');
 
-        $this->assertInstanceOf('Cake\Validation\Validator', $validator);
-        $this->assertSame($validator, $form->validator(), 'Same instance each time');
+            $validator = $this->getMockBuilder('Cake\Validation\Validator')->getMock();
+            $this->assertSame($validator, $form->validator($validator));
+            $this->assertSame($validator, $form->validator());
+        });
+    }
 
+    /**
+     * Test getValidator()
+     *
+     * @return void
+     */
+    public function testGetValidator()
+    {
+        $form = $this->getMockBuilder(Form::class)
+            ->setMethods(['buildValidator'])
+            ->getMock();
+
+        $form->expects($this->once())
+            ->method('buildValidator');
+
+        $this->assertInstanceof(Validator::class, $form->getValidator());
+    }
+
+    /**
+     * Test setValidator()
+     *
+     * @return void
+     */
+    public function testSetValidator()
+    {
+        $form = new Form();
         $validator = $this->getMockBuilder('Cake\Validation\Validator')->getMock();
-        $this->assertSame($validator, $form->validator($validator));
-        $this->assertSame($validator, $form->validator());
+
+        $form->setValidator('default', $validator);
+        $this->assertSame($validator, $form->getValidator());
     }
 
     /**
@@ -67,7 +102,7 @@ class FormTest extends TestCase
     public function testValidate()
     {
         $form = new Form();
-        $form->validator()
+        $form->getValidator()
             ->add('email', 'format', ['rule' => 'email'])
             ->add('body', 'length', ['rule' => ['minLength', 12]]);
 
@@ -94,7 +129,7 @@ class FormTest extends TestCase
     public function testErrors()
     {
         $form = new Form();
-        $form->validator()
+        $form->getValidator()
             ->add('email', 'format', [
                 'message' => 'Must be a valid email',
                 'rule' => 'email'
@@ -141,7 +176,7 @@ class FormTest extends TestCase
         $form = $this->getMockBuilder('Cake\Form\Form')
             ->setMethods(['_execute'])
             ->getMock();
-        $form->validator()
+        $form->getValidator()
             ->add('email', 'format', ['rule' => 'email']);
         $data = [
             'email' => 'rong'
@@ -162,7 +197,7 @@ class FormTest extends TestCase
         $form = $this->getMockBuilder('Cake\Form\Form')
             ->setMethods(['_execute'])
             ->getMock();
-        $form->validator()
+        $form->getValidator()
             ->add('email', 'format', ['rule' => 'email']);
         $data = [
             'email' => 'test@example.com'

+ 4 - 4
tests/TestCase/View/Form/FormContextTest.php

@@ -139,7 +139,7 @@ class FormContextTest extends TestCase
     public function testIsRequired()
     {
         $form = new Form();
-        $form->validator()
+        $form->getValidator()
             ->requirePresence('name')
             ->add('email', 'format', ['rule' => 'email']);
 
@@ -211,7 +211,7 @@ class FormContextTest extends TestCase
             ->add('password', 'length', ['rule' => ['minLength', 8]])
             ->add('confirm', 'length', ['rule' => ['minLength', 8]]);
         $form = new Form();
-        $form->validator()
+        $form->getValidator()
             ->add('email', 'format', ['rule' => 'email'])
             ->add('name', 'length', ['rule' => ['minLength', 10]])
             ->addNested('pass', $nestedValidator);
@@ -238,7 +238,7 @@ class FormContextTest extends TestCase
         $mock->expects($this->once())
             ->method('errors')
             ->willReturn(['key' => 'should be an array, not a string']);
-        $form->validator($mock);
+        $form->setValidator('default', $mock);
         $form->validate([]);
         $context = new FormContext($this->request, ['entity' => $form]);
         $this->assertEquals(
@@ -260,7 +260,7 @@ class FormContextTest extends TestCase
             ->add('password', 'length', ['rule' => ['minLength', 8]])
             ->add('confirm', 'length', ['rule' => ['minLength', 8]]);
         $form = new Form();
-        $form->validator()
+        $form->getValidator()
             ->add('email', 'format', ['rule' => 'email'])
             ->add('name', 'length', ['rule' => ['minLength', 10]])
             ->addNested('pass', $nestedValidator);