1
votes

Using: Symfony 2.5 SonataAdminBundle

I am trying to change one of the entity fields (title) when data is submitted / saved to database by using two fields from associated entites ex.

DocumentRevision <- Document -> CustomEntity [title] = Document[title]+DocumentRevision[number]

But title of CustomEntity has to be unique - this was the problem I was trying to solve and managed with Database constraints and UniqueEntity validation (not quite - more on this later).

Now the issue is that I change the title data on Doctrine preUpdate/Persist effectivly skipping validation for that field since it's empty at validation time. When user puts wrong data Database layer throws an error about duplicate for unique constraint.

/**
* @ORM\PrePersist
* @ORM\PreUpdate
*/
public function setTitleFromDocumentName() {
    $this->setTitle($this->getDocument()->getName() . " rev. " . $this->getDocumentRevision()->getRevisionNumber());
}

The entity itself is using UniqueEntity constraint on field title, so custom constraints or validation groups are pointles from my perspective as it would only duplicate the already used constraint.

/**
 * @UniqueEntity(
 *      fields={"title"}
 * )
**/

The simplest solution as it seems would be to get somewhere between post Submit before validation, but it would have to be done from Entity.

My question is how can (can it?) be done without overriding SonataCRUD Controller or it's other parts, is it even possible?

It can be done, but there are issues:

I was able to change the title using Form Events like this:

protected function configureFormFields(FormMapper $formMapper) {
...
$builder = $formMapper->getFormBuilder();
    $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
        $data = $event->getData();
        $form = $event->getForm();

        if (!$data) {
            return;
        }

        $data['title'] = $data['document'] . ' rev. ' . $data['documentRevision'];
        $event->setData($data);
    }
...
formMapper
        ->add('title',null,array(
...
);

The current problem is that I am getting the IDs of 'document' and 'documentRevision' and I need their names or __toString() representation at least.

Another issue is that although I can set the title using the event it shows error from DB when it should show Form error since validation should be done on FormEvents::SUBMIT - this one I don't understand.

Last thing to note is that if I try to use callback function:

$builder->addEventListener(FormEvents::PRE_SUBMIT, array($this,'onPreSubmit'))

public function onPreSubmit() {
    $entity = $this->getSubject();
    $entity->setTitleFromDocumentName();
}

I will get null title and errors if Entity tries to get fields from related entites - Calling function on non object.

2

2 Answers

0
votes

Regarding entity data maybe this will help you to get the subject: https://gist.github.com/webdevilopers/fef9e296e77bb879d138

Then you could use getters to get the desired data for instance:

protected function configureFormFields(FormMapper $formMapper)
{
    $subject = $this->getSubject();

    $formMapper->getFormBuilder()->addEventListener(FormEvents::PRE_SET_DATA,
        function (FormEvent $event) use ($subject) { 
            $document = $subject->getDocument();

            // ...
    });
}

I also posted this on your issue: https://github.com/sonata-project/SonataAdminBundle/issues/2273

0
votes

To solved this when I changed the unique entity validation constraints as ones used by me where not completely valid from conceptual perspective.

Also it's important to note that functions that are marked as @PrePersist, @PreUpdate etc. must be public if they are to be used like that, marking them private will make Doctrine fail.

Note that the methods set as lifecycle callbacks need to be public and, when using these annotations, you have to apply the @HasLifecycleCallbacks marker annotation on the entity class.

See: http://doctrine-orm.readthedocs.org/en/latest/reference/events.html#lifecycle-callbacks (first paragraph after the code sample).