第一次设置字段的时间戳

时间:2018-07-29 01:07:44

标签: symfony

我在实体上具有state(状态机值)属性,并根据该实体何时转换为给定状态来设置日期时间字段。

任务状态:(简体)

  • in_progress
  • 完整

在将任务设置为in_progress之后,它可以转换回complete,但是我想使用时间戳记来设置$dateStarted属性,仅当它第一次转换为{{1 }}。

此代码将始终将in_progress设置为上一次$dateStarted过渡到$state的时间。

是否可以配置它,以便仅在任务第一次转换为in_progress时设置$dateStarted

in_progress

2 个答案:

答案 0 :(得分:2)

像Fyrye所说,您可以使用带有功能的生命周期回调仅将日期设置一次。我这样使用它:

/**
 * @ORM\PrePersist
 * @ORM\PreUpdate
 */
public function setTheStartDate()
{
    if ($this->getState() === 'in_progress' && $this->getDateStarted === null) {
        $this->setDateStarted(new \DateTime('now'));
    }
}

还要确保在类上具有以下注释以启用生命周期回调:

* @ORM\HasLifecycleCallbacks()

答案 1 :(得分:1)

正如我在评论中提到的,Timestampable无法使用表达式或回调方法来评估更改后的值。虽然您可以使用生命周期回调来完成所需的结果。生命周期回调确实为您的实体增加了额外的开销和复杂性,这很难跟踪由它们引起的问题,并且在以后的代码扩展中变得更加乏味。 使用Timestampable或Lifecycle回调的一个示例;在您之后将对数据库的更改刷新到数据库之前,dateStarted才会设置。这将导致无法将其他业务逻辑中的dateStarted值引用到数据库中。

因此,建议您将业务逻辑放入setState方法中。这样做将强制执行所需的dateStarted分配规则。这也使您可以使用其他业务逻辑,例如确保状态不从new跳到complete,而无需先设置为in_progress等。

示例:https://3v4l.org/s9PJu

class MyEntity
{

    const STATE_NEW = 'new';
    const STATE_IN_PROGRESS = 'in_progress';
    const STATE_COMPLETE = 'complete';
    const CONFIG_STATES = array(
        self::STATE_NEW,
        self::STATE_IN_PROGRESS,
        self::STATE_COMPLETE
    );

    /**
     * @var \DateTime|null
     * @ORM\Column(type="datetime", nullable=true)
     */
    private $dateStarted;

    /**
     * @var string|null
     * @ORM\Column(type="string", nullable=true)
     */
    private $state = self::STATE_NEW;

    /**
     * @var string|null $state
     */
    public function setState($state = null)
    {
        $this->validateState($state);
        $this->updateStateDate($state);
        $this->state = $state;

        return $this;
    }

    public function getState()
    {
        return $this->state;
    }

    public function getDateStarted()
    {
        return $this->dateStarted;
    }

    /**
     * used to update the started date when the state changes to in_progress
     * @var string $state
     */
    protected function updateStateDate($state) 
    {
        if ($this->state !== $state && $state === self::STATE_IN_PROGRESS && $this->dateStarted === null) {
            $this->dateStarted = new \DateTime();
        }

        return $this;
    }

    protected function validateState($state)
    {
       if (!in_array($state, self::CONFIG_STATES, true)) {
            throw new \InvalidArgumentException(
                sprintf('Unknown state "%s". Expected one of: "%s"', 
                    $state, 
                    implode('", "', self::CONFIG_STATES)
                )
            );
        }
        if ($this->state === self::STATE_NEW && $state !== self::STATE_IN_PROGRESS) {
            throw new \InvalidArgumentException(
                sprintf('Invalid state "%s" specified, Expected: %s', 
                    $state, 
                    self::STATE_IN_PROGRESS
                )
            );
        }

        return $this;
    }
}

用法:

//default state = "new", dateStarted = null
$entity = new \MyEntity;

//state = "in_progress",  dateStarted = \DateTime('now')
$entity->setState(MyEntity::STATE_IN_PROGRESS); 
//NOTE - $entity->getDateStarted()  will return a value now without first using $em->flush();

//only changes state to "complete", leaves the previously assigned date
$entity->setState(MyEntity::STATE_COMPLETE);

//only changes state to "in_progress", leaves the previously assigned date
$entity->setState(MyEntity::STATE_IN_PROGRESS);

我还建议(如果尚未这样做的话)将您的state字段从字符串转换为关联的实体。然后,您可以删除STATE常量并在validateState中进行检查,因为外键约束将强制执行有效的状态分配。


最后一点,由于您使用的是Symfony框架,因此您可以选择使用Form validation constraints并将您的业务逻辑移动到那些约束/回调中,这些约束/回调将在提交表单并更改您的实体和显示相应的友好消息。