这里涉及三个实体:Deployment、DeploymentStep和DeploymentStatusLog。我将从粘贴这些类的相关定义开始
src/My/Bundle/Entity/Deployment.php
<?php
namespace My\Bundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\PersistentCollection;
/**
* @ORM\Table(name="deployment")
* @ORM\Entity()
*/
class Deployment
{
/**
* Status Log Entries for this deployment
*
* @var \Doctrine\ORM\PersistentCollection
*
* @ORM\OneToMany(targetEntity="DeploymentStatusLog", mappedBy="deployment", cascade={"persist","remove"})
* @ORM\OrderBy({"created_at"="DESC"})
*/
protected $status_logs;
/**
* @var \Doctrine\ORM\PersistentCollection
*
* @ORM\OneToMany(targetEntity="DeploymentStep", mappedBy="deployment", cascade={"persist","remove"})
* @ORM\OrderBy({"sequence" = "ASC"})
*/
protected $steps;
public function __construct()
{
$this->status_logs = new ArrayCollection();
$this->steps = new ArrayCollection();
}
/**
* Add status_logs
*
* @param DeploymentStatusLog $statusLogs
*/
public function addDeploymentStatusLog(DeploymentStatusLog $statusLogs)
{
$this->status_logs[] = $statusLogs;
}
/**
* Add steps
*
* @param DeploymentStep $steps
*/
public function addDeploymentStep(DeploymentStep $steps)
{
$this->steps[] = $steps;
}
// ...
}
src/My/Bundle/Entity/DeploymentStep.php
<?php
namespace My\Bundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Table(name="deployment_step")
* @ORM\Entity()
*/
class DeploymentStep
{
/**
* @var Deployment
*
* @ORM\ManyToOne(targetEntity="Deployment", cascade={"all"})
* @ORM\JoinColumn(name="deployment_id", referencedColumnName="id")
* @Gedmo\SortableGroup
*/
private $deployment;
/**
* Set deployment
*
* @param Deployment $deployment
*/
public function setDeployment(Deployment $deployment)
{
$this->deployment = $deployment;
}
// ...
}
src/My/Bundle/Entity/DeploymentStatusLog.php
<?php
namespace My\Bundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Table(name="deployment_status_log")
* @ORM\Entity()
*/
class DeploymentStatusLog
{
/**
* @var Deployment
*
* @ORM\ManyToOne(targetEntity="Deployment", cascade={"all"})
* @ORM\JoinColumn(name="deployment_id", referencedColumnName="id", nullable=false)
*/
protected $deployment;
/**
* Set deployment
*
* @param Deployment $deployment
*/
public function setDeployment( Deployment $deployment)
{
$this->deployment = $deployment;
}
// ...
}
现在,当我尝试同时为所有这三个实体创建全新的记录时,问题就出现了。在控制器中:
$em = $this->getDoctrine()->getEntityManager();
$deployment = new Deployment();
$form = $this->createForm(new DeploymentType($em), $deployment);
if ($request->getMethod() == 'POST')
{
$form->bindRequest($request);
if ($form->isValid())
{
$codeStep = new DeploymentStep();
$codeStep->setDeployment( $deployment );
// Other setters on DeploymentStep
$deploymentStatusLog = new DeploymentStatusLog();
$deploymentStatusLog->setDeployment( $deployment );
// Other setters on DeploymentStatusLog
$deployment->addDeploymentStep( $codeStep );
$deployment->addDeploymentStatusLog( $deploymentStatusLog );
$em->persist( $deployment );
$em->flush();
}
}
当 UnitOfWork 处理时会发生什么,它会抛出一个看起来很奇怪的异常,抱怨未定义的索引:
/project/vendor/doctrine/lib/Doctrine/ORM/UnitOfWork.php 第 2252 行中的 /project/vendor/symfony/src/Symfony/Component/HttpKernel/Debug /ErrorHandler.php:67
现在,如果我先持久化/刷新部署实体,然后持久化/刷新关联,它就会成功。
因此,虽然我可以这样做以使应用程序的这一部分功能化,但感觉有点不对,因为这个过程应该是原子的,而且这就是事务查询的重点。
有什么线索吗?
- Symfony 2.0.15
- 教义 2.1.7
- PHP 5.3.3
- MySQL 5.1.52
- 阿帕奇 2.2.15
编辑
请求的完整堆栈跟踪
exception 'ErrorException' with message 'Notice: Undefined index: 000000004081f5f9000000005f1dbbfc in /project/vendor/doctrine/lib/Doctrine/ORM/UnitOfWork.php line 2252' in /project/vendor/symfony/src/Symfony/Component/HttpKernel/Debug/ErrorHandler.php:67
Stack trace:
#0 /project/vendor/doctrine/lib/Doctrine/ORM/UnitOfWork.php(2252): Symfony\Component\HttpKernel\Debug\ErrorHandler->handle(8, 'Undefined index...', '/mnt/hgfs/mount...', 2252, Array)
#1 /project/vendor/doctrine/lib/Doctrine/ORM/Query.php(321): Doctrine\ORM\UnitOfWork->getEntityIdentifier(Object(My\Bundle\Entity\Deployment))
#2 /project/vendor/doctrine/lib/Doctrine/ORM/Query.php(274): Doctrine\ORM\Query->processParameterValue(Object(My\Bundle\Entity\Deployment))
#3 /project/vendor/doctrine/lib/Doctrine/ORM/Query.php(243): Doctrine\ORM\Query->processParameterMappings(Array)
#4 /project/vendor/doctrine/lib/Doctrine/ORM/AbstractQuery.php(607): Doctrine\ORM\Query->_doExecute()
#5 /project/vendor/doctrine/lib/Doctrine/ORM/AbstractQuery.php(413): Doctrine\ORM\AbstractQuery->execute(Array, 1)
#6 /project/vendor/gedmo-doctrine-extensions/lib/Gedmo/Sortable/SortableListener.php(344): Doctrine\ORM\AbstractQuery->getResult()
#7 /project/vendor/gedmo-doctrine-extensions/lib/Gedmo/Sortable/SortableListener.php(133): Gedmo\Sortable\SortableListener->getMaxPosition(Object(Doctrine\ORM\EntityManager), Object(Doctrine\ORM\Mapping\ClassMetadata), Array, Object(My\Bundle\Entity\DeploymentStep))
#8 /project/vendor/gedmo-doctrine-extensions/lib/Gedmo/Sortable/SortableListener.php(100): Gedmo\Sortable\SortableListener->processInsert(Object(Doctrine\ORM\EntityManager), Array, Object(Doctrine\ORM\Mapping\ClassMetadata), Object(My\Bundle\Entity\DeploymentStep))
#9 /project/vendor/doctrine-common/lib/Doctrine/Common/EventManager.php(64): Gedmo\Sortable\SortableListener->onFlush(Object(Doctrine\ORM\Event\OnFlushEventArgs))
#10 /project/vendor/doctrine/lib/Doctrine/ORM/UnitOfWork.php(280): Doctrine\Common\EventManager->dispatchEvent('onFlush', Object(Doctrine\ORM\Event\OnFlushEventArgs))
#11 /project/vendor/doctrine/lib/Doctrine/ORM/EntityManager.php(334): Doctrine\ORM\UnitOfWork->commit()
#12 /project/src/My/Bundle/Controller/DeploymentController.php(214): Doctrine\ORM\EntityManager->flush()
#13 [internal function]: My\Bundle\Controller\DeploymentController->createAction(Object(My\Bundle\Entity\Release), Object(Symfony\Component\HttpFoundation\Request))
#14 /project/vendor/bundles/JMS/SecurityExtraBundle/Security/Authorization/Interception/MethodSecurityInterceptor.php(73): ReflectionMethod->invokeArgs(Object(My\Bundle\Controller\DeploymentController), Array)
#15 /project/app/cache/dev/classes.php(9391) : eval()'d code(1): JMS\SecurityExtraBundle\Security\Authorization\Interception\MethodSecurityInterceptor->invoke(Object(JMS\SecurityExtraBundle\Security\Authorization\Interception\MethodInvocation), Array)
#16 [internal function]: {closure}(Object(My\Bundle\Entity\Release), Object(Symfony\Component\HttpFoundation\Request))
#17 /project/app/cache/dev/classes.php(3925): call_user_func_array(Object(Closure), Array)
#18 /project/app/cache/dev/classes.php(3895): Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object(Symfony\Component\HttpFoundation\Request), 1)
#19 /project/app/cache/dev/classes.php(4899): Symfony\Component\HttpKernel\HttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#20 /project/app/bootstrap.php.cache(551): Symfony\Bundle\FrameworkBundle\HttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#21 /project/web/app_dev.php(18): Symfony\Component\HttpKernel\Kernel->handle(Object(Symfony\Component\HttpFoundation\Request))
#22 {main}
编辑 2
根据要求创建操作的完整代码
/**
* @Route("/create/{id}", name="deployment_create_id")
* @ParamConverter("release", class="MyBundle:Release")
* @Method({"POST","GET"})
* @Secure(roles="ROLE_DEPLOYMENT_PLANNER")
* @Template()
*/
public function createAction( Release $release, Request $request )
{
$em = $this->getDoctrine()->getEntityManager();
$sessionUser = $this->get('security.context')->getToken()->getUser();
$deployment = new Deployment();
$deployment->setRelease( $release );
$deployment->setAuthor( $sessionUser );
$form = $this->createForm(new DeploymentType($em), $deployment);
if ($request->getMethod() == 'POST')
{
$form->bindRequest($request);
if ($form->isValid())
{
$codeStep = new DeploymentStep();
$codeStep->setDeployment( $deployment );
$codeStep->setSequence( 0 );
$codeStep->setTitle( "Update Code" );
$codeStep->setDetails( "Update codebase per the plan's specifications" );
$codeStep->setDeploymentStepType(
$em->getRepository('MyBundle:DeploymentStepType')->findOneBy(
array( 'name' => DeploymentStepType::TYPE_OTHER )
)
);
$deploymentStatusLog = new DeploymentStatusLog();
$deploymentStatusLog->setDeployment( $deployment );
$deploymentStatusLog->setUser( $sessionUser );
$deploymentStatusLog->setNotes( 'New Deployment Created' );
$deploymentStatusLog->setDeploymentStatus(
$em->getRepository('MyBundle:DeploymentStatus')->findOneBy(
array( 'title' => DeploymentStatus::STATUS_NEW )
)
);
$deployment->addDeploymentStep( $codeStep );
$deployment->addDeploymentStatusLog( $deploymentStatusLog );
try {
$em->persist( $deployment );
$em->persist( $codeStep );
$em->persist( $deploymentStatusLog );
$em->flush();
return $this->redirectSuccess(
'Deployment created.'
, $release->getRouteName()
, $release->getRouteParameters()
);
}
catch ( \Exception $e )
{
$this->setFlashErrorMessage( 'Error saving deployment.' );
}
}
}
return array(
'release' => $release
, 'form' => $form->createView()
);
}