【问题标题】:Change data of form field after submit提交后更改表单域的数据
【发布时间】:2015-02-18 20:43:10
【问题描述】:

我有带有“url”字段的消息实体。和 MessageType 形式。用户可以在“url”中键入不同的 url,但只有主机被持久化到数据库中。我需要使用 Symfony2 表单事件。所以我尝试在下一个代码中实现它:

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

谢谢

【问题讨论】:

    标签: php forms symfony


    【解决方案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);
                    }
            );
        }
    }
    

    【讨论】:

    • 参数很好,我第一次看问题时错过了。
    • @paulgv 如果对您来说不难,请演示代码示例如何更改表单数据。我尝试了不同的方法,但完整的 url 被保存到我的数据库中
    • 我的意思是 paulgv 注意到您传递的参数错误。您将PHP_URL_HOST 传递给setData() 函数而不是parse_url 函数,他还注意到您缺少定义要修改的参数。
    • @Jason Roman 你能帮我理解如何在持久化到数据库之前修改 uri 字段吗?我阅读了关于表单事件的 symfony 食谱,但我无法为我的任务实施它......
    • 我会使用数据转换器。我很快就会发布一个例子。
    【解决方案2】:

    您可以尝试使用专为此目的设计的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() 并且它已经被转换,它只会保留已经存在的值。

    更好的是,您可以简单地保存他们发送给您的完整 URL,这样您就可以维护 @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));
    });
    

    【讨论】:

    • 谢谢!有用!我还为此任务实施了 PrePersist 事件。但接下来我想明白。如何在持久化之前更改表单数据,然后使用 FormEvents 将结果持久化到数据库中。你能帮帮我吗?
    • 你尝试了我上面的哪种方法? PrePersist 也可以工作,但请记住,这只是用于初始插入而不是任何更新。你想在 PrePersist 中完成什么任务?你的意思是通过它而不是我上面的其他方法来做?
    • 对不起,我以前没有写过。我的意思是在代码公共函数 buildForm(FormBuilderInterface $builder, array $options) { $builder->add('url'); $builder->addEventListener( TO DO ) 中实现 FormEvents 之一
    • 那么您还有什么要问的吗?如果您使用我上面的任何方法(或 PrePersist),那么您不需要表单事件。
    • 我不明白。但对我来说,了解如何使用 FormEvent 执行任务很重要)))
    猜你喜欢
    • 2012-03-03
    • 1970-01-01
    • 1970-01-01
    • 2017-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-25
    相关资源
    最近更新 更多