[PHP MG] - Mapear fetchAll(\PDO::FETCH_CLASS para tipos reais da classe

97 views
Skip to first unread message

Paulo Vianna

unread,
May 28, 2017, 2:14:06 PM5/28/17
to ph...@googlegroups.com
OLá pessoal, boa tarde.

Estou fazendo uns testes com o PDO e estou tendo problema com o retorno do FETCH_CLASS.

Tenho uma classe cliente dessa forma:

class Cliente{
     private $id;
     private $nome;

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

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

Quando eu utilizo o comando: return $stmt->fetchAll(\PDO::FETCH_CLASS, "Cliente"); 

Está me retornando um erro dizendo que o id do meu resultado tem que ser inteiro quando preciso chamar meu método getId() de Cliente (Fatal error: Uncaught TypeError: Return value of Cliente::getId() must be of the type integer). Isso acontece porque o fetchAll traz todas colunas do banco em formato string.

Por isso alguém sabe se existe uma forma do PDO trazer os atributos com os tipos originais já mapeados para minha classe como faço na linha de retorno acima? Obrigado.

Tayron Miranda

unread,
May 28, 2017, 2:42:11 PM5/28/17
to ph...@googlegroups.com
No seu método getId vc tem que fazer o cast do seu retorno, pois não tem como o ODO saber o tipo certo de cada atributo da sua classe. 

--
Você recebeu esta mensagem porque está inscrito no grupo "PHP MG" no grupos do Google.
Site oficial do grupo: http://www.phpmg.com
Para postar neste grupo, envie um e-mail para ph...@googlegroups.com
Para cancelar a sua inscrição neste grupo, envie um e-mail para phpmg-unsubscribe@googlegroups.com
Regras da lista: http://groups.google.com/group/phpmg/web/regras-da-lista
Para ver mais opções, acesse http://groups.google.com/group/phpmg
---
Você recebeu essa mensagem porque está inscrito no grupo "PHP MG" dos Grupos do Google.
Para cancelar inscrição nesse grupo e parar de receber e-mails dele, envie um e-mail para phpmg+unsubscribe@googlegroups.com.
Para mais opções, acesse https://groups.google.com/d/optout.

Paulo Vianna

unread,
May 28, 2017, 6:41:12 PM5/28/17
to ph...@googlegroups.com
Não tem como o PDO trazer os tipos primitivos originais do banco de dados?

Tayron Miranda

unread,
May 29, 2017, 4:14:09 AM5/29/17
to ph...@googlegroups.com
Cara, PHP é uma linguagem dinamicamente ou fracamente tipada. Quem desenvolveu a classe PDO com certeza não se preocupou com tipagem igual é feito no java por conta da caracteristica da linguagem. 


A solução para seu problema são apenas duas:

!) Você itera sobre os dados vindo do banco e monta seu objeto na mão, o que seria mais indicado caso queira uma melhor tipagem e representação dos dados em seu objeto.

2) Nos seus métodos get da classe de seus objetos, faça sempre cast para o formato correto.



Tayron Miranda 
Site Pessoal - Linkedin - Github - Twitter


Paulo Vianna

unread,
May 29, 2017, 1:07:32 PM5/29/17
to ph...@googlegroups.com
É, tá certo.

Eu andei fazendo a solução 1 ontem a noite e vai ser assim mesmo, até porque é o melhor caminho. Valeu.

Tayron Miranda

unread,
May 29, 2017, 1:29:05 PM5/29/17
to ph...@googlegroups.com
Eu também seguiria a primeira opção



Tayron Miranda 
Site Pessoal - Linkedin - Github - Twitter



Rattones B-)

unread,
May 29, 2017, 1:52:52 PM5/29/17
to ph...@googlegroups.com
Já tentou criar uns __constructor pra classe e setar os tipos das variáveis (settype)

