Ограничение Symfony для предотвращения дублирования в перекрывающихся временных рамках

#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. Да, было бы лучше на уровне класса, поскольку мы рассматриваем три атрибута для предотвращения создания объекта