Ir para conteúdo

POWERED BY:

Arquivado

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

lorena85

Classe DAO

Recommended Posts

Ola, to com umas dúvidas sobre a classe DAO, espero que vocês me ajudem :)

 

1. Vejo em artigos e tutoriais na web, que sempre uma classe 'DAO' é responsável por instanciar um objeto do 'banco de dados' ou de 'arquivo' dentro dela Exemplo:

class pessoaDAO {
    protected $_db
    public $_tabela = null;
    public function __construct(){
        $this->_db = new PDO('mysql:host=localhost;dbname=sucosjuices', 'root', '', array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));
    }
	
    // Métodos ( create, read, update, delete )...
}

Como vocês viram a classe 'pessoaDAO' foi responsável de instanciar um objeto do banco de dados no seu construtor. Bom só não existe 'pessoaDAO', poderá existir também : contatoDAO, htmlDAO e assim por diante...

 

Veja que cada classe 'DAO', vai possuir uma instância do banco de dados diferentes.

Será que se fizermos a instância do banco de dados, fora da classe DAO e depois passasse essa instância num argumento de um método da classe DAO, seria bem melhor??

Logo a instância do BD serviria para outras classe DAO, evitando repetições de instancias desnecessárias. Então qual a opiniões de vocês, sobre instanciar o DB dentro do DAO, é obrigatório ou não ??


2. Além do padrão 'DAO'. existem outros tipos de "Padrões de Persistência" ??

 

Obrigada desde já

Compartilhar este post


Link para o post
Compartilhar em outros sites

Bom só não existe 'pessoaDAO', poderá existir também : contatoDAO, htmlDAO e assim por diante...

por isso que eu prefiro usar Value Objects.

 

Assim tenho uma só classe de DAO, que recebe como argumento no construtor o DB, e cada método recebe um VO para salvar ou deletar..

 

Será que se fizermos a instância do banco de dados, fora da classe DAO e depois passasse essa instância num argumento de um método da classe DAO, seria bem melhor??

Sim.

E pode considerar usar um Registry também.

 

 

sobre instanciar o DB dentro do DAO, é obrigatório ou não ??

 

 

não é obrigatório não.

 

2. Além do padrão 'DAO'. existem outros tipos de "Padrões de Persistência" ??

Existe por exemplo o Active Record (eu pessoalmente não curto, não vi nenhuma implementação "legal" deele).

Compartilhar este post


Link para o post
Compartilhar em outros sites


 

class connectDAO {

public function __construct(){

return new PDO('mysql:host=localhost;dbname=sucosjuices', 'root', '', array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));

}

}

 

 

class pessoaDAO {

protected $_db

public $_tabela = null;

public function __construct($conn){

$this->_db = $conn;

}

 

// Métodos ( create, read, update, delete )...

}

 

 

$db_conn = new connectDAO();

$pessoa_dao = new pessoaDAO($db_conn);

 

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Uma solução elegante é usar Dependency Injection, que diminui o acoplamento e facilita testes unitários e uma forma de modelar a lógica da aplicação é usando Domain Driven Design, que é um conceito bem difundido e interessante nas comunidades de OOP.

 

 

class connectDAO {
   public function __construct(){
     return new PDO('mysql:host=localhost;dbname=sucosjuices', 'root', '', array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));
    }
}

 

Só um adendo: construtor não retorna valor.

Compartilhar este post


Link para o post
Compartilhar em outros sites

por isso que eu prefiro usar Value Objects.

 

Assim tenho uma só classe de DAO, que recebe como argumento no construtor o DB, e cada método recebe um VO para salvar ou deletar..

 

@William Bruno, poderia dá um exemplo de uso, desse Value Objects ?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Uma solução elegante é usar Dependency Injection, que diminui o acoplamento e facilita testes unitários e uma forma de modelar a lógica da aplicação é usando Domain Driven Design, que é um conceito bem difundido e interessante nas comunidades de OOP.

 

 

Só um adendo: construtor não retorna valor.

Obrigado por lembrar kkk.. fiz aqui rapidão

Compartilhar este post


Link para o post
Compartilhar em outros sites

instanciar o DB dentro do DAO, é obrigatório ou não ??

 

