Consultas personalizadas com Zend_Db_Select

Neste artigo iremos introduzir o uso da classe Zend_Db_Select, que é utilizada para abstrair a criação de select query. A classe oferece métodos adicionais que permitem compor o comando select por partes, deixando que a classe fique responsável por montar a query corretamente, após terminar a construção, basta trabalhar como se fosse uma string.

echo $db->select()
        ->from('product');

// resultado: select 'product'.* from 'product'

Vantagens de usar Zend_Db_Select

  • Métodos orientados a objeto para construir consultas pedaço por pedaço
  • Abstração do banco de dados independente de algumas partes da consulta
  • Quoting automático, reduzindo o risco de ataques por SQL injection

Criando uma instância da classe Zend_Db_Select

Para começar a construir seu comando select é preciso ter seu banco de dados configurado, confira abaixo algumas alternativas para criar uma instância da classe Zend_Db_Select.

// criando objeto da classe Zend_Db_Select
$db = Zend_Db::factory( ...options... );
$select = $db->select();

// Instância direta da classe Zend_Db_Table
$db = Zend_Db::factory( ...options... );
$select = new Zend_Db_Select($db);

// Usando método estático da classe Zend_Db_Table
$db = Zend_Db_Table::getDefaultAdapter();
$select = $db->select();

// Zend_Db_Select na classe Zend_Db_Table
public function metodo() {
    $select = $this->select();
}

Para saber mais sobre a classe Zend_Db: http://framework.zend.com/manual/en/zend.db.html

Principais métodos da classe Zend_Db_Select

select() – Método utilizado para inicializar a construção do comando select.

from([array] $table, array $columns) – Método utilizado para informar a tabela no qual o select será realizado, passando o nome da tabela e caso seja necessário os campos de retorno, você pode usar alias(apelido, para renomear o campo) tanto para o nome da tabela quanto para os campos de retorno usando array(chave=>valor), onde a chave será o alias e o valor a tabela/campo.

// resgata todos os produtos, retornando o campo name
$select = $db->select()
             ->from(array('p'=>'product'),
                    array('name'));

columns(array $columns, $table) – Método utilizado para adicionar campos de retorno, caso você não queira especificar ou não tenha especificado seus campos de retorno no método from.

// resgata todos os produtos, retornando os campos id, name e value
$select = $db->select()
             ->from(array('p'=>'product'),
                    array('id'=>'product_id', 'name'));
$select->columns(array('value'), 'p');

where($condition) – Método utilizado para adicionar condições ao comando select, caso você use mais de uma vez na mesma query a classe irá adicionar os seguintes usando o operador AND.

// resgata todos os produtos com valor maior que R$50,00
$select = $db->select()
             ->from('product')
             ->where('value > ?', 50.00);

orWhere($condition) – Parecido com o método where, mas nesse método o operador utilizado será OR.

// resgata todos os produtos
// com valor menor que R$20,00 ou maior que R$50,00
$select = $db->select()
             ->from('product')
             ->where('value < ?', 20.00)
             ->orWhere('value > ?', 50,00);

order($order) – Método utilizado para especificar um tipo de ordenação para os resultados, onde deve ser informado o campo e o tipo de ordenação.

// resgata todos os produtos
// ordenados pelo campo name em ordem decrescente
$select = $db->select()
             ->from('product')
             ->order('name desc');

Para esse artigo, os métodos apresentados acima são suficientes, no próximo artigo no qual iremos falar sobre o uso de join no zend framework, veremos outros métodos da classe Zend_Db_Select.

Estrutura para criar um select simples

// inicializa a criação do comando select
$select = $db->select();

// especifica a tabela e as colunas que deseja de retorno
$select->from('product', array('product_id', 'name'));

// adiciona uma condição
$price = 50.00;
$select->where('value > ?', $price);

// especifica um tipo de ordenação para os resultados
$select->order('product_id desc');

Projeto Usando Zend_Db_Select

Agora que vimos um pouco sobre a classe Zend_Db_Select e suas vantagens, vamos criar um projeto para trabalhar com métodos usando Zend_Db_Select, baseado na estrutura apresentada no tópico Preparando o ambiente para desenvolvimento com Zend Framework, crie um projeto com nome de example-select.

Estrutura do nosso projeto
Estrutura do nosso projeto

No exemplo que será visto agora, iremos utilizar o banco apresentado no artigo Mapeando relacionamentos nos modelos, para criar nossos métodos usando Zend_Db_Select, confira o diagrama do nosso banco de dados:

Diagrama do banco de dados
Diagrama do banco de dados

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.

Criando modelos e métodos usando Zend_Db_Select

Com o banco devidamente criado, crie os arquivos das abas abaixo na pasta “application/models”, para configurar nossos modelos e criar métodos usando Zend_Db_Select.

