如何在持久化时限制对实体的选定属性的修改?

时间:2016-06-07 15:22:29

标签: php doctrine-orm symfony

假设我有一个实体类和一个与实体

对应的泛型FormType
class Entry {
    protected $start_time;
    protected $end_time;
    protected $confirmed; // Boolean

    // ... Other fields and getters and setters
}

在此实体的CRUD上,如果实体已被确认,我不希望允许对start_timeend_time进行任何修改,或者$confirmed === true

在twig文件中,我禁用了我想要限制的字段,如下所示:

{% if entity.approved == true %}
    {{ form_row(entity.start_time), { 'attr' : { 'disabled' : 'disabled' } }) }}
{% endif %}
{# Sameway for another field #}

现在的问题是,这是一个前端分辨率,现在可以使用Web浏览器中的Web开发人员工具轻松篡改。但无论我想要实现什么,一旦确认实体,就不会改变这两个字段。

所以,我试过的一种方法是在提交表单之后,我检查实体是否已经确认,如果是,我获取实体的早期状态并设置新实体的值(即将到来)坚持使用旧的价值观。

在控制器上:

$confirmed = $entity->getConfirmed();
$form->handleRequest($request);

if($form->isSubmitted() && $editForm->isValid()) {
    // The form was submitted
    if($confirmed === true) { // if the entity was confirmed previously
        $oldEntity = $em->getRepository('...')->find($entity->getId());
        $entity->setStartTime($oldEntity->getStartTime());
        $entity->setEndTime($oldEntity->getEndTime());
    }
    $em->persist($entity);
    $em->flush();
}

此处的问题是$oldEntity$entity完全相同。我的猜测是学说它已经有了被要求的实体并且刚刚给我回复了相同的对象。无论如何,我解决这个问题的尝试失败了。

知道如何限制/恢复所选属性的更改,同时允许更改实体的其余属性吗?

  

更新

修改表单类型以禁用该字段不是一个选项,因为我只希望它们只有在确认实体时才是只读/禁用,剩下的时间我希望表单不变。

3 个答案:

答案 0 :(得分:1)

您必须在表单构建器中添加属性'disabled' => true,而不仅仅是在树枝中。

  

如果您不希望用户修改字段的值,可以将disabled选项设置为true。任何提交的值都将被忽略。

参考:http://symfony.com/doc/current/reference/forms/types/form.html#disabled

如果您希望动态修改,请使用表单事件,例如:

$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
    $form = $event->getForm();
    $entity = $event->getData();

    // if confirmed, disable start_time field
     if ($entity->getConfirmed()) {
        $config = $form->get('start_time')->getConfig();
        $options = $config->getOptions();

        // set disabled option to true
        $options['disabled'] = true;
        // replace origin field
        $form->add(
            'start_time',
            $config->getType()->getName(),
            $options
        );
    }
});

答案 1 :(得分:0)

我想我现在理解你的问题@Starx,我一开始并没有仔细阅读,你的更新也有所帮助。

也许您需要分离您的实体?

查看此链接(会话中的实体)[http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/cookbook/entities-in-session.html]。也许将实体存储在会话中会起作用。作为单独的实体分离可能会起作用,并将分离的实体与更新的实体进行比较。

答案 2 :(得分:0)

我找到了两种方法来解决这个问题:

  1. 您可以检索实体的原始数据。它返回一个数组,其中包含一个实体的旧数据,可用于重置数据。

    if($form->isSubmitted() && $editForm->isValid()) {
        // The form was submitted
        if($confirmed === true) { // if the entity was confirmed previously
            $oldData = $em->getUnitOfWork()->getOriginalEntityData($entity);
            $entity->setStartTime($oldData['start_time']);
            $entity->setEndTime($oldData['end_time']);
        }
        $em->persist($entity);
        $em->flush();
    }
    
  2. 使用表单事件

    以下是Symfony 3解决方案,请尝试使用maximkou的answer进行Symfony 2。

    class EntityType extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            // ....
            // ....
            // Your form fields
    
            $builder->addEventListener(FormEvents::POST_SET_DATA, array($this, 'onPreSetData'));
        }
    
    
        public function onPreSetData(FormEvent $event) {
            /** @var YourEntity $entity */
            $entity = $event->getData();
            $form = $event->getForm();
    
            if($entity instanceof YourEntity) {
                if ($entity->getTimesheetEntry()->getTimeApproved() === true) {
                    $config = $form->get('start_time')->getConfig();
                    $options = $config->getOptions();
                    $options['disabled'] = true;
                    $form->add('start_time', get_class($config->getType()->getInnerType()), $options);
    
                    $config = $form->get('end_time')->getConfig();
                    $options = $config->getOptions();
                    $options['disabled'] = true;
                    $form->add('end_time', get_class($config->getType()->getInnerType()), $options);
                }
            }
    
        }
    }
    

    Source