diff --git a/composer.json b/composer.json index 32d2eaa8ff3fc9957bdcc5b20ee76046aaa5ab73..1c02bdae2228ecd3bf64592af8317b0195261280 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,7 @@ "symfony/flex": "^1.3.1", "symfony/form": "5.0.*", "symfony/framework-bundle": "5.0.*", + "symfony/validator": "5.0.*", "symfony/yaml": "5.0.*" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 7b0ad7766b3c1c81762662b478d7fbbefe431ddd..2a338a486565cb07704b3a57cb269245c82d2a49 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "91ff357897f2296188db5dd0fce8893b", + "content-hash": "18678eee5ad956a975c1e6ae6eb2a1dd", "packages": [ { "name": "doctrine/annotations", @@ -2114,6 +2114,156 @@ ], "time": "2019-11-18T17:27:11+00:00" }, + { + "name": "symfony/translation-contracts", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "8cc682ac458d75557203b2f2f14b0b92e1c744ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/8cc682ac458d75557203b2f2f14b0b92e1c744ed", + "reference": "8cc682ac458d75557203b2f2f14b0b92e1c744ed", + "shasum": "" + }, + "require": { + "php": "^7.2.5" + }, + "suggest": { + "symfony/translation-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "time": "2019-11-18T17:27:11+00:00" + }, + { + "name": "symfony/validator", + "version": "v5.0.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/validator.git", + "reference": "fc459a3d66bda9c0f8231a4d44dddd6daf23db92" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/validator/zipball/fc459a3d66bda9c0f8231a4d44dddd6daf23db92", + "reference": "fc459a3d66bda9c0f8231a4d44dddd6daf23db92", + "shasum": "" + }, + "require": { + "php": "^7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^1.1|^2" + }, + "conflict": { + "doctrine/lexer": "<1.0.2", + "phpunit/phpunit": "<5.4.3", + "symfony/dependency-injection": "<4.4", + "symfony/http-kernel": "<4.4", + "symfony/intl": "<4.4", + "symfony/translation": "<4.4", + "symfony/yaml": "<4.4" + }, + "require-dev": { + "doctrine/annotations": "~1.7", + "doctrine/cache": "~1.0", + "egulias/email-validator": "^2.1.10", + "symfony/cache": "^4.4|^5.0", + "symfony/config": "^4.4|^5.0", + "symfony/dependency-injection": "^4.4|^5.0", + "symfony/expression-language": "^4.4|^5.0", + "symfony/http-client": "^4.4|^5.0", + "symfony/http-foundation": "^4.4|^5.0", + "symfony/http-kernel": "^4.4|^5.0", + "symfony/intl": "^4.4|^5.0", + "symfony/mime": "^4.4|^5.0", + "symfony/property-access": "^4.4|^5.0", + "symfony/property-info": "^4.4|^5.0", + "symfony/translation": "^4.4|^5.0", + "symfony/yaml": "^4.4|^5.0" + }, + "suggest": { + "doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.", + "doctrine/cache": "For using the default cached annotation reader.", + "egulias/email-validator": "Strict (RFC compliant) email validation", + "psr/cache-implementation": "For using the mapping cache.", + "symfony/config": "", + "symfony/expression-language": "For using the Expression validator", + "symfony/http-foundation": "", + "symfony/intl": "", + "symfony/property-access": "For accessing properties within comparison constraints", + "symfony/property-info": "To automatically add NotNull and Type constraints", + "symfony/translation": "For translating validation errors.", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Validator\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Validator Component", + "homepage": "https://symfony.com", + "time": "2020-03-30T11:42:42+00:00" + }, { "name": "symfony/var-dumper", "version": "v5.0.5", diff --git a/config/packages/test/validator.yaml b/config/packages/test/validator.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1e5ab7880bf02b6c8982877393c846b9b3ca400d --- /dev/null +++ b/config/packages/test/validator.yaml @@ -0,0 +1,3 @@ +framework: + validation: + not_compromised_password: false diff --git a/config/packages/validator.yaml b/config/packages/validator.yaml new file mode 100644 index 0000000000000000000000000000000000000000..350786a13d9cc5f54e945984c0590cf37792ffcc --- /dev/null +++ b/config/packages/validator.yaml @@ -0,0 +1,8 @@ +framework: + validation: + email_validation_mode: html5 + + # Enables validator auto-mapping support. + # For instance, basic validation constraints will be inferred from Doctrine's metadata. + #auto_mapping: + # App\Entity\: [] diff --git a/src/Controller/TestHeahdudeController.php b/src/Controller/TestHeahdudeController.php new file mode 100644 index 0000000000000000000000000000000000000000..7d375fce30c3f24ac42b3792743e00caf738444f --- /dev/null +++ b/src/Controller/TestHeahdudeController.php @@ -0,0 +1,83 @@ +<?php +declare(strict_types=1); + +namespace App\Controller; + + +use App\Exception\InvalidColorException; +use App\Form\ColorType; +use App\Model\ColorVo; +use App\Model\ParentModel; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\Form\DataMapperInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Extension\Core\Type\CheckboxType; +use Symfony\Component\Form\Extension\Core\Type\FormType; +use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\Form\FormFactoryInterface; +use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Validator\Constraints\Callback; +use Symfony\Component\Validator\Constraints\IsTrue; +use Symfony\Component\Validator\Constraints\Type; + +class TestHeahdudeController extends AbstractController +{ + /** + * @Route("/test-heahdude", name="test_heah_form") + */ + public function index(FormFactoryInterface $formFactory) + { + $form = $formFactory->createNamedBuilder('', FormType::class, null, [ + 'data_class' => ParentModel::class, + 'empty_data' => null, //function () { return new ParentModel('', new ColorVo('foo')); } + ]) + ->setDataMapper(new class implements DataMapperInterface { + public function mapDataToForms($viewData, iterable $forms) + { + if ($viewData === null) { + return; + } + $forms = iterator_to_array($forms); + $forms['color']->setData($viewData->getColor()); + $forms['content']->setData($viewData->getContent()); + } + + public function mapFormsToData(iterable $forms, &$viewData) + { + $forms = iterator_to_array($forms); + try { + $viewData = new ParentModel($forms['content']->getData(), new ColorVo($forms['color']->getData())); + } catch (InvalidColorException $error) { + // What to do here ?! + + // This does not work + // $forms['colorCondition']->setData(false); + // Even if it would, the error path is wrong! + } + } + }) + ->add('color', ColorType::class, [ + 'constraints' => [ + new Type(['type' => 'string']), + new Callback(['callback' => ColorVo::class . '::validateColor']) + ] + ]) + ->add('content', TextType::class) + ->add('colorCondition', CheckboxType::class, [ + 'label' => 'Accept color', + 'required' => false, + 'mapped' => false, + 'empty_data' => true, + 'constraints' => new IsTrue(['message' => 'Wrong color input']) + ]) + ->getForm(); + $form->submit(['color' => 10, 'content' => 'foobar']); + + dump($form->getErrors(true, true)); + + return $this->json([ + 'message' => 'Welcome to your new controller!', + 'path' => 'src/Controller/TestVoFormController.php', + ]); + } +} diff --git a/src/Controller/TestVoFormController.php b/src/Controller/TestVoFormController.php index 2d7e9c71285d9fc2f782060bdc51d447d7977f84..63f3f07b53f5076c9c873dce676ef4d8647d32ad 100644 --- a/src/Controller/TestVoFormController.php +++ b/src/Controller/TestVoFormController.php @@ -1,4 +1,5 @@ <?php +declare(strict_types=1); namespace App\Controller; diff --git a/src/Exception/InvalidColorException.php b/src/Exception/InvalidColorException.php new file mode 100644 index 0000000000000000000000000000000000000000..7c275a6036bd7b40ca8672599f31127d9b12721d --- /dev/null +++ b/src/Exception/InvalidColorException.php @@ -0,0 +1,9 @@ +<?php + +namespace App\Exception; + + +class InvalidColorException extends \InvalidArgumentException +{ + +} diff --git a/src/Form/ColorType.php b/src/Form/ColorType.php index aaea4c82a29bc128118798b39efcee55c19139f6..489f9edfe33b1e6272f15534ae8161d81c3e2d6e 100644 --- a/src/Form/ColorType.php +++ b/src/Form/ColorType.php @@ -1,8 +1,10 @@ <?php +declare(strict_types=1); namespace App\Form; +use App\Exception\InvalidColorException; use App\Model\ColorVo; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\DataMapperInterface; @@ -12,6 +14,7 @@ use Symfony\Component\Form\Exception\TransformationFailedException; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Validator\Context\ExecutionContextInterface; class ColorType extends AbstractType implements DataTransformerInterface { @@ -57,4 +60,24 @@ class ColorType extends AbstractType implements DataTransformerInterface { $viewData = new ColorVo($forms->getData()); } + + public static function assertColorIsValid(string $color): void + { + if (preg_match('#^\#[0-9]{6}$#', $color) === 1) { + return; + } + + throw new InvalidColorException('Invalid color'); + } + + public static function validateColor($color, ExecutionContextInterface $context, $payload) + { + try { + self::assertColorIsValid($color); + } catch (InvalidColorException $e) { + $context->buildViolation('Impossible color bruuuu') + ->addViolation() + ; + } + } } diff --git a/src/Model/ColorVo.php b/src/Model/ColorVo.php index 832d3ce513f463a3dd13cabc9acde0a5315e79f2..b6da30789f8120a6bb51ac92d666a6e2b92fd27e 100644 --- a/src/Model/ColorVo.php +++ b/src/Model/ColorVo.php @@ -1,4 +1,5 @@ <?php +declare(strict_types=1); namespace App\Model; diff --git a/src/Model/ParentModel.php b/src/Model/ParentModel.php index 1c548126954e0e15b2952f75d91fa70c777ef6ca..bc7fb11c799ae770f8493cf1c868d93776610f7c 100644 --- a/src/Model/ParentModel.php +++ b/src/Model/ParentModel.php @@ -1,4 +1,5 @@ <?php +declare(strict_types=1); namespace App\Model; diff --git a/symfony.lock b/symfony.lock index ee44accf92fe73059b0b693c53ff19c4564110a3..415a1dae4e5c303a85b9163e39742770e74144ff 100644 --- a/symfony.lock +++ b/symfony.lock @@ -172,6 +172,22 @@ "symfony/service-contracts": { "version": "v2.0.1" }, + "symfony/translation-contracts": { + "version": "v2.0.1" + }, + "symfony/validator": { + "version": "4.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "master", + "version": "4.3", + "ref": "d902da3e4952f18d3bf05aab29512eb61cabd869" + }, + "files": [ + "config/packages/test/validator.yaml", + "config/packages/validator.yaml" + ] + }, "symfony/var-dumper": { "version": "v5.0.5" },