├── CHANGELOG.md ├── src ├── ValidatedInputInterface.php ├── ValidatedInputTrait.php ├── Attribute │ ├── Validate.php │ └── ValidateResolver.php └── ValidatingHydrator.php ├── rector.php ├── UPGRADE.md ├── LICENSE.md ├── composer.json └── README.md /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Yii Validating Hydrator Change Log 2 | 3 | ## 2.0.3 under development 4 | 5 | - no changes in this release. 6 | 7 | ## 2.0.2 December 16, 2025 8 | 9 | - Chg #29, #33: Change PHP constraint in `composer.json` to `8.0 - 8.5` (@vjik) 10 | - Bug #31: Clear the `result` property of the `ValidateResolver` object after hydration (@olegbaturin) 11 | 12 | ## 2.0.1 August 06, 2024 13 | 14 | - Enh #24: Add `yiisoft/validator` of version `^2.0` support (@vjik) 15 | 16 | ## 2.0.0 March 06, 2024 17 | 18 | - Chg #17: Throws `LogicException` on call `ValidatedInputInterface::getValidatedInput()` method when object is not 19 | validated (@vjik) 20 | 21 | ## 1.0.0 February 02, 2024 22 | 23 | - Initial release. 24 | -------------------------------------------------------------------------------- /src/ValidatedInputInterface.php: -------------------------------------------------------------------------------- 1 | paths([ 12 | __DIR__ . '/src', 13 | __DIR__ . '/tests', 14 | ]); 15 | 16 | // register a single rule 17 | $rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class); 18 | 19 | // define sets of rules 20 | $rectorConfig->sets([ 21 | LevelSetList::UP_TO_PHP_80, 22 | ]); 23 | 24 | $rectorConfig->skip([ 25 | ClosureToArrowFunctionRector::class, 26 | ]); 27 | }; 28 | -------------------------------------------------------------------------------- /UPGRADE.md: -------------------------------------------------------------------------------- 1 | # Upgrading Instructions for Yii Validating Hydrator 2 | 3 | This file contains the upgrade notes. These notes highlight changes that could break your 4 | application when you upgrade the package from one version to another. 5 | 6 | > **Important!** The following upgrading instructions are cumulative. That is, if you want 7 | > to upgrade from version A to version C and there is version B between A and C, you need 8 | > to following the instructions for both A and B. 9 | 10 | ## Upgrade from 1.x to 2.x 11 | 12 | If you use `ValidatedInputInterface::getValidatedInput()` on non-validated inputs or form models, wrap it with 13 | `try ... catch`: 14 | 15 | ```php 16 | $result = $input->getValidatedInput(); 17 | // ↓ 18 | try { 19 | $result = $input->getValidatedInput(); 20 | } catch (LogicException) { 21 | $result = null; 22 | } 23 | ``` 24 | -------------------------------------------------------------------------------- /src/ValidatedInputTrait.php: -------------------------------------------------------------------------------- 1 | validationResult = $result; 25 | } 26 | 27 | public function getValidationResult(): Result 28 | { 29 | if (empty($this->validationResult)) { 30 | throw new LogicException('Validation result is not set.'); 31 | } 32 | 33 | return $this->validationResult; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Attribute/Validate.php: -------------------------------------------------------------------------------- 1 | $rules 21 | */ 22 | private array $rules; 23 | 24 | /** 25 | * @param RuleInterface ...$rules Validation rules. 26 | */ 27 | public function __construct(RuleInterface ...$rules) 28 | { 29 | $this->rules = array_values($rules); 30 | } 31 | 32 | /** 33 | * @return RuleInterface[] Validation rules. 34 | * @psalm-return list 35 | */ 36 | public function getRules(): array 37 | { 38 | return $this->rules; 39 | } 40 | 41 | public function getResolver(): string 42 | { 43 | return ValidateResolver::class; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright © 2008 by Yii Software () 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the 13 | distribution. 14 | * Neither the name of Yii Software nor the names of its 15 | contributors may be used to endorse or promote products derived 16 | from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 21 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 22 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 28 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yiisoft/hydrator-validator", 3 | "type": "library", 4 | "description": "Validating hydrator with raw data validation support", 5 | "keywords": [ 6 | "input", 7 | "validation" 8 | ], 9 | "homepage": "https://www.yiiframework.com/", 10 | "license": "BSD-3-Clause", 11 | "support": { 12 | "issues": "https://github.com/yiisoft/hydrator-validator/issues?state=open", 13 | "source": "https://github.com/yiisoft/hydrator-validator", 14 | "forum": "https://www.yiiframework.com/forum/", 15 | "wiki": "https://www.yiiframework.com/wiki/", 16 | "irc": "ircs://irc.libera.chat:6697/yii", 17 | "chat": "https://t.me/yii3en" 18 | }, 19 | "funding": [ 20 | { 21 | "type": "opencollective", 22 | "url": "https://opencollective.com/yiisoft" 23 | }, 24 | { 25 | "type": "github", 26 | "url": "https://github.com/sponsors/yiisoft" 27 | } 28 | ], 29 | "require": { 30 | "php": "8.0 - 8.5", 31 | "yiisoft/hydrator": "^1.0", 32 | "yiisoft/validator": "^1.0 || ^2.0" 33 | }, 34 | "require-dev": { 35 | "maglnet/composer-require-checker": "^4.4", 36 | "phpunit/phpunit": "^9.6.22", 37 | "rector/rector": "^2.0.11", 38 | "roave/infection-static-analysis-plugin": "^1.25", 39 | "spatie/phpunit-watcher": "^1.23.6", 40 | "vimeo/psalm": "^4.30 || ^5.26.1 || ^6.9.6", 41 | "yiisoft/test-support": "^3.0.2" 42 | }, 43 | "autoload": { 44 | "psr-4": { 45 | "Yiisoft\\Hydrator\\Validator\\": "src" 46 | } 47 | }, 48 | "autoload-dev": { 49 | "psr-4": { 50 | "Yiisoft\\Hydrator\\Validator\\Tests\\": "tests" 51 | } 52 | }, 53 | "config": { 54 | "sort-packages": true, 55 | "allow-plugins": { 56 | "infection/extension-installer": true, 57 | "composer/package-versions-deprecated": true 58 | } 59 | }, 60 | "scripts": { 61 | "test": "phpunit --testdox --no-interaction", 62 | "test-watch": "phpunit-watcher watch" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/ValidatingHydrator.php: -------------------------------------------------------------------------------- 1 | beforeAction(); 37 | $this->hydrator->hydrate($object, $data); 38 | $this->afterAction($object, $result); 39 | } 40 | 41 | public function create(string $class, array|DataInterface $data = []): object 42 | { 43 | $result = $this->beforeAction(); 44 | $object = $this->hydrator->create($class, $data); 45 | $this->afterAction($object, $result); 46 | return $object; 47 | } 48 | 49 | private function beforeAction(): Result 50 | { 51 | $result = new Result(); 52 | $this->validateResolver->setResult($result); 53 | return $result; 54 | } 55 | 56 | private function afterAction(object $object, Result $result): void 57 | { 58 | $this->validateResolver->setResult(null); 59 | 60 | if (!$object instanceof ValidatedInputInterface) { 61 | return; 62 | } 63 | 64 | $result->isValid() 65 | ? $this->validator->validate($object) 66 | : $object->processValidationResult($result); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Attribute/ValidateResolver.php: -------------------------------------------------------------------------------- 1 | result = $result; 41 | } 42 | 43 | public function getParameterValue( 44 | ParameterAttributeInterface $attribute, 45 | ParameterAttributeResolveContext $context, 46 | ): Result { 47 | if (!$attribute instanceof Validate) { 48 | throw new UnexpectedAttributeException(Validate::class, $attribute); 49 | } 50 | 51 | if ($this->result !== null) { 52 | $parameterName = $context->getParameter()->getName(); 53 | $result = $this->validator->validate( 54 | $context->isResolved() ? [$parameterName => $context->getResolvedValue()] : [], 55 | [$parameterName => $attribute->getRules()], 56 | ); 57 | 58 | foreach ($result->getErrors() as $error) { 59 | $this->result->addError( 60 | $error->getMessage(), 61 | $error->getParameters(), 62 | $error->getValuePath(), 63 | ); 64 | } 65 | } 66 | 67 | return Result::fail(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Yii 4 | 5 |

