提交后更改表单字段的数据

时间:2014-12-19 21:23:08

标签: php forms symfony

我有字段'url'的Message实体。和MessageType表单。用户可以在“url”中键入不同的URL,但只有主机持久保存到数据库中。我需要使用Symfony2 Form Event。所以我尝试在下一个代码中实现它:

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

我收到以下通知:

  

“注意:数组转换为字符串”

这是什么意思?

我的消息实体有方法__toString()

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

谢谢

2 个答案:

答案 0 :(得分:5)

您可以尝试使用专为此目的而设计的Data Transformer。例如:

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

然后以表格形式实施:

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

然而,这种方法确实存在缺陷。您将无法在实体上添加Assert\URL,因为验证在转换后发生,并且只是让主机部分不再使其有效。你可以通过将Symfony验证器传递给你的数据转换器来解决这个问题,然后验证$ Assert\Url的$ url,类似于这里的方法:Combine constraints and data transformers,但这也感觉有点hacky。

比所有这些更简单的解决方案只是将实体中的setUrl($url)方法更新为:

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

因此,在这种情况下,如果您传入一个URL,它只会保存为$ host,如果您使用的是setUrl()且它已经被转换,它只会保留已存在的值。

更好的是,您可以直接保存他们发送给您的完整网址,这样您就可以维护@Assert\URL验证,然后添加以下两个功能:

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

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

以上允许您保留所有已保存的数据,并更改解析URL的工作由您的应用程序完成,而不是尝试在数据库层中解析。 __toString()方法将始终返回主机,并且您仍然可以访问在需要时传入的完整URL。你甚至可以摆脱上面的getHost()函数。

如果你想使用FormEvent,你就在正确的轨道上,但代码有点偏。有关详细信息,请参阅http://symfony.com/doc/current/components/form/form_events.html#b-the-formevents-submit-event

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

答案 1 :(得分:1)

根据parse_url()的PHP文档:

  

如果省略组件参数,则为关联数组   返回。

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

看起来你没有在你的代码中正确传递组件参数,有一个错位的括号:

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

应该是:

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

另外,我不确定你是否按照自己的意愿设置数据,但我可能错了。这就是我认为你应该这样做的方式:

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

这是我想到的完整(和未经测试)的代码:

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