Ir para conteúdo

POWERED BY:

Arquivado

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

Khwesten Heiner

Multiplos inserts/rollback com PDO

Recommended Posts

Bom, já faz um bom tempo que não ando por aqui, mas estou com uma dúvida cruel...

Seguinte, Eu tenho um objeto pessoa e dentro dele um objeto endereco.

Pessoa
|_ $nome
|_ $endereco
|_$codPessoa

|_$estado

|_$municipio

 

Essa estrutura, por exemplo. Ai eu chamo o DAO de pessoa e insiro a pessoa, verifico se aquela pessoa tem endereço e chamo o DAO de endereço, até aqui tudo massa, mas suponhamos que na hora de inserir o endereço ele dê erro, como eu faço para desfazer a inserção??? Sei que existe o rollback, mas para esse exemplo aqui talvez sirva, mas e para um objeto que tem 6 objetos dentro? Múltiplos rollbacks, múltiplas inserções ou deletar o objeto pai pelo id (Sabendo que ficariam "buracos" no BD por essa deleção), qual seria a melhor solução?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu deixaria tudo numa funcão, não sei como seria uma forma "profissional" de se fazer, mas eu faria todos os inserts na mesma funcão. Talvez apareça um mais profissa com uma dica melhor.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu acabei fazendo uma classe ManageDAO, onde eu tenho um método que passo como parâmetro um objeto, pego a classe do objeto e chamo o DAO dela, se correr tudo ok, retorno true, caso não, retorno a exception.

No ManageDAO também tem outro método que é para poder chamar o insert de outros DAO, quando tem objetos dentro de objetos.

 

Também não sei se é a forma certa, mas ficou assim:
(Ainda mudarei os nomes...)

<?php

require_once("../../lib/Conexao.class.php");
require_once("ClienteDAO.class.php");
require_once("FornecedorDAO.class.php");
/*require_once("EnderecoDAO.class.php");
require_once("EmpresaDAO.class.php");
require_once("ContatoDAO.class.php");
require_once("AcessoDAO.class.php");
require_once("BancoDAO.class.php");*/

class ManageDAO {
    
    public static $pdo;
    
    public $cliente;
    
    const FORNECEDOR = "fornecedor";
    const ENDERECO = "endereco";
    const EMPRESA = "empresa";
    const CONTATO = "contato";
    const ACESSO = "acesso";
    const BANCO = "banco";
    const GET = "get";
    const DAO = "DAO";
    
    public function inserirDAO($classe)
    {
        $classe = ucfirst($classe);
        
        $get = self::GET . $classe;
        
        $classeDAO = $classe . self::DAO;
        
        if(strtolower($classe) == self::FORNECEDOR)
        {
           $get = self::GET . "TabelaDeCompra";
        }
        
        $objetoParaInsercao = $this->cliente->$get();
        
        $objetoParaInsercao
            ->setCodigoCliente(
                $this->cliente->getId()
            );
        
        $DAO = new $classeDAO();
        
        return $DAO->insert($objetoParaInsercao);
    }
    
    public function setConexao()
    {
        try
        {
            self::$pdo = Conexao::getInstance();
        } 
        catch (PDOException $exc) 
        {
            return $exc->getTraceAsString();
        }
    }
    
    public function insertManagement($objeto){
        
        $this->setConexao();
        
        $class = get_class($objeto);
        
        $classDAO = $class . self::DAO;
        
        $dao = new $classDAO();
        
        try
        {
            self::$pdo->beginTransaction();
            
            if($dao->insert($objeto))
            {
                self::$pdo->commit();
                
                return $class . " inserido com sucesso!";
            }
        }
        catch (PDOException $exc) 
        {
            self::$pdo->rollback();
            
            return $exc->getTraceAsString();
        }
    }
}
?>

Compartilhar este post


Link para o post
Compartilhar em outros sites

Mas para reverter uma transação em caso de erro, a PDO tem que lançar o erro.

 

e para reverter todos, caso uma falhe, os inserts tem que estar dentro da mesma função, é como mau rs disse!

self::$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {

	self::$pdo->beginTransaction();
	
	//new
	self::$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	
        //Os inserts
	$this->cadastro1();
	$this->cadastro2();
	$this->cadastro3();
	$this->cadastro4();
	
	self::$pdo->commit();

} catch (PDOException $e) {

	self::$pdo->rollback();

}

http://bit.ly/1jbWkYG

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu suponho que a sua DAO é utilizada pela sua camada Model, e não que ela seja a Model em si.

 

