Я работаю над проектом, используя Zend Framework 3 и Doctrine 2, используя для интеграции DcotrineModule следующее моделирование сущностей, с которым у меня проблемы:
Чтобы работать с этим моделированием с помощью доктрины, которую я использую @InheritanceType, ниже приведены соответствующие выдержки из Entities:
Пессоа Сущность:
/**
* Abstração de Pessoa
*
* @author Rodrigo Teixeira Andreotti <[email protected]>
*
* @Entity
* @InheritanceType("JOINED")
* @DiscriminatorColumn(name = "tipo", type = "string")
* @DiscriminatorMap( { "pessoa" = "Pessoa",
* "pessoa_fisica" = "PessoaFisica",
* "pessoa_juridica" = "PessoaJuridica" } )
* @Table(name = "pessoa")
*/
abstract class Pessoa implements JsonSerializable, PessoaInterface
{
use JsonSerializeTrait;
/**
* @Id
* @GeneratedValue(strategy = "IDENTITY")
* @Column(type = "integer", length=32, unique=true, nullable=false, name = "id_pessoa")
* @var integer
*/
protected $idPessoa;
/**
* Usuário
* @OneToOne(targetEntity = "User\Entity\User", inversedBy = "pessoa", cascade = {"persist"})
* @JoinColumn(name = "usuario", referencedColumnName = "id")
*
* @var User
*/
protected $usuario;
/**
* @OneToOne(targetEntity = "EnderecoPessoa", mappedBy = "pessoa", cascade = {"persist"})
* @var EnderecoPessoa
*/
protected $endereco;
/**
* Contatos da pessoa
* @OneToMany(targetEntity = "ContatoPessoa", mappedBy = "pessoa", cascade = {"persist"}, orphanRemoval=true)
* @var ArrayCollection|array
*/
protected $contatos;
const PESSOA_FISICA = "pessoa_fisica", PESSOA_JURIDICA = "pessoa_juridica";
public function __construct()
{
$this->contatos = new ArrayCollection();
}
}
Компания PessoaFisica:
/**
* Abstração da pessoa física
*
* @Entity
* @Table(name = "pessoa_fisica")
* @author Rodrigo Teixeira Andreotti <[email protected]>
*/
class PessoaFisica extends Pessoa implements JsonSerializable {
use JsonSerializeTrait;
/**
* Nome da pessoa física
* @Column(type = "string", length=14)
* @var string
*/
private $nome;
/**
* Número do CPF da pessoa (quando brasileiro)
* @Column(type = "string", length=14)
* @var string
*/
private $cpf;
/**
* Número do RG (quando brasileiro)
* @Column(type = "string", length=13)
* @var string
*/
private $rg;
/**
* Data de nascimento
* @Column(type = "date", name = "data_nascimento")
* @var DateTime
*/
private $dataNascimento;
}
ПессоаЮридика Юридическое лицо:
/**
* Abstração de Pessoa Jurídica
*
* @Entity
* @Table(name = "pessoa_juridica")
* @InheritanceType("JOINED")
* @author Rodrigo Teixeira Andreotti <[email protected]>
*/
class PessoaJuridica extends Pessoa implements JsonSerializable {
use JsonSerializeTrait;
/**
* @Id
* @GeneratedValue(strategy = "IDENTITY")
* @Column(type = "integer", length=32, unique=true, nullable=false, name = "id_pessoa")
* @var integer
*/
protected $idPessoa;
/**
* Nome fantasia
* @Column(type = "string", length=32, name = "nome_fantasia")
* @var String
*/
protected $nomeFantasia;
/**
* Número do CNPJ
* @Column(type = "string", length=14, unique=true, name = "cnpj")
* @var string
*/
protected $cnpj;
/**
* Razão social da empresa
* @Column(type = "string", length=32, name = "razao_social")
* @var string Razão social da empresa, quando necessário
*/
protected $razaoSocial;
}
Пока все работает отлично, проблема в том, что когда мне нужно создать форму для этой информации, я сейчас работаю над модулем «Клиент», в основном то, что я сделал для него, было:
На экране ниже вы можете увидеть мою регистрационную форму:

