Symfony2自定义字段类型:从值填充中创建

4
这是之前问题的延续 Symfony2 Custom Form Type or Extension 我正在尝试将自定义字段类型“Product”附加到订单上。名称字段将包含产品名称,而ID字段将包含产品ID。
我使用FormEvents :: PRE_SET_DATA尝试填充数据,但会抛出一个错误,getData()返回Form\Type\ProductAutoCompleteType。
我该如何纠正代码?
OrderType拥有以下内容:
    $builder->add('product', new Type\ProductAutoCompleteType(), array(
        'data_class' => 'Acme\TestBundle\Entity\Product'
    ));        

产品自动完成类型:

class ProductAutoCompleteType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options) {
        $builder
            ->add('name');
        $builder
            ->add('id');

    /* Turns out this is not needed any more
    $builder->addEventListener(
        FormEvents::PRE_SET_DATA, function (FormEvent $event) {
        $form = $event->getForm();
        $product = $form->getData();
        $form
            ->add('name', 'text', array('mapped' => false, 'data' => $product->getName()));
    }
    );  
    *//

    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
    }

    public function getParent()
    {
        return 'form';
    }

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

更新

错误:FatalErrorException:错误:在/var/www/symblog/src/Acme/TestBundle/Form/Type/ProductAutoCompleteType.php的第26行中调用非对象的getProduct()成员函数。

控制器

    $em = $this->getDoctrine()->getManager();
    $order = $em->getRepository('AcmeTestBundle:Order')->find(6);

    $form = $this->createForm(new OrderType(), $order);

更新2

在更改字段[product][id]并提交表单后,我现在收到以下错误信息,我认为这是因为字段名称是[product][id]而不是[product]导致的?

在类“Proxies__CG\Acme\TestBundle\Entity\Product”中,属性"id"和方法"setId()"、"set()"或"__call()"中没有一个存在且具有公共访问权限。

更新3

现在我已经成功将数据提交到控制器,在我的控制器上提交时,我不得不添加以下内容,以这种方式进行验证和附加产品看起来很混乱,这是正确的方法吗?

        $data = $request->request->get($form->getName());
        if ($data['product']['id']) {
            $product = $em->getRepository('AcmeTestBundle:Product')->find($data['product']['id']);
            if ($product) {
                if ($product->getShop()->getId() != $order->getShop()->getId()) {
                    $form->get('product')->get('name')->addError(new FormError('Invalid shop product'));
                }
                $form->getData()->setProduct($product);
            } else {
                $form->get('product')->get('name')->addError(new FormError('A product must be selected'));
            }
        }

在更新中,问题已添加错误。 - lookbadgers
当您在控制器中创建表单时,是否传递了一个“订单实体”? - Debreczeni András
是的,我已经将代码添加到问题中了。 - lookbadgers
由于此事件为PRE_SET_DATA,我认为您还没有在其中使用$order。尝试使用POST_SET_DATA事件来处理这种情况。(我希望在让您实现表单监听器时不会将您推向完全错误的方向。可能是我没有看到您完整的用例。) - Debreczeni András
我只是想熟悉Symfony表单,并了解它们的功能。 切换到POST仍然意味着getData()返回ProductAutoCompleteType。我试图做的是创建一个可以重复使用并具有自动完成的ProductField。 - lookbadgers
显示剩余5条评论
1个回答

0

将所有逻辑放入控制器并不是一个好的实践。您的表单应该尽可能地处理这些内容,以确保这些表单的可重用性。

如果您查询的订单中没有相关产品,则$form->getData()返回null是正常的。

调试提示:

输出$form->getData()的值以检查产品对象。 输出$form->getParent()->getData()的值以检查父表单中的订单对象。

验证提示:

如果此表单中的产品可以为null,请确保仅在产品不为null时设置名称字段的数据。

查询提示:

由于存储库上的findBy方法不会自动包含相关的product,因此建议使用QueryBuilder并进行连接。以下是控制器中的示例:

$order = $em->getRepository('AcmeTestBundle:Order')
  ->createQueryBuilder('order')
  ->select('order, product')
  ->innerJoin('order.product', 'product')
  ->where('order.id = :order_id')
  ->setParameter('order_id', $order_id)
  ->getQuery()
  ->getOneOrNullResult();

当然,在控制器中有太多的代码也不是很好,所以一旦你让你的东西工作起来了,就创建一个自定义存储库和自定义方法,并将这些代码移动到那里。
在你的实体中:
/**
 * @Entity(repositoryClass="Acme\TestBundle\Repository\OrderRepository")
 */
class Order {

在你的代码库里
class OrderRepository {
  public function findByIdWithProduct() {
    // $order = $this->createQueryBuilder...
    return $order;
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接