Ir para conteúdo
Entre para seguir isso  
Master_Cyber

Relacionamento entre classes com PDO

Recommended Posts

Bom dia pessoal, estou precisando da ajuda de vocês para resolver um problema de relacionamento de classes que será usado no PDO.

Tenho uma classe Produto que tem um atributo que faz referência a classe Categoria. Até aqui tudo bem.

Porém quando eu faço a consulta no banco para trazer os produtos, ele me retorna o valor da categoria NULL, como posso resolver isso?

Não estar NULL, no banco coloquei categoria de id 1 para todos os produtos.

Utilizo "fetchAll(\PDO::FETCH_CLASS, 'Produto')" para me devolver em forma de objeto e não em array.

OBS: Não levem em consideração os nomes de atributos e métodos, serão refatorados kkkkkk

class Categoria {

    private $id;
    private $descricao;

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

    function getDescricao() {
        return $this->descricao;
    }

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

    function setDescricao($descricao) {
        $this->descricao = $descricao;
    }

}
class Produto {

    private $id;
    private $nome;
    private $descricao;
    private $categoria_id;
    
    public function __construct() {
        $this->categoria_id = new Categoria();
    }
    
    function getId() {
        return $this->id;
    }

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

    function getDescricao() {
        return $this->descricao;
    }

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

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

    function setDescricao($descricao) {
        $this->descricao = $descricao;
    }

   
    function getCategoria_id() {
        return $this->categoria_id;
    }

    function setCategoria_id($categoria) {
        $this->categoria_id = $categoria;
    }

}
$stmt = $query->execute();
$stmt->fetchAll(\PDO::FETCH_CLASS, 'Produto');

0 => 
    object(Produto)[18]
      private 'id' => int 3
      private 'nome' => string 'Produto B55333' (length=14)
      private 'descricao' => string 'Teste B66666' (length=12)
      private 'categoria_id' => 
        object(Categoria)[19]
          private 'id' => null
          private 'descricao' => null

Compartilhar este post


Link para o post
Compartilhar em outros sites

Até onde eu sei, você não vai conseguir mapear os parâmetros da sua classe "Categoria" com os campos da tabela dessa forma, utilizando somente o PDO.

O que pode ser feito é, com o "id_categoria" de "Produto" fazer outra consulta para pegar o objeto "Categoria", o problema é que assim você pode perder um pouco de performance.

Outra maneira é você retornar os campos da tabela categoria nos parâmetros da classe "Produto" e quando o método "getCategoria" for chamado, você cria o objeto categoria, assim será feito apenas uma consulta, o código ficaria parecido com isso: (obs: não executei esse código)

class Produto
{
    private $id;
    private $nome;
    private $descricao;
    private $categoria;
    private $categoriaId;
    private $categoriaDescricao;
    
    // getters..
    
    /**
     * Verifica se objeto categoria já foi criado,
     * se sim, apenas retorna, se não, cria o objeto categoria
     * e então retorna
     */
    public function getCategoria() 
    {
        if ($this->categoria instanceof Categoria) {
            return $this->categoria;
        }
        
        $this->categoria = new Categoria();
        $this->categoria->setDescricao($this->categoriaDescricao);
        return $this->categoria;
    }
}