Não e nem é recomendado. Como o Enrico Pereira disse, o mais aconcelhavel é Dependency Injection, porque assim você não fica dependente de uma só conexão, caso mude, não tendo problemas com o OCP (o software deve estar aberto para extensão, mas fechadas para modificação).

Compartilhar este post


Link para o post
Compartilhar em outros sites

Creio que o William Bruno está chamando de Value Objects são na verdade os Data Transfer Objects. Há um problema com os termos aí.

Data transfer object (DTO), formerly known as value objects or VO, is a design pattern used to transfer data between software application subsystems. DTOs are often used in conjunction with data access objects to retrieve data from a database.

 

The difference between data transfer objects and business objects or data access objects is that a DTO does not have any behaviour except for storage and retrieval of its own data (accessors and mutators).

Traduzindo livremente:

 

Data Transfer Objects (DTO), antes conhecidos como Value Objects (VO), é um design pattern usado para transferir dados entre os subsistemas de uma aplicação. DTOs são normalmente usados em conjunto com Data Access Objects (DAOs) para retornar dados de um banco de dados.

 

A diferença entre DTOs e Business Objects (BO) ou Data Access Objects (DAO) é que um DTO não contém nenhum comportamento, exceto de armazenar e retornar seus próprios dados (acessores e mutadores).

 

Basicamente, são objetos que representam dados. Particularmente, no caso do PHP e outras linguagens dinâmicas, como Python, Ruby, etc., eu acho um tanto overkill aplicar esse padrão na forma tradicional. Eu costumo ter apenas alguns wrappers para arrays associativos ao invés de objetos separados com propriedades, mas aí vai do gosto do freguês.

 

Quanto ao seu problema:

 

 

class pessoaDAO {
    protected $_db
    public $_tabela = null;
    public function __construct(){
        $this->_db = new PDO('mysql:host=localhost;dbname=sucosjuices', 'root', '', array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));
    }
	
    // Métodos ( create, read, update, delete )...
}

 

 

 

 

 

Bom, temos alguns problemas...

 

Primeiro que vc cria uma instância PDO a cada DAO que vc utiliza, mas isso é totalmente desnecessário.

Segundo, eu realmente acho DAO um padrão muito ineficiente em termos de quantidade de código a se escrever. Eu normalmente tenho um único ponto de entrada para o banco de dados, o que eu chamo de [inline]Storage[/inline].

 

Vou mostrar um pouco da estrutura que eu tenho, mas simplifiquei algumas coisas para facilitar o entendimento.

interface StorageInterface {
    public function insert($container, array $data);
    public function update($container, array $data, $idField);
    public function delete($container, $idField, $idValue);
    public function getById($container, $idField, $idValue);
    public function getAll(
        $container, 
        $cond = null, 
        array $condParams = array(), 
        $order = null,
        $group = null,
        $limit = 0, 
        $offset = 0
    );
    public function beginTransaction();
    public function commitTransaction();
    public function rollBackTransaction();
}
Note que apesar dos parâmetros de [inline]getAll()[/inline] serem os mesmo de uma query SQL, essa interface é genérica, é possivel usar um XML como storage, um CSV, como vc quiser. Para o caso de bancos de dados relacionais, eu apenas estendo essa interface:
interface SqlStorageInterface extends StorageInterface {
    public function query($sql);
    public function prepare($sql);
    public function execute(array $boundParams = array());
    public function reset();
}
Para exemplificar, supondo que eu use PDO como meu mecanismo de storage:
class PdoStorage implements SqlStorageInterface {
    private $dbh;
    private $dsn;
    private $user;
    private $password;
    // mais propriedades...

    public function __construct($dsn, $user, $password) {
        $this->dsn = $dsn;
        $this->user = $user;
        $this->password = $password;
    }

    public function setDbh(\Pdo $dbh) {
        $this->dbh = $dbh;
        $this->dbh->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
    }

    // Lazy initialization...
    public function getDbh() {
        if ($this->dbh === null) {
            $this->setDbh(new \Pdo($this->dsn, $this->user, $this->password));
        }
        return $this->dbh;
    }
    // ... continua
}
Caso eu queira usar arquivos CSV como storage:
class CsvStorage implements StorageInterface {
    private $fileName;

    public function __construct($fileName) {
        $this->setFileName($fileName);
    }

