awurth/SlimValidation

Update respect/validation to ^2.0

Closed this issue · 14 comments

Is it possible to update respect/validation to version 2.x?

Hi

I also would be happy, if SlimValidation would use respect/validation 2.x. Recently I noticed, that with PHP8.2 I get notices when using the emailValidator of respect/validation 1.x because of creation of dynamic property, which is deprecated with PHP8.2.

Thanks to seateng who made PR #23 , which would use respect/validation 2.x.
Is there a special reason, why this PR isn't merged?`
For me the changes in the PR worked without errors.

Thanks to all the contributors of this Repo!

philipp

PR #23 is not enough to make the validator compatible with respect/validation 2.x, it requires a bit more work than that, plus it introduces breaking changes. Version 3.x should stay compatible with respect/validation 1.x, adding support for both versions would require a lot more work, I would prefer creating a new major version compatible with respect/validation 2.x only. The work has already been done in branch 4.x but it is not ready to be released yet.

This is a project I developed as a student, there are probably better alternatives. I am not happy with the code quality so I tried a complete rewrite, but since I don't know why and how people continue to use this, I don't know in which direction I should go. If you could tell me a bit about your usage of the validator, maybe I'll consider taking the time to continue its development and release a new version.

Hi Alexis
Thanks a lot for your reply.
I absolutely see your point. I also think it would be a good idea to release a new major version for respect/validation 2.x.
I use your validator in a webshop which I built on Slim Framework, Twig and Eloquent. I think it was in the beginning slim3 and because I use Twig I used your library. Meanwhile the shop runs on Slim4.
At the moment I'm working on bumping my app to php8.2 and there I get deprecation notices because of respect/validation 1.x.
You wrote, that the work for a new major version for respect/validation 2.x has been done in branch 4.x. Is there a way that I could support you to release this?
philipp

You wrote, that the work for a new major version for respect/validation 2.x has been done in branch 4.x. Is there a way that I could support you to release this? philipp

I also like this library and would like to support your effort to upgrade it to a newer version of Respect\Validation.

Thank you for your effort, @awurth, it seems your project is really useful to people. If there's any way to give you money to compensate your efforts, please let us know!

I'm using the library in a Slim 4 project, slowly modernizing a legacy codebase's API. Quickly validating a request and getting clean error messages that I can easily render as JSON is very useful.

I actually learned about Respect\Validation because of your library, I think SlimValidation is listed somewhere on the main Slim website.

Hi, sorry for the late response, and thank you for your feedback. I'm about to release a version 5.0 (I'm skipping v4.x) but I need some more feedback first since some features (that you might have been using) have been removed:

  • The minimum required PHP version is now 8.1
  • There is no more a Twig Extension to display error messages in a Twig template. The extension was possible because the validator was used to store all the errors, but now the validator just returns a collection of errors
  • Error groups and the option to store errors in an associative or indexed array no longer exist, all errors are stored in a ValidationFailureCollection

Are you ok with these changes?

Those all seem good. The project I'm working on doesn't use Twig, and the ValidationFailureCollection is a neutral API change. Thanks for all your hard work!

Hi Alexis
Thanks a lot for your reply!
The minimum requirement of PHP 8.1 for me is fine.
That there is no more a Twig Extension is a problem for me. I used your library especially because of this feature. So I would have to rewrite a lot of Twig Templates to somehow still display the errors.
So, for me, this wouldn't be a good change, but I don't expect you to develop your library according to my personal needs.

@codingphildotdev I added a new SatefulValidator that decorates the base Validator class and holds all the errors. I copied the old Twig Extension, renamed it LegacyValidatorExtension and adapted the code to the new API, but I had to remove the val function since you can only get values from the ValidationFailureCollection which only contains the invalid values. I'll try to find a way to collect all values passed to the validator.

So you don't have to change your templates, but only the instanciation of the validator and the Twig extension:

use Awurth\Validator\StatefulValidator;
use Awurth\Validator\Twig\LegacyValidatorExtension;

$validator = StatefulValidator::create();

$extension = new LegacyValidatorExtension($validator);

I finally found a way to collect all validated data. You must create a DataCollectorAsserter instance and pass it to your StatefulValidator, then use that same instance and pass it to the ValidatorExtension:

use Awurth\Validator\Assertion\DataCollectorAsserter;
use Awurth\Validator\StatefulValidator;
use Awurth\Validator\Twig\LegacyValidatorExtension;

$asserter = DataCollectorAsserter::create();
$validator = StatefulValidator::create($asserter);

$extension = new LegacyValidatorExtension($validator, $asserter);

Now you can use the val function in templates to pre-fill fields with submitted data.

If these changes seem fine to you I'll tag a new version and then I'll update the docs when I have the time

Thanks a lot for your effort!
It looks quite good!

As I use PHP-DI I had to create the DataCollectorAsserter and the StatefulValidator in my app.php like this:

use Awurth\Validator\Assertion\DataCollectorAsserter;
use Awurth\Validator\StatefulValidator;
use Awurth\Validator\Twig\LegacyValidatorExtension;

