提问人:Naico04 提问时间:9/12/2023 更新时间:9/13/2023 访问量:75
如何使用symfony/validator验证不可更新的列?
How to validate a not updatable column with symfony/validator?
问:
我有一列,它是 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]”这样的断言类型将是完美的解决方案,但事实并非如此。
答:
2赞
Chafik Kerboute
9/13/2023
#1
您可以创建自己的自定义验证器,查看此处的文档,在此处输入链接描述,在您的 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();
}
}
}
您可以将自定义验证程序添加到实体断言中
/** * @ORM\Column(updatable=false) * @customValidator\validatorName */ private ?\DateTimeImmutable $joinedAt = null;
-1赞
Bademeister
9/13/2023
#2
Doctrine 不验证 Symfony Validation 属性。在保存实体之前,您必须使用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;
}
}
}
示例 UnitTest
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());
}
}
评论
0赞
Naico04
9/13/2023
我认为这是有效的,但这并不完全是我要搜索的。谢谢你的回答!
1赞
kasali
9/13/2023
#3
Symfony Validator没有像#[Assert\NotUpdatable]这样的内置约束来处理这个特定的用例。但是,您可以创建自定义验证约束来实现所需的功能。这是你如何做到的:
- 创建自定义验证约束类: 创建一个新的约束类,该类将负责检查是否正在更新 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.';
}
- 创建自定义验证程序类: 接下来,创建一个将执行验证的自定义验证程序类。
// 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();
}
}
}
}
- 将自定义约束应用于实体属性: 现在,将自定义 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;
// ...
}
- 更新测试以触发验证: 最后,您可以更新 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 属性,并将验证已创建的自定义约束。如果违反了约束,它将捕获冲突,您可以断言错误消息,如上所示。
评论
0赞
Naico04
9/13/2023
我不得不稍微修改一下代码。我不知道为什么,但是->getManager()->contains($entity)和->getManager()->getUnitOfWork()->getOriginalEntityData($entity)无法正常工作。但是,逻辑很清楚,我已经设法适应了它。
0赞
kasali
9/13/2023
好的,你有什么错误?
评论