    public function setFileName($fileName) {
        if (!is_file($fileName)) {
            throw new Exception("File '{$fileName}' does not exists or is not writable");
        }
    }

    public function insert($container, array $data) {
        // note que ignoramos o container, pois arquivos CSV contém sempre a mesma estrutura de dados
        $handler = new SplFileObject($this->fileName, 'a+');
        return $handler->fputcsv($data);
    }

    // outros métodos...
}
Para um arquivo XML, será parecido e assim por diante... Dessa forma, vc abstrai completamente (ou quase) seu meio de armazenamento.

 

Agora, uma camada acima disso eu tenho o que chamo de ApplicationModels, que contém a lógica do negócio e há um model para cada componente do meu sistema e Entities, que nada mais são do que uma variação do padrão DTO.

abstract class AbstractModel {
    private $storage;
    private $container;

    public function __construct($container, StorageInterface $storage) {
        $this->setContainer($container);
        $this->setStorage($storage);
    }

    public function setStorage(StorageInterface $storage) {
        $this->storage = $storage;
        $this->storageFilterFactory = $storage->getFilterFactory();
    }

    public function getStorage() {
        return $this->storage;
    }

    public function setContainer($container) {
        $this->container = (string) $container;
    }

    public function getContainer() {
        return $this->container;
    }

    public function save(AbstractEntity $data, array $context = array()) {
        // implementação...
    }

    public function delete($idValue, array $context = array()) {
        // implementação...
    }

    // ... continua
}

class Client extends AbstractModel {
    // lógica da aplicação para clientes...
}

class Sale extends AbstractModel {
    // lógica da aplicação para vendas...
}

abstract class AbstractEntity {
    // implementação...
}

class ClientEntity extends AbstractEntiry {
    // representação dos dados de um cliente...
}
class SaleEntity extends AbstractEntiry {
    // representação dos dados de uma venda...
}
Agora eu uso desta maneira:
// Cria a classe que de fato faz operações com o meio persistente...
$storage = new PdoStorage('mysql:dbname=test;host=localhost', 'root', 'nopass');
$clientModel = new ClientModel('clients', $storage);
$saleModel = new saleModel('sales', $storage);

$client = new ClientEntity([
    'id' => 102318182
    'name' => 'João', 
    'surname'=>'da Silva',
    'cpf'=>'00000000000'
]);

$clientModel->save($client);

$clientInserted = $clientModel->getById(102318182); // retorna o objeto contendo os dados inseridos

$sale = new SaleEntity([
    'value' => 200.00,
    'date' => '2013-11-14 16:55:15'
]);
$saleId = $saleModel->save($sale);

$saleInserted = $saleModel->getById($saleId);
Tá, mas e se eu quero usar um arquivo CSV invés de um banco de dados relacional agora, o que eu faço?

Apenas troque o [inline]Storage[/inline]:

$storage = new CsvStorage('clients.csv'); // altero apenas esta linha
$clientModel = new ClientModel('clients', $storage);
Isso é fácil devido a eu estar seguindo o OCP e o DIP.

 

Dúvidas?

Além do padrão 'DAO'. existem outros tipos de "Padrões de Persistência" ??

Temos o DataMapper e o TableDataGateway.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Data Transfer Objects (DTO), antes conhecidos como Value Objects (VO)

Bah! mesma coisa.. huahuahua

Não teve confusão nenhuma de nomes.

 

eu realmente acho DAO um padrão muito ineficiente em termos de quantidade de código a se escrever.

O seu "storage" é um DAO.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Só uma coisa: eu, para usuários, uso uma classe que guarda as informações deles, mais ou menos assim:

class User{
    private $userInfo;

    public function __construct( $id = null, $email = null, $nome = null ){
        $this->userInfo = new ArrayObject();
        $this->userInfo->offsetSet( 'id', $id );
        $this->userInfo->offsetSet( 'email', $email );
        $this->userInfo->offsetSet( 'nome', $nome );
    }

    public function setId( $id ){
        $this->userInfo->offsetSet( 'id', $id );
    }

    public function setEmail( $email ){
        $this->userInfo->offsetSet( 'email', $email );
    }

    public function setNome( $nome ){
        $this->userInfo->offsetSet( 'nome', $nome );
    }

