Symfony表单(作为Doctrine的独立组件)EntityType不起作用

Wil*_*ill 6 php symfony-forms symfony doctrine-orm

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

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

class CreateMyEntityForm extends BaseFormType {

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

加载页面时,我收到以下错误.

传递给Symfony\Bridge\Doctrine\Form\Type\DoctrineType :: __ construct()的参数1必须是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;
    }
}
Run Code Online (Sandbox Code Playgroud)

fel*_*ins 5

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

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

在应用程序中的某个地方:

$repo = $entityManager->getRepository(Product::class);
$formOptions = ['products' => $repo->findAll()];
$formFactory = Forms::createFormFactory();
$formFactory->create(InvoiceItemtType::class, new InvoiceItem, $formOptions);
Run Code Online (Sandbox Code Playgroud)

这才是重点!


fyr*_*rye 5

扩展 xabbuh 的答案。

我能够在没有太多额外工作的EntityType情况下实现。FormBuilder然而,它不能与注释一起使用,以便Constraints直接在实体内部使用,这将需要更多的工作。

通过ManagerRegistry扩展现有的AbstractManagerRegistry并在自定义ManagerRegistry.

然后只需像注册任何其他扩展( 、 等)一样注册表单扩展ValidatorExtension即可HttpFoundationExtension

经理登记处

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

}
Run Code Online (Sandbox Code Playgroud)

创建表格

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]);
    }
}
Run Code Online (Sandbox Code Playgroud)

配置表单生成器以使用扩展并使用表单

$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);
Run Code Online (Sandbox Code Playgroud)

以上仅用于演示目的!虽然它确实起作用,但避免在 Forms 内部使用 Doctrine Entities 被认为是 最佳实践。请改用 DTO(数据传输对象)。

实体应始终有效

无效状态应该位于不同的对象中
(您可能需要 DTO)
(也适用于临时状态)

避免二传手

避免与应用层耦合

表单组件破坏实体有效性
SYMFONY\FORM 和 ZEND\FORM 都很糟糕
(对于此用例)
请改用 DTO

Doctrine 2.5+ “新”运算符语法

class CustomerDTO
{
    public function __construct($name, $email, $city, $value = null)
    {
        // Bind values to the object properties.
    }
}
Run Code Online (Sandbox Code Playgroud)
$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
Run Code Online (Sandbox Code Playgroud)