Наследование таблиц доктрин с набором полей zf3 - PullRequest
0 голосов
/ 16 февраля 2019

Я работаю над проектом, использующим Zend Framework 3 и Doctrine 2, использующим для интеграции DcotrineModule, следующее моделирование сущностей, с которым у меня проблемы:

Entity Model

Для работы с этим моделированием с помощью доктрины, которую я использую @InheritanceType, ниже приведены соответствующие выдержки из сущностей:

Сущность Пессоа:

/**
 * Abstração de Pessoa
 *
 * @author Rodrigo Teixeira Andreotti <ro.andriotti@gmail.com>
 * 
 * @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 <ro.andriotti@gmail.com>
 */
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;
}

PessoaJuridica Entity:

/**
 * Abstração de Pessoa Jurídica
 * 
 * @Entity
 * @Table(name="pessoa_juridica")
 * @InheritanceType("JOINED")
 * @author Rodrigo Teixeira Andreotti <ro.andriotti@gmail.com>
 */
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;
}

Пока все работает идеально, проблема в том, что когда мне нужно создать форму для этой информации, я сейчас работаю над "Клиентский модуль, в основном то, что я сделал для него:

  • Создание формы с идентификатором клиента + Pessoa Fieldset
  • В Pessoa Fieldset я создал наборы полей для общей информации (пользователь, адрес, контакты и т. д.)
  • В наборе полей Pessoa он также включает два других набора полей, по одному для каждого дочернего класса Pessoa (PessoaFisica и PessoaJuridica) - и вот в чем проблема.

На приведенном ниже экране вы можете увидеть мою регистрационную форму: Screen of system

Эта форма отображает или скрывает набор полей PessoaJuridica или PessoaFisica в соответствии с выбранным типомиспользуя javascript, однако, поскольку они являются различными наборами полей в форме, когда zend их гидратирует, они также гидратируются как разные объекты, то есть наследование не применяется к объекту Person, который следует выбирать в соответствии с типом.

По сути, что, на мой взгляд, должно было бы произойти, это то, что у Zend есть способ не отображать наборы полей, ссылающиеся на дочерние классы класса Person, как отдельные объекты, в момент отображения формыс этими полями так (например):

person [fsPeople] [name]

person [fsPessoaJuridica] [nameFantasica]

И это заставляет Zend не генерироватьправильный класс для сохранения в базе данных.

Каков будет правильный способ сделать это реализация oв форме?

1 Ответ

0 голосов
/ 26 февраля 2019

Что ж, ответ @rkeet очень помог мне понять, в чем проблема, что на самом деле не проблема =]

Из-за использования наследования вы создалиотдельные сущности.Однако форма, которую вы изначально создаете в бэкэнде, работает с одним объектом.Внешний интерфейс, который вы изменили для обработки 2. Таким образом, ваш внешний интерфейс не соответствует вашему внутреннему.Поскольку из-за наследования у вас теперь есть 2 отдельных объекта, вы должны создать 2 отдельных формы, используя разные наборы полей (PessoaJuridica или PessoaFisica) в качестве базовых наборов полей.

Я оставлю путь Iдалее, это может помочь кому-то с теми же сомнениями, что и у меня.

Сначала, следуя логике, объясненной в его комментарии, я создал абстрактный набор полей для PessoaEntity с информацией, разделяемой между двумя типами людей, ирасширил его до двух дочерних классов PessoaFisicaFieldset и PessoaJuridicaFieldset, которые я опишу ниже:

/**
 * Fieldset com dados para a pessoa
 *
 * @author Rodrigo Teixeira Andreotti <ro.andriotti@gmail.com>
 */
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 <ro.andriotti@gmail.com>
 */
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 <ro.andriotti@gmail.com>
 */
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);
                    }

                    // ...
                }
            }
        }
//...

Опять же, спасибо @rkeet!

...