User.php

No modelo User, além das configurações padrões e de mapeamento, criamos dois métodos, o método findByPartialName($name), no qual criamos uma consulta personalizada para selecionar os usuários pelo nome e o método findByEmail($email), no qual criamos uma consulta para selecionar um usuário através do seu email de cadastro.

<?php
class User extends Zend_Db_Table_Abstract
{
    /**
    * The default table name
    */
    protected $_name = 'user';

    /**
    * Dependent tables
    */
    protected $_dependentTables = array('Product', 'Order');

    /**
    * findByPartialName
    *
    * @param string $name
    */
    public function findByPartialName($name)
    {
        $select = $this->select()
                       ->from($this->_name)
                       ->where('name like ?', "%{$name}%")
                       ->order('name ASC');

        return $this->fetchAll($select);
    }

    /**
    * findByEmail
    *
    * @param string $email
    */
    public function findByEmail($email)
    {
        $select = $this->select()
                       ->from($this->_name)
                       ->where('email = ?', $email);

        return $this->fetchRow($select);
    }
}

Product.php

No modelo Product, além das configurações padrões e do mapeamento, criamos dois métodos, o primeiro findByPriceRange($minimumPrice, $maximumPrice), utilizamos uma consulta personalizada para selecionar os produtos entre dois valores, ambos passados por parâmetro, no segundo método, getLatest($limit), criamos uma consulta personalizada para selecionar os últimos produtos adicionados, ordenando pelo id em decrescente e limitando o numero de resultado, o limite é passado por parametro.

<?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',
        )
    );

    /**
    * findByPriceRange
    *
    * @param float $minimumPrice
    * @param float $maximumPrice
    */
    public function findByPriceRange($minimumPrice, $maximumPrice)
    {
        $select = $this->select()
                       ->from($this->_name)
                       ->where('value > ?', $minimumPrice)
                       ->where('value < ?', $maximumPrice)
                       ->order('value ASC');

        return $this->fetchAll($select);
    }

    /**
    * getLatest
    *
    * @param int $limit
    */
    public function getLatest($limit)
    {
        $select = $this->select()
                       ->from($this->_name)
                       ->order('product_id DESC')
                       ->limit($limit);

        return $this->fetchAll($select);
    }
}

Order.php

No modelo Order, realizamos a configuração e o mapeamento do modelo, apenas para representar o diagrama apresentado, pois não iremos utilizar no exemplo.

<?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',
        )
    );
}

OrderItem.php

No modelo OrderItem, realizamos a configuração e o mapeamento do modelo, apenas para representar o diagrama apresentado, pois não iremos utilizar no exemplo.

<?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 Zend_Db_Select

Com os nossos modelos criados e nossos métodos usando Zend_Db_Select prontos, vamos trabalhar com esses métodos, 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();

        // restaga os 3 últimos produtos cadastrados
        $latestProducts = $product->getLatest(3);
        $this->view->assign('latestProducts', $latestProducts);

        // restaga os produtos de R$20,00 até R$50,00
        $productsByPrice = $product->findByPriceRange(20.00, 50.00);
        $this->view->assign('productsByPrice', $productsByPrice);

        // resgata usuários que contenha 'Diogo' no nome
        $usersByName = $user->findByPartialName('Diogo');
        $this->view->assign('usersByName', $usersByName);

        // resgata um usuário pelo email
        $userByEmail = $user->findByEmail('thiago@gmail.com');
        $this->view->assign('userByEmail', $userByEmail);
    }
}

index.phtml

<h3>Últimos 3 produtos adicionados</h3>
<?php foreach($this->latestProducts as $product): ?>
<p><?php echo $product->name; ?></p>
<?php endforeach; ?>

<h3>Produtos com valor maior que R$20,00 e menor que R$50,00</h3>
<?php foreach($this->productsByPrice as $product): ?>
<p><?php echo $product->name; ?></p>
<?php endforeach; ?>

<h3>Procurar usuários pelo nome</h3>
<?php foreach($this->usersByName as $user): ?>
<p><?php echo $user->name; ?></p>
<?php endforeach; ?>

<h3>Procurar usuário pelo email</h3>
<p><?php echo $this->userByEmail->name; ?></p>

Estrutura final do nosso projeto:

Estrutura final do nosso projeto
Estrutura final do nosso projeto

Resultado

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

Resultado do projeto usando Zend_Db_Select
Resultado do projeto usando Zend_Db_Select

Visualizar ou efetuar download do exemplo, lembrando que no repositório desse projeto no github não consta os arquivos do framework.

Deixe uma resposta

O seu endereço de e-mail não será publicado.. Campos obrigatórios são marcados com *