Removendo a pasta public da sua aplicação Zend Framework

A primeira coisa que questionei quando iniciei os estudos do Zend Framework foi como colocar a sua aplicação dentro de uma pasta em um site já estruturado, uma vez que as pastas principais devem estar abaixo da pasta public_html do seu site. Um outro problema que encontrava era aparecer no endereço o nome da pasta public, por exemplo, http://localhost/app-de-teste/public/controller/action

A solução é bem simples.

  1. Remova o conteúdo da pasta public e jogue na pasta root. Se estiver iniciando com a sua aplicação deve haver somente um arquivo index.php
  2. Abra esse arquivo e na linha:
    defined('APPLICATION_PATH') || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));

    altere para:

    defined('APPLICATION_PATH') || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/application'));
  3. Crie um arquivo .htaccess na raiz do seu diretório e insira esta expressão regular:
    RewriteEngine on
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule !.(js|gif|jpg|png|css|txt)$ index.php?rt=$1 [L,QSA]

O que é feito com tudo isso? A alteração do caminho que é necessário para sua aplicação rodar e o segundo permite que se acessem arquivos (-f) e diretórios (-d) caso existam.

Utilizando Zend Auth

A Zend Auth serve para autenticar usuários em sua aplicação, ou seja, com ela você pode disponibilizar conteúdos exclusivos somente para quem estiver logado em seu sistema. Ela não distribui níveis de acesso diretamente, você pode configurar esses níveis de acesso e restringir no momento da autenticação ou usar a Zend ACL.

Neste momento não irei criar o formulário de postagem para focar bem na Zend Auth.

Crie dois controllers em sua aplicação, um para login e outro para o logout. Sugiro criar um terceiro controller apenas para testar a autenticação.

No controller dedicado ao login insira

/* Resgata o adaptador do banco de dados utilizado */
$dbAdapter = Zend_Db_Table::getDefaultAdapter();

$authAdapter = new Zend_Auth_Adapter_DbTable(
  $dbAdapter, //adaptador do banco de dados padrão da sua app
  'users', // nome da tabela de usuários
  'usuario', // nome da coluna com o nome do usuário
  'senha', // nome da coluna com o campo da senha
  'sha1(?)' // campo opcional caso a senha seja criptografada
);

/* Opcionalmente você pode capturar estes dados com os setter methods */
$authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter);
$authAdapter->setTableName('users')
            ->setIdentityColumn('username')
            ->setCredentialColumn('password');

/* Estou pressupondo que o login e senha foram capturados nas variáveis $username e $password */
$authAdapter->setIdentity($username)->setCredential($password);

/* Variável que guarda o resultado da tentativa de autenticação */
$result = $authAdapter->authenticate();

/* Agora verificamos se o retorno de $result é verdadeiro ou falso */
if($result->isValid()){
  /* Se verdadeiro inicia uma instância do Zend Auth */
  $auth = Zend_Auth::getInstance();
  /* Resgata os dados do usuário, exceto a coluna senha */
  $data = $authAdapter->getResultRowObject(null,'senha');
  /* Grava a seção no sistema */
  $auth->getStorage()->write($data);
  /* Uma vez que o usuário está logado você pode optar por redirecioná-lo ou exibir alguma mensagem */
  $this->_redirect("/");
} else {
  /* Caso o usuário não tenha sido autenticado por algum motivo você registra aqui que atitude tomar */
  $this->view->mensagem = "falhou";
}

No controller responsável pelo logout é bem mais simples.

/* Inicia uma instância de Zend Auth */
$auth = Zend_Auth::getInstance();
$auth->clearIdentity(); // apaga a identidade / encerra a seção
$this->_redirect("/"); // opcionalmente você pode redirecionar para alguma página ou exibir uma mensagem

Ok, e como fazer para saber se o usuário está logado ou não?

Nas páginas onde deseja restringir o acesso, acesse o seu respectivo controller e insira a função preDispatch.

public function preDispatch()
{
  /* Verifica se o usuário não está logado */
  if(!Zend_Auth::getInstance()->hasIdentity())
    // Se não estiver logado, você redireciona para uma página de erro.
    $this->_redirect("/erro");
}

Trabalhando com relacionamento de tabelas no Zend Db Table

Existe uma facilidade muito grande para se trabalhar com relacionamento de tabelas no Zend Framework, e evita-se aquelas enormes querys para trazer meia dúzia de dados.

Após criar os Models via Zend_Tool

zf create model nome-da-tabela

Abra o model da tabela principal e insira na classe a linha que registra qual a tabela filha:

protected $_dependentTables = array('Application_Model_DbTable_NomeDaTabelaFilha');

