具有zf3字段集的主义表继承

时间:2019-02-16 19:57:12

标签: php doctrine-orm zend-framework2 zend-framework3 fieldset

我正在使用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实体:

/**
 * 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;
}

到目前为止,一切都运行良好,问题是当我需要为该信息生成表单时,我目前正在使用“客户”模块,基本上我所做的是:

  • 使用客户ID + Pessoa字段集创建表单
  • 在Pessoa字段集中,我创建了用于共享信息(用户,地址,联系人等)的字段集
  • 在Pessoa字段集中,它还包括其他两个字段集,每个Pessoa的子类(PessoaFisica和PessoaJuridica)一个-这就是问题所在。

在下面的屏幕中,您可以看到我的注册表: Screen of system

此表单根据使用javascript的选定类型显示或隐藏PessoaJuridica或PessoaFisica的字段集,但是由于它们是表单中的不同字段集,因此zend将它们水合时,它们也被水合为不同的对象,即,继承不是应用于Person对象,应根据类型进行选择。

从我的角度来看,基本上需要发生的事情是,有一种zend的方法,在表单为当前形式时,它不会将引用Person类的子类的字段集呈现为单独的对象使用这些字段进行渲染(例如):

人[fsPeople] [姓名]

人[fsPessoaJuridica] [nameFantasica]

这会导致zend无法生成要保存在数据库中的正确类。

执行表单的正确方法是什么?

1 个答案:

答案 0 :(得分:0)

好吧,@ rkeet的回复帮助我了解了问题出在哪里,这实际上不是问题=]

  

由于继承的使用,您创建了单独的实体。   但是,您最初在后端创建的表单可以使用   单一实体。您已修改以处理2的前端。   前端与您的后端不匹配。因为,由于继承,   您现在有2个独立的实体,应该创建2个独立的表单,   使用不同的字段集(PessoaJuridica或PessoaFisica)作为基础   字段集。

我将离开我遵循的道路,这可能会帮助和我一样有疑问的人。

首先,按照他的评论中解释的逻辑,我为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]
                )
            )
        );
    }

}

最后,我在Controller上进行了实体类型处理,将加载此表单,如下所示:(仅相关部分)

//...
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!