A Model controla as regras de negócio, DAO é responsável pelo acesso aos dados e não pelas regras das entidades.

Eu gosto de trabalhar com a ideia de que, sua camada de persistência, só deve lhe dar um retorno, quando um erro acontece. Caso contrário, tudo ocorreu perfeitamente.

 

Remova os blocos try/catch do seu ManagementDAO e suas respectivas transactions. Trate na camada que utiliza o DAO a forma que os dados devem ser trabalhados.

 

Farei um exemplo bem rápido:

//Em alguma parte de uma Model
public function savePessoa(Pessoa $pessoa) {
    
    /**
    * Não irei tratar sobre como você pode ou deveria iniciar a conexão.
    * Nem em questões de diferentes tipos de persistências.
    * Não acho relevante no momento.
    **/
    $pdo = new PDO('/** dados de conexão **/');
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    
    try {
        $pdo->beginTransaction();
    
        $pessoaDAO = new PessoaDAO($pdo);
        $pessoaDAO->save($pessoa);
    
        $enderecoDAO = new EnderecoDAO($pdo);        
        /**
         * aqui pode haver diversas abordagens, 
         * nesse caso, deixo explícito que, para salvar um objeto endereço, 
         * é necessário informar a pessoa que será vinculada
         **/
        $enderecoDAO->save($pessoa->getEndereco() , $pessoa);
        
        $pdo->commit();        
    } catch  (PDOException $e) {
        $pdo->rollback();
    }
}

 

veja que não utilizei a linha abaixo para retornar o ID, o que poderia ser feito dentro do DAO. Como objetos são passados por referência, o ID poderia ser inserido no objeto pessoa, passado por referência para o DAO

$pessoa->setId($pdo->getLastInsertId()); 
Edit---

Por algum motivo obscuro, coloquei o código em tags de citação...

Compartilhar este post


Link para o post
Compartilhar em outros sites

Mas para reverter uma transação em caso de erro, a PDO tem que lançar o erro.

 

e para reverter todos, caso uma falhe, os inserts tem que estar dentro da mesma função, é como mau rs disse!

self::$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {

	self::$pdo->beginTransaction();
	
	//new
	self::$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	
        //Os inserts
	$this->cadastro1();
	$this->cadastro2();
	$this->cadastro3();
	$this->cadastro4();
	
	self::$pdo->commit();

} catch (PDOException $e) {

	self::$pdo->rollback();

}

http://bit.ly/1jbWkYG

 

Eles estão dentro da mesma função, na verdade aquela função insere o cliente e do cliente sai inserindo outras coisas, caso dê erro, uma dessas inserções lança o erro, chegando assim na parte do rollback, isso ai já ta testado. Mas valeu a dica.

Eu suponho que a sua DAO é utilizada pela sua camada Model, e não que ela seja a Model em si.

 

A Model controla as regras de negócio, DAO é responsável pelo acesso aos dados e não pelas regras das entidades.

Eu gosto de trabalhar com a ideia de que, sua camada de persistência, só deve lhe dar um retorno, quando um erro acontece. Caso contrário, tudo ocorreu perfeitamente.

 

Remova os blocos try/catch do seu ManagementDAO e suas respectivas transactions. Trate na camada que utiliza o DAO a forma que os dados devem ser trabalhados.

 

Farei um exemplo bem rápido:

//Em alguma parte de uma Model
public function savePessoa(Pessoa $pessoa) {
    
    /**
    * Não irei tratar sobre como você pode ou deveria iniciar a conexão.
    * Nem em questões de diferentes tipos de persistências.
    * Não acho relevante no momento.
    **/
    $pdo = new PDO('/** dados de conexão **/');
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    
    try {
        $pdo->beginTransaction();
    
        $pessoaDAO = new PessoaDAO($pdo);
        $pessoaDAO->save($pessoa);
    
        $enderecoDAO = new EnderecoDAO($pdo);        
        /**
         * aqui pode haver diversas abordagens, 
         * nesse caso, deixo explícito que, para salvar um objeto endereço, 
         * é necessário informar a pessoa que será vinculada
         **/
        $enderecoDAO->save($pessoa->getEndereco() , $pessoa);
        
        $pdo->commit();        
    } catch  (PDOException $e) {
        $pdo->rollback();
    }
}

 

veja que não utilizei a linha abaixo para retornar o ID, o que poderia ser feito dentro do DAO. Como objetos são passados por referência, o ID poderia ser inserido no objeto pessoa, passado por referência para o DAO