Marcelo Ratton
Desenvolvedor Web PHP
Celular: (37) 9.9845-8823
Email/GTalk: ratt...@marceloratton.com
Site: www.MarceloRatton.com
=======================================================
NETqueta:
Se você repassar esta mensagem, por favor:
1. Apague o MEU ENDEREÇO eletrônico;
2. Encaminhe CCO ou BCC (CÓPIA OCULTA) aos seus destinatários;
======================================================= 

Tayron Miranda

unread,
May 29, 2017, 2:05:47 PM5/29/17
to ph...@googlegroups.com
Paulo, o PDO consegue valores para atributos private? Acho que só publico né?



Tayron Miranda 
Site Pessoal - Linkedin - Github - Twitter



Tayron Miranda

unread,
May 29, 2017, 2:09:57 PM5/29/17
to ph...@googlegroups.com
Seguindo a minha linha de reciocínio acima, o PDO internamente deve setar os dados dessa forma:

class Pessoa
{
public $nome;
public $telefone;
public function __construct()
{
settype($this->nome, 'string');
settype($this->telefone, 'int');
}
}

$pessoa = new Pessoa;
$pessoa->nome = 'Maria';
$pessoa->telefone = '323366';

echo '<pre>';
var_dump($pessoa);

object(Pessoa)#1 (2) {
  ["nome"]=>
  string(5) "Maria"
  ["telefone"]=>
  string(6) "323366"
}

Ou seja, ele não deve invocar o setNome e nem setTelefone, 
mas sim fazer atribuição direta, o que fará com que usar o settype no construtor 
não ter efeito como mostrado acima.




Tayron Miranda 
Site Pessoal - Linkedin - Github - Twitter



Tayron Miranda

unread,
May 29, 2017, 2:28:10 PM5/29/17
to ph...@googlegroups.com
Uma outra solução para seu problema, seguindo a linha de raciocínio do Rattones:

class Cliente
{
private $nome;
private $telefone;
public function __set($atributo, $valor)
{
if($atributo == 'nome'){
$this->$atributo = (string)$valor;
}

if($atributo == 'telefone'){
$this->$atributo = (int)$valor;
}
}
public function getNome() : string
{
return (string)$this->nome;
}
public function setNome(string $nome)
{
$this->nome = $nome;
}

public function getTelefone() : int
{
return (int)$this->telefone;
}
public function setTelefone(int $telefone)
{
$this->nome = $telefone;
}
}

$pessoa = new Cliente;
$pessoa->nome = 'Maria';
$pessoa->telefone = '323366';

echo '<pre>';
var_dump($pessoa);

Tayron Miranda 
Site Pessoal - Linkedin - Github - Twitter


Tayron Miranda

unread,
May 29, 2017, 2:29:34 PM5/29/17
to ph...@googlegroups.com
Resultado do teste acima:

object(Cliente)#1 (2) {
  ["nome":"Cliente":private]=>
  string(5) "Maria"
  ["telefone":"Cliente":private]=>
  int(323366)
}



Tayron Miranda 
Site Pessoal - Linkedin - Github - Twitter



Rattones B-)

unread,
May 29, 2017, 3:34:19 PM5/29/17
to ph...@googlegroups.com
na versão 5.x do PHP isto q eu passei funciona sim Tayron .. 
estou baixando o PHP 7 pra testar nele, mas acredito que vá funcionar pois o PDO é bem mais antigo que o 5.x

Marcelo Ratton
Desenvolvedor Web PHP
Celular: (37) 9.9845-8823
Email/GTalk: ratt...@marceloratton.com
Site: www.MarceloRatton.com
=======================================================
NETqueta:
Se você repassar esta mensagem, por favor:
1. Apague o MEU ENDEREÇO eletrônico;
2. Encaminhe CCO ou BCC (CÓPIA OCULTA) aos seus destinatários;
======================================================= 


Tayron Miranda

unread,
May 29, 2017, 3:37:06 PM5/29/17
to ph...@googlegroups.com
Com settype no constructor igual no exemplo que postei? Se for bacana :D



Tayron Miranda 
Site Pessoal - Linkedin - Github - Twitter



Rattones B-)

