Usamos cookies para medir audiência e melhorar sua experiência. Você pode aceitar ou recusar a qualquer momento. Veja sobre o iMasters.
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 )...
}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á
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);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.
>
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 ?
>
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
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).
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(); public function query($sql);
public function prepare($sql);
public function execute(array $boundParams = array());
public function reset(); 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
} 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...
}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.
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.
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 );
}
}
}$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();>
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.
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.
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...
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.
Objetos com muitas dependências são pesados, objetos com poucas são leves.
Gerar cópidas de objetos gera overhead em QUALQUER linguagem.
@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 ??
>
@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.
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.
>
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!
>
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
>
class User extends AbstractEntity {
public function hasField($field) {
return in_array(
$field,
array(
'id',
'email',
'login',
'password',
'salt',
),
true
);
}
}O que foi @Evandro Oliveira, tem algo de errado ae ??
>
O que foi @Evandro Oliveira, tem algo de errado ae ??
Nada de mais. Apenas que, vindo do Henrique, eu esperava algo envolvendo http://php.net/ReflectionClass'>Reflection ou afins pra fazer introspecção de entidades, não um Array hard-coded dentro do método que será criado e destruído cada vez que for necessário consultar um campo da entidade.
Isso é um bom exemplo de primitive obsession. O problema prático de usar __set/__get é a má performance.
Nada de mais. Apenas que, vindo do Henrique, eu esperava algo envolvendo Reflection ou afins pra fazer introspecção de entidades, não um Array hard-coded dentro do método que será criado e destruído cada vez que for necessário consultar um campo da entidade.
Hahaha, concordo que não é a melhor opção, trouxe esse exemplo antigo por questão de simplicidade, meu caro...
>
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!
Vamos com calma aí... Qual a razão para você não querer que o atributo [inline]id[/inline] seja diretamente setado?
Eu não vejo problema algum em fazê-lo, já que ao alterar o [inline]id[/inline], vc está criando um novo dado. Mas, é possível trabalhar nisso.
Vamos repensar essa estrutura um pouco:
<?php
interface FieldTypeInterface {
public function convert($value);
}
class IntType implements FieldTypeInterface {
public function convert($value) {
return (int) $value;
}
}
class DoubleType implements FieldTypeInterface {
public function convert($value) {
return (double) $value;
}
}
class StringType implements FieldTypeInterface {
public function convert($value) {
return (string) $value;
}
}
interface EntityFieldInterface {
public function getName();
public function getType();
public function isReadOnly();
}
class EntityField implements EntityFieldInterface {
private $name;
private $type;
private $readOnly;
public function __construct($name, FieldTypeInterface $type, $readOnly = false) {
$this->name = $name;
$this->type = $type;
$this->readOnly = $readOnly;
}
public function getName() {
return $this->name;
}
public function getType() {
return $this->type;
}
public function isReadOnly() {
return $this->readOnly;
}
}
interface EntityModelInterface {
public function getName();
public function getField($fieldName);
public function hasField($fieldName);
}
class DefaultEntityModel implements EntityModelInterface {
private $name;
private $fields = array();
public function __construct($name, array $fields) {
$this->name = $name;
$this->setFields($fields);
}
public function getName() {
return $this->name;
}
private function setFields($fields) {
foreach ($fields as $field) {
if (! $field instanceof EntityFieldInterface) {
throw new InvalidArgumentException('Field must be instance of EntityFieldInterface');
}
$this->fields[$field->getName()] = $field;
}
}
public function hasField($fieldName) {
return array_key_exists($fieldName, $this->fields);
}
public function getField($fieldName) {
if (!$this->hasField($fieldName)) {
throw new DomainException("EntityModel '{$this->name}' has no field named '{$fieldName}'");
}
return $this->fields[$fieldName];
}
}
interface EntityInterface {
public function get($fieldName, $defaultValue = null);
public function set($fieldName, $fieldValue);
public function __get($fieldName);
public function __set($fieldName, $fieldValue);
public function setFromArray(array $data);
public function toArray($useDirty = true);
}
class DefaultEntity implements EntityInterface {
private $dataModel;
private $data = array();
private $cleanData = array();
private $dirty = array();
public function __construct(EntityModelInterface $dataModel, array $data, $dirty = false) {
$this->dataModel = $dataModel;
$this->setupData($data, $dirty);
}
private function setupData(array $data, $dirty) {
foreach ($data as $field => $value) {
if ($this->dataModel->hasField($field)) {
// Law of Demeter diz oi =]
$this->data[$field] = $this->dataModel->getField($field)->getType()->convert($value);
$this->dirty[$field] = $dirty;
}
}
if ($dirty === false) {
$this->cleanData = $this->data;
}
}
public function set($field, $value) {
$fieldObj = $this->dataModel->getField($field); // Irá lançar uma exceção se não houver o tal campo...
if ($fieldObj->isReadOnly()) {
throw new LogicException("Cannot set value to read-only field '{$field}'");
}
$this->data[$field] = $fieldObj->getType()->convert($value);
$this->dirty[$field] = true;
}
public function get($field, $defaultValue = null) {
$fieldObj = $this->dataModel->getField($field); // Irá lançar uma exceção se não houver o tal campo...
return array_key_exists($field, $this->data) ? $this->data[$field] : $defaultValue;
}
public function __set($field, $value) {
$this->set($field, $value);
}
public function __get($field) {
return $this->get($field);
}
public function setFromArray(array $data) {
foreach ($data as $field => $value) {
try {
$this->set($field, $value);
} catch (DomainException $e) {
// Não faz nada...
}
}
}
public function toArray($useDirty = true) {
return $useDirty === true ? $this->data : $this->cleanData;
}
}
Usando:
$intType = new IntType();
$stringType = new StringType();
$doubleType = new DoubleType();
$userModel = new DefaultEntityModel('user', array(
new EntityField('id', $intType, true),
new EntityField('name', $stringType),
new EntityField('surname', $stringType),
new EntityField('birth_date', $stringType),
));
$productModel = new DefaultEntityModel('product', array(
new EntityField('id', $intType, true),
new EntityField('name', $stringType),
new EntityField('description', $stringType),
new EntityField('value', $doubleType),
));
$user = new DefaultEntity($userModel, array(
'id' => 1,
'name' => 'João',
'surname' => 'da Silva',
'birth_date' => '1990-01-01',
));
$product = new DefaultEntity($productModel, array(
'id' => 1,
'name' => 'Facas Ginsu',
'description' => 'As incríveis facas Ginsu, inquebráveis!',
'value' => 499.99,
));
var_dump($user->toArray());
var_dump($product->toArray());
$user->id = 2; // erro, pois é somente-leitura
$user->value = 30; // erro, pois a propriedade não existe
Perceba que agora ficou um pouco mais difícil criar uma entidade. Aqui temos um bom caso para aplicarmos o padrão [inline]Factory Method[/inline] ou até mesmo um [inline]AbstractFactory[/inline] para facilitar a criação das entidades:// Na inicialização:
$factory = new EntityFactory();
$factory->registerModel('user', $userModel);
$factory->registerModel('product', $productModel);
// Em algum lugar do código:
$factory->createEntiry('user', $data);
Só para a questão de performance apontada pelo Enrico:http://www.garfieldtech.com/blog/magic-benchmarks
Apesar de existir um impacto considerável, os testes são para 2.000.000 (dois milhões) de chamadas.
Considerando que um código comum não terá nem 2.000 chamadas, a diferença de performance é desprezível.
A ideia de que métodos mágicos são gargalos de performance é uma falácia: o acesso a banco de dados é MUITO mais custoso.
Um dica, já que está utilzando php orientado, use um framework codeigniter2 por exemplo. Melhor eficiência e menos tempo gasto.;)
Prefiro me dar um tiro no pé do que usar CodeIgniter...
Frameworkzinho furreca, pseudo-OO, cheio de gambiarra.
A ideia aqui é justamente entender COMO funciona um framework, para só aí passar a utilizar um. Do contrário, se você não entende os conceitos, acaba ficando viciado em um framework específico. No dia em que precisar de outro, não vai saber usar.
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..
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.
Existe por exemplo o Active Record (eu pessoalmente não curto, não vi nenhuma implementação "legal" deele).