Эта форма отображает или скрывает набор полей PessoaJuridica или PessoaFisica в соответствии с выбранным типом с использованием javascript, однако, поскольку они представляют собой разные наборы полей в форме, когда Zend гидратирует их, они также гидратируются как разные объекты, т. е. наследование не применяется к Объект Person, который должен быть выбран в соответствии с типом.
В основном, с моей точки зрения, должно было бы произойти, чтобы Zend мог не отображать наборы полей, ссылающиеся на дочерние классы класса Person, как на отдельные объекты, в тот момент, когда форма отображается с этими поля так (например):
человек [fsPeople] [имя]
человек [fsPessoaJuridica] [имяFantasica]
И это приводит к тому, что Zend не генерирует правильный класс для сохранения в базе данных.
Каков был бы правильный способ сделать эту реализацию формы?
Кроме того, вы можете удалить DiscriminatorMap, если вы не создадите его, Doctrine автоматически сгенерирует его для вас (так будет меньше ошибок).
Кроме того, вы объявляете "pessoa" = "Pessoa" в своем DiscriminatorMap, этого никогда не может произойти, поскольку вы сделали Pessoa класс abstract.
привет, спасибо за предложения! Получит удовольствие! Я действительно думал о создании двух форм или, в случае, наборов полей, разделенных для этого, но я подумал, что есть какой-то способ заставить эти наборы полей не генерировать отдельные объекты, но я думаю, что я буду использовать некоторую процедуру ajax для перезагрузки всего форму в соответствии с выбранным типом.
Я понятия не имел, что доктрина автоматически генерирует карту дискриминации, я собираюсь внести это изменение, и человек = человек, которого я поставил только потому, что доктрина сообщила мне об ошибках, но я проверю, решит ли это удаление карты дискриминации, это решит этот вопрос . Спасибо! С Уважением!
Конечно вещь. Если вы не можете понять это, обновите вопрос, указав, что вы пробовали и с чем столкнулись, это даст мне еще одно уведомление;)
Вы поняли это? Если это так, пожалуйста, напишите ответ, чтобы помочь другим. Если нет, не могли бы вы либо обновить вопрос, либо удалить его и создать новый?
Привет друг! Я собирался прийти сюда, чтобы прокомментировать, сегодня я смог закончить тесты, мне не хватило времени для работы над этим проектом (который является частным), но я реализовал два набора полей для человека (PersonFisica и PessoaJuridica), оба расширяющие персональный Fieldset, поля, которые не повторяются, и я перезагружаю форму во время выполнения и ajax, то есть, когда пользователь выбирает тип человека, я ищу новую форму по ajax и заменяю предыдущую форму, естественно, на правильную данные, как я хотел бы. Спасибо большое за вашу помощь! Я создам подробный ответ, чтобы оставить в качестве ссылки.






