Symfony 表单(作为带有 Doctrine 的独立组件)EntityType 不工作
Symfony forms (as standalone component with Doctrine) EntityType not working
我使用的是没有 Symfony 框架其余部分的 Symfony 表单 (v3.0)。使用 Doctrine v2.5.
我创建了一个表单,这里是表单类型 class:
class CreateMyEntityForm extends BaseFormType {
public function buildForm(FormBuilderInterface $builder, array $options){
$builder->add('myEntity', EntityType::class);
}
}
加载页面时,出现以下错误。
Argument 1 passed to
Symfony\Bridge\Doctrine\Form\Type\DoctrineType::__construct() must be
an instance of Doctrine\Common\Persistence\ManagerRegistry, none
given, called in /var/www/dev3/Vendor/symfony/form/FormRegistry.php on
line 85
我认为这里需要进行一些配置,但我不知道如何创建一个 class 来实现 ManagerRegistryInterface - 如果这是正确的做法。
有什么指点吗?
编辑 - 这是我设置 Doctrine 的代码
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\Setup;
class Bootstrap {
//...some other methods, including getCredentials() which returns DB credentials for Doctrine
public function getEntityManager($env){
$isDevMode = $env == 'dev';
$paths = [ROOT_DIR . '/src'];
$config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode, null, null, false);
$dbParams = $this->getCredentials($env);
$em = EntityManager::create($dbParams, $config);
return $em;
}
}
相信我,你是在自找麻烦!
当 EntityType::class
无缝集成到 "Symfony" 框架时可以工作(引擎盖下有魔法 - 通过 DoctrineBundle)。否则,您需要编写大量代码才能使其正常工作。
不值得努力!
如果您创建实体存储库并将其注入表单构造函数,然后在 ChoiceType::class
字段中使用,就会容易得多。像这样思考:
<?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)
}
}
在应用程序的某处:
$repo = $entityManager->getRepository(Product::class);
$formOptions = ['products' => $repo->findAll()];
$formFactory = Forms::createFormFactory();
$formFactory->create(InvoiceItemtType::class, new InvoiceItem, $formOptions);
这就是重点!
解决您的问题的最简单方法是从 Doctrine 桥中注册 DoctrineOrmExtension,以确保实体类型已注册到所需的依赖项。
基本上,引导 Form 组件的过程如下所示:
// a Doctrine ManagerRegistry instance (you will probably already build this somewhere else)
$managerRegistry = ...;
$doctrineOrmExtension = new DoctrineOrmExtension($managerRegistry);
// the list of form extensions
$extensions = array();
// register other extensions
// ...
// add the DoctrineOrmExtension
$extensions[] = $doctrineOrmExtension;
// a ResolvedFormTypeFactoryInterface instance
$resolvedTypeFactory = ...;
$formRegistry = new FormRegistry($extensions, $resolvedTypeFactory);
扩展 xabbuh 的答案。
我能够在 FormBuilder
中实现 EntityType
,而无需太多额外的工作。但是,它不适用于注释,以便直接在实体内部使用 Constraints
,这需要做更多的工作。
您可以轻松满足 Doctrine ORM Forms Extension 的 ManagerRegistry
要求,方法是扩展现有 AbstractManagerRegistry
并在自定义 ManagerRegistry
中创建您自己的容器 属性 .
那么只需像注册任何其他扩展一样注册表单扩展(ValidatorExtension
、HttpFoundationExtension
等)。
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');
}
}
创建表单
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]);
}
}
配置表单生成器以使用扩展并使用表单
$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);
以上仅供演示!虽然它确实
功能,它被认为是 最好的
练习
avoid using Doctrine Entities inside of Forms。使用 DTO(数据
Transfer Object) 而不是。
ENTITIES SHOULD ALWAYS BE VALID
INVALID STATE SHOULD BE IN A DIFFERENT OBJECT
(You may need a DTO)
(Also applies to Temporary State)
AVOID SETTERS
AVOID COUPLING WITH THE APPLICATION LAYER
FORM COMPONENTS BREAK ENTITY VALIDITY
BOTH SYMFONY\FORM AND ZEND\FORM ARE TERRIBLE
(For this use-case)
Use a DTO instead
学说 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
我使用的是没有 Symfony 框架其余部分的 Symfony 表单 (v3.0)。使用 Doctrine v2.5.
我创建了一个表单,这里是表单类型 class:
class CreateMyEntityForm extends BaseFormType {
public function buildForm(FormBuilderInterface $builder, array $options){
$builder->add('myEntity', EntityType::class);
}
}
加载页面时,出现以下错误。
Argument 1 passed to Symfony\Bridge\Doctrine\Form\Type\DoctrineType::__construct() must be an instance of Doctrine\Common\Persistence\ManagerRegistry, none given, called in /var/www/dev3/Vendor/symfony/form/FormRegistry.php on line 85
我认为这里需要进行一些配置,但我不知道如何创建一个 class 来实现 ManagerRegistryInterface - 如果这是正确的做法。
有什么指点吗?
编辑 - 这是我设置 Doctrine 的代码
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\Setup;
class Bootstrap {
//...some other methods, including getCredentials() which returns DB credentials for Doctrine
public function getEntityManager($env){
$isDevMode = $env == 'dev';
$paths = [ROOT_DIR . '/src'];
$config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode, null, null, false);
$dbParams = $this->getCredentials($env);
$em = EntityManager::create($dbParams, $config);
return $em;
}
}
相信我,你是在自找麻烦!
当EntityType::class
无缝集成到 "Symfony" 框架时可以工作(引擎盖下有魔法 - 通过 DoctrineBundle)。否则,您需要编写大量代码才能使其正常工作。
不值得努力!
如果您创建实体存储库并将其注入表单构造函数,然后在 ChoiceType::class
字段中使用,就会容易得多。像这样思考:
<?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)
}
}
在应用程序的某处:
$repo = $entityManager->getRepository(Product::class);
$formOptions = ['products' => $repo->findAll()];
$formFactory = Forms::createFormFactory();
$formFactory->create(InvoiceItemtType::class, new InvoiceItem, $formOptions);
这就是重点!
解决您的问题的最简单方法是从 Doctrine 桥中注册 DoctrineOrmExtension,以确保实体类型已注册到所需的依赖项。
基本上,引导 Form 组件的过程如下所示:
// a Doctrine ManagerRegistry instance (you will probably already build this somewhere else)
$managerRegistry = ...;
$doctrineOrmExtension = new DoctrineOrmExtension($managerRegistry);
// the list of form extensions
$extensions = array();
// register other extensions
// ...
// add the DoctrineOrmExtension
$extensions[] = $doctrineOrmExtension;
// a ResolvedFormTypeFactoryInterface instance
$resolvedTypeFactory = ...;
$formRegistry = new FormRegistry($extensions, $resolvedTypeFactory);
扩展 xabbuh 的答案。
我能够在 FormBuilder
中实现 EntityType
,而无需太多额外的工作。但是,它不适用于注释,以便直接在实体内部使用 Constraints
,这需要做更多的工作。
您可以轻松满足 Doctrine ORM Forms Extension 的 ManagerRegistry
要求,方法是扩展现有 AbstractManagerRegistry
并在自定义 ManagerRegistry
中创建您自己的容器 属性 .
那么只需像注册任何其他扩展一样注册表单扩展(ValidatorExtension
、HttpFoundationExtension
等)。
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');
}
}
创建表单
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]);
}
}
配置表单生成器以使用扩展并使用表单
$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);
以上仅供演示!虽然它确实 功能,它被认为是 最好的 练习 avoid using Doctrine Entities inside of Forms。使用 DTO(数据 Transfer Object) 而不是。
ENTITIES SHOULD ALWAYS BE VALID
INVALID STATE SHOULD BE IN A DIFFERENT OBJECT
(You may need a DTO)
(Also applies to Temporary State)AVOID SETTERS
AVOID COUPLING WITH THE APPLICATION LAYER
FORM COMPONENTS BREAK ENTITY VALIDITY
BOTH SYMFONY\FORM AND ZEND\FORM ARE TERRIBLE
(For this use-case)
Use a DTO instead
学说 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