Symfony forms (as standalone component with Doctrine) EntityType not working
Believe me, you're asking for trouble!
EntityType::class
works when it is seamsly integrated to "Symfony" framework (there's magic under the hoods - via DoctrineBundle). Otherwise, you need to write a lot of code for it to work properly.
Not worth the effort!
It's a lot easier if you to create an entity repository and inject it in form constructor, then use in a ChoiceType::class
field. Somethink like this:
<?php
# you form class
namespace Application\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class InvoiceItemtType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('product', ChoiceType::class, [
'choices' => $this->loadProducts($options['products'])
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['products' => [],]); # custom form option
}
private function loadProducts($productsCollection)
{
# custom logic here (if any)
}
}
And somewhere in application:
$repo = $entityManager->getRepository(Product::class);
$formOptions = ['products' => $repo->findAll()];
$formFactory = Forms::createFormFactory();
$formFactory->create(InvoiceItemtType::class, new InvoiceItem, $formOptions);
That's the point!
Expanding on the answer by xabbuh.
I was able to implement EntityType
in the FormBuilder
without too much extra work. However it does not work with the annotations in order to use Constraints
directly inside the entity, which would require a lot more work.
You can easily facilitate the ManagerRegistry
requirement of the Doctrine ORM Forms Extension, by extending the existing AbstractManagerRegistry
and making your own container property within the custom ManagerRegistry
.
Then it's just a matter of registering the Form extension just like any other extension (ValidatorExtension
, HttpFoundationExtension
, etc).
The ManagerRegistry
use \Doctrine\Common\Persistence\AbstractManagerRegistry;
class ManagerRegistry extends AbstractManagerRegistry
{
/**
* @var array
*/
protected $container = [];
public function __construct($name, array $connections, array $managers, $defaultConnection, $defaultManager, $proxyInterfaceName)
{
$this->container = $managers;
parent::__construct($name, $connections, array_keys($managers), $defaultConnection, $defaultManager, $proxyInterfaceName);
}
protected function getService($name)
{
return $this->container[$name];
//alternatively supply the entity manager here instead
}
protected function resetService($name)
{
//unset($this->container[$name]);
return; //don't want to lose the manager
}
public function getAliasNamespace($alias)
{
throw new \BadMethodCallException('Namespace aliases not supported');
}
}
Create the Form
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('field_name', EntityType::class, [
'class' => YourEntity::class,
'choice_label' => 'id'
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['data_class' => YourAssociatedEntity::class]);
}
}
Configure the Form Builder to use the extension and use the Form
$managerRegistry = new \ManagerRegistry('default', [], ['default' => $entityManager], null, 'default', 'Doctrine\\ORM\\Proxy\\Proxy');
$extension = new \Symfony\Bridge\Doctrine\Form\DoctrineOrmExtension($managerRegistry);
$formBuilder = \Symfony\Component\Form\FormFactoryBuilder::createFormFactoryBuilder();
$formBuilder->addExtension($extension);
$formFactory = $formBuilder->getFormFactory();
$form = $formFactory->create(new \UserType, $data, $options);
The above is intended for demonstration purposes only! While it does function, it is considered best practice to avoid using Doctrine Entities inside of Forms. Use a [DTO (Data Transfer Object) instead.
Doctrine 2.5+ "NEW" Operator Syntax
class CustomerDTO
{
public function __construct($name, $email, $city, $value = null)
{
// Bind values to the object properties.
}
}
$query = $em->createQuery('SELECT NEW CustomerDTO(c.name, e.email, a.city) FROM Customer c JOIN c.email e JOIN c.address a');
$users = $query->getResult(); // array of CustomerDTO