{"id":144,"date":"2017-09-10T03:56:17","date_gmt":"2017-09-10T06:56:17","guid":{"rendered":"http:\/\/hackerzone.com.br\/blog\/?p=144"},"modified":"2022-08-13T00:20:46","modified_gmt":"2022-08-13T03:20:46","slug":"doctrine-relacao-muitos-para-muitos-no-zend-framework-3","status":"publish","type":"post","link":"http:\/\/hackerzone.com.br\/blog\/doctrine-relacao-muitos-para-muitos-no-zend-framework-3\/","title":{"rendered":"Doctrine &#8211; Rela\u00e7\u00e3o muitos para muitos no Zend Framework 3"},"content":{"rendered":"<p>Vou demonstrar atrav\u00e9s de exemplos pr\u00e1ticos um relacionamento ManyToMany ou em nosso querido portugu\u00eas &#8220;muitos para muitos&#8221; no Doctrine com Zend Framework 3+<br \/>\nEste tutorial foi resultado de estudos referente a ferramenta, portanto serei breve e sem muitos detalhes j\u00e1 que n\u00e3o possuo conhecimento avan\u00e7ado sobre a ferramenta.<\/p>\n<p>Primeiro vamos gerar nossas entidades:<br \/>\nVeiculo<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\n&lt;?php\r\n\r\nnamespace MODVeiculo\\Entity;\r\n\r\nuse Doctrine\\ORM\\Mapping as ORM;\r\nuse Zend\\Hydrator;\r\n\r\n\/**\r\n * ModveiculoVeiculos\r\n *\r\n * @ORM\\Table(name=&quot;modveiculo_veiculos&quot;)\r\n * @ORM\\Entity\r\n * @ORM\\HasLifecycleCallbacks\r\n * @ORM\\Entity(repositoryClass=&quot;MODVeiculo\\Entity\\VeiculoRepository&quot;)\r\n *\/\r\nclass Veiculo\r\n{\r\n    \/**\r\n     * @var integer\r\n     *\r\n     * @ORM\\Column(name=&quot;id&quot;, type=&quot;integer&quot;, nullable=false)\r\n     * @ORM\\Id\r\n     * @ORM\\GeneratedValue(strategy=&quot;IDENTITY&quot;)\r\n     *\/\r\n    private $id;   \r\n    \r\n    \/**\r\n     * @var string\r\n     *\r\n     * @ORM\\Column(name=&quot;identificacao_placa&quot;, type=&quot;string&quot;, length=8, nullable=true)\r\n     *\/\r\n    private $identificacaoPlaca;\r\n}\r\n?&gt;\r\n<\/pre>\n<p>Imei &#8211; \u00c9 a identifica\u00e7\u00e3o \u00fanica de rastreadores, nesse caso usaremos esse tipo de rela\u00e7\u00e3o pois o mesmo rastreador pode fazer parte de mais ve\u00edculos dentro de uma frota, \u00e9 claro que n\u00e3o ao mesmo tempo, mas vamos supor que um cliente adquiriu este servi\u00e7o e depois de um tempo deixou de utilizar, obviamente voc\u00ea n\u00e3o jogar\u00e1 o rastreador fora, muito pelo contr\u00e1rio voc\u00ea vai aproveitar e utilizar ele em outro ve\u00edculo, mas se voc\u00ea precisa manter o hist\u00f3rico de rastreamento para o cliente (antigo) e os novos dados para o novo cliente, voc\u00ea precisa relacionar esse imei com mais de um ve\u00edculo. Outro caso pr\u00e1tico seria o rastreador de um determinado ve\u00edculo queimar e voc\u00ea precisar trocar esse rastreador, quando voc\u00ea for realizar a consulta em seu banco de dados trav\u00e9s do novo imei os dados do imei antigo n\u00e3o ser\u00e3o exibidos, ou seja, o hist\u00f3rico de rastreio iria zerar, dessa forma fazemos com que ele busque os dados de todos os rastreadores que j\u00e1 estiveram ativos neste ve\u00edculo.<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\n&lt;?php\r\n\r\nnamespace MODImei\\Entity;\r\n\r\nuse MODVeiculo\\Entity\\Veiculo;\r\n\r\nuse Doctrine\\ORM\\Mapping as ORM;\r\nuse Zend\\Hydrator;\r\n\r\n\/**\r\n * Imei\r\n *\r\n * @ORM\\Table(name=&quot;modimei_imei&quot;)\r\n * @ORM\\Entity\r\n * @ORM\\HasLifecycleCallbacks\r\n * @ORM\\Entity(repositoryClass=&quot;MODImei\\Entity\\ImeiRepository&quot;)\r\n *\/\r\nclass Imei\r\n{\r\n    \/**\r\n     * @var integer\r\n     *\r\n     * @ORM\\Column(name=&quot;id&quot;, type=&quot;integer&quot;, nullable=false)\r\n     * @ORM\\Id\r\n     * @ORM\\GeneratedValue(strategy=&quot;IDENTITY&quot;)\r\n     *\/\r\n    private $id;\r\n\r\n    \/**\r\n     * @var string\r\n     *\r\n     * @ORM\\Column(name=&quot;numero&quot;, type=&quot;string&quot;, length=45, nullable=true)\r\n     *\/\r\n    private $numero;\r\n}\r\n?&gt;\r\n<\/pre>\n<p>Agora nossa tabela de relacionamento (Voc\u00ea n\u00e3o vai criar entidade para ela)<br \/>\n<img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/hackerzone.com.br\/blog\/wp-content\/uploads\/2017\/09\/tabela-de-relacionamento.png\" alt=\"\" width=\"225\" height=\"176\" class=\"alignnone size-full wp-image-146\" \/><\/p>\n<p>Os super-entendedores j\u00e1 identificaram que existe um erro grav\u00edssimo nessa tabela, que no final do post vou explicar do que se trata, aos olhos dos mortais vai passar desapercebido. Na verdade s\u00e3o dois erros, o segundo mais comum.<\/p>\n<p>Antes de continuar preciso avisar alguns detalhes caso voc\u00ea esteja totalmente por fora, no caso do Doctrine se voc\u00ea esta fazendo apenas um relacionamento <strong>muitos para muitos<\/strong> voc\u00ea n\u00e3o precisa criar uma nova entidade para fazer este relacionamento, ele ocorrer\u00e1 de forma totalmente autom\u00e1tica, bastando informar o nome da tabela que relaciona as entidades.<\/p>\n<p>Primeiramente voc\u00ea vai precisar determinar qual entidade \u00e9 a propriet\u00e1ria e qual \u00e9 o lado inverso da hist\u00f3ria, at\u00e9 ai tudo bem, mas como determinar quem \u00e9 propriet\u00e1rio? Nos tutoriais abobalhados que voc\u00ea encontra por ai sempre ver\u00e1 os exemplos com Artigos e Categorias, neste caso \u00e9 obvio identificar quem vai ser o propriet\u00e1rio, vai ser Artigos certo? Mas na pratica nem sempre \u00e9 assim, voc\u00ea acaba encontrando algumas situa\u00e7\u00f5es complicadas, por exemplo, neste caso de Ve\u00edculos e Imeis os dois tem o mesmo grau de import\u00e2ncia, pois o rastreador no sistema \u00e9 uma entidade totalmente independente e importante, se n\u00e3o fosse o rastreador voc\u00ea jamais identificaria um ve\u00edculo.<\/p>\n<p>Mas ainda sim eu escolhi o ve\u00edculo para ser propriet\u00e1rio, pois por mais que o Imei (rastreador) seja mais importante eu acabo realizando mais consultas nos ve\u00edculos para apresenta\u00e7\u00e3o dentro do sistema, portanto isso poderia me ajudar a economizar as consultas j\u00e1 que trazendo os ve\u00edculos eu j\u00e1 traria os rastreadores que neles se encontram, os super-entendedores podem estar me julgando neste momento, mas at\u00e9 ent\u00e3o essa filosofia e linha de racioc\u00ednio tem me ajudado e n\u00e3o me prejudicado.<\/p>\n<p>Portanto vamos deixar de falat\u00f3rio e montar nossa entidade propriet\u00e1ria, vamos na entitudade Veiculo e adicionar mais uma propriedade.<\/p>\n<h3>Relacionamento muito para muitos bidirecional<\/h3>\n<p>Veiculo<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\n\/\/ ... entity Veiculo\r\n\r\n    \/**\r\n     * Many Users have Many Groups.\r\n     * @ManyToMany(targetEntity=&quot;MODImei\\Entity\\Imei&quot;, inversedBy=&quot;veiculos&quot;)\r\n     * @JoinTable(name=&quot;modimei_imei_veiculo&quot;)\r\n     *\/\r\n     private $imeis;\r\n\r\n    public function __construct(array $options = array()) {\r\n        $this-&gt;imeis = new \\Doctrine\\Common\\Collections\\ArrayCollection();\r\n    }\r\n\r\n    public function setImei(\\MODImei\\Entity\\Imei $imeis){\r\n        $imeis-&gt;setVeiculos($this);\r\n        $this-&gt;imeis-&gt;add($imeis);\r\n    }\r\n\r\n\/\/ ...\r\n<\/pre>\n<p>Imei &#8211; Como estamos falando de um relacionamento bidirecional precisamos tamb\u00e9m mexer em nossa tabela de imei, se n\u00e3o nem precisar\u00edamos.<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\n    \/**\r\n     * Many Imeis have Many Users.\r\n     * @ORM\\ManyToMany(targetEntity=&quot;MODVeiculo\\Entity\\Veiculo&quot;, mappedBy=&quot;imeis&quot;)\r\n     *\/\r\n    private $veiculos;\r\n\r\n    public function __construct(array $options = array())\r\n    {\r\n        $this-&gt;veiculos = new \\Doctrine\\Common\\Collections\\ArrayCollection();\r\n    }\r\n\r\n    public function setVeiculos(\\MODVeiculo\\Entity\\Veiculo $veiculos) {\r\n        $this-&gt;veiculos-&gt;add($veiculos);\r\n        return $this;\r\n    }\r\n<\/pre>\n<p>Como sou apressadinho eu j\u00e1 quero sair inserindo dados nessas tabelas, no meu caso estou utilizando de Fixtures (Dados de demonstra\u00e7\u00e3o) que voc\u00ea pode gerar com algumas ferramentas interessantes, dentre elas para o Zend Framework 3+ o dkorsak, falo a respeito dele neste artigo:<br \/>\n<a href=\"http:\/\/hackerzone.com.br\/blog\/trabalhando-com-data-fixture-no-zend-framework-3\/\">Trabalhando com Data Fixture no Zend Framework 3<\/a><\/p>\n<p>Ent\u00e3o vamos a minha Fixture:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\n&lt;?php\r\n\r\nnamespace MODVeiculo\\Fixture;\r\n\r\nuse Doctrine\\Common\\DataFixtures\\AbstractFixture;\r\nuse \\Doctrine\\Common\\Persistence\\ObjectManager;\r\nuse \\Doctrine\\Common\\DataFixtures\\OrderedFixtureInterface;\r\n\r\nuse MODVeiculo\\Entity\\Veiculo;\r\n\r\nclass LoadVeiculo extends AbstractFixture implements OrderedFixtureInterface{\r\n    \r\n    public function getOrder() {\r\n        return 9;\r\n    }\r\n\r\n    public function load(ObjectManager $manager) {\r\n        \r\n        \/\/1        \r\n        $idEmpresa = $manager-&gt;getReference(\\MODEmpresa\\Entity\\Empresa::class, 1);\r\n        $idCliente = $manager-&gt;getReference('MODCliente\\Entity\\Cliente', 4);\r\n        $idTipoVeiculo = $manager-&gt;getReference(\\MODVeiculo\\Entity\\TipoVeiculo::class, 1);\r\n        $imei = $manager-&gt;getReference(\\MODImei\\Entity\\Imei::class, 1); \/\/ busco o imei\r\n                \r\n        $veiculo = new Veiculo();\r\n        $veiculo-&gt;setIdCliente($idCliente)\r\n                -&gt;setIdEmpresa($idEmpresa)\r\n                -&gt;setIdTipoVeiculo($idTipoVeiculo)\r\n                -&gt;setTelefone('1496969696') \r\n                -&gt;setDescricao('BLA BLA BLA LEGAL TIO')\r\n                -&gt;setIdentificacaoPlaca('CCC3333')\r\n                -&gt;setMonitorado(true)\r\n                \/\/-&gt;setImei('111222333444666')\r\n                -&gt;setIcone('carrinho');\r\n\r\n        $veiculo-&gt;setImei($imei); \/\/ &lt;- olha aqui viado\r\n        \r\n        $manager-&gt;persist($veiculo);\r\n    }\r\n}\r\n?&gt;\r\n<\/pre>\n<p>Esque\u00e7am os demais relacionamentos e se atentem aos imeis ok? Bom, ao tentar inserir os dados foi me retornado o seguinte erro:<br \/>\n<img decoding=\"async\" src=\"http:\/\/hackerzone.com.br\/blog\/wp-content\/uploads\/2017\/09\/erro-no-relacionamento-doctrine-muito-para-muitos.png\" alt=\"\" width=\"100%\" height=\"auto\" class=\"alignnone size-medium wp-image-147\" srcset=\"http:\/\/hackerzone.com.br\/blog\/wp-content\/uploads\/2017\/09\/erro-no-relacionamento-doctrine-muito-para-muitos.png 527w, http:\/\/hackerzone.com.br\/blog\/wp-content\/uploads\/2017\/09\/erro-no-relacionamento-doctrine-muito-para-muitos-300x175.png 300w\" sizes=\"(max-width: 527px) 100vw, 527px\" \/><\/p>\n<p>Como podemos notar ele tentou gravar em nossa tabela de relacionamentos, mas n\u00e3o conseguiu, pois ele tentou gravar os campos veiculo_id e imei_id onde ele assumiu por padr\u00e3o que o nome das colunas de relacionamento seriam estes, mas n\u00e3o! Nossos campos s\u00e3o id_veiculo e id_imei. Este era o erro mais comum que havia citado anteriormente portanto voc\u00ea acaba de adquirir mais um conhecimento a respeito destes relacionamentos, se voc\u00ea n\u00e3o seguir o padr\u00e3o pr\u00e9-estabelecido pelo Doctrine voc\u00ea ter\u00e1 que informar os campos de relacionamento, portanto vamos alterar nossa entidade Veiculo<\/p>\n<p>Veiculo<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\n\/\/ ... entity Veiculo\r\n\r\n    \/**\r\n     * @var \\Doctrine\\Common\\Collections\\Collection\r\n     *\r\n     * @ORM\\ManyToMany(targetEntity=&quot;MODImei\\Entity\\Imei&quot;, inversedBy=&quot;veiculos&quot;)\r\n     * @ORM\\JoinTable(name=&quot;modimei_imei_veiculo&quot;,\r\n     *  joinColumns={@ORM\\JoinColumn(name=&quot;id_veiculo&quot;, referencedColumnName=&quot;id&quot;)},\r\n     *      inverseJoinColumns={@ORM\\JoinColumn(name=&quot;id_imei&quot;, referencedColumnName=&quot;id&quot;)})\r\n     *\/\r\n     private $imeis;\r\n\r\n    public function __construct(array $options = array()) {\r\n        $this-&gt;imeis = new \\Doctrine\\Common\\Collections\\ArrayCollection();\r\n    }\r\n\r\n    public function setImei(\\MODImei\\Entity\\Imei $imeis){\r\n        $imeis-&gt;setVeiculos($this);\r\n        $this-&gt;imeis-&gt;add($imeis);\r\n    }\r\n\r\n\/\/ ...\r\n<\/pre>\n<p>Pronto, agora n\u00f3s j\u00e1 temos os dados sendo inseridos no banco de dados, mas lembra que eu informei que os super g\u00eanios j\u00e1 sabiam que havia um erro grave nesse relacionamento? Pois ent\u00e3o geralmente nossa tend\u00eancia \u00e9 sempre armazenar mais dados na tabela, por exemplo, eu queria adicionar a data de transfer\u00eancia daquele IMEI (rastreador) para aquele determinado ve\u00edculo, mas ao fazer isso j\u00e1 deixou de ser um relacionamento muitos para muitos, pois \u00e9! Ao ficar muito preocupado de como faria esse relacionamento no Doctrine com Zend Framework deixei passar esse erro bobo a respeito de relacionamentos em banco de dados. No caso se voc\u00ea quiser adicionar mais informa\u00e7\u00f5es ele deixa de ser um relacionamento muitos para muitos e passa ser uma nova entidade!<\/p>\n<p>Ent\u00e3o pelo menos nesse meu caso \u00e9 o que vai acontecer, esse relacionamento de muitos para muitos deixar\u00e1 de existir para surgir uma nova entidade. Ainda sim eu achei os estudos a respeito dos relacionamentos no Doctrine 100% aproveitosos e resolvi compartilhar com a Web e tamb\u00e9m para ter um reposit\u00f3rio particular.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Vou demonstrar atrav\u00e9s de exemplos pr\u00e1ticos um relacionamento ManyToMany ou em nosso querido portugu\u00eas &#8220;muitos para muitos&#8221; no Doctrine com Zend Framework 3+ Este tutorial foi resultado de estudos referente a ferramenta, portanto serei breve e sem muitos detalhes j\u00e1 que n\u00e3o possuo conhecimento avan\u00e7ado sobre a ferramenta. Primeiro vamos gerar nossas entidades: Veiculo Imei [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":459,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[14,12,13],"tags":[65,16,67,68,66,20,18],"class_list":["post-144","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-doctrine","category-php","category-zend-framework-3","tag-banco-de-dados","tag-doctrine","tag-manytomany","tag-muitos-para-muitos","tag-relacionamento-manytomany","tag-zend","tag-zend-framework-3","cat-14-id","cat-12-id","cat-13-id","has_thumb"],"_links":{"self":[{"href":"http:\/\/hackerzone.com.br\/blog\/wp-json\/wp\/v2\/posts\/144","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/hackerzone.com.br\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/hackerzone.com.br\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/hackerzone.com.br\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/hackerzone.com.br\/blog\/wp-json\/wp\/v2\/comments?post=144"}],"version-history":[{"count":3,"href":"http:\/\/hackerzone.com.br\/blog\/wp-json\/wp\/v2\/posts\/144\/revisions"}],"predecessor-version":[{"id":149,"href":"http:\/\/hackerzone.com.br\/blog\/wp-json\/wp\/v2\/posts\/144\/revisions\/149"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/hackerzone.com.br\/blog\/wp-json\/wp\/v2\/media\/459"}],"wp:attachment":[{"href":"http:\/\/hackerzone.com.br\/blog\/wp-json\/wp\/v2\/media?parent=144"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/hackerzone.com.br\/blog\/wp-json\/wp\/v2\/categories?post=144"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/hackerzone.com.br\/blog\/wp-json\/wp\/v2\/tags?post=144"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}