Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

vieira.rrafael

[Resolvido] Doctrine ORM

Recommended Posts

Afinal de contas, o Doctrine implementa ou não a estratégia de herança JOINED? O mapeamento é idêntico entre JOINED e SINGLE_TABLE, porém parece que só SINGLE_TABLE funciona.

CREATE TABLE `pessoas` (
`id` int(5) unsigned NOT NULL AUTO_INCREMENT,
`nome` varchar(80) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;

CREATE TABLE `alunos` (
`matricula` varchar(80) NOT NULL,
`curso_id` int(5) unsigned NOT NULL,
`pessoa_id` int(5) unsigned NOT NULL,
UNIQUE KEY `matricula` (`matricula`),
KEY `pessoa_id` (`pessoa_id`),
KEY `curso_id` (`curso_id`),
CONSTRAINT `alunos_ibfk_1` FOREIGN KEY (`pessoa_id`) REFERENCES `pessoas` (`id`),
CONSTRAINT `alunos_ibfk_2` FOREIGN KEY (`curso_id`) REFERENCES `cursos` (`id`)
) ENGINE=InnoDB;

CREATE TABLE `cursos` (
`id` int(5) unsigned NOT NULL AUTO_INCREMENT,
`nome` varchar(80) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT 'o nome dos cursos que devem ser únicos',
PRIMARY KEY (`id`),
UNIQUE KEY `nome` (`nome`)
) ENGINE=InnoDB;
INSERT INTO cursos(nome) VALUES('Física');

 

<?php

namespace model;

class Curso {

   private $id;
   private $nome;
   private $alunos = array();

   public function getId() {
       return $this->id;
   }

   public function setId($id) {
       $this->id = $id;
   }

   public function getNome() {
       return $this->nome;
   }

   public function setNome($nome) {
       $this->nome = $nome;
   }

   public function getAlunos() {
       return $this->alunos;
   }    

}

?>


<?php
namespace model;

abstract class Pessoa {

   private $id;
   private $nome;

   public function getId() {
       return $this->id;
   }

   public function setId($id) {
       $this->id = $id;
   }

   public function getNome() {
       return $this->nome;
   }

   public function setNome($nome) {
       $this->nome = $nome;
   }

}

?>

<?php

namespace model;

class Aluno extends Pessoa{

   private $matricula;
   private $curso;

   public function getMatricula() {
       return $this->matricula;
   }

   public function setMatricula($matricula) {
       $this->matricula = $matricula;
   }

   public function getCurso() {
       return $this->curso;
   }

   public function setCurso(Curso $curso) {
       $this->curso = $curso;
   }

}

?>

 

<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
   <entity name="model\Pessoa" table="pessoas" inheritance-type="JOINED">
       <!-- discriminator-map só faz sentido na estratégia single_table -->
       <discriminator-map>
           <discriminator-mapping value="aluno" class="model\Aluno"/>
       </discriminator-map>
       <id name="id" type="integer">
           <generator strategy="IDENTITY"/>
       </id>
       <field name="nome" type="string" length="255" nullable="false"/>
   </entity>
</doctrine-mapping>


<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
   <entity name="model\Aluno" table="alunos">
       <discriminator-column name="pessoa_id" type="integer"/>
       <field name="matricula" type="string" length="255" nullable="false" unique="true" />
       <one-to-one field="curso" target-entity="model\Curso" inversed-by="alunos">
           <join-column name="curso_id" referenced-column-name="id"/>
           <cascade>
               <cascade-persist/>
               <cascade-merge/>
               <cascade-refresh/>
           </cascade>
       </one-to-one>
   </entity>
</doctrine-mapping>


<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
   <entity name="model\Curso" table="cursos">
       <id name="id" type="integer">
           <generator strategy="IDENTITY"/>
       </id>       
       <field name="nome" nullable="false" type="string" length="255"/>
       <one-to-many target-entity="model\Aluno" mapped-by="curso" field="alunos">
           <cascade><cascade-all/></cascade>
       </one-to-many>
   </entity>
</doctrine-mapping>

 

#index.php
#esta é a parte que interesa
#EchoSQLLogger para exibir o SQL montado.
$configuration->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
$em = \Doctrine\ORM\EntityManager::create($options, $configuration);

$aluno = new model\Aluno();
$aluno->setCurso($em->find("model\Curso", 1));
$aluno->setMatricula("123abc");
$aluno->setNome("Rafael");

$em->persist($aluno);
$em->flush();

 

O erro é o seguinte:

"INSERT INTO pessoas (nome, dtype) VALUES (?, ?) array(2) { [1]=> string(6) "Rafael" [2]=> string(11) "aluno" }

Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[42S22]: Column not found: 1054 Unknown column 'dtype' in 'field list'' in..."

Ou seja, JOINED está tentando inserir o valor definido em 'value' na tag <discriminator-mapping> numa coluna que não existe na tabela pessoas. Segui o manual do Doctrine e é evidente que ele está errado. Por isso, qual é o jeito certo? Ou não há jeito certo porque JOINED não é suportado por este ORM?

Compartilhar este post


Link para o post
Compartilhar em outros sites

É apenas um exemplo. Se é model ou não é irrelevante. O que quero saber é se dá ou não dá para usar JOINED como estratégia de herança.

 

Pode ou não pode?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Primeiramente, o Andrey tem razão cara, você deve levar a nomeclatura muito a sério. E o ideal é você dar o namespace adequado à suas classes.

 

Segundo, qual a sua versão do Doctrine? você usa apenas XML Mapping? Não usa annotation? você leu o manual? Pq já notei erros que são descritos no manual a forma correta de se fazer.

Compartilhar este post


Link para o post
Compartilhar em outros sites
Primeiramente, o Andrey tem razão cara, você deve levar a nomeclatura muito a sério. E o ideal é você dar o namespace adequado à suas classes.

Eu levo a sério. Como disse antes: é apenas um exemplo. Escrevi rapidamente.

A versão que uso é a 2.1.2. Uso XML. Experimentei anotações, mas não curti muito. Em Java com hibernate uso anotações, mas em PHP com o Doctrine prefiro XML.

O código acima foi feito seguindo o manual.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Hum, então vamos lá.

 

class Curso {

   private $id;
   private $nome;
   private $alunos = array();
   //...
}

 

Primeiro, alunos aqui não é uma simples array, é uma ArrayCollection.

Você deve ter no seu constructor:

$this->alunos = new \Doctrine\Common\Collections\ArrayCollection;

 

http://www.doctrine-project.org/docs/orm/2.0/en/reference/association-mapping.html#collections

 

Mas o problema não é aí...

 

O teu discriminator-column está na entidade errada, deve está na entidade Pessoa e não na Aluno.

 

Se você puder postar um exemplo usando Annotation fica melhor, odeio XML, aí fica díficil ajudar. =)

