Zend Framework 2 - Doctrine 关系的 Hydrator 策略不起作用

如前所述此处我正在构建自定义水化策略来处理表单中选择框中的相关对象.

As mentioned here I'm building a custom hydration strategy to handle my related objects in a select box in a form.

我的表单如下所示:

$builder = new AnnotationBuilder($entityManager);
$form    = $builder->createForm(new MyEntity());
$form->add(new MyFieldSet());

$hydrator = new ClassMethodsHydrator();
$hydrator->addStrategy('my_attribute', new MyHydrationStrategy());
$form->setHydrator($hydrator);

$form->get('my_attribute')->setValueOptions(
      $entityManager->getRepository('SecEntityEntitySecEntity')->fetchAllAsArray()
);

当我通过 addAction 添加一个新的 MyEntity 时,一切都很好.

When I add a new MyEntity via the addAction everything works great.

我编写了 fetchAllAsArray() 来填充我的选择框.它位于我的 SecEntityRepository 中:

I wrote fetchAllAsArray() to populate my selectbox. It lives within my SecEntityRepository:

public function fetchAllAsArray() {

    $objects = $this->createQueryBuilder('s')
        ->add('select', 's.id, s.name')
        ->add('orderBy', 's.name ASC')
        ->getQuery()
        ->getResult();

    $list = array();
    foreach($objects as $obj) {
        $list[$obj['id']] = $obj['name'];
    }

    return $list;
}

但在编辑案例中,extract() 函数不起作用.我现在还没有看到 hydrate() 的内容,所以我暂时将其忽略.

But in the edit-case the extract() function doesn't work. I'm not at the point where I see something of hydrate() so I'll leave it out for now.

我的保湿策略如下:

class MyHydrationStrategy extends DefaultStrategy
{
    public function extract($value) {        
        print_r($value);
        $result = array();
        foreach ($value as $instance) {
            print_r($instance);
            $result[] = $instance->getId();
        }
        return $result;
    }

    public function hydrate($value) {
        ...
    }

问题如下:

致命错误:在非对象上调用成员函数 getId()

Fatal error: Call to a member function getId() on a non-object

print_r($value) 返回大量以

DoctrineORMModuleProxy__CG__SecEntityEntitySecEntity Object

DoctrineORMModuleProxy__CG__SecEntityEntitySecEntity Object

接下来是关于 BasicEntityPersister 和混乱的某个地方是我引用的实体.

following with something about BasicEntityPersister and somewhere in the mess are my referenced entities.

print_r($instance) 什么也不打印.它只是空的.因此我猜错误消息是合法的...但为什么我不能遍历这些对象?

The print_r($instance) prints nothing. It's just empty. Therefore I guess is the error message legit... but why can't I iterate over these objects?

有什么想法吗?

关于@Sam:

实体中我的属性:

    /**
 * @ORMManyToOne(targetEntity="Path/To/Entity", inversedBy="whatever")
 * @ORMJoinColumn(name="attribute_id", referencedColumnName="id")
 * @FormAttributes({"type":"hidden"})
 *
 */
protected $attribute;

我的新选择框:

$form->add(array(
        'name'       => 'attribute',
        'type'       => 'DoctrineModuleFormElementObjectSelect',
        'attributes' => array(
            'required' => true
        ),
        'options'    => array(
            'label'           => 'MyLabel',
            'object_manager'  => $entityManager,
            'target_class'    => 'Path/To/Entity',
            'property'        => 'name'
        )
    ));

我最后的希望是我在控制器中做错了什么.我的选择框既没有被预选,也没有保存值...

My final hope is that I'm doing something wrong within the controller. Neither my selectbox is preselected nor the value is saved...

...

$obj= $this->getEntityManager()->find('Path/To/Entity', $id);

    $builder = new MyEnityMyFormBuilder();
    $form = $builder->newForm($this->getEntityManager());

    $form->setBindOnValidate(false);
    $form->bind($obj);
    $form->setData($obj->getArrayCopy());

    $request = $this->getRequest();
    if ($request->isPost()) {
        $form->setData($request->getPost());

        if ($form->isValid()) {
            $form->bindValues();
            $this->getEntityManager()->flush();

            return $this->redirect()->toRoute('entity');
        }
    }

推荐答案

我还没来得及为此编写教程 :S

I still haven't come around to write the tutorial for that :S

我不知道这是否适用于 annotationbuilder!由于 DoctrineModuleFormElementObjectSelect 需要 EntityManager 才能工作.ObjectSelect 的选项如下:

I don't know if this is working with the annotationbuilder though! As the DoctrineModuleFormElementObjectSelect needs the EntityManager to work. The options for the ObjectSelect are as follows:

