【问题标题】:Symfony forms (as standalone component with Doctrine) EntityType not workingSymfony 表单(作为带有 Doctrine 的独立组件)EntityType 不起作用
【发布时间】:2016-01-11 17:04:31
【问题描述】:

我正在使用 Symfony 表单 (v3.0),而没有使用 Symfony 框架的其余部分。使用 Doctrine v2.5。

我已经创建了一个表单,这里是表单类型类:

class CreateMyEntityForm extends BaseFormType {

    public function buildForm(FormBuilderInterface $builder, array $options){
        $builder->add('myEntity', EntityType::class);
    }
}

加载页面时,出现以下错误。

参数 1 传递给 Symfony\Bridge\Doctrine\Form\Type\DoctrineType::__construct() 必须是 Doctrine\Common\Persistence\ManagerRegistry 的一个实例,无 给定,在 /var/www/dev3/Vendor/symfony/form/FormRegistry.php 中调用 第 85 行

我相信这里需要进行一些配置,但我不知道如何创建一个实现 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;
    }
}

【问题讨论】:

  • 您是否记得将use Symfony\Bridge\Doctrine\Form\Type\EntityType; 放在表单顶部?
  • @JasonRoman 存在 use 语句。
  • @malcolm 我确实看到了这个问题,但解决方案使用了 Silex\Application 类。我没有使用 Silex,我想了解我的班级应该做什么:)
  • @malcolm 我看过那个例子,但它没有使用 Doctrine(所以显然没有使用 EntityType 字段)

标签: php symfony doctrine-orm symfony-forms


【解决方案1】:

相信我,你是在自找麻烦!

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

这就是重点!

【讨论】:

  • 谢谢!对于Symfony 2.8 而不是configureOption,您需要使用setDefaultOptions: public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( 'products' => array() )); }
  • @AndreyTykhonov 不!实际上,setDefaultOptions 自 Symfony v2.7 以来已被弃用,取而代之的是 configureOptions(参见博客 postGithub PR)。所以你应该改用 configureOptions 。 ;)
  • 表单提交怎么样?提交的表单数据绑定到目标实体。请填写您的答案以显示其余部分
【解决方案2】:

扩展 xabbuh 的答案。

我能够在FormBuilder 中实现EntityType,而无需太多额外的工作。但是,它不能与注释一起使用,以便直接在实体内部使用Constraints,这需要更多的工作。

您可以通过扩展现有的 AbstractManagerRegistry 并在自定义 ManagerRegistry 中创建自己的容器属性,轻松满足 Doctrine ORM 表单扩展的 ManagerRegistry 要求。

然后,只需像任何其他扩展(ValidatorExtensionHttpFoundationExtension 等)一样注册表单扩展。

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

以上内容仅用于演示目的!虽然它确实 函数,它被认为是best practice 避免在 Forms 中使用 Doctrine Entity。使用 [DTO(数据 转移对象)。

教义 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

【讨论】:

  • 迄今为止最好的答案。
  • 然而,由于 2 个实体管理器导致实体分离,导致持久性噩梦。
  • 如果将实体管理器作为服务传递,则不应拥有多个实体管理器,从而有效地复制 Symfony 框架处理它的方式。
【解决方案3】:

解决您的问题的最简单方法是从 Doctrine 桥中注册 DoctrineOrmExtension,以确保实体类型已注册所需的依赖项。

所以基本上,引导表单组件的过程如下所示:

// 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);

【讨论】:

  • 我之前尝试过,但是使用 symfony/doctrine-bridge 需要付出很多努力才能工作。
  • 你能解释一下你将如何“从 Doctrine 桥注册 DoctrineOrmExtension” - 我可以看到该类在那里,但是在哪里使用它?
  • 谢谢...我仍然不确定您将如何构建/查找 Doctrine ManagerRegistry 实例。我还没有在其他地方使用过这个——但我很感激这可能是我对 Doctrine 的了解不够。我在 ManagerRegistry 类上找不到任何文档。
  • 那么,你是如何在你的项目中设置 Doctrine 的呢?具体答案取决于您的设置。
  • 我已经用我的代码编辑了我的问题,以设置 Doctrine。我包含的代码差不多就是这样。
猜你喜欢
  • 2018-09-15
  • 2023-02-16
  • 2013-03-26
  • 2013-09-07
  • 2014-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多