Compartilhar este post


Link para o post
Compartilhar em outros sites

com discriminator-column na classe Pessoa, o erro e o sql são os seguintes:

INSERT INTO pessoas (nome, pessoa_id) VALUES (?, ?) array(2) { [1]=> string(6) "Rafael" [2]=> string(5) "aluno" }

Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[42S22]: Column not found: 1054 Unknown column 'pessoa_id' in 'field list'

<?php

namespace entidade;

/**
* @Entity
* @Table(name="pessoas")
* @InheritanceType("JOINED")
* @DiscriminatorColumn(name="pessoa_id", type="integer")
* @DiscriminatorMap({"aluno" = "Aluno"})
*
*/
abstract class Pessoa {

   /**
    * @Id
    * @Column(type="integer")
    * @generatedValue(strategy="IDENTITY")
    */
   private $id;

   /*
    * @Column
    */
   private $nome;

   public function getId() {
       return $this->id;
   }

   public function setId($id) {
       $this->id = $id;
   }

   public function getNome() {
       return $this->nome;
   }

   public function setNome($nome) {
       $this->nome = $nome;
   }

}

?>

<?php

namespace entidade;

/*
* @Entity
* @Table(name="cursos")
*/
class Curso {

   /**
    * @Id
    * @Column(type="integer")
    * @generatedValue(strategy="IDENTITY")
    */
   private $id;

   /*
    * @Column
    */
   private $nome;
   /**
    * @OneToMany(targetEntity="Alunos", mappedBy="curso", cascade={"all"}, orphanRemoval=true)
    */
   private $alunos;

   function __construct() {
       $this->alunos = new \Doctrine\Common\Collections\ArrayCollection();
   }

   public function getId() {
       return $this->id;
   }

   public function setId($id) {
       $this->id = $id;
   }

   public function getNome() {
       return $this->nome;
   }

   public function setNome($nome) {
       $this->nome = $nome;
   }

   public function getAlunos() {
       return $this->alunos;
   }

}

?>

<?php

namespace entidade;

/*
* @Entity
* @Table(name="alunos")
*/

class Aluno extends Pessoa {
   /*
    * @Column
    */

