Usamos cookies para medir audiência e melhorar sua experiência. Você pode aceitar ou recusar a qualquer momento. Veja sobre o iMasters.
Estou migrando o sistema para dentro do ZEND e me deparei com a seguinte situação:
Tenho a seguinte query:
SELECT cat.nome nome_categoria, sub . *
FROM categorias cat
INNER JOIN subcategorias sub
ON cat.id = sub.categoria_id
ORDER BY cat.nome
E ao passar pra dentro do framework, eu até achei simples, de acordo com a documentação:
http://framework.zend.com/manual/en/zend.db.select.html
No entanto pra criar esta query, eu preciso de fato criar o objeto Zend_Db_Select?
Lá eu tenho o seguinte exemplo:
$db = Zend_Db::factory( ...options... );
$select = $db->select();
Eu consigo criar esta query, sem ter de passar os dados através do factory()? Eles já estão configurados no application.ini.
Eu criei um protótipo, segue abaixo:
$rs = $this->getDbAdapter()->select()
->from(array('cat' => 'categorias'),
array('nome'))
->join(array('sub' => 'subcategorias'),
array('cat.id = sub.categoria_id'));
Valeu pela ajuda.
Abraço,
Não fiz o mapeamento! Tu acha melhor fazê-lo?
Meu 'application.ini' está da seguinte forma:
[production]
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
includePaths.library = APPLICATION_PATH "/../library"
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"
appnamespace = "Application"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
resources.frontController.params.displayExceptions = 0
; Banco de Dados
resources.db.adapter = "PDO_MYSQL"
resources.db.params.host = "localhost"
resources.db.params.dbname = "store"
resources.db.params.username = "root"
resources.db.params.password = ""
Meu factory está da seguinte forma:
$db = Zend_Db::factory('Pdo_Mysql', array(
'host' => 'localhost',
'username' => 'root',
'password' => '',
'dbname' => 'store'
));
No entanto não queria utilizar o factory. Queria saber como fazer a consulta SQL sem ter que 'conectar' no banco e ao invés disso, utilizar as configs do application.ini
Valeu!
O mapeamento é interessante, mas não é obrigatório. Vai depender da sua aplicação.
Ao fazer como você descreve no application.ini, não é necessário utilizar o Factory. Você pode utilizar getDefaultAdapter(); Algo assim:
$db = Zend_Db_Table::getDefaultAdapter ();
$select = $db->select ();
Isto considerando que você não esteja estendendo a classe Zend_Db_Table_Abstract. Caso esteja, não é necessário fazer nada disto. Só utilizar $this->select().
Carlos Eduardo
Minha classe Application_Model_DbTable_SubCategorias esta estendendo da Zend_Db_Table_Abstract,
Então, na Mapper, eu faço o seguinte:
public function fetchAll(){
$select = $this->getDbTable()->select();
$select->from(array('cat' => 'categorias'),array('nome'));
$select->join(array('sub' => 'subcategorias'),array('cat.id = sub.categoria_id'));
$resultado = $this->fetchAll($select);
return $resultado;
}
Quando vou na view e mando printar o fetchAll, me aparece o seguinte resultado:
Fatal error: Maximum function nesting level of '100' reached, aborting! in C:\wamp\library\Zend\Db\Table\Abstract.php on line 889
/applications/core/interface/imageproxy/imageproxy.php?img=http://img64.imageshack.us/img64/4435/errozendframework.png&key=73870ff1600f9385db82655a07c79d146ff68f7fce911ab49a9f59f68b403855" alt="errozendframework.png" />
Não consegui entender como você está fazendo. Poste a Mapper completa. Qual versão do Zend Framework você tá usando?
Carlos Eduardo
Mapper
<?php
/**
* Mapeamento da tabela SUBCATEGORIAS
* Funções para inclusao, exclusao e edicao
*
* @author Matheus
* @version 1.0
*/
class Application_Model_SubCategoriasMapper {
protected $_dbTable;
public function setDbTable($dbTable) {
// Verifica se o parâmetro é mesmo uma string
if (is_string($dbTable)) {
$dbTable = new $dbTable;
}
// Testa se o objeto é uma instancia de uma classe especifica
if (!$dbTable instanceof Zend_Db_Table_Abstract) {
// lanca excessao
throw new Exception("Gateway Inválido");
}
// se passar nos dois testes, aceita a atribuicao
$this->_dbTable = $dbTable;
// retorna o objeto
return $this;
}
public function getDbTable() {
// testa pra saber se a instancia criada pelo setDbTable é nula
if (NULL === $this->_dbTable) {
$this->setDbTable("Application_Model_DbTable_SubCategorias");
}
return $this->_dbTable;
}
public function fetchAll(){
$select = $this->getDbTable()->select();
$select->from(array('cat' => 'categorias'),array('nome'));
$select->join(array('sub' => 'subcategorias'),array('cat.id = sub.categoria_id'));
$resultado = $this->fetchAll($select);
var_dump($select);
/*
$results = array();
foreach ($resultado AS $row){
$entry = new Application_Model_SubCategorias();
$entry->setId($row->id);
$entry->setNomeCategoria($row->nome_categoria);
$entry->setNome($row->nome);
$results[] = $entry;
}
*/
return $resultado;
}
/**
* Método para cadastrar subcategorias
*
* @param array $dados
*/
public function cadastrar($dados){
$this->getDbTable()->insert($dados);
}
/**
* Método que exclui a subcategoria que é passada pelo IDs
*
* @param int $id
*/
public function excluir($id){
$table = $this->getDbTable();
$where = $table->getAdapter()->quoteInto('id = ?',$id);
$table->delete($where);
}
}
?>
DbTable
<?php
class Application_Model_DbTable_SubCategorias extends Zend_Db_Table_Abstract {
protected $_name = "subcategorias";
}
?>Uhn... Acho que aqui tem coisa errada...
$resultado = $this->fetchAll($select);
Quando você faz $this->fetchAll(), está chamando o próprio método (recursividade). Isto é interessante, mas acredito que no seu caso você queria pegar o método fetchAll() da classe Application_Model_DbTable_SubCategorias. Então, seria:
$resultado = $this->getDbTable()->fetchAll($select).
--------------------------------
Além disto, veja que você utiliza no seu application.ini o seguinte:
appnamespace = "Application"
Então, não há necessidade de utilizar Application_* no nome das classes.
Outra sugestão que eu faço, já que você está fazendo com Mapper, é utilizar uma classe Pai (AbstractMapper) que vai abstrair os métodos getDbTable e setDbTable, já que eles serão exatamente iguais em qualquer Mapper que você fizer, sendo diferente somente o nome da classe que será instanciada. Para isto, eu passaria o nome em uma propriedade $_dbTableName, definida na classe filha.
Carlos Eduardo
Fala ae Carlos, cara muito obrigado pelas dicas e ajuda que está me dando! Estou muito grato mesmo.
Experimentei fazer da forma como tu falou, no entanto não sei o que acontece de errado.
Primeiramente, deixei o método fetchAll() da seguinte forma:
$select = $this->getDbTable()->select()
->from(array('cat' => 'categorias'),array('nome'))
->joinInner(array('sub' => 'subcategorias'),array('cat.id = sub.categoria_id'));
$resultado = $this->getDbTable()->fetchAll($select);
return $resultado;
Desta forma, retorna a seguinte msg:
Exception information:
**Message: Select query cannot join with another table**
Se eu tirar o $this->getDbTable() da var $select e deixar apenas $this->select() retorna o erro:
**Fatal error: Call to undefined method Application_Model_SubCategoriasMapper::select() in C:\wamp\www\loja\application\models\SubCategoriasMapper.php on line 40**
Ou seja, ele não acha o método select() dentro de Model_SubCategoriasMapper.
Então, resolvi fazer da seguinte forma:
$db = $this->getDbTable()->getDefaultAdapter();
$select = $db->select()
->from(array('cat' => 'categorias'),array('nome'))
->join(array('sub' => 'subcategorias'),array('cat.id = sub.categoria_id'));
$resultado = $this->getDbTable()->fetchAll($select);
return $resultado;
Mas isso me retorna um erro de SQL:
Message: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
Fala ae Carlos, cara muito obrigado pelas dicas e ajuda que está me dando! Estou muito grato mesmo.
:joia:
Sobre o seu problema, o primeiro código é o mais correto, mas o Zend Framework tem um bug (não vou achar agora o bug tracing, mas depois você procura). Para que queries com join funcionem, você deve fazer assim:
$select = $this->getDbTable()->select()->setIntegrityCheck(false)
->from(array('cat' => 'categorias'),array('nome'))
->joinInner(array('sub' => 'subcategorias'),array('cat.id = sub.categoria_id'));
$resultado = $this->getDbTable()->fetchAll($select);
return $resultado;
Veja se assim funciona.
Carlos Eduardo
E ae Carlos,
Na verdade não funcionou, mas era um erro de sintaxe. Vou colocar aqui pra alertar o pessoal:
$select = $this->getDbTable()->select()->setIntegrityCheck(false)
->from(array('cat' => 'categorias'),array('nome'))
->joinInner(array('sub' => 'subcategorias'),array('cat.id = sub.categoria_id'));
$resultado = $this->getDbTable()->fetchAll($select);
return $resultado;
O correto é não ter o ultimo array() - array('cat.id = sub.categoria_id'), deve ficar apenas desta forma:
$select = $this->getDbTable()->select()->setIntegrityCheck(false)
->from(array('cat' => 'categorias'),array('nome'))
->joinInner(array('sub' => 'subcategorias'), 'cat.id = sub.categoria_id');
$resultado = $this->getDbTable()->fetchAll($select);
return $resultado;
Dai funcionou.
Estava tentando recuperar o NOME da categoria associada à subcategoria, então meu select ficou desta forma:
$select = $this->getDbTable()->select()
->from(array('cat' => 'categorias'))
->join(array('sub' => 'subcategorias'),
'sub.categoria_id = cat.id',
array ('categoria_nome' => 'cat.nome',
'categoria_id' => 'cat.id',
'subcategoria_nome' => 'sub.nome',
'subcategoria_id' => 'sub.id'))
->setIntegrityCheck(false);
$resultado = $this->getDbTable()->fetchAll($select);
Porém, ele me retorna este resultado:
array
0 =>
array
'id' => string '7' (length=1)
'nome' => string 'Bijuteria' (length=9)
'categoria_nome' => string 'Bijuteria' (length=9)
'categoria_id' => string '7' (length=1)
'subcategoria_nome' => string 'Anel' (length=4)
'subcategoria_id' => string '4' (length=1)
Se reparar, a posição id e nome são a mesma de categoria_nome e categoria_id. Tu sabe porque isso ocorre? Isso é detalhe, mas é curiosidade.Fiz desta forma de acordo com a documentação [http://framework.zen....db.select.html](http://framework.zend.com/manual/en/zend.db.select.html) , se tiver uma dica, ela é muito bem vinda.
Mais uma vez, muito obrigado pela força.
Abraço,
Então... O objeto Select tem um método __toString(), que é chamado ao se tratar o objeto como uma string. Este método imprime a query como ela está no momento. Faça um teste:
$select = $this->getDbTable()->select()
->from(array('cat' => 'categorias'))
->join(array('sub' => 'subcategorias'),
'sub.categoria_id = cat.id',
array ('categoria_nome' => 'cat.nome',
'categoria_id' => 'cat.id',
'subcategoria_nome' => 'sub.nome',
'subcategoria_id' => 'sub.id'))
->setIntegrityCheck(false);
echo $select;
exit;
$resultado = $this->getDbTable()->fetchAll($select);
Isto é muito útil quando estamos fazendo debug e para analisar as querys enviadas, buscando melhorar a performance.
No seu caso, isto ocorre porque você não define quais campos quer buscar da tabela no from, então ele busca *. Caso não queira nenhum campo, coloque array().
Carlos Eduardo
Entendi!
Cara, muito obrigado pela força! Vou seguir desenvolvendo aqui agora que desempaquei nessa parte.
Abraço,
Matheus
Como você fez o ORM? Ou não vai fazer mapeamento? Como você definiu no application.ini? O que você está passando para a factory?
Carlos Eduardo