Em seguida, abra a tabela filha e registre mapeamento dos campos de relacionamento:

protected $_referenceMap = array(
	//Array das tabelas relacionadas
	'Empresa' => array(
		//Nome da Tabela relacionada
		'refTableClass'=>'Application_Model_DbTable_NomeDaTabelaPai',
		//Campo de relacionamento da tabela pai
		'refColumns'=>'id',
		//Campo de relacionamento da tabela filha
		'columns'=>'empresa_id',
		//Opcionais
		'onDelete'=>self::CASCADE,
		'onUpdate'=>self::RESTRICT
		)
	);

A partir de agora você pode chamar diretamente no seu controller:

$tabela_principal = new NomeDoModel();
$this->view->dados = $tabela_principal->fetchAll();

 E na sua view fazer o tratamento com um “foreach”, a única coisa que deverá ser acrescentada é no ponto onde você quer as informações da tabela filha usar o seguinte comando:

$tabela_secundaria = $tabela_principal->findDependentRowSet('Application_Model_DbTable_NomeDaTabelaFilha');

Isto retorna um array que poderá ser tratado norlmamente com o uso de um foreach.

Configurações opcionais para os módulos de uma aplicação Zend

Após modular a sua aplicação, se faz necessário adaptar algumas coisas. Por exemplo, os diretórios padrão /controllers e /views da sua aplicação não serão mais usados pois agora para qualquer controller e view criado deverá ser apontado o Módulo a qual pertence, ou seja:

zf create controller teste 1 default

onde teste é o controller que quer criar, 1 é o parâmetro para que uma view relacionada seja criada e default é o nome do módulo ao qual o controller se refere. Portanto, se somente depois de concluir a aplicação você decidiu modular, será necessário retrabalhar todos os controllers para obedecer a nova estrutura. Não basta somente mover as pastas para dentro dos novos módulos, será necessário editar o nome da classe de cada controller para abrigar o nome do módulo, ou seja, antes de modular a aplicação a sua classe controller iniciava-se assim:

class IndexController extends Zend_Controller_Action{}

Após a modulação, a mesma classe iniciará desta forma:

class Default_IndexController extends Zend_Controller_Action{}

Onde “Default_ ” é o nome do módulo a qual pertence. Depois de migrar suas classes e views pode remover as respectivas pastas da raiz já que não serão necessárias.

Para obter melhor controle sobre essa transição, existem alguns parâmetros que podem auxiliar.

No seu arquivo Application.ini insira as seguintes linhas:

; Nome do módulo que quer usar como Padrão
resources.frontController.defaultModule = "default"
resources.modules[] =
; Opcionais, somente para o caso de alterar o nome padrão
resources.frontController.defaultController = "index" 
resources.frontController.defaultAction = "index"

Configurando DB Adapter pela Zend Tool

Existem duas formas de configurar o acesso ao seu banco de dados no Zend.

A primeira e mais comum é editar o arquivo /application/configs/Application.ini e inserir as linhas com os dados de acesso ao banco de dados:

resources.db.adapter = "PDO_MySQL"
resources.db.params.host = "localhost"
resources.db.params.dbname = "zf_exemplo"
resources.db.params.username = "root"
resources.db.params.password = "root"

A segunda que considero mais rápida, uma vez que você já tem todos os dados necessários é por meio da Zend Tool. Acesse via terminal a pasta da sua aplicação e digite:

zf configure db-adapter 
"adapter=PDO_MySQL
&host=localhost
&dbname=zf_exemplo
&username=root
&password=root"

Para verificar se os dados foram inseridos corretamente acesse o arquivo de configuração e verá que os dados foram gravados como citado no terceiro parágrafo.

Integrar a biblioteca Zend com WordPress

Procurando uma forma de integrar a biblioteca do Zend Framework para implementar algumas funcões do WordPress, pesquisei em alguns sites algumas sugestões de como proceder. O resultado postado abaixo sugere uma mescla dos diversos resultados encontrados adaptados à minha atual necessidade. Os sites usados na pesquisa encontram-se relacionados abaixo.