Yii Validating Hydrator

6 |
7 |

8 | 9 | [![Latest Stable Version](https://poser.pugx.org/yiisoft/hydrator-validator/v)](https://packagist.org/packages/yiisoft/hydrator-validator) 10 | [![Total Downloads](https://poser.pugx.org/yiisoft/hydrator-validator/downloads)](https://packagist.org/packages/yiisoft/hydrator-validator) 11 | [![Build status](https://github.com/yiisoft/hydrator-validator/actions/workflows/build.yml/badge.svg)](https://github.com/yiisoft/hydrator-validator/actions/workflows/build.yml) 12 | [![codecov](https://codecov.io/gh/yiisoft/hydrator-validator/graph/badge.svg?token=vfLtWNY7nu)](https://codecov.io/gh/yiisoft/hydrator-validator) 13 | [![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fyiisoft%2Fhydrator-validator%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/yiisoft/hydrator-validator/master) 14 | [![static analysis](https://github.com/yiisoft/hydrator-validator/workflows/static%20analysis/badge.svg)](https://github.com/yiisoft/hydrator-validator/actions?query=workflow%3A%22static+analysis%22) 15 | [![type-coverage](https://shepherd.dev/github/yiisoft/hydrator-validator/coverage.svg)](https://shepherd.dev/github/yiisoft/hydrator-validator) 16 | [![psalm-level](https://shepherd.dev/github/yiisoft/hydrator-validator/level.svg)](https://shepherd.dev/github/yiisoft/hydrator-validator) 17 | 18 | The package provides a [hydrator](https://github.com/yiisoft/hydrator) 19 | that also does [validation](https://github.com/yiisoft/validator), including raw data. 20 | It's useful when input data comes from a user, and you need to validate it and then put it into DTOs. 21 | 22 | ## Requirements 23 | 24 | - PHP 8.0 - 8.5. 25 | 26 | ## Installation 27 | 28 | The package could be installed with [Composer](https://getcomposer.org): 29 | 30 | ```shell 31 | composer require yiisoft/hydrator-validator 32 | ``` 33 | 34 | ## General usage 35 | 36 | Validating hydrator is a decorator for [hydrator](https://github.com/yiisoft/hydrator) that allows to validate: 37 | 38 | - raw data of properties marked with `Validate` PHP attribute; 39 | - an object after creating or populating it. 40 | 41 | To use it, the object being validated must implement `ValidatedInputInterface`. You can use `ValidatedInputTrait` to 42 | easily create such object. The validation rules for raw values of the object are defined with `Validate` PHP attribute. 43 | 44 | Example of object: 45 | 46 | ```php 47 | use Yiisoft\Hydrator\Validator\Attribute\Validate; 48 | use Yiisoft\Hydrator\Validator\ValidatedInputInterface; 49 | use Yiisoft\Hydrator\Validator\ValidatedInputTrait; 50 | use Yiisoft\Validator\Rule\Email; 51 | use Yiisoft\Validator\Rule\Required; 52 | 53 | final class InputDto implements ValidatedInputInterface 54 | { 55 | use ValidatedInputTrait; 56 | 57 | #[Email] 58 | private string $email; 59 | 60 | #[Validate(new Required())] 61 | private string $name; 62 | } 63 | ``` 64 | 65 | Validation result could be obtained via `getValidationResult()` method. For further working with result, refer to 66 | corresponding [validator's guide section](https://github.com/yiisoft/validator/blob/master/docs/guide/en/result.md). 67 | 68 | Validating hydrator usage example: 69 | 70 | ```php 71 | use Psr\Http\Message\RequestInterface; 72 | use Yiisoft\Hydrator\Validator\ValidatingHydrator; 73 | 74 | public function actionEdit(RequestInterface $request, ValidatingHydrator $hydrator): ResponseInterface 75 | { 76 | $data = $request->getParsedBody(); 77 | $inputDto = $hydrator->create(InputDto::class, $data); 78 | 79 | if (!$inputDto->getValidationResult()->isValid()) { 80 | // Validation didn't pass :( 81 | } 82 | 83 | // Everything is fine. You can use $inputDto now. 84 | } 85 | ``` 86 | 87 | ## Documentation 88 | 89 | - [Internals](docs/internals.md) 90 | 91 | If you need help or have a question, the [Yii Forum](https://forum.yiiframework.com/c/yii-3-0/63) is a good place for that. 92 | You may also check out other [Yii Community Resources](https://www.yiiframework.com/community). 93 | 94 | ## License 95 | 96 | The Yii Validating Hydrator is free software. It is released under the terms of the BSD License. 97 | Please see [`LICENSE`](./LICENSE.md) for more information. 98 | 99 | Maintained by [Yii Software](https://www.yiiframework.com/). 100 | 101 | ## Support the project 102 | 103 | [![Open Collective](https://img.shields.io/badge/Open%20Collective-sponsor-7eadf1?logo=open%20collective&logoColor=7eadf1&labelColor=555555)](https://opencollective.com/yiisoft) 104 | 105 | ## Follow updates 106 | 107 | [![Official website](https://img.shields.io/badge/Powered_by-Yii_Framework-green.svg?style=flat)](https://www.yiiframework.com/) 108 | [![Twitter](https://img.shields.io/badge/twitter-follow-1DA1F2?logo=twitter&logoColor=1DA1F2&labelColor=555555?style=flat)](https://twitter.com/yiiframework) 109 | [![Telegram](https://img.shields.io/badge/telegram-join-1DA1F2?style=flat&logo=telegram)](https://t.me/yii3en) 110 | [![Facebook](https://img.shields.io/badge/facebook-join-1DA1F2?style=flat&logo=facebook&logoColor=ffffff)](https://www.facebook.com/groups/yiitalk) 111 | [![Slack](https://img.shields.io/badge/slack-join-1DA1F2?style=flat&logo=slack)](https://yiiframework.com/go/slack) 112 | --------------------------------------------------------------------------------