Symfony 调用getReference()时,ReferencePository会剥离相关实体

Symfony 调用getReference()时,ReferencePository会剥离相关实体,symfony,doctrine-orm,Symfony,Doctrine Orm,我正在编写一个严重依赖于数据装置的symfony2单元测试。作为一种快捷方式,我连接了一个方法,该方法将允许我访问fixture loader的referencepository,以便我可以访问测试中的共享实体 但是,当我从referenceposition中拉出一个对象时,它没有关系,即使我将它们保存在数据装置中 奇怪的是,referencepositional中有一些代码似乎在剥离这些关系,我不明白它为什么要这样做(更不用说如何防止了) 例如,以下是数据固定装置的外观: public fun

我正在编写一个严重依赖于数据装置的symfony2单元测试。作为一种快捷方式,我连接了一个方法,该方法将允许我访问fixture loader的
referencepository
,以便我可以访问测试中的共享实体

但是,当我从
referenceposition
中拉出一个对象时,它没有关系,即使我将它们保存在数据装置中

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

例如,以下是数据固定装置的外观:

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');
当我在测试用例中调用方法来获取这个
项目
对象时,我注意到一些奇怪的行为:

来自
条令\Common\DataFixtures\referenceposition
(添加注释):

referencepository->getReference()中发生了什么?为什么要从
$reference
中删除相关对象,如何防止这种情况发生?

发生了什么 fixture loader运行后,它将清除UnitOfWork的标识映射

请参阅
\doctor\Common\DataFixtures\Executor\AbstractExecutor

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

    $fixture->load($manager);
    $manager->clear();
}
因此,条件
$夹具加载程序完成后,
referencepository->getReference()
中的uow->isInIdentityMap($reference)
将始终计算为
false

解决办法 您可以通过清除
referencepository->$identies
来解决此问题。不幸的是,您无法直接访问此阵列,因此需要执行一些稍微笨拙的操作,如:

/* @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);
}
但是,如果这样做,如果在测试夹具中设置相关对象,则可能会遇到一些讨厌的
或InvalidargumentException
s:

/** 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.
         */
    }
}
条令\ORM\ORMInvalidArgumentException:通过关系“…”找到了一个新实体,该关系未配置为级联实体url的持久化操作。要解决此问题,请对此未知实体显式调用EntityManager#persist(),或在映射中配置cascade persist此关联,例如@manytone(..,cascade={“persist”})

解决方案 最终,如果您想让它正常工作,您需要更改您在测试用例中使用的fixture executor的行为,以便它在加载fixture后不会清除管理器:

/** 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.
         */
    }
}