Faça o download do Zend Framework Minimal Package no site da Zend (http://www.zend.com/community/downloads)

Descompacte e transfira a pasta library para a raiz do seu template.

No arquivo functions.php insira o código abaixo que fará o include da biblioteca Zend no WP.

set_include_path(
  get_include_path() .
  PATH_SEPARATOR .
  dirname(__FILE__) .
  DIRECTORY_SEPARATOR .
  'library'
);
require_once 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();

A partir de agora, qualquer chamada às classes do Zend Framework pelo seu template retornará resultado. Para testar insira o código abaixo em algum arquivo do wordpress, pode ser o single.php se for visualizar um post ou  page.php se for visualizar uma página padrão.

$data = new Zend_Date();
echo $data; // Exibe a data corrente de acordo com o padrão do servidor
$data->toString('YYYY-MM-dd'); // Formata a data no padrão descrito na string

Mas e como fazer para usar as classes que traballham com banco de dados, como o Zend_Db_Table?

No arquivo functions.php, faça a chamada do método Zend_Loader e configure o acesso ao banco de dados.

Zend_Loader::registerAutoload();
$db = Zend_Db::factory('Pdo_Mysql', array(
  'host'          => 'localhost',
  'username'      => 'usuario',
  'password'      => 'senha',
  'dbname'        => 'nomedobanco'
  ));

Zend_Db_Table::setDefaultAdapter($db);

Para cada tabela no banco de dados você deve criar uma classe semelhante a descrita abaixo. Para quem já conhece POO será fácil compreender a estrutura e os elementos.

class Users extends Zend_Db_Table_Abstract {
  protected $_name = 'wp_users';
  }

Para fazer as pesquisas faça a chamada das funções no seu template.

$users = new Users();
$users = $users->fetchAll()->toArray();

echo "<pre>";
  print_r($users[0]);
echo "</pre>";

Não tenho certeza se esse é o método mais seguro ou o mais prático, talvez dê para colocar a configuração do banco no wp-config.php, enfim, ainda serão necessários alguns testes para moldar qual a melhor forma.

Existe um método mais intrusivo descrito em http://www.krotscheck.net/2009/05/16/bootstrapping-a-startup-zend-and-wordpress-auth-integration.html mas que demanda muito mais conhecimento.

Qualquer sugestão a respeito, envie seu comentário.

Referências

http://stackoverflow.com/questions/377628/can-i-integrate-a-zend-framework-powered-web-application-into-a-wordpress-site

http://pt.w3support.net/index.php?db=so&id=377628

http://www.projeteweb.com.br/blog/como-integrar-o-zend-framework-em-um-tema-wordpress

 

Projeto Básico com Zend Tool

Existem uma série de diferenças na composição de um projeto com o Zend Framework e em cada fonte que se procura, é visto uma solução diferente.

Baseado nas minhas pesquisas, estou anotando abaixo uma sequência inicial para a configuração de um projeto inicial com o Zend Framework.

Estou levando em conta que a Zend Tool já está corretamente configurada no sistema. Então abra o prompt (windows) ou o terminal (mac/linux), navegue até a pasta onde será criado o seu projeto e digite:

zf create project helloworld

A Zend Tool neste ponto deve ter criado a estrutura do seu projeto, mas é necessário fazer algumas adaptações para um melhor aproveitamento e desempenho.

Abra o arquivo public/.htaccess e acrescente esta linha no início do arquivo:

SetEnv APPLICATION_ENV development

Crie uma pasta modules dentro de application, em seguida crie uma pasta default dentro de modules e mova os diretórios controllers e views para dentro dela.

É preciso ajustar algumas configurações antes de testar o seu projeto. Abra o arquivo application.ini e substitua a linha:

resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"

por:

resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"

Aproveitando que estamos tratando do frontController, adicione a variável de ambiente.

resources.frontController.env = APPLICATION_ENV

Adicione um include path para a pasta models logo abaixo do include para os arquivos do ZF.

includePaths.library = APPLICATION_PATH "/../library"
includePaths.models = APPLICATION_PATH "/models"

Inclua também o caminho da sua view e o encoding da sua aplicação.

resources.view.basePath = APPLICATION_PATH "/modules/default/views"
resources.view.encoding = "UTF-8"

Teste agora o seu projeto digitando http://localhost/helloworld/public/

Para adicionar um novo módulo, vá até a pasta modules e digite:

zf create module [nome-do-módulo]

Para adicionar um controller e uma view ao módulo digite:

zf create controller [nome_do_controller] 1 [nome-do-módulo]

Isto já é suficiente para ter um projeto básico rodando.

Como configurações adicionais ao application.ini podemos inserir

resources.frontController.controllerDirectory = APPLICATION_PATH "/modules/default/controllers"
resources.frontController.defaultModule = "default"
resources.frontController.baseUrl = "/" 

Mas até o momento não achei necessário utilizar, pois o módulo default sempre vai ser o default, indicar o defaultModule seria útil se decirmos usar outro módulo como padrão. O encoding você precisa estar atento na criação das suas views e quando salvar o seu arquivo. E os demais apontamentos de pastas já são executados normalmente pelo ZF, portanto até agora sem necessidade de indicar.