2

我正在编写一个严重依赖数据夹具的 Symfony 2 单元测试。作为捷径,我连接了一个方法,让我可以访问夹具加载器ReferenceRepository,以便我可以在测试中访问共享实体。

但是,当我从 中拉出一个对象时ReferenceRepository,它没有任何关系,即使我将它们保存在数据夹具中。

奇怪的是,其中有一些代码ReferenceRepository似乎正在剥离这些关系,我不明白它为什么这样做(更不用说如何防止它了)。

例如,以下是数据夹具的样子:

public function load(ObjectManager $manager)
{
    $project = new Project();
    // ... populate fields ...

    /* Add one detail field to the Project. */
    $detail = new ProjectDetail();
    // ... populate fields ...
    $project->addDetail($detail);

    $manager->persist($project);
    $manager->flush();

    $this->addReference('project-onedetail', $project);
}

在我的测试用例中,我正在做这样的事情(或多或少):

$project =
    $this->fixtureLoader->getReferenceRepository()
        ->getReference('project-onedetail');

当我调用测试用例中的方法来获取这个Project对象时,我注意到一些奇怪的行为:

来自Doctrine\Common\DataFixtures\ReferenceRepository(添加评论):

public function getReference($name)
{
    $reference = $this->references[$name];

    // At this point, $reference contains the Project object with related ProjectDetail.
    //  It would be awesome if the method would just return $reference...

    $meta = $this->manager->getClassMetadata(get_class($reference));
    $uow = $this->manager->getUnitOfWork();

    if (!$uow->isInIdentityMap($reference) && isset($this->identities[$name])) {
        // ... but instead it goes into this conditional....

        $reference = $this->manager->getReference(
            $meta->name,
            $this->identities[$name]
        );

        // ... and now $reference->getDetails() is empty!  What just happened??

        $this->references[$name] = $reference; // already in identity map
    }

    return $reference;
}

里面发生了什么ReferenceRepository->getReference()?为什么相关对象会从 中删除$reference,我该如何防止呢?

4

1 回答 1

1

这是怎么回事

夹具加载程序运行后,它会清除 UnitOfWork 的身份映射。

\Doctrine\Common\DataFixtures\Executor\AbstractExecutor

public function load(ObjectManager $manager, FixtureInterface $fixture)
{
    ...

    $fixture->load($manager);
    $manager->clear();
}

因此,在夹具加载器完成后,条件!$uow->isInIdentityMap($reference)将始终ReferenceRepository->getReference()评估false

解决方法

您可以通过清除ReferenceRepository->$identities. 不幸的是,你不能直接访问这个数组,所以你需要做一些有点笨拙的事情,比如:

/* @kludge The fixture loader clears out its UnitOfWork object after
 *  loading each fixture, so we also need to clear the
 *  ReferenceRepository's identity map.
 */
$repository = $this->fixtureLoader->getReferenceRepository();
$identities = array_keys($repository->getIdentities());

foreach($identities as $key)
{
    $repository->setReferenceIdentity($key, null);
}

ORMInvalidArgumentException但是,如果您这样做,如果您在测试夹具中设置相关对象,您可能会遇到一些讨厌的问题:

Doctrine\ORM\ORMInvalidArgumentException:通过关系“...”找到了一个新实体,该实体未配置为对实体进行级联持久操作:url。要解决这个问题:要么在这个未知实体上显式调用 EntityManager#persist(),要么配置级联在映射中保持此关联,例如 @ManyToOne(..,cascade={"persist"})。

解决方案

最终,如果您希望它正常工作,您需要更改您在测试用例中使用的夹具执行器的行为,以便在加载夹具后它不会清除管理器:

/** Executes data fixtures for unit tests.
 */
class TestExecutor extends ORMExecutor
{
    /** Load a fixture with the given persistence manager.
     *
     * @param ObjectManager|EntityManager $manager
     * @param FixtureInterface $fixture
     */
    public function load(ObjectManager $manager, FixtureInterface $fixture)
    {
        /** @kludge Unfortunately, we have to copy-paste a bit of code.
         *
         * The only difference between this method and AbstractExecutor->load()
         *  is that we don't call $manager->clear() when we're done loading.
         */

        if($this->logger)
        {
            $prefix = '';
            if($fixture instanceof OrderedFixtureInterface)
            {
                $prefix = sprintf('[%d] ', $fixture->getOrder());
            }
            $this->log('loading ' . $prefix . get_class($fixture));
        }

        // additionally pass the instance of reference repository to shared fixtures
        if($fixture instanceof SharedFixtureInterface)
        {
            $fixture->setReferenceRepository($this->referenceRepository);
        }

        $fixture->load($manager);

        /* Do NOT clear the unit of work; we will keep managed entities so that
         *  they are available to tests.
         */
    }
}
于 2013-09-10T20:47:44.173 回答