Я использую Zend Framework 3 с Doctrine и пытаюсь сохранить объект "Cidade", связанный с другим объектом "Estado", который уже хранится в базе данных. Однако Doctrine пытается сохранить Entity «Estado», и единственный атрибут, который у меня есть от Estado, - это первичный ключ в комбинации HTML.
Мои формы представления построены в формах и наборах полей Zend, что означает, что данные POST автоматически преобразуются в целевые сущности с помощью гидратора ClassMethods.
Проблема в том, что если я установил атрибут $estado
с cascade={"persist"}
в Cidade Entity, Doctrine попытается сохранить в Estado Entity все необходимые атрибуты, кроме идентификатора первичного ключа, который поступает из запроса POST (комбинация HTML). Я также рассматривал возможность использования cascade={"detach"}
ir для того, чтобы Doctrine игнорировала объект Estado в EntityManager. Но я получаю такую ошибку:
Была обнаружена новая сущность через отношение Application \ Entity \ Cidade # estado, которая не была настроена для каскадного сохранения операций для объекта: Application \ Entity \ Estado @ 000000007598ee720000000027904e61.
Я нашел аналогичное сомнение здесь и единственный способ найти по этому поводу сначала извлекал Estado Entity и устанавливал его на Cidade Entity перед сохранением. Если это единственный способ, могу ли я сказать, что моя структура формы не будет работать, если я не извлечу все отношения перед сохранением зависимых сущностей? Другими словами, как лучше всего сделать это в Doctrine (например):
<?php
/*I'm simulating the creation of Estado Entity representing an
existing Estado in database, so "3" is the ID rendered in HTML combo*/
$estado = new Entity\Estado();
$estado->setId(3);
$cidade = new Entity\Cidade();
$cidade->setNome("City Test");
$cidade->setEstado($estado); //relationship here
$entityManager->persist($cidade);
$entityManager->flush();
Как это сделать, не возвращая Estado все время, когда мне нужно спасти Cidade? Не повлияет на производительность?
Моя сущность Cidade:
<?php
namespace Application\Entity;
use Zend\InputFilter\Factory;
use Zend\InputFilter\InputFilterInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* Class Cidade
* @package Application\Entity
* @ORM\Entity
*/
class Cidade extends AbstractEntity
{
/**
* @var string
* @ORM\Column(length=50)
*/
private $nome;
/**
* @var Estado
* @ORM\ManyToOne(targetEntity="Estado", cascade={"detach"})
* @ORM\JoinColumn(name="id_estado", referencedColumnName="id")
*/
private $estado;
/**
* Retrieve input filter
*
* @return InputFilterInterface
*/
public function getInputFilter()
{
if (!$this->inputFilter) {
$factory = new Factory();
$this->inputFilter = $factory->createInputFilter([
"nome" => ["required" => true]
]);
}
return $this->inputFilter;
}
/**
* @return string
*/
public function getNome()
{
return $this->nome;
}
/**
* @param string $nome
*/
public function setNome($nome)
{
$this->nome = $nome;
}
/**
* @return Estado
*/
public function getEstado()
{
return $this->estado;
}
/**
* @param Estado $estado
*/
public function setEstado($estado)
{
$this->estado = $estado;
}
}
Моя компания Estado:
<?php
namespace Application\Entity;
use Doctrine\ORM\Mapping as ORM;
use Zend\InputFilter\Factory;
use Zend\InputFilter\InputFilterInterface;
/**
* Class Estado
* @package Application\Entity
* @ORM\Entity
*/
class Estado extends AbstractEntity
{
/**
* @var string
* @ORM\Column(length=50)
*/
private $nome;
/**
* @var string
* @ORM\Column(length=3)
*/
private $sigla;
/**
* @return string
*/
public function getNome()
{
return $this->nome;
}
/**
* @param string $nome
*/
public function setNome($nome)
{
$this->nome = $nome;
}
/**
* @return string
*/
public function getSigla()
{
return $this->sigla;
}
/**
* @param string $sigla
*/
public function setSigla($sigla)
{
$this->sigla = $sigla;
}
/**
* Retrieve input filter
*
* @return InputFilterInterface
*/
public function getInputFilter()
{
if (!$this->inputFilter) {
$factory = new Factory();
$this->inputFilter = $factory->createInputFilter([
"nome" => ["required" => true],
"sigla" => ["required" => true]
]);
}
return $this->inputFilter;
}
}
Обе сущности расширяют мой суперкласс AbstractEntity:
<?php
namespace Application\Entity;
use Doctrine\ORM\Mapping\MappedSuperclass;
use Doctrine\ORM\Mapping as ORM;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
/**
* Class AbstractEntity
* @package Application\Entity
* @MappedSuperClass
*/
abstract class AbstractEntity implements InputFilterAwareInterface
{
/**
* @var int
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
protected $id;
/**
* @var InputFilterAwareInterface
*/
protected $inputFilter;
/**
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* @param int $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* @param InputFilterInterface $inputFilter
* @return InputFilterAwareInterface
* @throws \Exception
*/
public function setInputFilter(InputFilterInterface $inputFilter)
{
throw new \Exception("Método não utilizado");
}
}
Мои входные данные HTML отображаются следующим образом:
<input name="cidade[nome]" class="form-control" value="" type="text">
<select name="cidade[estado][id]" class="form-control">
<option value="3">Bahia</option>
<option value="2">Espírito Santo</option>
<option value="1">Minas Gerais</option>
<option value="9">Pará</option>
</select>
Каждый option
выше - это объект Estado, полученный из базы данных. Мои данные POST представлены в следующем примере:
[
"cidade" => [
"nome" => "Test",
"estado" => [
"id" => 3
]
]
]
В методе isValid()
Zend Form эти данные POST автоматически преобразуются в целевые сущности, что приводит к сбою в этой проблеме с Doctrine. Как мне двигаться дальше?