   private $matricula;
   /**
    * @OneToOne(targetEntity="Curso")
    * @JoinColumn(name="curso_id", referencedColumnName="id")
    */
   private $curso;

   public function getMatricula() {
       return $this->matricula;
   }

   public function setMatricula($matricula) {
       $this->matricula = $matricula;
   }

   public function getCurso() {
       return $this->curso;
   }

   public function setCurso(Curso $curso) {
       $this->curso = $curso;
   }

}

?>

O que eu não compreendo é o mapeamento com a estratégia joined ser igual a estratégia single_table.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Como você definiu

* @DiscriminatorColumn(name="pessoa_id", type="integer") <- você definiu integer
* @DiscriminatorMap({"aluno" = "Aluno"}) <- mas usa string

 

E está usando uma string no campo?

Outra coisa, você poderia mostrar como está o seu código que está inserindo um novo aluno?

E a tabela "pessoas" realmente tem o campo pessoa_id?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Dê uma pesquisa pela anotação MappedSuperclass.

Dependendo da aplicação, você tem que usa-la em pessoa (pois é uma classe pai).

 

http://www.doctrine-project.org/docs/orm/2.0/en/reference/inheritance-mapping.html

 

O discriminator deve ser uma string.

É o campo que "descreve" o tipo de classe, e como o JCMais bem disse, tem que ser na superclasse mesmo.

 

@braços e fique com Deus!

Compartilhar este post


Link para o post
Compartilhar em outros sites

Como você definiu

* @DiscriminatorColumn(name="pessoa_id", type="integer") <- você definiu integer
* @DiscriminatorMap({"aluno" = "Aluno"}) <- mas usa string

 

E está usando uma string no campo?

Outra coisa, você poderia mostrar como está o seu código que está inserindo um novo aluno?

E a tabela "pessoas" realmente tem o campo pessoa_id?

 

Eu mostrei no 1º post o script das tabelas deste exemplo. A tabela pessoa tem apenas id e nome.

Eu também já mostrei o código para inserir um novo aluno. Forneci todo código que escrevi.

 

Dê uma pesquisa pela anotação MappedSuperclass.

Dependendo da aplicação, você tem que usa-la em pessoa (pois é uma classe pai).

Acredito que MappedSuperclass não se aplica a este exemplo porque todas a tabelas existem: pessoas, alunos e cursos.

 

Em Java seria assim:

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package entidades;

import java.io.Serializable;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity()
@Table(name = "cursos")
public class Curso implements Serializable {

   @OneToMany(mappedBy = "curso", cascade=CascadeType.ALL)
   private List<Aluno> alunos;
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Integer id;
   @Column()
   private String nome;

   public List<Aluno> getAlunos() {
       return alunos;
   }

   public void setAlunos(List<Aluno> alunos) {
       this.alunos = alunos;
   }

   public Integer getId() {
       return id;
   }

   public void setId(Integer id) {
       this.id = id;
   }

   public String getNome() {
       return nome;
   }

   public void setNome(String nome) {
       this.nome = nome;
   }

   @Override
   public boolean equals(Object obj) {
       if (obj == null) {
           return false;
       }
       if (getClass() != obj.getClass()) {
           return false;
       }
       final Curso other = (Curso) obj;
       if (this.id != other.id && (this.id == null || !this.id.equals(other.id))) {
           return false;
       }
       return true;
   }

   @Override
   public int hashCode() {
       int hash = 7;
       hash = 41 * hash + (this.id != null ? this.id.hashCode() : 0);
       return hash;
   }


}


/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package entidades;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;


@Entity()
@Table(name = "pessoas")
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Pessoa implements Serializable {

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Integer id;
   @Column
   private String nome;

   @Override
   public boolean equals(Object obj) {
       if (obj == null) {
           return false;
       }
       if (getClass() != obj.getClass()) {
           return false;
       }
       final Pessoa other = (Pessoa) obj;
       if (this.id != other.id && (this.id == null || !this.id.equals(other.id))) {
           return false;
       }
       return true;
   }

   @Override
   public int hashCode() {
       int hash = 3;
       hash = 47 * hash + (this.id != null ? this.id.hashCode() : 0);
       return hash;
   }

   public Integer getId() {
       return id;
   }

   public void setId(Integer id) {
       this.id = id;
   }

   public String getNome() {
       return nome;
   }

   public void setNome(String nome) {
       this.nome = nome;
   }
}

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package entidades;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;

@Entity()
@Table(name="alunos")
@PrimaryKeyJoinColumn(name="pessoa_id")
public class Aluno extends Pessoa{

