Symfony2表单,其中数据对象与需要填写的内容完全不匹配

时间:2011-10-04 12:47:58

标签: forms validation symfony

我们有监控服务,我们的监控单元会关注某些机器。

我正在创建一个表单来在Symfony2中注册一台新机器。

所以我们有机器实体

  • ID
  • 机器名称
  • 监视器ID

监控实体

  • ID
  • SERIALNUMBER
  • ...

对于新机器,客户需要填写表单

  • 机器名称
  • 附加监视器的序列号
  • ...

现在,如果我用一个机器实体作为数据支持来制作一个表单,我没有要求序列号的“字段”。 Symfony不允许它,因为后台实体没有名为“序列号”的字段。

我怎么能:

  • 在后备实体中没有此类字段时询问序列号
  • 如果我收到序列号,如何在验证并绑定数据后,如何将其与内部ID链接以与实体保持一致

我想我可以:

  • 制作一个没有背后的对象的表单,正如@weaverryan在这篇优秀的文章中所描述的那样:http://knplabs.com/en/blog-csi/symfony-validators-standalone - 这样我自己做了持久化,但我需要为自定义表单和我的机器单独设置约束实体,这很可惜。
  • 提供某种链接,以便Symfony知道从哪里获得序列号字段存在的事实,以及它的约束是什么。也许通过定义关系?我希望在我的实体代码中避免关系,因为我在邮件列表上看到了很多问题,但也许我别无选择: - )
  • 其他什么?

我希望我能解释这一点。我想很多人都必须解决这个问题。我想我只是在查看一些非常标准的Symfony功能,因为我不确定它叫什么: - )

2 个答案:

答案 0 :(得分:5)

首先,你使用关系 - 即机器有一个监控。我认为人们对此有疑问,因为当你习惯于用“id”和外键来思考事物时,以完全面向对象的方式思考关系是新的:)。

因此,假设您的计算机与Monitor相关,您现在可以创建一个嵌入MonitorType的MachineType(它将是一个包含serialnumber字段的表单)。然后,在提交复合表单时,将使用Monitor类上的serialnumber约束。

默认情况下,绑定所有这些内容时,它将创建一个新的Machine对象,并通过$ machine-> getMonitor()提供新的Monitor对象。如果你坚持,这将是两个插入。但是,我猜你宁愿用它的序列号查找监视器并使用现有的监视器,对吗?这样做很简单,只需在绑定表单后短路即可。

$form = new MachineType();
$form->bindRequest($request);
if ($form->isValid()) {
    $em = $this->getDoctrine()->getEntityManager();
    $machine = $form->getData();
    $serialNumber = $machine->getMonitor->getSerialNumber();

    $existingMonitor = $em
        ->getRepository('YourBundle:Monitor')
        ->findOneBy(array('serialNumber' => $serialNumber));
    if ($existingMonitory) {
        $machine->setMonitor($existingMonitor);
    }

    $em->persist($machine);
    $em->persist($machine->getMonitor());
    $em->flush();

    // ...
}

所以,这就是主意。最重要的是,通过不使用关系,你会让自己处于劣势。你看,通过将这两个表单嵌入到一起,你真的可以自然地访问序列号字段。

祝你好运!

答案 1 :(得分:4)

我最近解决了这个问题,创建了一个自定义表单类型(在我的情况下称为“选择器”),它附有一个datatransformer,它通过serial返回监视器:

机器类型:

class MachineType extends AbstractType {
    public function buildForm(FormBuilder $builder, array $options) {
        $builder->add('name');
        $builder->add('monitor', 'monitor_selector_type');
    }

    public function getName() {
        return 'machine;
    }
}

监视器选择器类型(最终这将只是一个文本字段(默认类型为父“text”),您将在其中输入监视器序列。如果输入了非现有序列,该字段将显示错误。

class MonitorSelectorType extends AbstractType {
    private $doctrine

    public function __construct(Registry $doctrine) {
        $this->doctrine = $doctrine;
    }

    public function buildForm(FormBuilder $builder, array $options) {
        $transformer = new MonitorToSerialTransformer($this->doctrine->getEntityManager());
        $builder->appendClientTransformer($transformer);
    }

    public function getDefaultOptions(array $options) {
        return array(
            'invalid_message'=>'The selected monitor does not exist',
            'error_bubbling'=>false
        );
    }

    public function getName() {
        return 'machine;
    }
}

此类将串行转换为实际监视器,将实际监视器转换为串行

class MonitorToSerialTransformer {
    private $em

    public function __construct(EntityManager $em) {
        $this->em = $em;
    }

    public function transform($val) {
        if (null === $val) {
            return '';
        }
        return $val->getSerial();
    }

    public function reverseTransform($val) {
        if (!$val) {
            return null;
        }
        $monitor = $this->em->getRepository('xBundle:Monitor')->findOneBy(array('serial' => $val));
        if (null === $monitor) {
            throw new TransformationFailedException('A monitor with serial '.$val.' does not exist!');
        }
        return $monitor;
    }
}

最后,在服务容器中注册MonitorSelectorType,其id为“monitor_selector_type”,并标记为“form.type”,并为其注入了学说。

现在,您可以在应用程序的任何随机位置轻松使用此选择器类型按ID选择监视器。根本不需要将逻辑添加到控制器中。

如果您希望在输入未知序列时创建新监视器,则可以实例化它而不是抛出TransformationFailedException,如果计算机没有监视器的附加选项,甚至可以将其保留到实体管理器。

此致 巴特