Ir para conteúdo

Arquivado

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

Bruno Augusto

Divisão de Métodos...

Recommended Posts

Uma pergunta simples mas que, por entender melhor de programação do que de banco de dados, estou meio na dúvida.

 

Não estou bem certo se DAO é o nome correto dessas classes que criam SQL Statements através de seus métodos (insert(), delete(), where(), order()...).

 

Mas a questão é quais desses métodos combinam melhor com qual classe?

 

Reli a sintaxe de uso de alguns SGBD's e separei os métodos from(), distinct(), having() e group() que, respectivamente, definem as tabelas a serem buscadas, adiciona a cláusula DISTINCT, HAVING e GROUP BY na classe Select, juntamente com fetch() e fetchAll() para obtenção dos dados resultantes de um SELECT.

 

Já na abstrata, pai dessa Select, deixei a maior parte: union(), where(), order(), limit() e etc., assim como os getters de algumas propriedades, o todo poderoso __toString(), que monta a query toda e, por fim, àquele que executa a query construída.

 

Há uma terceira classe, Table, que também extende essa mesma acima e oferece os métodos mais comuns, insert(), delete(), update() e um "atalho" para a Select.

 

Fiz bem?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Olá Bruno Augusto, eu acho que DAO apenas acessa o banco de dados executando as querys

assim como o Row Data Gateway do ORM, no ORM tem também a Table Data Gateway que é responsável por preparar o SQL

de acordo com o Martin Fowler

Compartilhar este post


Link para o post
Compartilhar em outros sites

Então no meu caso tanto Table quanto Select são DAO's, certo? Já que ambas extendem AbstractTable e ela é quem usa o PDO::prepare() e PDOStatement::execute().

 

Mas e quando a separação de métodos? Algo mais deveria fazer parte exclusivamente da Select, ao invés de parte da AbstractTable (que, através da Table garante ao acesso à INSERT< DELETE e UPDATE)?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu tenho uma classe QueryConstructor abstrata e dela derivo os construtores dos bancos de dados existentes, como MySQLQueryConstructor.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Nossa, que desenterrada :P

 

De lá pra cá mudei completamente o conjunto DB. Agora também faço assim, Rick, com uma classe abstrata contendo os métodos comuns à SELECT, INSERT, DELETE e UPDATE.

 

Preciso melhorar ainda o renderizador de SQL's cujo código ficou um pouquinho ruim de ser lido.

 

Mas já que você me lembrou do tópico ( :assobiando: ), queria saber qual a diferença entre Mapper e ActiveRecord.

 

Como os exemplos do livro Martin Fowler são todos em JAVA, ficou meio complicado de eu entender, mas deu pra perceber que são parecidos.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Pois é cara... ahsusahsahu... eu esqueço que esse fórum não é muito movimentado, tava na primeira página, nem vi a data...

 

Quanto a sua dúvida, vamos lá.

 

O Active Record é um padrão que cria uma correspondência entre uma entidade do seu conjunto de entidades (uma linha da tabela) e um objeto da sua linguagem.

Toda vez que esse objeto da linguagem é alterado, os dados do banco devem ser atualizados ("registro ativo", deu pra sacar? :lol:). Cheirou mal pra você também?

 

Primeiro que claramente isso vai além da responsabilidade desse objeto, afinal ele representa os dados, não deveria ser responsável por se comunicar com o banco.

Segundo que pela natureza do PHP, a cada mudança, você teria que disparar uma requisição para atualizar o banco de dados.

Sugestão: ingore-o.

 

O Data Mapper é sempre usado em conjunto com um Domain Object, ou Data Transfer Object, ou Table Row Gateway, eu não sei a exata diferença entre nenhum desses aí, o importante é: ele é usado com algum objeto que faz a representação dos dados.

O Data Mapper é o que faz o mapeamento desses objetos no banco de dados e fica responsável pela primeira camada das operações de CRUD.

A grosso modo, o uso do Data Mapper + Domain Object faz o que o Active Record tenta fazer, mas com as responsabilidades mais bem definidas.

Um exemplo beeeem básico é esse:

 

