您可以尝试使用专为此目的设计的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));
});