$pessoa->setId($pdo->getLastInsertId()); 
Edit---

Por algum motivo obscuro, coloquei o código em tags de citação...

 

 

Entendi muito bem o que você quis dizer cara, na verdade eu estava justamente pensando nisso hoje.

Meu processo ta assim:

Pego os dados do html, dou new no cliente, insiro tudo, inclusive dou new em endereco por exemplo e seto no cliente, do um new no ManageDAO (que estou querendo fazer uma camada acima ou colocar a chamada do mesmo no modelo [não curto muito isso, mas vou fazê-lo]) e lá dentro do modelo, faço como você disse, begin, commit e talz... aquele método (inserirManagement) está ali por ser teoricamente genérico, pois eu insiro ali qualquer objeto, e ele pega a classe e usa o DAO de referência (por isso colocar uma camada acima e não no modelo). Mas acho que vou criar um método no modelo e dele chama esse que está no manageDAO.

 

*O inserirDAO foi uma abstração, para poder inserir os objetos que estão dentro de outros objetos com o ponteiro (id) do objeto superior.

 

Deu para compreender ou falei d+?

 

Achei muito bacana a abordagem sobre o lastInsertId... Pensarei nisso.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Você também pode fazer a composição entre os objetos através do DAO. Utilizando o EnderecoDAO, e o seu método save, dentro do método PessoaDAO::save(). Pois, comumente, o endereço pode estar no mesmo Storage que pessoa (apesar de conhecer bons casos aonde são bases distintas).

Nesse caso, o PessoaDAO possui os métodos que devem tratar dos dados de uma Pessoa, e os dados de endereço são associados a essa pessoa. As vezes associação simples, outras vezes composição, depende da abordagem e modelagem (não quero entrar no método do que é correto, nesse caso, depende do projeto).

Logo, isso pode ser efetuado:

class PessoaDAO {

    private $storage;
    private $enderecoDAO;

    public function __construct(PDO $pdo) {
        $this->storage = $pdo;
        $this->enderecoDAO = new EnderecoDAO($pdo);
    }

    public function save(Pessoa $pessoa) {
        /** 
        * códigos para salvar uma pessoa no storage
        **/
 
        //Salva o objeto endereco.
        $this->enderecoDAO->save($pessoa->getEndereco() , $pessoa); 
    }
}

A abordage sobre os transactions, continua a mesma:

try {
    $pdo->beginTransaction();
    
    $pessoaDAO = new PessoaDAO($pdo);
    $pessoaDAO->save($pessoa);
        
    $pdo->commit();        
} catch  (PDOException $e) {
    $pdo->rollback();
}

Compartilhar este post


Link para o post
Compartilhar em outros sites

 

Você também pode fazer a composição entre os objetos através do DAO. Utilizando o EnderecoDAO, e o seu método save, dentro do método PessoaDAO::save(). Pois, comumente, o endereço pode estar no mesmo Storage que pessoa (apesar de conhecer bons casos aonde são bases distintas).

 

Nesse caso, o PessoaDAO possui os métodos que devem tratar dos dados de uma Pessoa, e os dados de endereço são associados a essa pessoa. As vezes associação simples, outras vezes composição, depende da abordagem e modelagem (não quero entrar no método do que é correto, nesse caso, depende do projeto).

 

Logo, isso pode ser efetuado:

class PessoaDAO {

    private $storage;
    private $enderecoDAO;

    public function __construct(PDO $pdo) {
        $this->storage = $pdo;
        $this->enderecoDAO = new EnderecoDAO($pdo);
    }

    public function save(Pessoa $pessoa) {
        /** 
        * códigos para salvar uma pessoa no storage
        **/
 
        //Salva o objeto endereco.
        $this->enderecoDAO->save($pessoa->getEndereco() , $pessoa); 
    }
}

A abordage sobre os transactions, continua a mesma:

try {
    $pdo->beginTransaction();
    
    $pessoaDAO = new PessoaDAO($pdo);
    $pessoaDAO->save($pessoa);
        
    $pdo->commit();        
} catch  (PDOException $e) {
    $pdo->rollback();
}

 

É exatamente o que meu código está fazendo. Valeu, man. Gostei da sua explicação... Meus DAO's chamam os outros DAO's para inserção, ao invés de retornar tudo e depois inserir o próximo e assim sucessivamente.

Mando uma pessoa para o DAO e ele verifica se tem endereço, caso positivo ele chama o respectivo DAO.

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.