<?php
/**
* Implementação do padrão DataMapper 
* @author Henrique
*/
abstract class AbstractDataMapper {
/**
	* Cria um objeto DomainObject
	* @param array $data : os dados para preencher o objeto criado
	* @return DomainObject
	*/
public function create(array $data = array()){
	$obj = $this->doCreate();
	$this->fillData($obj, $data);
	return $obj;
}

/**
	* Salva um DomainObject no BD, inserindo-o ou fazendo o update
	* @param DomainObject $obj
	* @throws DBException
	* @return void
	*/
public function save(DomainObject $obj) {
	if($obj->getId() == 0){
		$this->doInsert($obj);
	} else {
		$this->doUpdate($obj);
	}
}

/**
	* Remove um DomainObject do BD
	* @param DomainObject $obj
	* @throws DBException
	* @return void
	*/
public function delete(DomainObject $obj) {
	$this->doDelete();
}

/**
	* Preenche um DomainObject com os dados do array
	* @param DomainObject $obj
	* @param array $data
	* @return void
	*/
public function fillData(DomainObject $obj, array $data){
	foreach($data as $key=>$value){
		$obj->set($key, $value);
	}
}

/**
	* A operação de inserção deve ser implementada pelo DataMapper concreto 
	* @param DomainObject $obj
	* @throws DBException
	*/
abstract protected function doInsert(DomainObject $obj);

/**
	* A operação de atualização deve ser implementada pelo DataMapper concreto 
	* @param DomainObject $obj
	* @throws DBException
	*/
abstract protected function doUpdate(DomainObject $obj);

/**
	* A operação de deleção deve ser implementada pelo DataMapper concreto 
	* @param DomainObject $obj
	* @throws DBException
	*/
abstract protected function doDelete(DomainObject $obj);

/**
	* A operação de criação deve ser implementada pelo DataMapper concreto 
	* @param DomainObject $obj
	* @throws DBException
	*/
abstract protected function doCreate();
}

/**
* Representa um objeto do Modelo do Domínio
* @author Henrique
*/
abstract class DomainObject implements IteratorAggregate {
/**
	* Os valores do banco de dados são mantidos em um array. O índice é o nome do campo.
	* @var array
	*/
private $fields = array();

/**
	* Seta o valor de um campo já existente
	* @param string $key
	* @param mixed $value
	* @return void
	*/
final public function set($key, $value) {
	if(array_key_exists($key, $this->fields)){
		$this->fields[$key] = $value;
	}
}

/**
	* Retorna um valor de um campo, se ele existir
	* @param string $key
	* @return mixed
	*/
final public function get($key) {
	return isset($this->fields[$key]) ? $this->fields[$key] : null;
}

/**
	* Adiciona um campo ao Domain Object. Vale notar que é um método protegido, logo, um DO deve se auto-definir
	* @param string $key
	* @return void
	*/
final protected function addField($key){
	$this->fields[$key] = null;
}

/**
	* Retorna todos os campos do DomainObject
	* @param string $key
	* @return array
	*/
final protected function getFields(){
	return $this->fields;
}

/**
	* Implementa a interface IteratorAggregate, para permitir a utilização em laços foreach
	* @return void
	*/
final public function getIterator(){
	return new ArrayIterator($this->fields);
}

/**
	* Retorna o ID do registro
	* @return int
	*/
abstract public function getId();
}

class UserDataMapper extends AbstractDataMapper {
protected function doCreate(){
	return new UserDO();
}

protected function doInsert(DomainObject $obj){
	echo 'Inserindo...';
	var_dump($obj);
}

protected function doUpdate(DomainObject $obj){
	echo 'Salvando...';
	var_dump($obj);
}

protected function doDelete(DomainObject $obj){
	echo 'Deletando...';
	var_dump($obj);
}
}

class UserDO extends DomainObject {
public function __construct(){
	$this->addField('user_id');
	$this->addField('user_name');
	$this->addField('user_login');
	$this->addField('user_password');
}

public function getId(){
	return $this->get('user_id');
}
}

$mapper = new UserDataMapper();

$user1 = $mapper->create(array('user_id'=>1, 'user_name'=>'Henrique'));
$mapper->save($user1);

$user2 = $mapper->create();
$user2->set('user_name', 'Bruno Augusto');
$mapper->save($user2);

 

