如何使用symfony/validator验证不可更新的列?

x6492ojm  于 2023-10-24  发布在  其他
关注(0)|答案(3)|浏览(165)

我有一个列,它是一个DateTimeImmutable,当用户加入平台时保存。我正在进行phpunit测试,我想验证该列无法更新。我想在到达SQL错误之前检测错误(尝试更新列时出错)。

#[ORM\Column(updatable: false)]
    #[Assert\Type(
        type: 'object',
        message: 'The value {{ value }} is not a valid {{ type }}.'
    )]
    private ?\DateTimeImmutable $joinedAt = null;

我尝试更新实体列,然后调用$manager->persist($entity)来查看当时是否有错误,但什么也没有发生。

public function testInvalidJoinedAt(): void
    {
        $manager = static::getContainer()->get(EntityManagerInterface::class);

        // User joinedAt cannot be updated
        $now = new DateTimeImmutable();
        $existingUser = $this->getRepository(UserRepository::class)->find(0);
        $existingUser->setJoinedAt($now);

        try {
            $manager->persist($existingUser);
        } catch (\Throwable $err) {
            $this->assertEquals('???', $err->getMessage());
        }
    }

像“#[Assert\NotUpdatable]”这样的Assert类型将是完美的解决方案,但没有这样的东西。

o4tp2gmn

o4tp2gmn1#

您可以创建自己的自定义验证器在这里 checkout 文档enter link description here在您的src/Validator/Constraints中添加文件1)

class fileName extends Constraint
{
    public $message = 'Error Message Goes here for not modifd property';
}
class FileName extends ConstraintValidator
 {
     public function validate($value, Constraint $constraint)
     {
         if ($value !== null) {
         $this->context->buildViolation($constraint->message)->addViolation();
     }
   }
 }

1.您可以将自定义验证器添加到实体Assert中

/**
  * @ORM\Column(updatable=false)
  * @customValidator\validatorName
  */
 private ?\DateTimeImmutable $joinedAt = null;
11dmarpk

11dmarpk2#

Symfony Validator没有像#[Assert\NotUpdatable]这样的内置约束来处理这个特定用例。但是,您可以创建一个自定义验证约束来实现所需的功能。下面是您的操作方法:
1.创建自定义验证约束类:创建一个新的约束类,它将负责检查joinedAt属性是否正在更新。

// src/Validator/Constraints/NotUpdatable.php

namespace App\Validator\Constraints;

use Symfony\Component\Validator\Constraint;

/**
 * @Annotation
 */
class NotUpdatable extends Constraint
{
    public $message = 'The "{{ field }}" field cannot be updated.';
}

1.创建自定义验证器类:接下来,创建一个将执行验证的自定义验证器类。

// src/Validator/Constraints/NotUpdatableValidator.php

namespace App\Validator\Constraints;

use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

class NotUpdatableValidator extends ConstraintValidator
{
    private $doctrine;

    public function __construct(ManagerRegistry $doctrine)
    {
        $this->doctrine = $doctrine;
    }

    public function validate($value, Constraint $constraint)
    {
        $entity = $this->context->getObject();

        // Check if the entity is managed by Doctrine (already in the database)
        if ($this->doctrine->getManager()->contains($entity)) {
            $originalEntity = $this->doctrine->getManager()->getUnitOfWork()->getOriginalEntityData($entity);

            // Compare the original joinedAt value with the new one
            if ($originalEntity['joinedAt'] !== $value) {
                $this->context->buildViolation($constraint->message)
                    ->setParameter('{{ field }}', $this->context->getPropertyName())
                    ->addViolation();
            }
        }
    }
}

1.将自定义约束应用于实体属性:现在,将自定义NotUpdatable约束应用于实体类中的joinedAt属性:

// src/Entity/User.php

namespace App\Entity;

use App\Validator\Constraints as CustomAssert;
use Symfony\Component\Validator\Constraints as Assert;

class User
{
    // ...

    /**
     * @ORM\Column(updatable: false)
     * @Assert\Type(
     *     type='object',
     *     message='The value {{ value }} is not a valid {{ type }}.'
     * )
     * @CustomAssert\NotUpdatable
     */
    private ?\DateTimeImmutable $joinedAt = null;

    // ...
}

1.更新测试以触发验证:最后,您可以更新PHPUnit测试以触发验证,并确保joinedAt属性无法更新:

public function testInvalidJoinedAt(): void
{
    $manager = static::getContainer()->get(EntityManagerInterface::class);

    // User joinedAt cannot be updated
    $now = new DateTimeImmutable();
    $existingUser = $this->getRepository(UserRepository::class)->find(0);
    $existingUser->setJoinedAt($now);

    $validator = static::getContainer()->get('validator');
    $violations = $validator->validate($existingUser);

    $this->assertCount(1, $violations);
    $this->assertEquals('The "joinedAt" field cannot be updated.', $violations[0]->getMessage());
}

此测试将确保joinedAt属性无法更新,并将验证您创建的自定义约束。如果违反了约束,它将捕获违规行为,您可以如上所示Assert错误消息。

yeotifhr

yeotifhr3#

Doctrine不验证Symfony验证属性。您必须在保存实体之前使用Symfony验证器验证实体。
使用自定义验证器,您需要知道它是插入还是更新,因为Symfony验证器不知道您正在执行插入还是更新。这可能会创建这样的自定义验证器。但这是一个更大的努力。
我会根据你的方法testInvalidJoinedAt()解决它,所以你不必验证。

namespace App\Entity;

use Doctrine\ORM\Mapping\Column;

class Entity
{
    #[Column(updatable: false)]
    private ?\DateTimeImmutable $joinedAt = null;

    public function getJoinedAt(): ?\DateTimeImmutable
    {
        return $this->joinedAt;
    }

    public function setJoinedAt(?\DateTimeImmutable $joinedAt): void
    {
        if (!$this->joinedAt instanceof \DateTimeImmutable) {
            $this->joinedAt = $joinedAt;
        }
    }
}

单元测试示例

class EntityTest extends TestCase
{
    public function testNewEntity() {
        $dateTime = new DateTimeImmutable();
        $entity = new Entity();
        $entity->setJoinedAt($dateTime);
        $this->assertEquals($dateTime, $entity->getJoinedAt());
    }

    public function testEntityFromDatabase() {
        // Mock entity from database
        $dateTime = new DateTimeImmutable();
        $dateTime->setDate(2022, 9, 17)->setTime(19, 31, 41);
        $entityFromDatabase = new Entity();
        $entityFromDatabase->setJoinedAt($dateTime);

        // Set joinedAt update
        $entityFromDatabase->setJoinedAt(
            (new DateTimeImmutable())->setDate(2023, 10, 19)->setTime(8, 11, 15)
        );

        $this->assertEquals($dateTime, $entityFromDatabase->getJoinedAt());
    }
}

相关问题