DataCollectorAsserter::class => function(){
    return DataCollectorAsserter::create();
  },
  StatefulValidator::class => function(ContainerInterface $container){
    return StatefulValidator::create($container->get(DataCollectorAsserter::class));
  },

'view' => function(ContainerInterface $container){
    $view = Twig::create(__DIR__ . '/../ressources/views',['cache' => false]);
    $view->addExtension(new LegacyValidatorExtension($container->get(StatefulValidator::class), $container->get(DataCollectorAsserter::class)));
   );

Then in my Controller I can use the validator with dependency injection like this:

use Awurth\Validator\StatefulValidator;

class KundenkontoController
{
  protected $validator;

   public function __construct(StatefulValidator $validator) {
			 $this->validator = $validator;
   }

//Validation
    $validator = $this->validator->validate($request, [
        'email' => [
            'rules' => v::notEmpty()->email(),
            'messages' => [
                'notEmpty' => 'Bitte geben Sie Ihre Email-Adresse ein.',
                'email' => 'Bitte geben Sie eine gültige Email-Adresse ein.'
            ]
        ],
        'passwort' => [
            'rules' => v::notEmpty(),
            'messages' => [
                'notEmpty' => 'Bitte geben Sie Ihr Passwort ein.',
            ]
        ],
    ]);

    if($validator->count() > 0){
        return $this->view->render($response, 'kundenkonto/login.twig');
    }

In the Twig-Templates I can use the val function with no issues.

What I realized is, that it's no longer possible to use $validator->isValid() which I used a lot. Of course I can use $validator->count() > 0 but I think the isValid() function was quite handy.

On some occasions I also made checks independent of the validator but added custom errors, which I could use in the Twig Template. Like this:

if(!$this->kundeAuth->attempt($request->getParam('email'), $request->getParam('passwort'))){
        $validator->addError('email', 'Bitte geben Sie gültige Logindaten an.');
        $validator->addError('passwort', 'Bitte geben Sie gültige Logindaten an. <a href="/kundenkonto/passwortvergessen">Passwort vergessen?</a>');
        return $this->view->render($response, 'kundenkonto/login.twig');
    }

Now $validator->addError() doesn't work anymore.
Is there a way to add Errors in Code?

Thanks!

You can add errors like that: $validator->getFailures()->add(new ValidationFailure('message', 'value', 'property', 'notBlank'));

I'd rather not add those methods directly in the Validator, or at least not with the same signature, but I made pretty much everything extendable, so you could create your own StatefulValidator by implementing the StatefulValidatorInterface and implement those methods in it.

It would look something like this:

Method 1, decorating the base Validator:

final class MyCustomValidator implements StatefulValidatorInterface
{
    private ValidationFailureCollectionInterface $failures;

    public function __construct(private readonly ValidatorInterface $validator)
    {
        $this->failures = new ValidationFailureCollection();
    }

    public function validate(mixed $subject, Validatable|array $rules, array $messages = []): ValidationFailureCollectionInterface
    {
        $this->failures->addAll($this->validator->validate($subject, $rules, $messages));

        return $this->failures;
    }

    public function getFailures(): ValidationFailureCollectionInterface
    {
        return $this->failures;
    }

    public function isValid(): bool
    {
        return $this->failures->count() === 0;
    }

    public function addError(string $key, string $message): void
    {
        $this->failures->add(new ValidationFailure($message, null, $key));
    }
}

Method 2, decorating the StatefulValidator:

final class MyCustomValidator implements StatefulValidatorInterface
{
    public function __construct(private readonly StatefulValidatorInterface $validator)
    {
    }

    public function validate(mixed $subject, Validatable|array $rules, array $messages = []): ValidationFailureCollectionInterface
    {
        return $this->validator->validate($subject, $rules, $messages);
    }

    public function getFailures(): ValidationFailureCollectionInterface
    {
        return $this->validator->getFailures();
    }

    public function isValid(): bool
    {
        return $this->validator->getFailures()->count() === 0;
    }

    public function addError(string $key, string $message): void
    {
        $this->validator->getFailures()->add(new ValidationFailure($message, null, $key));
    }
}

Great!
Using a Custom Class works superb!

I hope the last thing I encountered was that the LegacyValidatorExtension offers the hasError() function, but it's not possible to restrict it on a certain HTML-Form on the page.

Before in the ValidatorExtension:

    /**
     * Tells whether there are validation errors for a parameter.
     *
     * @param string $key
     * @param string $group
     *
     * @return bool
     */
    public function hasError($key, $group = null)
    {
        return !empty($this->validator->getErrors($key, $group));
    }

With this I could use {{ has_error('fieldname', 'formname') ? ' is-invalid' : ''}} which now obviously gives me an error as the second argument for the new getError function expects an integer.

Is there a way to handle this?

I've just added the possibility to pass a context as 4th argument of the validate method (it can be anything, not just a string):

$validator->validate('Too short', V::notBlank()->length(min: 10), context: 'formname');

Now your Twig templates should work as before

Hi Alexis

I'm really sorry, that I didn't reply sooner.
I was travelling through Europe in April during the whole month and started a new job in May.

Finally I tested the context argument of the validate method, which works flawlessly.

Again thanks a lot for your great work!