Continuando o tópico Entendendo modelos no zend framework, que fizemos uma introdução sobre o uso de modelos, vamos falar agora sobre o mapeamento de relacionamentos das tabelas do banco de dados nos modelos baseados na classe Zend_Db_Table.
Banco de dados relacional
Um banco de dados relacional é um conjunto de tabelas relacionadas entre si gerenciadas por um SGBD (Sistema Gerenciador de Banco de Dados), que utiliza, por padrão, a linguagem SQL (Structured Query Language – linguagem de Consulta estruturada). Além de possibilitar a criação de tabelas, em um banco de dados relacional é possível criar relacionamentos entre as tabelas, o que garante a integridade dos dados que essas irão receber.
Tipos de relacionamentos de um banco de dados relacional
- 1 para 1 – Este tipo de relacionamento se dá, de forma direta entre duas tabelas, quando a chave primária do registro de uma determinada tabela pode ser utilizada uma única vez em um dos registros da outra tabela.
- 1 para N – Também acontece de forma direta entre duas tabelas sempre que a chave primária do registro de uma determinada tabela é utilizada várias vezes em outra tabela, sendo este, o tipo de relacionamento mais comum entre tabelas de um banco de dados relacional.
- N para N – Esse tipo de relacionamento que acontece de forma indireta entre duas tabelas, pois para que ele possa ser concebido é necessário a geração de uma terceira tabela. Na prática o relacionamento vários para vários não existe de fato, o que existe são dois ou mais relacionamentos um para vários, que ganha o sentido de vários para vários. Ocorre sempre que surge a necessidade de se relacionar duas chaves primárias de registros de diferentes tabelas em vários registros de uma terceira tabela.
Mapeamento usando Zend_Db_Table
Para que os relacionamentos funcionem nos modelos baseados na classe Zend_Db_Table, basta informar quais modelos são dependentes e quais os modelos são referênciados, ou seja, indicamos quais modelos dependem do modelo que está sendo mapeado e quais modelos são necessários para ele.
Tipos de mapeamentos usando Zend_Db_Table
- $_dependentTables – Responsável por mapear os modelos dependentes, sendo necessário informar o nome de cada modelo dependente.
- $_referenceMap – Responsável pelo mapeamento dos modelos referênciados, nele informamos as colunas equivalentes e o nome do modelo necessário.
Confira mais sobre mapeamento na documentação sobre relacionamentos com Zend_Db_Table.
Projeto usando relacionamento nos modelos
Agora que vimos um pouco sobre banco de dados relacional e mapeamento usando Zend_Db_Table, vamos criar um projeto para trabalhar com relacionamentos, baseado na estrutura apresentada no tópico Preparando o ambiente para desenvolvimento com Zend Framework, crie um projeto com nome de example-relationship.

No exemplo que será visto agora, iremos colocar em prática os dois tipos de mapeamento para que o relacionamento entre as tabelas aconteça, confira o diagrama do nosso banco de dados:

