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.
 Diogo Matheus
            
						
              Diogo Matheus 
               
				 Entendendo os modelos do Zend Framework
  Entendendo os modelos do Zend Framework