   @Column
   private String matricula;
   @ManyToOne()
   private Curso curso;

   public Curso getCurso() {
       return curso;
   }

   public void setCurso(Curso curso) {
       this.curso = curso;
   }

   public String getMatricula() {
       return matricula;
   }

   public void setMatricula(String matricula) {
       this.matricula = matricula;
   }


}


Dá para fazer igual com o Doctrine?

Compartilhar este post


Link para o post
Compartilhar em outros sites

<?php

namespace entidade;

/**
* @Entity
* @Table(name="cursos")
*/
class Curso {

   /**
    * @Id
    * @Column(type="integer")
    * @generatedValue(strategy="IDENTITY")
    */
   private $id;
   /**
    * @Column
    */
   private $nome;
   /**
    * @OneToMany(targetEntity="Aluno", mappedBy="curso", cascade={"all"}, orphanRemoval=true)
    */
   private $alunos;

   function __construct() {
       $this->alunos = new \Doctrine\Common\Collections\ArrayCollection();
   }

   public function getId() {
       return $this->id;
   }

   public function setId($id) {
       $this->id = $id;
   }

   public function getNome() {
       return $this->nome;
   }

   public function setNome($nome) {
       $this->nome = $nome;
   }

   public function getAlunos() {
       return $this->alunos;
   }

}

?>

<?php

namespace entidade;

/*
* @Entity()
* @Table(name="alunos")
*/

class Aluno extends Pessoa {

   /**
    * @Column
    */
   private $matricula;
   /**
    * @OneToOne(targetEntity="Curso")
    * @JoinColumn(name="curso_id", referencedColumnName="id")
    */
   private $curso;

   public function getMatricula() {
       return $this->matricula;
   }

   public function setMatricula($matricula) {
       $this->matricula = $matricula;
   }

   public function getCurso() {
       return $this->curso;
   }

   public function setCurso(Curso $curso) {
       $this->curso = $curso;
   }

}

?>

<?php

namespace entidade;

/**
* @Entity
* @Table(name="pessoas")
* @InheritanceType("JOINED")
* @DiscriminatorColumn(name="disc", type="string")
* @DiscriminatorMap({"aluno" = "Aluno"})
*/
abstract class Pessoa {

   /**
    * @Id
    * @Column(type="integer")
    * @generatedValue(strategy="IDENTITY")
    */
   private $id;

   /**
    * @Column
    */
   private $nome;

   public function getId() {
       return $this->id;
   }

   public function setId($id) {
       $this->id = $id;
   }

   public function getNome() {
       return $this->nome;
   }

   public function setNome($nome) {
       $this->nome = $nome;
   }

}

?>

 

CREATE TABLE `cursos` (
`id` int(5) unsigned NOT NULL AUTO_INCREMENT,
`nome` varchar(80) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT 'o nome dos cursos que devem ser únicos',
PRIMARY KEY (`id`),
UNIQUE KEY `nome` (`nome`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO cursos(nome) VALUES('português', 'matemática');

CREATE TABLE IF NOT EXISTS `pessoas` (
 `id` int(5) unsigned NOT NULL AUTO_INCREMENT,
 `nome` varchar(80) NOT NULL,
 `disc` varchar(100) DEFAULT 'aluno',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `alunos` (
 `id` int(5) unsigned NOT NULL,
 `curso_id` int(5) unsigned NOT NULL,
 `matricula` varchar(100) NOT NULL,
 UNIQUE KEY `matricula` (`matricula`),
 KEY `fk_curso` (`curso_id`),
 KEY `fk_pessoa` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;



ALTER TABLE `alunos`
 ADD CONSTRAINT `fk_curso` FOREIGN KEY (`curso_id`) REFERENCES `cursos` (`id`),
 ADD CONSTRAINT `fk_pessoa` FOREIGN KEY (`id`) REFERENCES `pessoas` (`id`);

 

Para funcionar, a tabela pai precisa de uma coluna para discriminar as tabelas filhas. É nesse ponto que entra o descriminator-column e descriminator-mapping. As tabelas filhas precisam que o id faça referência a pai.

Eu não gostei, mas é a forma como ele funciona.

 

#parte que interessa
$em = \Doctrine\ORM\EntityManager::create($options, $configuration);

$aluno = new entidade\Aluno();
$aluno->setCurso($em->find("entidade\Curso", 1));
$aluno->setMatricula(md5('1234'));
$aluno->setNome("zezinho");

$em->persist($aluno);
$em->flush();

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.