Detalhando o diagrama:
- Usuário cadastra vários produtos / Produto é cadastrado por um usuário
- Usuário faz vários pedidos / Pedido é realizado por um usuário
- Pedido possui vários itens / Item pertence a um pedido
- Item tem um produto / Produto pode estar em vários itens
Obs: Na prática um relacionamento N para N não existe, no diagrama acima representamos o relacionamento N para N usando dois relacionamentos 1 para N, o relacionamento Order – Product é um relacionamento N para N, um produto pode estar em vários pedidos e um pedido pode ter vários produtos, para isso criamos a tabela order_item, que fica responsável por esse relacionamento, armazenando as chaves primárias das tabelas Order e Product, como vimos no diagrama, esse tipo de tabela também pode ter colunas extras, no caso adicionamos uma coluna para guardar a quantidade de cada item do pedido.
Visualize ou efetue o download do script sql, no script além dos comandos de criação das tabelas, contém comandos para inserir valores nas tabelas para que o exemplo no final do artigo funcione corretamente.
Acesse o phpmyadmin, crie o banco “zf-order” e execute o script sql acima para criar e preencher as tabelas do banco. Após criar o banco edite o arquivo application.ini com as informações do banco de dados, caso tenha dúvida, visualize o artigo Entendendo modelos no zend framework.
Mapeando os relacionamentos
Agora que conheçemos os tipos de relacionamentos, tipos de mapeamentos e o banco de dados que iremos trabalhar, vamos mapear os relacionamentos das tabelas nos modelos, para isso crie os arquivos das abas abaixo na pasta “application/models”.
No modelo User mapeamos os modelos Product e Order como dependentes, já que o usuário cadastra produto e faz pedido, logo usuário é referênciado pelos modelos Product e Order que são dependentes do modelo User.
<?php
class User extends Zend_Db_Table_Abstract {
/**
* The default table name
*/
protected $_name = 'user';
/**
* Dependent tables
*/
protected $_dependentTables = array('Product', 'Order');
}
?>
No modelo Product além de mapear o modelo OrderItem(que representa a tabela de junção do relacionamento N para N), como dependente, informamos que o modelo Product faz referência ao modelo User, perceba que nesse tipo de mapeamento precisamos informar o nome do modelo(refTableClass), coluna de referência(refColumns) e a coluna do modelo produto que corresponde a coluna de referência(columns).
<?php
class Product extends Zend_Db_Table_Abstract {
/**
* The default table name
*/
protected $_name = 'product';
/**
* Dependent tables
*/
protected $_dependentTables = array('OrderItem');
/**
* Reference map
*/
protected $_referenceMap = array(
array(
'refTableClass' => 'User',
'refColumns' => 'user_id',
'columns' => 'user_id',
)
);
}
?>
No modelo Order é feito um mapeamento semelhante ao do produto, informando que o modelo OrderItem é dependente e o modelo User é referênciado para saber quem realizou o pedido.
<?php
class Order extends Zend_Db_Table_Abstract {
/**
* The default table name
*/
protected $_name = 'order';
/**
* Dependent tables
*/
protected $_dependentTables = array('OrderItem');
/**
* Reference map
*/
protected $_referenceMap = array(
array(
'refTableClass' => 'User',
'refColumns' => 'user_id',
'columns' => 'user_id',
)
);
}
?>
Para finalizar no modelo OrderItem, mapeamos a referência aos modelos Order e Produto, que são os dois modelos que fazem o relacionamento N para N.
<?php
class OrderItem extends Zend_Db_Table_Abstract {
/**
* The default table name
*/
protected $_name = 'order_item';
/**
* Reference map
*/
protected $_referenceMap = array(
array(
'refTableClass' => 'Order',
'refColumns' => 'order_id',
'columns' => 'order_id',
),
array(
'refTableClass' => 'Product',
'refColumns' => 'product_id',
'columns' => 'product_id',
)
);
}
?>
Trabalhando com os relacionamentos
Com os nossos modelos criados e devidamente mapeados, vamos explorar alguns métodos que utilizam os relacionamentos e facilita o resgate dos registros, para isso vamos utilizar o IndexController.php e sua view, index.phtml.
IndexController.php
<?php
class IndexController extends Zend_Controller_Action {
public function indexAction() {
// modelos
$user = new User();
$product = new Product();
$order = new Order();
// lista de usuários
$users = $user->fetchAll();
$this->view->assign('users', $users);
// lista de produtos
$products = $product->fetchAll();
$this->view->assign('products', $products);
// resgatando o usuário "Diogo Matheus"
$diogo = $user->find(1)->current();
// quais produtos foram cadastrados por ele?
$diogo_products = $diogo->findDependentRowset('Product');
$this->view->assign('diogo_products', $diogo_products);
// resgatando o produto "Casaco"
$casaco = $product->find(4)->current();
// quem cadastrou esse produto?
$casaco_user = $casaco->findParentRow('User');
$this->view->assign('casaco_user', $casaco_user);
// resgata um pedido, usuário que realizou e produtos que comprou
$pedido = $order->find(1)->current();
$pedido_user = $pedido->findParentRow('User');
$pedido_produtos = $pedido->findManyToManyRowset('Product', 'OrderItem');
$this->view->assign('pedido', $pedido);
$this->view->assign('pedido_user', $pedido_user);
$this->view->assign('pedido_produtos', $pedido_produtos);
}
}
?>
index.phtml
<h3>Lista de usuários</h3>
<?php foreach($this->users as $user): ?>
<p><?php echo $user->name; ?></p>
<?php endforeach; ?>
<h3>Lista de produtos</h3>
<?php foreach($this->products as $product): ?>
<p><?php echo $product->name; ?></p>
<?php endforeach; ?>
<h3>Produtos adicionados por "Diogo Matheus"</h3>
<?php foreach($this->diogo_products as $product): ?>
<p><?php echo $product->name; ?></p>
<?php endforeach; ?>
<h3>Quem cadastrou o produto "Casaco"?</h3>
<p><?php echo $this->casaco_user->name; ?></p>
<h3>Detalhando um pedido</h3>
<p>Usuário: <?php echo $this->pedido_user->name; ?>, Data do pedido: <?php echo $this->pedido->create_date; ?></p>
<?php foreach($this->pedido_produtos as $produto): ?>
<p>Produto: <?php echo $produto->name; ?></p>
<?php endforeach; ?>
Estrutura final do nosso projeto:

Resultado
Ao executar nossa aplicação iremos obter o seguinte resultado:

Confira o código-fonte do exemplo no GitHub.
E a quantidade de cada item do pedido? isso veremos em outro artigo, já que o método findManyToManyRowset() parou de retornar as colunas da tabela de junção, iremos estudar outras maneiras de realizar essa tarefa.