4

I'm using symfony 4 and Api Platform 2.5 . I have created a custom operation to change user password.

The problem is that when I send an empty oldPassword or any other required attributes,the validation doesn't work. As you can see, I used a validation group named "change_password".

This is a part of Entity User :

/**
 * @ApiResource(
 *     itemOperations={
 *         "user_change_password"={
 *             "method"="PUT",
 *             "path"="/users/change-password/{id}",
 *             "controller"=UserChangePassword::class,
 *             "formats"={"json"},
 *             "denormalization_context"={"groups"={"user:change-password"}},
 *             "normalization_context"={"groups"={"user:read"}},
 *             "validation_groups"={"change_password"},
 *             "access_control"="is_granted('EDIT', object)",
 *             "access_control_message"="access denied"
 *         },
 *     },
 * )
 *
 */
class User implements UserInterface, \Serializable, EquatableInterface
{
    /**
     * @Assert\NotBlank(
     *     message=Tthis value should not be blank",
     *     groups={"change_password"}
     * )
     * @SecurityAssert\UserPassword(
     *     message = "Wrong value for your current password",
     *     groups={"change_password"}
     * )
     * @Groups({"user:change-password"})
     */
    private $oldPassword;

    /**
     * @Assert\NotBlank(
     *     message="This value should not be blank",
     *     groups={"registration", "change_password"}
     * )
     * @Assert\Length(
     *     min="6",
     *     max="32",
     *     minMessage="Password must be at least ({{ limit }}) characters.",
     *     maxMessage="Password must have a maximum of ({{ limit }}) characters.",
     *     groups={"registration", "change_password"}
     * )
     * @Groups({"user:write", "user:change-password"})
     */
    private $plainPassword;

    /**
     * @Assert\NotBlank(
     *     message="This value should not be blank",
     *     groups={"registration", "change_password"}
     * )
     * @Assert\Expression(
     *     "this.getPlainPassword() == this.getRetypedPlainPassword()",
     *     groups={"registration", "change_password"}
     * )
     * @Groups({"user:write", "user:change-password"})
     */
    private $retypedPlainPassword;

}

This is the custom operation class :

class UserChangePassword
{

    public function __invoke(User $data)
    {
        $errors = $this->validator->validate($data);
        if (count($errors) > 0) {
            throw new ValidationException($errors);
        }

        dd($errors) ;

        $oldPassword = $data->getOldPassword();
        if ($this->passwordEncoder->isPasswordValid($data, $oldPassword)) {
            // do changes
        }

        return $data;

    }
}

The output of dd($erros) when the oldPassword is empty :

enter image description here

And without dd($erros) I get this error :

enter image description here

1
  • 1
    your __invoke is called before your assert is checkin. so you encoder is crashing. and so you see this error. your asserts are called if symfony persist the entity. Commented Jan 29, 2020 at 18:02

2 Answers 2

9

First

For v2.5+ replace access_control to security. And replace access_control_message to security_message.

Second

Try add Default group in you'r validation_groups if you don't use custom controller:

"validation_groups"={"Default", "change_password"},

Or add Default group in constraint:

//src/Entity/User.php

@Assert\NotBlank(groups={"Default", "change_password"})

and call validate:

<?php
// src/Controller/Api/CustomClass.php

declare(strict_types=1);

namespace App\Controller\Api;

use ApiPlatform\Core\Validator\ValidatorInterface;
use App\Entity\User;

class CustomClass extends AbstractController
{
    /**
     * @var ValidatorInterface
     */
    private $validator;

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

    public function __invoke(User $data): User
    {
        $this->validator->validate($data);

        //other code ...
    }
}

Also you can check this Url

https://symfony.com/doc/current/validation/groups.html

So the solution can be like this without adding "Default" :

    $errors = $this->validator->validate($data, null, ['change_password']);
    if (count($errors) > 0) {
        throw new ValidationException($errors);
    }
Sign up to request clarification or add additional context in comments.

4 Comments

Your solution works , but I need to put Default for all attributes . I found a solution thanks to the url in your post (I think you have deleted it). ` $errors = $this->validator->validate($data, null, ['change_password']); if (count($errors) > 0) { throw new ValidationException($errors); } ` I'll edit your post and after you accept the modification I'll accept your answar
Oh thanks. I answered a similar question before (stackoverflow.com/questions/59533789/…). I applied your changes for a fully answer.
Yes I have seen it. When you test the second solution "without Defaut" in your project, tell me if it works or no.
Yes, you'r variant must be work, if you validate value and use(or not) constraints. This is a usual Symfony validate. The way that you pick, must be use use Symfony\Component\Validator\Validator\ValidatorInterface. But it’s easier for me to put Default for all the necessary properties and use use ApiPlatform\Core\Validator\ValidatorInterface after that I call $this->validator->validate($data); in my __invoke method for to check and display errors, if any.
2

in my case i use symfony 4.4 and api-platform version 2.5 is use folloing code

$this->validator->validate($data,['groups'=>'register']);

while validator is instanceof ApiPlatform\Core\Validator\ValidatorInterface .

2 Comments

This is what I end up doing as well, unfortunately, I found this answer only after I found the fix (after a couple of hours trying different things). I don't really like it since everything can't be set up at the entity level and can cause problems later on if I change things in the entity without making sure everything is also valid in the controller. But I would consider it a temporary fix. Should anyone know how to do it properly I would appreciate a better solution. I'm using Symfony 5.2.1 with API Platform 2.5.9.
you can use symfony validator also and in invalid input return response error with status 400 but the response format is diffrent with api response

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.