unread,
May 29, 2017, 3:38:55 PM5/29/17
to ph...@googlegroups.com
o PDO chama o construtor da classe pelo jeito .. 

como vc fez, usando public ou set sem verificação de tipagem o PHP por ser tipagem dinâmica aceita e altera o tipo da variável .. sem erro, como aconteceria no java e outras linguagens fortemente tipadas

Marcelo Ratton
Desenvolvedor Web PHP
Celular: (37) 9.9845-8823
Email/GTalk: ratt...@marceloratton.com
Site: www.MarceloRatton.com
=======================================================
NETqueta:
Se você repassar esta mensagem, por favor:
1. Apague o MEU ENDEREÇO eletrônico;
2. Encaminhe CCO ou BCC (CÓPIA OCULTA) aos seus destinatários;
======================================================= 


Rattones B-)

unread,
May 29, 2017, 4:09:23 PM5/29/17
to ph...@googlegroups.com
Testado e funciona corretamente no PHP7

o banco tá fácil de deduzir ... 

<?php
$dsn = 'mysql:dbname=test;host=127.0.0.1';
$user = 'root';
$password = '';

try {
    $dbh = new PDO($dsn, $user, $password);
#echo 'Conectou';
} catch (PDOException $e) {
    echo 'Connection failed: ' . $e->getMessage();
}

class Cliente {
protected $id;
private $nome;
private $dt_nascimento;
public function __construct() {
settype($this->id, 'int');
settype($this->nome, 'string');
settype($this->dt_nascimento, 'string');
}
public function getId(): int {
return $this->id;
}
public function getNome(): string {
return $this->nome;
}
public function getDtNascimento(): string {
return $this->dt_nascimento;
}
}

$sql= "select * from cliente";

$rt= $dbh->query($sql);
$rs= $rt->execute();

echo '<pre>';
while ($dados= $rt->fetchObject('Cliente')) {
echo 'dados->getId(): '.$dados->getId().'<br>';

var_dump($rt, $rs, $dados);
}


dados->getId(): 1
object(PDOStatement)#2 (1) { ["queryString"]=> string(21) "select * from cliente" } bool(true) object(Cliente)#3 (3) { ["id":protected]=> int(1) ["nome":"Cliente":private]=> string(14) "Marcelo Ratton" ["dt_nascimento":"Cliente":private]=> string(10) "1977-11-03" } dados->getId(): 2
object(PDOStatement)#2 (1) { ["queryString"]=> string(21) "select * from cliente" } bool(true) object(Cliente)#4 (3) { ["id":protected]=> int(2) ["nome":"Cliente":private]=> string(17) "Jo�o Jos� Geraldo" ["dt_nascimento":"Cliente":private]=> string(10) "1900-04-01" }

Marcelo Ratton
Desenvolvedor Web PHP
Celular: (37) 9.9845-8823
Email/GTalk: ratt...@marceloratton.com
Site: www.MarceloRatton.com
=======================================================
NETqueta:
Se você repassar esta mensagem, por favor:
1. Apague o MEU ENDEREÇO eletrônico;
2. Encaminhe CCO ou BCC (CÓPIA OCULTA) aos seus destinatários;
======================================================= 


Tayron Miranda

unread,
May 29, 2017, 6:16:48 PM5/29/17
to ph...@googlegroups.com
Que legal :D



Tayron Miranda 
Site Pessoal - Linkedin - Github - Twitter



Tayron Miranda

unread,
May 29, 2017, 6:17:21 PM5/29/17
to ph...@googlegroups.com
Vivendo e aprendendo, muito obrigado por realizar o teste e compartilhar com a gente :D



Tayron Miranda 
Site Pessoal - Linkedin - Github - Twitter



Paulo Vianna

unread,
Jun 2, 2017, 2:46:00 PM6/2/17
to ph...@googlegroups.com
Tayron. Rattones, muito obrigado.

