Starx
Starx

Reputation: 79021

How to restrict modification to selected properties of an entity while persisting?

Let's suppose I have an entity class and a generic FormType corresponding to the entity

class Entry {
    protected $start_time;
    protected $end_time;
    protected $confirmed; // Boolean

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

On the CRUD of this entity, I don't want to allow any modification on start_time or end_time if the entity has been confirmed or in the above case when $confirmed === true

On the twig file, I disable the fields I want to restrict, like the following:

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

Now the problem is that this is a front end resolution which can be tampered very easily using web developer tools in web browsers now. But regardless what I am trying to achieve is not have those two fields changed once the entity in confirmed.

So, one way I tried was after the form was submitted, I check if the entity was confirmed and if it was was, I fetch the earlier state of the entity and set the value of the new one (which is about to be persisted) with the values from old one.

On Controller:

$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();
}

The problem here was $oldEntity was exactly same as $entity. My guess is doctrine picked up that it already has the entity that is being asked and just returned me with the same object. Anyways, my attempt to solve this problem failed.

Any idea how to restrict/revert changes on selected properties while allowing changes on rest of the properties of the entity?

Update:

Modifying the form type to disable the field is not an option because I only want them to be read-only/disabled only if entity is confirmed and rest of time I want the form to be as it is.

Upvotes: 2

Views: 174

Answers (3)

Starx
Starx

Reputation: 79021

I found two ways to solve this:

  1. You can retrieve original data of the entity. It returns an array with old data of an entity which can be used to reset the data.

    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. Using Form Events

    The following is a Symfony 3 Solution, try maximkou's answer for 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

Upvotes: 0

maximkou
maximkou

Reputation: 5332

You must add attribute 'disabled' => true in form builder, not only in twig.

If you don't want a user to modify the value of a field, you can set the disabled option to true. Any submitted value will be ignored.

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

If you wish modify dynamically, use form events, example:

$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
        );
    }
});

Upvotes: 1

Alvin Bunk
Alvin Bunk

Reputation: 7764

I think I understand your problem now @Starx, I didn't read very carefully at first, and your update helped.

Maybe you need to detach your Entity?

Check this link about (Entities in Session)[http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/cookbook/entities-in-session.html]. Maybe storing the Entity in a session will work. Detaching as a separate Entity might work, and do a comparison on the detached Entity to your updated Entity.

Upvotes: 0

Related Questions