   $this->add(array(
        'name'       => 'formElementName',
        'type'       => 'DoctrineModuleFormElementObjectSelect',
        'attributes' => array(
            'required' => true
        ),
        'options'    => array(
            'label'           => 'formElementLabel',
            'empty_option'    => '--- choose formElementName ---',
            'object_manager'  => $this->getEntityManager(),
            'target_class'    => 'MynamespaceEntityEntityname',
            'property'        => 'nameOfEntityPropertyAsSelect'
        )
    ));

在这种情况下,我使用 $this->getEntityManager().我在从 ServiceManager 调用表单时设置了这个依赖项.就我个人而言,我总是从 FactoryClasses 这样做.我的 FormFactory 看起来像这样:

In this case i make use of $this->getEntityManager(). I set up this dependency when calling the form from the ServiceManager. Personally i always do this from FactoryClasses. My FormFactory looks like this:

public function createService(ServiceLocatorInterface $serviceLocator)
{
    $em = $serviceLocator->get('DoctrineORMEntityManager');

    $form = new ErgebnishaushaltProduktForm('ergebnisform', array(
        'entity_manager' => $em
    ));

    $classMethodsHydrator = new ClassMethodsHydrator(false);

    // Wir fügen zwei Strategien, um benutzerdefinierte Logik während Extrakt auszuführen
    $classMethodsHydrator->addStrategy('produktBereich', new StrategyProduktbereichStrategy())
                         ->addStrategy('produktGruppe', new StrategyProduktgruppeStrategy());

    $hydrator = new DoctrineEntity($em, $classMethodsHydrator);

    $form->setHydrator($hydrator)
         ->setObject(new ErgebnishaushaltProdukt())
         ->setInputFilter(new ErgebnishaushaltProduktFilter())
         ->setAttribute('method', 'post');

    return $form;
}

这就是所有魔法发生的地方.魔术,这也与您在 SO 上的其他主题相关.首先,我获取EntityManager.然后我创建我的表单,并为 EntityManager 注入依赖项.我使用我自己的表单执行此操作,您可以编写并使用 Setter-Function 来注入 EntityManager.

And this is where all the magic is happening. Magic, that is also relevant to your other Thread here on SO. First, i grab the EntityManager. Then i create my form, and inject the dependency for the EntityManager. I do this using my own Form, you may write and use a Setter-Function to inject the EntityManager.

接下来,我创建一个 ClassMethodsHydrator 并向其添加两个 HydrationStrategies.我个人需要为每个 ObjectSelect-Element 应用这些策略.您可能不必在您身边这样做.先试试看没有它是否能正常工作!

Next i create a ClassMethodsHydrator and add two HydrationStrategies to it. Personally i need to apply those strategies for each ObjectSelect-Element. You may not have to do this on your side. Try to see if it is working without it first!

之后,我创建了 DoctrineEntity-Hydrator,注入了 EntityManager 以及我的自定义 ClassMethodsHydrator.这样就可以轻松添加策略.

After that, i create the DoctrineEntity-Hydrator, inject the EntityManager as well as my custom ClassMethodsHydrator. This way the Strategies will be added easily.

其余的应该是不言自明的(尽管是德语类名:D)

The rest should be quite self-explanatory (despite the german classnames :D)

为什么需要策略

Imo,这是 DoctrineEntity 目前缺少的东西,但事情仍处于早期阶段.一旦 DoctrineModule-Issue#106 上线,事情会再次发生变化,可能会成功更舒服.

Imo, this is something missing from the DoctrineEntity currently, but things are still in an early stage. And once DoctrineModule-Issue#106 will be live, things will change again, probably making it more comfortable.

策略如下所示:

<?php
namespace HaushaltportalStdlibHydratorStrategy;

use ZendStdlibHydratorStrategyStrategyInterface;

class ProduktbereichStrategy implements StrategyInterface
{
    public function extract($value)
    {
        if (is_numeric($value) || $value === null) {
            return $value;
        }

        return $value->getId();
    }

    public function hydrate($value)
    {
        return $value;
    }
}

所以每当 $value 不是数字或 null,意思是:它应该是一个对象时,我们将调用 getId() 函数.就我个人而言,我认为为每个元素提供自己的策略是个好主意,但是如果您确定以后不需要更改策略,您可以为多个元素创建一个全局策略,例如 DefaultGetIdStrategy 什么的.

So whenever the $value is not numeric or null, meaning: it should be an Object, we will call the getId() function. Personally i think it's a good idea to give each Element it's own strategy, but if you are sure you won't be needing to change the strategy at a later point, you could create a global Strategy for several elements like DefaultGetIdStrategy or something.

所有这些基本上都是 Michael Gallego aka Bakura 的出色工作!万一你路过 IRC,就抱抱他一次 ;)

All this is basically the good work of Michael Gallego aka Bakura! In case you drop by the IRC, just hug him once ;)

编辑带有 的附加资源展望未来 - 更新了 hydrator-docs 以获取很可能很快就会被包含在内的拉取请求

Edit An additional resource with a look into the future - updated hydrator-docs for a very likely, soon to be included, pull request

相关文章