Что ж, ответ от @rkeet очень помог мне понять, в чем проблема, что на самом деле не проблема =]
Due to the usage of inheritance, you've created separate Entities. However, the form you initially create in the back-end works with a single Entity. The front-end you've modified to handle 2. So your front-end does not match your back-end. As, due to the inheritance, you now have 2 separate Entities, you should create 2 separate forms, using different fieldsets (PessoaJuridica or PessoaFisica) as the base fieldsets.
Я оставлю путь, которым я следовал здесь, это может помочь кому-то с такими же сомнениями, как и я.
Во-первых, следуя логике, описанной в его комментарии, я создал абстрактный набор полей для PessoaEntity с информацией, совместно используемой двумя типами людей, и расширил его до двух дочерних классов PessoaFisicaFieldset и PessoaJuridicaFieldset, которые я описываю ниже:
/**
* Fieldset com dados para a pessoa
*
* @author Rodrigo Teixeira Andreotti <[email protected]>
*/
abstract class PessoaFieldset extends Fieldset implements InputFilterProviderInterface
{
private $em;
private $userFs;
private $enderecoFs;
private $contatoFs;
public function __construct(ObjectManager $em,
UserFieldset $userFs,
PessoaEnderecoFieldset $enderecoFs,
ContatoFieldset $contatoFs)
{
parent::__construct('pessoa');
$this->em = $em;
$this->userFs = $userFs;
$this->enderecoFs = $enderecoFs;
$this->contatoFs = $contatoFs;
$this->init();
}
protected function getEm()
{
return $this->em;
}
public function init()
{
$this
->setHydrator(new DoctrineObject($this->getEm()));
$this->add(array(
'type' => 'Hidden',
'name' => 'id_pessoa',
'attributes' => array(
'id' => 'txtId'
)
));
$this->add(array(
'type' => 'hidden',
'name' => 'tipo',
));
$this->add($this->userFs);
$this->add($this->enderecoFs);
$elCollection = new Collection;
$elCollection
->setName('contatos')
->setLabel('Informações de Contato')
->setCount(1)
->setShouldCreateTemplate(true)
->setAllowAdd(true)
->setAllowRemove(true)
->setTargetElement($this->contatoFs);
$this->add($elCollection);
$this->add(array(
'type' => 'Button',
'name' => 'btAddContato',
'options' => array(
'label' => '<i class = "fa fa-fw fa-plus"></i> Adicionar',
'label_options' => array(
'disable_html_escape' => true
)
),
'attributes' => array(
'class' => 'btn btn-info',
'id' => 'btAddContato'
)
));
}
public function getInputFilterSpecification(): array
{
return array(
'id_pessoa' => array(
'required' => false,
'filters' => array(
['name'=>'Int']
)
),
'tipo' => array(
'required' => true,
)
);
}
}
Это мой класс PessoaFisicaFieldset.
/**
* Fieldset com dados para a pessoa Física
*
* @author Rodrigo Teixeira Andreotti <[email protected]>
*/
class PessoaFisicaFieldset extends PessoaFieldset implements InputFilterProviderInterface
{
private $em;
public function __construct(ObjectManager $em,
\User\Form\UserFieldset $userFs,
PessoaEnderecoFieldset $enderecoFs,
\Common\Form\ContatoFieldset $contatoFs)
{
parent::__construct($em, $userFs, $enderecoFs, $contatoFs);
$this->init();
}
public function init()
{
parent::init();
$this
->setObject(new PessoaFisica());
$this->get('tipo')->setValue(\Pessoa\Entity\Pessoa::PESSOA_FISICA);
$this->add(array(
'type' => 'Text',
'name' => 'cpf',
'options' => array(
'label' => 'CPF',
'label_attributes' => array(
'class' => 'col-sm-12'
)
),
'attributes' => array(
'class' => 'form-control form-control-line',
'id' => 'txtCpf'
)
));
$this->add(array(
'type' => 'Text',
'name' => 'nome',
'options' => array(
'label' => 'Nome',
'label_attributes' => array(
'class' => 'col-sm-12'
)
),
'attributes' => array(
'class' => 'form-control form-control-line',
'id' => 'txtNome'
)
));
$this->add(array(
'type' => 'Text',
'name' => 'rg',
'options' => array(
'label' => 'RG',
'label_attributes' => array(
'class' => 'col-sm-12'
)
),
'attributes' => array(
'class' => 'form-control form-control-line',
'id' => 'txtRazaoSocial'
)
));
$this->add(array(
'type' => 'DateTime',
'name' => 'dataNascimento',
'options' => array(
'format' => 'd/m/Y',
'label' => 'Data de Nascimento',
'label_attributes' => array(
'class' => 'col-sm-12'
)
),
'attributes' => array(
'class' => 'form-control form-control-line data',
)
));
}
public function getInputFilterSpecification(): array
{
return array(
'nome' => array(
'required' => true,
'filters' => array(
['name' => 'StripTags'],
['name' => 'StringTrim']
)
),
'rg' => array(
'required' => false,
'filters' => array(
['name' => 'StripTags'],
['name' => 'StringTrim']
)
),
'cpf' => array(
'required' => false,
'filters' => array(
['name' => 'StripTags'],
['name' => 'StringTrim']
),
'validators' => array(
['name' => CpfValidator::class]
)
),
'dataNascimento' => array(
'required' => true,
'filters' => array(
array(
'name' => 'Zend\Filter\DatetimeFormatter',
'options' => array (
'format' => 'd/m/Y',
),
),
),
'validators' => array(
array(
'name' => Date::class,
'options' => array(
'format' => 'd/m/Y'
)
)
)
)
);
}
}
А вот и мой PessoaJuridicaFieldset
/**
* Fieldset com dados específicos para a pessoa jurídica
*
* @author Rodrigo Teixeira Andreotti <[email protected]>
*/
class PessoaJuridicaFieldset extends PessoaFieldset implements InputFilterProviderInterface
{
public function __construct(ObjectManager $em,
\User\Form\UserFieldset $userFs, PessoaEnderecoFieldset $enderecoFs,
\Common\Form\ContatoFieldset $contatoFs)
{
parent::__construct($em, $userFs, $enderecoFs, $contatoFs);
$this->init();
}
public function init()
{
parent::init();
$this
->setObject(new PessoaJuridica());
$this->get('tipo')->setValue(\Pessoa\Entity\Pessoa::PESSOA_JURIDICA);
$this->add(array(
'type' => 'Text',
'name' => 'cnpj',
'options' => array(
'label' => 'CNPJ',
'label_attributes' => array(
'class' => 'col-sm-12'
)
),
'attributes' => array(
'class' => 'form-control form-control-line',
'id' => 'txtCnpj'
)
));
$this->add(array(
'type' => 'Text',
'name' => 'razaoSocial',
'options' => array(
'label' => 'Razão Social',
'label_attributes' => array(
'class' => 'col-sm-12'
)
),
'attributes' => array(
'class' => 'form-control form-control-line',
'id' => 'txtRazaoSocial'
)
));
$this->add(array(
'type' => 'Text',
'name' => 'nomeFantasia',
'options' => array(
'label' => 'Nome Fantasia',
'label_attributes' => array(
'class' => 'col-sm-12'
)
),
'attributes' => array(
'class' => 'form-control form-control-line',
'id' => 'txtNomeFantasia'
)
));
}
public function getInputFilterSpecification(): array
{
return array(
'razaoSocial' => array(
'required' => true,
'filters' => array(
['name' => 'StripTags'],
['name' => 'StringTrim']
)
),
'nomeFantasia' => array(
'required' => true,
'filters' => array(
['name' => 'StripTags'],
['name' => 'StringTrim']
)
),
'cnpj' => array(
'required' => true,
'filters' => array(
['name' => 'StripTags'],
['name' => 'StringTrim']
),
'validators' => array(
['name' => CnpjValidator::class]
)
)
);
}
}
И для завершения я выполнил обработку типа объекта в контроллере, который загрузит эту форму, как показано ниже: (только соответствующие части)
//...
if ($id) {
$cliente = $this->repository->getById($id);
$form->remove('pessoa');
// loads form according to the type loaded from the database
if (!$request->isXmlHttpRequest()) {
if ($cliente->getPessoa() instanceof \Pessoa\Entity\PessoaFisica) {
$form->add($this->pessoaFisicaFieldset);
} elseif ($cliente->getPessoa() instanceof \Pessoa\Entity\PessoaJuridica) {
$form->add($this->pessoaJuridicaFieldset);
}
var_dump($cliente->getPessoa());
}
$form->bind($cliente);
}
if ($request->isPost()) {
$form->remove('pessoa');
// loads form according to the type selected in the post
if ($request->getPost('tipo') == \Pessoa\Entity\Pessoa::PESSOA_FISICA) {
$form->add($this->pessoaFisicaFieldset);
} elseif ($request->getPost('tipo') == \Pessoa\Entity\Pessoa::PESSOA_JURIDICA) {
$form->add($this->pessoaJuridicaFieldset);
}
$form->get('tipo')->setValue($request->getPost('tipo'));
$form->setData($request->getPost());
if (!$request->isXmlHttpRequest()) {
if ($form->isValid()) {
$cliente = $form->getObject();
if ($cliente->getId() != 0) {
$cliente->getPessoa()->setCadastradoEm(new \DateTime);
}
// ...
}
}
}
//...
Еще раз спасибо @rket!
Из-за использования наследования вы создали отдельные сущности. Однако форма, которую вы изначально создаете в бэкэнде, работает с одной сущностью. Внешний интерфейс, который вы изменили, чтобы обрабатывать 2. Таким образом, ваш внешний интерфейс не соответствует вашему внутреннему. Поскольку из-за наследования у вас теперь есть 2 отдельных объекта, вы должны создать 2 отдельные формы, используя разные наборы полей (PessoaJuridica или PessoaFisica) в качестве базовых наборов полей.