学说无法删除 1:M 的 M 侧的实体
doctrine silently fails to delete entities on M side of 1:M
我开始怀疑这行不通,因为在我的用例中它根本行不通——而不是我遗漏了一些东西——但我必须咨询你的专业知识来确定,并看看如果有人可以建议解决方法。
我有一个多对多的情况,我正在使用一个关联 class 来实现,所以我们在 3 个参与的 class 之间有 one-to-many/many-to-one 个关联。有一个代表人的解释器实体和一个代表口语的语言实体(实际上是一对工作语言,但在这个以英语为中心的应用程序中,其中一半被理解为英语)。一个 Interpreter 可以有多种语言,Language 是多个 Interpreter 的工作语言之一。我们需要管理解释器语言的其他属性,因此需要 InterpreterLanguage class.
当我调用 $interpreter->removeInterpreterLanguage($interpreterLanguage);
后跟 $entityManager->flush()
时,内存中解释器实体的 $interpreterLanguages
集合中的元素少了一个,正如您所期望的那样,并且没有错误或抛出异常,但在数据库中发生了以下情况:nothing.
我已经在 MVC 上下文中尝试过此操作,使用 ZendFramework 3 和绑定 Zend\Form\Form
字段集,当这让我抓狂时,我编写了一个 CLI 脚本来尝试检查问题——同样的结果。也许值得注意的是,对于更新标量属性,它工作正常。
我很抱歉没有将 link 包含在我之前阅读的关于这个问题的讨论中——由于某种原因现在找不到了。但我记得有人说它只是行不通,因为 Doctrine 看到另一边的 M:1,因此不会删除,你必须说 $entityManager->remove($object)
才能完成。我的实验性 CLI 脚本似乎证实了这一点。不过,我想排除我做错事的可能性。
有什么想法吗?解决建议?
这是我的语言实体:
/** module/InterpretersOffice/src/Entity/Language.php */
namespace InterpretersOffice\Entity;
use Doctrine\ORM\Mapping as ORM;
use Zend\Form\Annotation;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Entity class representing a language used by an Interpreter.
*
* @Annotation\Name("language")
* @ORM\Entity(repositoryClass="InterpretersOffice\Entity\Repository\LanguageRepository")
* @ORM\Table(name="languages",uniqueConstraints={@ORM\UniqueConstraint(name="unique_language",columns={"name"})})
*/
class Language
{
/**
* entity id.
*
* @ORM\Id
* @ORM\GeneratedValue @ORM\Column(type="smallint",options={"unsigned":true})
*/
protected $id;
/**
* name of the language.
*
* @ORM\Column(type="string",length=50,nullable=false)
*
* @var string
*/
protected $name;
/**
* comments.
*
* @ORM\Column(type="string",length=300,nullable=false,options={"default":""})
*
* @var string
*/
protected $comments = '';
/**
*
* @ORM\OneToMany(targetEntity="InterpreterLanguage",mappedBy="language")
*/
protected $interpreterLanguages;
/**
* constructor
*/
public function __construct()
{
$this->interpreterLanguages = new ArrayCollection();
}
// setters and getters omitted for brevity
}
这是解释器实体:
<?php
/** module/InterpretersOffice/src/Entity/Interpreter.php */
namespace InterpretersOffice\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
/**
* Entity representing an Interpreter.
*
* @ORM\Entity(repositoryClass="InterpretersOffice\Entity\Repository\InterpreterRepository")
* @ORM\Table(name="interpreters")
*/
class Interpreter extends Person
{
/**
* entity id.
*
* @ORM\Id @ORM\GeneratedValue @ORM\Column(type="smallint",options={"unsigned":true})
*/
protected $id;
/**
* phone number.
*
* @ORM\Column(type="string",length=16,nullable=true)
*
* @var string
*/
protected $phone;
/**
* date of birth.
*
* @ORM\Column(type="date",nullable=true)
*
* @var string
*/
protected $dob;
/**
* working languages.
*
* @ORM\OneToMany(targetEntity="InterpreterLanguage",mappedBy="interpreter", cascade={"persist", "remove"})
*
*
* @var ArrayCollection of InterpreterLanguage
*/
protected $interpreterLanguages;
/**
* Constructor.
*/
public function __construct()
{
$this->interpreterLanguages = new ArrayCollection();
}
// some boring setters and getters omitted....
/**
* Add interpreterLanguage.
*
* @param InterpreterLanguage $interpreterLanguage
*
* @return Interpreter
*/
public function addInterpreterLanguage(InterpreterLanguage $interpreterLanguage)
{
$this->interpreterLanguages->add($interpreterLanguage);
return $this;
}
/**
* Remove interpreterLanguage.
*
* @param \InterpretersOffice\Entity\InterpreterLanguage $interpreterLanguage
*
* @return Interpreter
*/
public function removeInterpreterLanguage(InterpreterLanguage $interpreterLanguage)
{
$this->interpreterLanguages->removeElement($interpreterLanguage);
//$interpreterLanguage->setInterpreter(null)->setLanguage(null);
return $this;
}
/**
* Get interpreterLanguages.
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getInterpreterLanguages()
{
return $this->interpreterLanguages;
}
/*
because "AllowRemove strategy for DoctrineModule hydrator requires both addInterpreterLanguages and removeInterpreterLanguages to be defined in InterpretersOffice\Entity\Interpreter entity domain code, but one or both
[seemed] to be missing"
*/
public function addInterpreterLanguages(Collection $interpreterLanguages)
{
foreach ($interpreterLanguages as $interpreterLanguage) {
$interpreterLanguage->setInterpreter($this);
$this->interpreterLanguages->add($interpreterLanguage);
}
}
public function removeInterpreterLanguages(Collection $interpreterLanguages)
{
foreach ($interpreterLanguages as $interpreterLanguage) {
$this->interpreterLanguages->removeElement($interpreterLanguage);
}
}
}
和协会 class:
/** module/InterpretersOffice/src/Entity/InterpreterLanguage.php */
namespace InterpretersOffice\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Entity representing an Interpreter's Language.
*
* Technically, it is a language *pair*, but in this system it is understood that
* the other language of the pair is English. There is a many-to-many relationship
* between interpreters and languages. But because there is also metadata to record
* about the language (federal certification), it is implemented as a Many-To-One
* relationship on either side.
*
* @ORM\Entity
* @ORM\Table(name="interpreters_languages")
*/
class InterpreterLanguage
{
/**
* constructor.
*
* @param Interpreter $interpreter
* @param Language $language
*
* @todo a lifecycle callback to ensure certified languages have a boolean
* $federalCertification set
*/
public function __construct(
Interpreter $interpreter = null,
Language $language = null
) {
if ($interpreter) {
$this->setInterpreter($interpreter);
}
if ($language) {
$this->setLanguage($language);
}
}
/**
* The Interpreter who works in this language.
*
* @ORM\ManyToOne(targetEntity="Interpreter",inversedBy="interpreterLanguages")
* @ORM\Id
*
* @var Interpreter
*/
protected $interpreter;
/**
* The language in which this interpreter works.
*
* @ORM\ManyToOne(targetEntity="Language",inversedBy="interpreterLanguages")
* @ORM\Id
*
* @var Language
*/
protected $language;
/**
* Whether the Interpreter holds federal court interpreter certification in this language.
*
* The only certified languages in the US District Court system are Spanish,
* Navajo and Haitian Creole. Of these, only the Spanish certification
* program is active. This field should be a boolean for the certified
* languages and null for everything else.
*
* @link http://www.uscourts.gov/services-forms/federal-court-interpreters/federal-court-interpreter-certification-examination the federal court certification program
*
* @ORM\Column(name="federal_certification",type="boolean",nullable=true)
*
* @var bool
*/
protected $federalCertification;
/**
* Set interpreter.
*
* @param \InterpretersOffice\Entity\Interpreter $interpreter
*
* @return InterpreterLanguage
*/
public function setInterpreter(Interpreter $interpreter = null)
{
$this->interpreter = $interpreter;
return $this;
}
/**
* Get interpreter.
*
* @return Interpreter
*/
public function getInterpreter()
{
return $this->interpreter;
}
/**
* Set language.
*
* @param Language $language
*
* @return InterpreterLanguage
*/
public function setLanguage(Language $language = null)
{
$this->language = $language;
return $this;
}
/**
* Get language.
*
* @return Language
*/
public function getLanguage()
{
return $this->language;
}
/**
* Set federalCertification.
*
* @param bool $federalCertification
*
* @return InterpreterLanguage
*/
public function setFederalCertification($federalCertification)
{
$this->federalCertification = $federalCertification;
return $this;
}
/**
* Get federalCertification.
*
* @return bool
*/
public function getFederalCertification()
{
return $this->federalCertification;
}
}
为了简洁起见,我将省略 Form 和 Fieldset classes 的代码——它们似乎工作正常(看起来也很有品位。谢谢 Bootstrap) .我加载表单,删除其中一个 InterpreterLanguages 并提交...这是控制器操作:
/**
* updates an Interpreter entity.
*/
public function editAction()
{
$viewModel = (new ViewModel())
->setTemplate('interpreters-office/admin/interpreters/form.phtml')
->setVariable('title', 'edit an interpreter');
$id = $this->params()->fromRoute('id');
$entity = $this->entityManager->find('InterpretersOffice\Entity\Interpreter', $id);
if (!$entity) {
return $viewModel->setVariables(['errorMessage' => "interpreter with id $id not found"]);
}
$form = new InterpreterForm($this->entityManager, ['action' => 'update']);
$form->bind($entity);
$viewModel->setVariables(['form' => $form, 'id' => $id ]);
$request = $this->getRequest();
if ($request->isPost()) {
$form->setData($request->getPost());
if (!$form->isValid()) {
return $viewModel;
}
$this->entityManager->flush();
$this->flashMessenger()
->addSuccessMessage(sprintf(
'The interpreter <strong>%s %s</strong> has been updated.',
$entity->getFirstname(),
$entity->getLastname()
));
// dump the entity and see how it looksa after update
echo "NOT redirecting. entity:<pre>";
\Doctrine\Common\Util\Debug::dump($entity); echo "</pre>";
//$this->redirect()->toRoute('interpreters');
} else {
// dump the entity fresh from the database
echo "loaded:<pre> "; \Doctrine\Common\Util\Debug::dump($entity);echo "</pre>";}
return $viewModel;
}
同样,数据在转储到屏幕时看起来是正确的,但是您重新加载表单并且集合中的元素与以前一样多。
谢谢!
在 Interpreter.php 中,orphanRemoval=true !!
/**
* working languages.
*
* @ORM\OneToMany(targetEntity="InterpreterLanguage",mappedBy="interpreter",
* cascade={"persist", "remove"},orphanRemoval=true)
*
* @var ArrayCollection of InterpreterLanguage
*/
protected $interpreterLanguages;
我开始怀疑这行不通,因为在我的用例中它根本行不通——而不是我遗漏了一些东西——但我必须咨询你的专业知识来确定,并看看如果有人可以建议解决方法。
我有一个多对多的情况,我正在使用一个关联 class 来实现,所以我们在 3 个参与的 class 之间有 one-to-many/many-to-one 个关联。有一个代表人的解释器实体和一个代表口语的语言实体(实际上是一对工作语言,但在这个以英语为中心的应用程序中,其中一半被理解为英语)。一个 Interpreter 可以有多种语言,Language 是多个 Interpreter 的工作语言之一。我们需要管理解释器语言的其他属性,因此需要 InterpreterLanguage class.
当我调用 $interpreter->removeInterpreterLanguage($interpreterLanguage);
后跟 $entityManager->flush()
时,内存中解释器实体的 $interpreterLanguages
集合中的元素少了一个,正如您所期望的那样,并且没有错误或抛出异常,但在数据库中发生了以下情况:nothing.
我已经在 MVC 上下文中尝试过此操作,使用 ZendFramework 3 和绑定 Zend\Form\Form
字段集,当这让我抓狂时,我编写了一个 CLI 脚本来尝试检查问题——同样的结果。也许值得注意的是,对于更新标量属性,它工作正常。
我很抱歉没有将 link 包含在我之前阅读的关于这个问题的讨论中——由于某种原因现在找不到了。但我记得有人说它只是行不通,因为 Doctrine 看到另一边的 M:1,因此不会删除,你必须说 $entityManager->remove($object)
才能完成。我的实验性 CLI 脚本似乎证实了这一点。不过,我想排除我做错事的可能性。
有什么想法吗?解决建议?
这是我的语言实体:
/** module/InterpretersOffice/src/Entity/Language.php */
namespace InterpretersOffice\Entity;
use Doctrine\ORM\Mapping as ORM;
use Zend\Form\Annotation;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Entity class representing a language used by an Interpreter.
*
* @Annotation\Name("language")
* @ORM\Entity(repositoryClass="InterpretersOffice\Entity\Repository\LanguageRepository")
* @ORM\Table(name="languages",uniqueConstraints={@ORM\UniqueConstraint(name="unique_language",columns={"name"})})
*/
class Language
{
/**
* entity id.
*
* @ORM\Id
* @ORM\GeneratedValue @ORM\Column(type="smallint",options={"unsigned":true})
*/
protected $id;
/**
* name of the language.
*
* @ORM\Column(type="string",length=50,nullable=false)
*
* @var string
*/
protected $name;
/**
* comments.
*
* @ORM\Column(type="string",length=300,nullable=false,options={"default":""})
*
* @var string
*/
protected $comments = '';
/**
*
* @ORM\OneToMany(targetEntity="InterpreterLanguage",mappedBy="language")
*/
protected $interpreterLanguages;
/**
* constructor
*/
public function __construct()
{
$this->interpreterLanguages = new ArrayCollection();
}
// setters and getters omitted for brevity
}
这是解释器实体:
<?php
/** module/InterpretersOffice/src/Entity/Interpreter.php */
namespace InterpretersOffice\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
/**
* Entity representing an Interpreter.
*
* @ORM\Entity(repositoryClass="InterpretersOffice\Entity\Repository\InterpreterRepository")
* @ORM\Table(name="interpreters")
*/
class Interpreter extends Person
{
/**
* entity id.
*
* @ORM\Id @ORM\GeneratedValue @ORM\Column(type="smallint",options={"unsigned":true})
*/
protected $id;
/**
* phone number.
*
* @ORM\Column(type="string",length=16,nullable=true)
*
* @var string
*/
protected $phone;
/**
* date of birth.
*
* @ORM\Column(type="date",nullable=true)
*
* @var string
*/
protected $dob;
/**
* working languages.
*
* @ORM\OneToMany(targetEntity="InterpreterLanguage",mappedBy="interpreter", cascade={"persist", "remove"})
*
*
* @var ArrayCollection of InterpreterLanguage
*/
protected $interpreterLanguages;
/**
* Constructor.
*/
public function __construct()
{
$this->interpreterLanguages = new ArrayCollection();
}
// some boring setters and getters omitted....
/**
* Add interpreterLanguage.
*
* @param InterpreterLanguage $interpreterLanguage
*
* @return Interpreter
*/
public function addInterpreterLanguage(InterpreterLanguage $interpreterLanguage)
{
$this->interpreterLanguages->add($interpreterLanguage);
return $this;
}
/**
* Remove interpreterLanguage.
*
* @param \InterpretersOffice\Entity\InterpreterLanguage $interpreterLanguage
*
* @return Interpreter
*/
public function removeInterpreterLanguage(InterpreterLanguage $interpreterLanguage)
{
$this->interpreterLanguages->removeElement($interpreterLanguage);
//$interpreterLanguage->setInterpreter(null)->setLanguage(null);
return $this;
}
/**
* Get interpreterLanguages.
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getInterpreterLanguages()
{
return $this->interpreterLanguages;
}
/*
because "AllowRemove strategy for DoctrineModule hydrator requires both addInterpreterLanguages and removeInterpreterLanguages to be defined in InterpretersOffice\Entity\Interpreter entity domain code, but one or both
[seemed] to be missing"
*/
public function addInterpreterLanguages(Collection $interpreterLanguages)
{
foreach ($interpreterLanguages as $interpreterLanguage) {
$interpreterLanguage->setInterpreter($this);
$this->interpreterLanguages->add($interpreterLanguage);
}
}
public function removeInterpreterLanguages(Collection $interpreterLanguages)
{
foreach ($interpreterLanguages as $interpreterLanguage) {
$this->interpreterLanguages->removeElement($interpreterLanguage);
}
}
}
和协会 class:
/** module/InterpretersOffice/src/Entity/InterpreterLanguage.php */
namespace InterpretersOffice\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Entity representing an Interpreter's Language.
*
* Technically, it is a language *pair*, but in this system it is understood that
* the other language of the pair is English. There is a many-to-many relationship
* between interpreters and languages. But because there is also metadata to record
* about the language (federal certification), it is implemented as a Many-To-One
* relationship on either side.
*
* @ORM\Entity
* @ORM\Table(name="interpreters_languages")
*/
class InterpreterLanguage
{
/**
* constructor.
*
* @param Interpreter $interpreter
* @param Language $language
*
* @todo a lifecycle callback to ensure certified languages have a boolean
* $federalCertification set
*/
public function __construct(
Interpreter $interpreter = null,
Language $language = null
) {
if ($interpreter) {
$this->setInterpreter($interpreter);
}
if ($language) {
$this->setLanguage($language);
}
}
/**
* The Interpreter who works in this language.
*
* @ORM\ManyToOne(targetEntity="Interpreter",inversedBy="interpreterLanguages")
* @ORM\Id
*
* @var Interpreter
*/
protected $interpreter;
/**
* The language in which this interpreter works.
*
* @ORM\ManyToOne(targetEntity="Language",inversedBy="interpreterLanguages")
* @ORM\Id
*
* @var Language
*/
protected $language;
/**
* Whether the Interpreter holds federal court interpreter certification in this language.
*
* The only certified languages in the US District Court system are Spanish,
* Navajo and Haitian Creole. Of these, only the Spanish certification
* program is active. This field should be a boolean for the certified
* languages and null for everything else.
*
* @link http://www.uscourts.gov/services-forms/federal-court-interpreters/federal-court-interpreter-certification-examination the federal court certification program
*
* @ORM\Column(name="federal_certification",type="boolean",nullable=true)
*
* @var bool
*/
protected $federalCertification;
/**
* Set interpreter.
*
* @param \InterpretersOffice\Entity\Interpreter $interpreter
*
* @return InterpreterLanguage
*/
public function setInterpreter(Interpreter $interpreter = null)
{
$this->interpreter = $interpreter;
return $this;
}
/**
* Get interpreter.
*
* @return Interpreter
*/
public function getInterpreter()
{
return $this->interpreter;
}
/**
* Set language.
*
* @param Language $language
*
* @return InterpreterLanguage
*/
public function setLanguage(Language $language = null)
{
$this->language = $language;
return $this;
}
/**
* Get language.
*
* @return Language
*/
public function getLanguage()
{
return $this->language;
}
/**
* Set federalCertification.
*
* @param bool $federalCertification
*
* @return InterpreterLanguage
*/
public function setFederalCertification($federalCertification)
{
$this->federalCertification = $federalCertification;
return $this;
}
/**
* Get federalCertification.
*
* @return bool
*/
public function getFederalCertification()
{
return $this->federalCertification;
}
}
为了简洁起见,我将省略 Form 和 Fieldset classes 的代码——它们似乎工作正常(看起来也很有品位。谢谢 Bootstrap) .我加载表单,删除其中一个 InterpreterLanguages 并提交...这是控制器操作:
/**
* updates an Interpreter entity.
*/
public function editAction()
{
$viewModel = (new ViewModel())
->setTemplate('interpreters-office/admin/interpreters/form.phtml')
->setVariable('title', 'edit an interpreter');
$id = $this->params()->fromRoute('id');
$entity = $this->entityManager->find('InterpretersOffice\Entity\Interpreter', $id);
if (!$entity) {
return $viewModel->setVariables(['errorMessage' => "interpreter with id $id not found"]);
}
$form = new InterpreterForm($this->entityManager, ['action' => 'update']);
$form->bind($entity);
$viewModel->setVariables(['form' => $form, 'id' => $id ]);
$request = $this->getRequest();
if ($request->isPost()) {
$form->setData($request->getPost());
if (!$form->isValid()) {
return $viewModel;
}
$this->entityManager->flush();
$this->flashMessenger()
->addSuccessMessage(sprintf(
'The interpreter <strong>%s %s</strong> has been updated.',
$entity->getFirstname(),
$entity->getLastname()
));
// dump the entity and see how it looksa after update
echo "NOT redirecting. entity:<pre>";
\Doctrine\Common\Util\Debug::dump($entity); echo "</pre>";
//$this->redirect()->toRoute('interpreters');
} else {
// dump the entity fresh from the database
echo "loaded:<pre> "; \Doctrine\Common\Util\Debug::dump($entity);echo "</pre>";}
return $viewModel;
}
同样,数据在转储到屏幕时看起来是正确的,但是您重新加载表单并且集合中的元素与以前一样多。
谢谢!
在 Interpreter.php 中,orphanRemoval=true !!
/**
* working languages.
*
* @ORM\OneToMany(targetEntity="InterpreterLanguage",mappedBy="interpreter",
* cascade={"persist", "remove"},orphanRemoval=true)
*
* @var ArrayCollection of InterpreterLanguage
*/
protected $interpreterLanguages;