Note que falta muita coisa aí... Por exemplo, se eu criasse uma classe ComentarioDO, para comentários em um post, por exemplo, nada me impediria de tentar inserir um objeto ComentarioDO usando um objeto UserDataMapper, bastaria eu criá-lo com o operador new invés de usar o método create do DataMappe e chamar o método:

$comentario = new ComentarioDO;
$mapper->save($comentario);

Compartilhar este post


Link para o post
Compartilhar em outros sites

Hmmmm...

 

Olhando esse seu exemplo, atualmente, tenho um pouquinho do AbstractMapper e do DomainObject, mas não implementados da forma correta, se é que a forma demonstrada é a correta e eu tenha entendido como acredito ter entendido. :P

 

POde ser que demore um pouco afinal é uma parte bem delicada que eu programo "pisando em ovos" até compreender direito esse padrões.

Compartilhar este post


Link para o post
Compartilhar em outros sites

O exemplo não tá completo por simplicidade, faltam algumas coisas mesmo...

Eu também comecei a utilizar isso sem saber, mas com algumas modificações que julguei necessárias e funciona muito bem. Pelo menos essa parte pra mim está 'fechada'.

Mas falta muita coisa ainda pra ficar bom...

Compartilhar este post


Link para o post
Compartilhar em outros sites

No inicio, antes de ler teoria e tudo mais, eu aprendi assim:

 

Active Record: Os metódos para salvar, procurar, atualizar e etc ficam na entidade que está sendo manipulada.

 

Data Mapper: Os metódos para salvar, procurar, atualizar e etc ficam em uma classe externa às entidades, e as entidades ficam sendo apenas POPO's (Plain Old PHP Objects). E não devem ter conhecimento sobre o Mapper, assim você pode mudar o Mapper, sem prejudicar as entidades existentes.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Provavelmente eu precisarei mudar, mas atualmente tenho uma classe externa que possui os métodos select(), insert(), update() e delete().

 

Cada classe concreta extende essa e define os campos com os quais essa classe vai trabalhar. E só! O resto é tudo automático.

 

Vou indo no caminho certo?

Compartilhar este post


Link para o post
Compartilhar em outros sites

A princípio sim. Funcionaria bem desse jeito.

 

Eu faço um pouco diferente. Eu tenho uma estrutura de classes que emulam/mapeiam uma tabela do banco de dados, contendo as informações relevantes: campos, campos adicionais (criados por aliasing ou subquerys), nome da tabela, nome da PK, quais campos são FK's, quais são índices, etc...

 

Para cada tabela então existe uma classe concreta, ex.:

class UserTable {}
class ProductTable {}

 

Existe somente um DomainObject, genérico e dinâmico, que recebe esse modelo de tabela como argumento do construtor para saber quais são os campos que ele pode conter.

Existe uma camada de DAO, que faz acesso ao BD. Como propriedades, existe um modelo de tabela, um conector ao BD (MySQL, PgSQL, etc) e um QueryConstructor.

Quando vou inserir/atualizar esse DomainObject no banco de dados ou recuperar os dados inseridos, o QueryConstructor lê esse modelo de tabela e lista os campos automaticamente.

Um exemplo:

$tModel = new ProductTable()
$do = new DomainObject($tModel);
$do->set('product_name', 'Caneca Bazzinga');
$do->set('product_description', 'Caneca Bazzinga TBBT');
$do->set('product_price', 19.90);

$dao = new DAO($tModel);
$dao->insert($do);

//Internamente ao DAO, o QueryConstructor gerará um SQL +/- como INSERT INTO product(id, product_name, product_description, product_price) VALUES(0, 'Caneca Bazzinga', 'Caneca Bazzinga TBBT', 19.90) e executará a query

Não estou conseguindo instalar o UML2Tools aqui. Assim que eu conseguir, posto o UML dessa parte, acho que fica mais fácil entender.

 

Faz um pouco mais de sentido pra mim essa abordagem quando eu vou passar o argumento $tModel para o DAO. Invés de passar um objeto 'pesado', com dados, passo só o 'frame'/header da tabela. Além disso, o DomainObject não precisa conter informações da tabela, ele apenas usaria um TableModel como base para sua construção, no mais, conteria apenas os dados.

 

Mano, como isso é complexo :huh:

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.