    public function getInfo( $info = null ){
        if( is_null( $info ) ){
            return get_object_vars( $this->userInfo );
        }if( $this->userInfo->offsetExists( $info ) ){
            return $this->userInfo->offsetGet( $info );
        }
    }
}

Dai quando eu quero pegar as informações de algum usuário:

$user = new User( '123' );
$userDAO = new UserDAO( $user );
$userInfo = $userDAO->get();//me retorna todas as informações do usuário
//ou
$userInfo = $userDAO->get( 'nome' ); //me retorna o nome do usuário do banco

//e quando quero salvar um usuário
$user = new User( '123', 'email@example.com', 'Gabriel' );
$userDAO = new UserDAO( $user );
$userDAO->save();

Quebro algum dos princípios S.O.L.I.D.?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Bah! mesma coisa.. huahuahua

Não teve confusão nenhuma de nomes.

O que o Martin Flower chama de Value Object é na verdade um objeto que se comporta como um tipo primitivo, em outras palavras, um objeto imutável. Exemplo:

class Money {
    private $value;
    private $currency;

    public function __construct($value, Currency $currency) {
       // ...
    }

    public function add($amount) {
        return new Money($value + amount, $this->currency);
    }
}

Outro exemplo de aplicação do padrão VO são as strings do Java.

O seu "storage" é um DAO.

É, mais ou meeeenos, mais ou meeeeeenos... Foge um pouco à forma canônica do padrão, mas pode ser considerado um mesmo. O que eu me referi como verboso demais foi a forma tradicional, de se ter um DAO para cada entidade.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Mas imutabilidade é algo bom, apesar de não ser adequada para tudo. Quando nós "abraçamos" a imutabilidade, o design é mais simples e conciso, a utilização é mais fácil (as referências do PHP são um exemplo de como a imutabilidade poderia ajudar) e, em linguagens que suportam, facilita concorrência.

 

Eu tento aplicar imutabilidade sempre que faz sentido e isso deixa o código bem mais limpo. Se você for até mesmo pegar livros conhecidos sobre OOP (Effective Java, DDD, etc.), eles têm geralmente essa recomendação. Em alguns casos, a mutabilidade não faz nem sentido, já que objetos também representam identidade.

 

Particularmente, no cado do PHP e outras linguagens dinâmicas, como Python, Ruby, etc., eu acho um tanto overkill aplicar esse padrão na forma tradicional. Eu costumo ter apenas alguns wrappers para arrays associativos ao invés de objetos separados com propriedades, mas aí vai do gosto do freguês.

 

O problema de fato é que nós temos que criar getters/setters manualmente ou mandar a IDE defecar o código. Eu até prefiro aplicar objetos separados para manter consistência. No Ruby, por exemplo, há o attr_accessor, que resolve o problema em uma linha de código. No Python, o Django faz um excelente trabalho na camada de negócio.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Mas imutabilidade é algo bom, apesar de não ser adequada para tudo.

Imutabilidade só é bom para objetos leves, do contrário, o overhead será gigante...

Compartilhar este post


Link para o post
Compartilhar em outros sites

O que você diz por "objetos leves"?

 

Como eu disse, nem sempre deve-se usar imutabilidade, mas quando possível é uma boa opção. Overheads dependem muito da linguagem e esse tópico, apesar de estar "escrito em PHP" não é necessariamente exclusivo a ele.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Objetos com muitas dependências são pesados, objetos com poucas são leves.

Gerar cópidas de objetos gera overhead em QUALQUER linguagem.

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Henrique Barcelos achei a abstração do seu código muito 10

mais fiquei com dúvidas nos seguintes itens:

 

public function setDbh(\Pdo $dbh) {
    $this->dbh = $dbh;
    $this->dbh->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
}

Para que serve essa barra \ nas entradas dos argumentos dos métodos ??

* O que quer dizer entidades ??

 

abstract class AbstractEntity {
    // implementação...
}

O que essa classe abstrata faz AbstractEntity ??

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Henrique Barcelos achei a abstração do seu código muito 10

 

mais fiquei com dúvidas nos seguintes itens:

Para que serve essa barra \ nas entradas dos argumentos dos métodos ??

 

* O que quer dizer entidades ??

 

