Доктрина молча не в состоянии удалить сущности на стороне M 1:M
Я начинаю подозревать, что это не работает, потому что в моем случае использования это просто - в отличие от того, что я что-то упустил, - но я должен проконсультироваться с вашим опытом, чтобы убедиться, и чтобы узнать, может ли кто-нибудь предложить обходной путь,
У меня есть ситуация "многие ко многим", которую я реализую с помощью класса ассоциаций, поэтому между 3 участвующими классами у нас есть связи "один ко многим / многие к одному". Существует сущность интерпретатора, представляющая человека, и сущность языка, представляющая разговорный язык (на самом деле это пара рабочих языков, но в этой англоцентрической программе считается, что половина этой пары считается английской). Переводчик может иметь несколько языков, а язык является одним из рабочих языков нескольких переводчиков. Нам нужно управлять другими атрибутами языка интерпретатора, следовательно, классом InterpreterLanguage.
Когда я звоню $interpreter->removeInterpreterLanguage($interpreterLanguage);
с последующим $entityManager->flush()
объект интерпретатора в памяти имеет на один элемент меньше $interpreterLanguages
сбор, как и следовало ожидать, и нет ошибок или исключений, но в базе данных вот что происходит: ничего.
Я пробовал это в контексте MVC, с ZendFramework 3 и связанной Zend\Form\Form
с полевыми наборами, и когда это сводило меня с ума, я написал скрипт CLI, чтобы попытаться изучить проблему - тот же результат. Возможно стоит отметить, что для обновления скалярных свойств все работает нормально.
Я прошу прощения за то, что не включил ссылку на обсуждение этой проблемы, которую я прочитал ранее - по какой-то причине не могу найти ее сейчас. Но я вспоминаю, как кто-то говорил, что это просто не работает, потому что 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);
}
}
}
и класс ассоциации:
/** 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 - кажется, что они работают нормально (выглядят со вкусом тоже. Спасибо 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;
}
Опять же, данные выглядят правильно, поскольку они выводятся на экран, но вы перезагружаете форму, и коллекция содержит столько же элементов, что и раньше.
Спасибо!
1 ответ
В Interpreter.php orphanRemoval=true!!
/**
* working languages.
*
* @ORM\OneToMany(targetEntity="InterpreterLanguage",mappedBy="interpreter",
* cascade={"persist", "remove"},orphanRemoval=true)
*
* @var ArrayCollection of InterpreterLanguage
*/
protected $interpreterLanguages;