4

This is a follow on from the earlier question Symfony2 Custom Form Type or Extension

I am trying to attach a custom field type for Product on an Order. The name field will contain the product name and the id field the product id.

I am using FormEvents::PRE_SET_DATA to try and populate the data but it throws an error, getData() returns Form\Type\ProductAutoCompleteType.

How do I correct the code?

OrderType has the following:

    $builder->add('product', new Type\ProductAutoCompleteType(), array(
        'data_class' => 'Acme\TestBundle\Entity\Product'
    ));        

ProductAutoCompleteType:

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

Updated

Error: FatalErrorException: Error: Call to a member function getProduct() on a non-object in /var/www/symblog/src/Acme/TestBundle/Form/Type/ProductAutoCompleteType.php line 26

Controller

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

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

Updated 2

After changing the field [product][id] and submitting the form I now get the following error, I assume this is because the field name is [product][id] and not [product]?

Neither the property "id" nor one of the methods "setId()", "set()" or "__call()" exist and have public access in class "Proxies__CG\Acme\TestBundle\Entity\Product".

Updated 3

I have it submitting data to the controller now, in my controller on submit I have had to add the following, it looks messy to do validation and attach the product this way, is this the right way?

        $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'));
            }
        }
10
  • What's the error thrown? Commented Jun 6, 2014 at 14:34
  • Added error to question in updated. Commented Jun 6, 2014 at 15:01
  • When you create your form in your controller. Are you passing an Order Entity? Commented Jun 6, 2014 at 15:13
  • Yes, I've added the code to the question as well. Commented Jun 6, 2014 at 15:21
  • Since this event is PRE_SET_DATA, I think you don't have the $order in there just yet. Try using the POST_SET_DATA event for this case. (I do hope I am not pushing you into a completely wrong direction when getting you implement a form listener. Could be that I don't see your complete use case.) Commented Jun 6, 2014 at 15:32

1 Answer 1

0

Putting all your logic into the controller is not a good practice. Your forms should handle as much of this as possible to ensure reusability of these forms.

If the Order that you query does not have a related product in it, then it's normal that $form->getData() returns null.

Debugging tip:

Output the value of $form->getData() to inspect the Product object. Output the value of $form->getParent()->getData() to inspect the Order object from the parent form.

Validation tip:

In case a product in this form can be null, make sure that you only set the name field's data if the product is not null.

Query tip:

Since the findBy method on the repository will not automatically include the related product, I recommend using a QueryBuilder and make a join. Example in the controller:

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

Of course, having so much code in your controller is not great either, so once you get your stuff working, create a custom repository with a custom method and move this code there.

In your entity:

/**
 * @Entity(repositoryClass="Acme\TestBundle\Repository\OrderRepository")
 */
class Order {

In your repository

class OrderRepository {
  public function findByIdWithProduct() {
    // $order = $this->createQueryBuilder...
    return $order;
  }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.