O que essa classe abstrata faz AbstractEntity ??

As barras seriam pelo uso de namescape...

 

Quebro algum dos princípios S.O.L.I.D.?

Não.

Compartilhar este post


Link para o post
Compartilhar em outros sites

As barras seriam pelo uso de namescape...

Exatamente, namespaces são uma feature do PHP 5.3, eu removi por simplicidade, mas na hora de copiar o código, ficou assim...

 

O que quer dizer entidades ??

O que essa classe abstrata faz AbstractEntity ??

Entidades são a representação do domínio da minha aplicação, os tipos de dados que eu quero tratar: clientes, usuários, vendas, etc, etc, etc.

 

Como eu disse, gosto de usar a flexibilidade do PHP a meu favor. Não utilizo ORMs, então essas entidades são apenas array associativos mais "parrudos". Ela é bastante simples:

 

 

abstract class AbstractEntity {
    private $data = array();
    private $cleanData = array();
    private $dirty = array();

    public function __construct(array $data, $isDirty = true) {
        $this->setupFromArray($data);
        $this->cleanData = $this->data;
        if ($isDirty === false) {
            $this->dirty = array();
        } else {
            $this->dirty = array_fill_keys(array_keys($this->data), true);
        }
    }

    public function __set($property, $value) {
        if ($this->hasField($property)) {
            if (!array_key_exists($value, $this->data)
                || $this->data[$property] !== $value) {
                $this->dirty[$property] = true;
                $this->data[$property] = $value;
            }
        }
    }

    public function __get($property) {
        if (!array_key_exists($property, $this->data)) {
            return null;
        }
        return $this->data[$property];
    }

    public function setFromArray(array $data, array $excludingFields = array()) {
        foreach ($data as $key => $value) {
            if (!in_array($key, $excludingFields)) {
                $this->$key = $value;
            }
        }
    }

    private function setupFromArray(array $data) {
        foreach ($data as $key => $value) {
            if ($this->hasField($key)) {
                $this->data[$key] = $value;
            }
        }
    }

    public function hasChanged($property) {
        return array_key_exists($property, $this->dirty);
    }

    public function getChanges() {
        return array_intersect_key($this->data, $this->dirty);
    }

    public function toArray($useDirty = true) {
        return $useDirty ? $this->data : $this->cleanData;
    }

    public abstract function hasField($field);
}

 

 

A única coisa que preciso para criar uma nova entidade é implementar [inline]hasField[/inline]:

 

class User extends AbstractEntity {
    public function hasField($field) {
        return in_array(
            $field,
            array(
                'id',
                'email',
                'login',
                'password',
                'salt',
            ),
            true
        );
    }
}

// E uso assim:

$user = new User();
$user->email = 'foo@bar.co';
$user->login = 'foo';
$user->password = 'senha';
$user->salt = 'randomStuff';

 

Não é a melhor maneira de se fazer isso, muitas vezes, é aconselhável você escolher um sitema ORM simples pra facilitar a sua vida.

Compartilhar este post


Link para o post
Compartilhar em outros sites

 

class User extends AbstractEntity {
    public function hasField($field) {
        return in_array(
            $field,
            array(
                'id',
                'email',
                'login',
                'password',
                'salt',
            ),
            true
        );
    }
}

@Henrique Barcelos, você falou que usa métodos __set e __get para tornar os atributos mais dinâmicos das entidades. Mas caso por exemplo eu queira que o atributo 'id' não fosse setado diretamente fora da classe pelo método __set , pois antes que o valor fosse atribuído na array, a classe verificaria se o valor é uma string ou numérico!

 

Como faríamos agora então? pensei numa maneira que seria criar um método só para setar o valor para id e depois passasse esse valor para o índice do id do atributo 'data' que é uma array, estaria certo??

 

Se tiver outra maneira ou algo a ser corrigido esteja a vontade!

Compartilhar este post


Link para o post
Compartilhar em outros sites

 

class User extends AbstractEntity {
    public function hasField($field) {
        return in_array(
            $field,
            array(
                'id',
                'email',
                'login',
                'password',
                'salt',
            ),
            true
        );
    }
}

 

Eu, sinceramente, não esperava isso de você, meu caro @Henrique Barcelos

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.