Доктрина молча не в состоянии удалить сущности на стороне 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;
Другие вопросы по тегам