Como já disse há algum tempo por aqui, tenho implementado uma solução para gerar CRUD's básicos já com Bootstrap embutido de uma forma que o projeto fique dentro dos padrões das PSR's publicadas no site do PHP-FIG, que tenha manual de utilização junto a documentação, que utilize Clean Code, de modificação e que sua estrutura já permita que qualquer framework seja acoplado sem nenhuma alteração de código. Assim que tiver o primeiro release colocarei o link aqui, pois é um projeto Open Source.

Bem, dito isso a minha classe genérica de PDO está ficando assim:

?php
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
namespace app\cgd;
use app\cgd\GenericDao;
use app\cgd\DBConnection;
/**
* Description of GenericDAO
*
* @author   Paulo Vianna
*/
class GenericDao{
private $db;
private $entity;
public function __construct($entity){
$this->db = new DBConnection();
$this->entity = $entity;
}
public function find($id){
$query = "Select * from {$this->entity->getTable()} where id=:id";
$stmt = $this->db->getDbconnect()->prepare($query);
$stmt->bindValue(':id', $id);
$stmt->execute();
return $stmt->fetch((PDO::FETCH_ASSOC));
}
//public function listar($ordem = null){
public function listar($ordem) : array{
$query = "";
try{
if($ordem){
$query ="Select * from {$this->entity->getTable()} order by {$ordem}";
}
else{
$query ="Select * from {$this->entity->getTable()}";
}
$stmt = $this->db->getDbconnect()->query($query);
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
}
catch (PDOException $ex) {
# call the get_error function
$this->get_error($e);
}
}
public function inserir() : bool {
$query = "Insert into {$this->entity->getTable()}(nome, email) Values(:nome, :email)";
$stmt = $this->db->prepare($query);
$stmt->bindValue(':nome', $this->entity->getNome());
$stmt->bindValue(':email', $this->entity->getEmail());
if($stmt->execute()){
return true;
}
else{
return false;
}
}
public function alterar() : bool{
$query = "Update {$this->entity->getTable()} set nome=:nome, email=:email Where id=:id";
$stmt = $this->db->prepare($query);
$stmt->bindValue(':id', $this->entity->getId());
$stmt->bindValue(':nome', $this->entity->getNome());
$stmt->bindValue(':email', $this->entity->getEmail());
if($stmt->execute()){
return true;
}
else{
return false;
}
}
public function deletar($id) : bool {
$query = "delete from {$this->entity->getTable()} where id=:id";
$stmt = $this->db->prepare($query);
$stmt->bindValue(':id', $id);
if($stmt->execute()){
return true;
}
else{
return false;
}
}
}

Ainda está bem crua, pois os métodos alterar e inserir não estão genéricos e para que isso torne-se real pretendo utilizar reflection para descobrir quantos e quais os tipos de atributos do meu modelo de domínio existem para em seguida descobrir seus nomes e incrementar a query num loop de foreach.

Já em relação ao problema que tive com o mapeamento do objeto PDO foi que quando estava gravando na view, eu instanciava uma lista de objetos e aí que o problema vinha porque para exibir os atributos nos campos eu utiliza os métodos get. Acabei vendo somente agora eu vocês deram continuidade.

Eu pensei bastante no que o Tayron disse a respeito da opção de ter de iterar os objetos do PDO na mão, pois não difere muito de iterar uma lista de objetos. Acho que o propósito do PHP ser fracamente tipado deve ser justamente aproveitado nesses momentos, a chamada flexibilidade para se trabalhar, mas claro, utilizando essa flexibilidade sem extrapolar o propósito, até porque quando se joga um resultado para a camada view tudo vira html e de lá só sai objeto Java Script, portanto não adiantar eu querer enfiar objeto em tudo.

Contudo vi a solução do Rattones e pelo que procurei o caminho vai por ali mesmo, mas se eu espalhar cast pra tudo quanto é lado é melhor justamente seguir o que já está.

Tayron Miranda

unread,
Jun 2, 2017, 2:53:34 PM6/2/17
to ph...@googlegroups.com



Tayron Miranda 
Site Pessoal - Linkedin - Github - Twitter



Reply all
Reply to author
Forward
0 new messages