$stm = $pdo->prepare("
    SELECT 
        p.id,
        p.nome,
        p.descricao,
        p.categoria_id AS categoriaId,
        c.descricao AS categoriaDescricao
    FROM 
        produtos p,
        categorias c
    WHERE 
        p.id=:id
    AND 
        p.categoria_id = c.id
");

$stm->setFetchMode(PDO::FETCH_CLASS, 'Produto');
$stm->bindValue(':id', 1);
$stm->execute();

var_dump($stm->fetch());

Apenas para complementar, caso você não conheça e queira fazer algo um pouco mais avançado, existem alguns frameworks ou ORM (Object Relational Mapper) que foram criados especificamente para trabalhar com isso; recomendo dar uma olhada no Doctrine.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Entendi...

Acho que vou optar por fazer isso:

O que pode ser feito é, com o "id_categoria" de "Produto" fazer outra consulta para pegar o objeto "Categoria", o problema é que assim você pode perder um pouco de performance.

Já usei o doctrine, mas prefiro usar algo mais simples, estou usando o SlimPDO.

Obrigado.

Editado por Gabriel Heming
Remover quote desnecessário

Compartilhar este post


Link para o post
Compartilhar em outros sites

Há a possibilidade em você mesmo criar seu sistema ORM e deixar ele com a simplicidade que deseja.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu coloquei um join e alias na minha consulta:

$query = $this->getDBInstance()
                    ->select(array('produto.*', 'categoria.descricao as descCategoria'))
                    ->from('Produto')
                    ->join('categoria', 'categoria.id', '=', 'produto.categoria_id', 'INNER');

$stmt = $query->execute();
$register = $stmt->fetchAll(\PDO::FETCH_CLASS);

0 =>
object(stdClass)[18]
public 'id' => int 3
public 'nome' => string 'Produto B55333' (length=14)
public 'descricao' => string 'Teste B66666' (length=12)
public 'categoria_id' => int 2
public 'descCategoria' => string 'Categoria 2' (length=11)

Ele juntou tudo em uma stdClass, porque coloquei fetchAll(\PDO::FETCH_CLASS).. Eu queria que fosse um objeto da classe Produto:

0 => 
    object(Produto)[18]
      private 'id' => int 3
      private 'nome' => string 'Produto B55333' (length=14)
      private 'descricao' => string 'Teste B66666' (length=12)
      private 'categoria_id' => 
        object(Categoria)[19]
          private 'id' => 2
          private 'descricao' => 'Categoria 2'

Mas quando coloco fetchAll(\PDO::FETCH_CLASS, 'Produto') ele dar erro: SQLSTATE[HY000]: General error: could not call class constructor.

Tem como fazer isso?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Mas quando coloco fetchAll(\PDO::FETCH_CLASS, 'Produto') ele dar erro: SQLSTATE[HY000]: General error: could not call class constructor.

Tem como fazer isso?

Tem sim.

Esse erro pode ser um problema de require/include, você está utilizando autoload? como está importando os arquivos ".php"? como ficou a classe Produto?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Tem sim. Isso pode ser um problema de require/include, você está utilizando autoload? como está importando os arquivos ".php"? como ficou a classe Produto?

Não acho que seja isso, estou usando autoload, psr-4, utilizo namespaces e talz..

O carregamento dos arquivos/classes estão funcionando perfeitamente.

Na verdade fica assim:

fetchAll(\PDO::FETCH_CLASS, 'app\Models\Produto')

Compartilhar este post


Link para o post
Compartilhar em outros sites

A diretiva PDO::FETCH_CLASS foi desenvolvida para uma consulta (que pode ter de 1 a N tabelas) representar um único objeto. Por isso, sua categoria nunca será criada dessa forma.

Nesse caso, o melhor que pode fazer, são duas consultas:

$stmt = $query->execute();
$produtoArr = $stmt->fetchAll(\PDO::FETCH_CLASS, 'Produto');

$statement = $pdo->prepare('select * from categoria where id = :id');
foreach($produtoArr as $produto) {
    $stmt = $query->execute(array(':id' , $produto->getCategoria_id()));
    $categoria = $stmt->fetch(\PDO::FETCH_CLASS, 'Categoria');

    $produto->setCategoria($categoria);//O método não existe, vai ter que ter algo similar
}

Compartilhar este post


Link para o post
Compartilhar em outros sites

Rapaz, tinha pensado exatamente isso, testei e funcionou do jeito que eu quero:

0 => 
    object(app\Models\Produto)[18]
      private 'id' => int 3
      private 'nome' => string 'Produto B55333' (length=14)
      private 'descricao' => string 'Teste B66666' (length=12)
      private 'categoria_id' => 
        object(app\Models\Categoria)[35]
          private 'id' => int 2
          private 'descricao' => string 'Categoria 2' (length=11)
Mas pensei que tivesse uma forma melhor ou até mais elegante. Funciona, mas achei feio kkkk..

Só deve perder em desempenho, porque para cada produto ele faz uma consulta na tabela de categoria.

Enfim, acho que vai ficar assim mesmo...

Obrigado pela ajuda de todos.

Editado por Gabriel Heming
remover quote desnecessário

Compartilhar este post


Link para o post
Compartilhar em outros sites

Não é feio, é apenas genérico.

É comum, falando-se de ORM, a maioria dos programadores pensarem que um objeto deve ser uma tabela e vice-versa, apesar de não ser verdade. Para PDO, apenas foi implementado o que é de consenso da maioria.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisar ser um membro para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar Agora
Entre para seguir isso  

×

Informação importante

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