#mysql #symfony #doctrine-orm
#mysql #symfony #доктрина-orm
Вопрос:
Как я могу обеспечить уникальность значения в перекрывающемся диапазоне дат в Symfony, используя Doctrine ORM.
У меня есть следующий объект
<?php
/**
* @ORMEntity
* @ORMTable("tax_component")
*/
class TaxComponent
{
/**
* @ORMId()
* @ORMGeneratedValue()
* @ORMColumn(name="tax_component_id", type="integer")
*/
private ?int $id;
/**
* @ORMColumn(name="tax_component_name", type="string", length=20)
*/
private string $name;
/**
* @ORMColumn(name="tax_component_rate", type="integer")
* @AssertGreaterThanOrEqual(0)
*/
private int $rate;
/**
* @ORMColumn(name="tax_component_applicable_from", type="datetime_immutable")
*/
private DateTimeInterface $applicableFrom;
/**
* @ORMColumn(name="tax_component_applicable_to", type="datetime_immutable")
*/
public function __construct(string $name, int $rate, ?DateTimeImmutable $applicableFrom = null, ?DateTimeImmutable $applicableTo = null)
{
...
}
}
Я хочу сделать $name
уникальным в перекрывающихся временных рамках $applicableFrom
и $applicableTo
. Например,
$repository->save(
new TaxComponent('inter-state', 1800, new DateTime('2018-04-01:00:00:00'), new DateTime('2019-03-31T23:59:59'))
);
// The following should be allowed since there is no overlap between the two time ranges using the name 'inter-state'
$repository->save(
new TaxComponent('inter-state', 1200, new DateTime('2019-04-01:00:00:00'), new DateTime('2020-03-31T23:59:59'))
);
// The following should fail since 'inter-state' is ambiguous during the period 2019-09-01:00:00:00 to 2020-03-31T23:59:59
$repository->save(
new TaxComponent('inter-state', 1800, new DateTime('2019-09-01:00:00:00'), new DateTime('2020-09-31T23:59:59'))
);
Существует ли ограничение для принудительного применения этого Symfony?
Я планирую проверить существующие объекты изнутри, прежде чем звонить. TaxComponentRepository::save
$this->entityManager->persist
Есть ли лучшее решение?
Ответ №1:
Более чистым способом было бы создать собственное пользовательское утверждение.
Начнем с создания вашего ограничения :
<?php
namespace AppValidatorConstraints;
use SymfonyComponentValidatorConstraint;
/**
* @Annotation
*/
class TaxComponentConstraint extends Constraint
{
public $message = 'Another tax component overlap this one: {{ taxComponent}}';
public function getTargets()
{
return self::CLASS_CONSTRAINT;
}
public function validatedBy()
{
return 'AppValidatorConstraintsTaxComponentValidator';
}
}
И теперь вам нужно создать валидатор, который проверит, существует ли перекрытие с двумя налоговыми компонентами.
<?php
namespace AppValidatorConstraints;
use SymfonyComponentValidatorConstraint;
use SymfonyComponentValidatorConstraintValidator;
use SymfonyComponentValidatorExceptionUnexpectedTypeException;
class TaxComponentValidator extends ConstraintValidator
{
public function validate($taxComponentObject, Constraint $constraint)
{
//Check however you want if the tax component can be created (So no overlap between two existing TaxComponent)
if($overlappingTaxComponent){
$this->context->buildViolation($constraint->message)
->setParameter('{{ taxComponent }}', $overlappingTaxComponent->__toString())
->addViolation();
}
}
}
Здесь $overlappingTaxComponent — это TaxComponent, который не позволяет нам создавать его из-за вашего ограничения.
Если ограничение выполнено правильно, теперь вы можете легко использовать его в своей сущности, чтобы оно автоматически проверялось при отправке формы :
<?php
//...
use AppValidatorConstraints as CustomAssert;
/**
* @ORMEntity
* @ORMTable("tax_component")
* @CustomAssertTaxComponentConstraint
*/
class TaxComponent
{
/**
* @ORMId()
* @ORMGeneratedValue()
* @ORMColumn(name="tax_component_id", type="integer")
*/
private ?int $id;
/**
* @ORMColumn(name="tax_component_name", type="string", length=20)
*/
private string $name;
Комментарии:
1. Это должно быть ограничение класса, а не ограничение свойства, чтобы он мог получить доступ к другим членам класса, которые необходимо учитывать при проверке.
2. В противном случае вы могли бы показать, как получить доступ к валидатору
context
для получения других свойств, но я лично считаю, что это «лучше» на уровне класса.3. Да, было бы лучше на уровне класса, поскольку мы рассматриваем три атрибута для предотвращения создания объекта