Change data of form field after submit

10,862

Solution 1

You could try using a Data Transformer which is designed for this purpose. For example:

namespace Vendor\MyBundle\Form\DataTransformer;

use Symfony\Component\Form\DataTransformerInterface;

class HostFromUrlTransformer implements DataTransformerInterface
{
    public function transform($host)
    {
        return ($host === null) ? "" : $host;
    }

    public function reverseTransform($url)
    {
        return parse_url($url, PHP_URL_HOST);
    }
}

Then implementing in the form:

use Vendor\MyBundle\Form\DataTransformer\HostFromUrlTransformer;

class MessageType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add(
            $builder->create('url')
                ->addModelTransformer(new HostFromUrlTransformer())
        );
    }
}

This method definitely has its faults, however. You would not be able to add an Assert\URL on the entity because the validation happens after the transformations, and the just having the host portion would no longer make it valid. You could work around this by passing in the Symfony validator to your data transformer, then validating the $url against Assert\Url similar to a method here: Combine constraints and data transformers , but that also feels a bit hacky.

An easier solution than all of this would simply be to update the setUrl($url) method in your entity to something like this:

public function setUrl($url)
{
    $host = parse_url($url, PHP_URL_HOST);
    $this->url = $host ?: $url;
}

So in that case, if you are passing in a URL it saves as just the $host, and if you are using setUrl() and it's already transformed, it would just keep the already-existing value.

Even better still, you could simply save the full URL they send you, which would allow you to maintain the @Assert\URL validation, and then add the following two functions:

public function getHost()
{
    return parse_url($this->url, PHP_URL_HOST);
}

public function __toString()
{
    return $this->getHost();
}

The above allows you to keep all data that was saved, and changes the work of parsing the URL to be done by your application, rather than try to parse out in the database layer. The __toString() method will always return the host, and you still have access to full URL that was passed in if you need it. You could even get rid of the getHost() function above.

If you want to use a FormEvent, you are on the right track but the code is a little off. See http://symfony.com/doc/current/components/form/form_events.html#b-the-formevents-submit-event for more details.

$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event)
{
    $data = $event->getData();
    $data['url'] = parse_url($data['url'], PHP_URL_HOST);

    $event->setData($data);

    // this one-liner might also work in place of the 3 lines above
    $event->setData('url', parse_url($event->getData('url'), PHP_URL_HOST));
});

Solution 2

According to PHP doc for parse_url() :

If the component parameter is omitted, an associative array is returned.

http://php.net/manual/en/function.parse-url.php

Looks like you're not properly passing the component parameter in your code, there's a misplaced parenthesis :

$form->setData(parse_url($url), PHP_URL_HOST);

Should be:

$form->setData(parse_url($url, PHP_URL_HOST));

Also, I'm not sure you're setting the data like you should but I may be wrong. This is how I think you should do it :

$form->get('url')->setData(parse_url($url, PHP_URL_HOST));

Here's the complete (and untested) code I was thinking of :

class MessageType extends AbstractType
{

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('url'); 

         $builder->addEventListener(
                FormEvents::SUBMIT,
                function(FormEvent $event) {
                    $form = $event->getForm(); // FormInterface
                    $data = $event->getData(); 
                    $url = $data->getUrl();
                    $host = parse_url($url, PHP_URL_HOST);
                    if ($host)
                        $form->get('url')->setData($host);
                }
        );
    }
}
Share:
10,862
Antin
Author by

Antin

Updated on June 05, 2022

Comments

  • Antin
    Antin almost 2 years

    I have Message entity with field 'url'. And MessageType form. User can type different urls in 'url' but only host is persisted to the database. I need to use Symfony2 Form Event. So I try to implement it in next code:

    class MessageType extends AbstractType
    {
    
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder->add('url'); 
    
             $builder->addEventListener(
                    FormEvents::SUBMIT,
                    function(FormEvent $event) {
                        $form = $event->getForm();
    
                        $data = $event->getData();
                        $url = $data->getUrl();
                        $form->setData(parse_url($url), PHP_URL_HOST);
                    }
            );
        }
    }
    

    I get the following notice:

    "Notice: Array to string conversion"

    What does this mean?

    My Message entity has method __toString():

    public function __toString()
    {
        return $this->getUrl();
    }
    

    Thank You