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.

Custom SQL no WordPress

As funções do WordPress são muito úteis e na maioria das vezes o programador não necessita sequer pensar em banco de dados, conexão, select e outros termos que são de uso corrente em uma pesquisa em banco de dados com PHP. Entretanto, mesmo com toda essa facilidade de pesquisa, existem alguns casos em que não é possível escapar de customizar a query para obter os resultados com menor uso de memória e evitar também o timeout da aplicação.

Uma boa referência do que será tratado aqui pode ser visto de forma mais ampla em http://codex.wordpress.org/Class_Reference/wpdb.

Preparei aqui uma busca customizada no banco para pesquisar

$sql = "SELECT wusers.ID,
               wmeta.user_id,
               wmeta.meta_key,
               wmeta.meta_value
        FROM
               $wpdb->users wusers
        INNER JOIN
               $wpdb->usermeta wmeta
	ON
                wusers.ID = wmeta.user_id
	WHERE
                wmeta.meta_key = 'Twitter'
	ORDER BY
                wmeta.meta_value ASC";

Então estamos pedindo para selecionar os campos ID da tabela users e user_id, meta_key e meta_value da tabela usermeta onde o campo meta_key é igual a Twitter e mandamos ordenar pelo valor. Observe que nesse caso eu estou citando um campo que foi personalizado, mas poderíamos pesquisar por first_name, last_name ou qualquer outro meta_key registrado. Em caso de dúvida consulte o post Melhorando o formulário de cadastro no WordPress.

Tendo a sintaxe SQL, vamos utilizar as funções do próprio WordPress para processar os resultados.

global $wpdb;

$users = $wpdb->get_results($sql);

foreach($users as $user){
  $uinfo = get_userdata($user->ID); 
  echo $uinfo->first_name . " : " . $uinfo->twitter<br />";
}

Com isso podemos gerenciar o cadastro de usuários, podemos fazer uma ficha com foto utilizando o Avatar por meio da função

get_avatar($user->ID);

ou mais completa:

get_avatar( $user->ID,
            $tamanho = '96',
            $img_padrao = 'http://www.seusite.com.br/avatar.jpg' )

Também é possível inserir diretamente o link para a imagem

$grav_url = "http://www.gravatar.com/avatar/" . 
             md5(strtolower($user->user_email)) .
             "?d=" . urlencode($avatar_padrao) .
             "&s=" . $tamanho;

echo "<img src='$grav_url'/>";

Agora é usar a criatividade e a necessidade para otimizar os seus resultados.

Melhorando o formulário de cadastro do WordPress

Existem uma série de plugins que gerenciam para você o cadastro de usuários no WordPress adicionando ou retirando campos, entretanto diversas vezes os achei muito limitados e até difíceis de gerenciar pelo usuário comum.

O problema que eu vejo em se utilizar plugins que são feitos por terceiros é que você sempre fica à mercê das atualizações promovidas pelo desenvolvedor ou a própria descontinuação do projeto. Então, exceto pelos plugins que são de desenvolvedores ou empresas conhecidas, eu prefiro sempre que possível fazer a minha implementação.

Neste exemplo não construirei um plugin, mas indico como fazer o uso das funções do WordPress para gerenciar o cadastro de usuários.

Primeiramente devemos relacionar o que queremos em nosso formulário.

  • Nome
  • Email
  • Twitter
  • Telefone
  • Endereço
  • Bairro
  • Cidade
  • Estado
  • CEP

A seguir, baseado no formulário de cadastro que o WP disponibiliza vamos personalizar o nosso. Iniciaremos excluindo alguns campos que não são necessários.

add_filter('user_contactmethods','add_contactmethod',10,1);

function add_contactmethod( $contactmethods ) {
  $contactmethods['user_twitter'] = 'Twitter';
  $contactmethods['user_telephone'] = 'Telefone';

  unset($contactmethods['aim']);
  unset($contactmethods['jabber']);
  unset($contactmethods['yim']);

  return $contactmethods;
}

A variável $contactmethods contempla todas os campos referentes às informações de contato. Poderíamos já incluir nesta função todos os campos que queremos como o de endereço e bairro, mas ficaria confuso misturar esse tipo de informação, então para esta situação, iremos criar uma seção exclusiva para estas informações extras. Em resumo, para adicionar campos utilize $contactmethods['nome_do_campo'] = ‘Label do Campo’ e para excluir um existente utilize a função do php unset($contactmethods['nome_do_campo']).

Então já temos assim os campos Nome e email (padrão do WordPress) e agora acabamos de adicionar Twitter e Telefone. Vamos agora criar o formulário interno com os novos campos tanto para inserção quanto para atualização de cadastro.

add_action( 'show_user_profile', 'extra_info_profile_fields' );
add_action( 'edit_user_profile', 'extra_info_profile_fields' ); 

function extra_info_profile_fields($user) { ?>
<h3>Informações extras</h3>
  <table>
    <tr>
      <th><label for="user_address">Endereço</label></th>
      <td>
        <input type="text" name="user_address" id="user_address" value="<?php echo esc_attr( get_the_author_meta( 'user_address', $user->ID ) ); ?>" /><br />
        <span>Preencha o seu endereço.</span>
      </td>
    </tr>
    <tr>   
      <th><label for="user_district">Bairro</label></th>
      <td>
        <input type="text" name="user_district" id="user_district" value="<?php echo esc_attr( get_the_author_meta( 'user_district', $user->ID ) ); ?>" /><br />
        <span>Preencha com o seu bairro.</span>
      </td>
    </tr>
    <tr>
      <th><label for="user_city">Cidade</label></th>
      <td>
        <input type="text" name="user_city" id="user_city" value="<?php echo esc_attr( get_the_author_meta( 'user_city', $user->ID ) ); ?>" /><br />
        <span>Preencha com a sua cidade. </span>
      </td>
    </tr>
    <tr>
      <th><label for="user_state">Estado</label></th>
      <td>
       	<input type="text" name="user_state" id="user_state" value="<?php echo esc_attr( get_the_author_meta( 'user_state', $user->ID ) ); ?>" /><br />
        <span>Preencha com o seu estado. </span>
      </td>
    </tr>
    <tr>
      <th><label for="user_zipcode">CEP</label></th>
      <td>
        <input type="text" name="user_zipcode" id="user_zipcode" value="<?php echo esc_attr( get_the_author_meta( 'user_zipcode', $user->ID ) ); ?>" /><br />
        <span>Digite o seu CEP. </span>
      </td>
    </tr>
 </table>

<?php }
add_action( 'personal_options_update', 'save_extra_info_profile_fields' );
add_action( 'edit_user_profile_update', 'save_extra_info_profile_fields' ); 

function save_extra_info_profile_fields( $user_id ) {
  if ( !current_user_can( 'edit_user', $user_id ) ) {
    return false;
  } 
  update_usermeta( $user_id, 'user_address', $_POST['user_address'] );
  update_usermeta( $user_id, 'user_district', $_POST['user_district'] );
  update_usermeta( $user_id, 'user_city', $_POST['user_city'] );
  update_usermeta( $user_id, 'user_state', $_POST['user_state'] );
  update_usermeta( $user_id, 'user_zipcode', $_POST['user_zipcode'] );
}

A função criada abaixo faz uso da função do WordPress wp_insert_user, que serve exclusivamente para criar o cadastro do usuário, mas ela só responde pelos campos padrão e pelos campos criados por meio do hook user_contactmethods. E os outros campos? Caso a função seja concluída, ela nos retorna o ID do usuário cadastrado, pegamos esse ID e mandamos atualizar o cadastro. Para mais informações sobre esta função acesse http://codex.wordpress.org/Function_Reference/wp_insert_user e para a função de atualizar o cadastro http://codex.wordpress.org/Function_Reference/wp_update_user.

function criar_usuario($firstname, $lastname, $nickname, $user_nicename, $user_email, $display_name, $user_site, $user_blog, $user_twitter, $user_telephone, $user_login, $user_pass, $user_role="subscriber"){
    $user_data = array();
    $user_data["first_name"] = $firstname;
    $user_data["last_name"] = $lastname;
    $user_data["nickname"] = $nickname;
    $user_data["user_nicename"] = $user_nicename;
    $user_data["user_email"] = $user_email;
    $user_data["display_name"] = $display_name;
    $user_data["user_url"] = $user_site;
    $user_data["user_blog"] = $user_blog;
    $user_data["user_twitter"] = $user_twitter;
    $user_data["user_telephone"] = $user_telephone;
    $user_data["user_login"] = $user_login;
    $user_data["user_pass"] = $user_pass;
    $user_data["role"] = $user_role;

    return  wp_insert_user( $user_data );
    update_user();
}
function update_user($user_id, $address, $district, $city, $state, $zipcode){
  if(!is_wp_error($user_id)){
    update_user_meta( $user_id, 'user_address', $address);
    update_user_meta( $user_id, 'user_complement', $complement);
    update_user_meta( $user_id, 'user_district', $district);
    update_user_meta( $user_id, 'user_city', $city);
    update_user_meta( $user_id, 'user_state', $state);
    update_user_meta( $user_id, 'user_zipcode', $zipcode);
  }
}

Para efetivar o seu cadastro, basta criar o seu formulário e na página de processamento chamar as funções acima criadas com as suas respectivas variáveis preenchidas.

 

Plugins Sugeridos

Register Plus
http://wordpress.org/extend/plugins/register-plus/

Simplr Registration Form
http://wordpress.org/extend/plugins/simplr-registration-form/

Register Plus Redux
http://wordpress.org/extend/plugins/register-plus-redux/

Enviar email no formato HTML via WordPress

Para o envio de email o WordPress faz uso da função wp_mail() que disponibilizam os seguintes argumentos para serem trabalhados:

wp_mail( $destinatario, $assunto, $mensagem, $headers, $anexos );

Os dois últimos argumentos são opcionais.

Por padrão, esta função somente envia emails no formato texto, sem formatação, ou seja, qualquer tag html que exista no seu email será mostrada ao usuário por extenso.

Para resolver este problema, existem duas formas de trabalhar essa função para que ela exiba corretamente as suas tags html.

A primeira e mais simples é adicionando o content-type diretamente na variável $header:

$headers = array("From: Wildiney <criacao@wildiney.com>",
	         "Content-Type: text/html"
	         );

Se forem poucos os lugares onde você fará uso desta função, o exemplo acima resolve perfeitamente. Agora se o uso for constante, você pode configurar diretamente no arquivo functions.php.

add_filter('wp_mail_content_type','set_contenttype');

function set_contenttype($content_type){
  return 'text/html';
}

Existe também uma versão mais resumida da função acima que eu encontrei em alguns fóruns.

add_filter('wp_mail_content_type', create_function('', 'return "text/html"; '));

Nesse caso fica a gosto, não notei nenhuma diferença no desempenho, apesar de acreditar que usar a função do php create_function possa gerar mais processamento do que explicitar diretamente o que quer.

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

 

Bloco de produtos em promoção e produtos randômicos do Magento

O template para os produtos em promocão fica localizado em app\code\core\Mage\Catalog\Block\Product\List\Promotion.php. Para utilizar, acrescente ou crie um atributo promotion, de escopo global, input type yes/no,  e adicione ao set de atributos que você utiliza.

Para exibir os produtos em promoção utilize no xml de gerenciamento da sua página o código:

<block type="catalog/product_list_promotion"
       name="product_promotion"
       template="catalog/product/list.phtml"/>

ou insira pelo CMS:

{{block type='catalog/product_list_promotion'
        template='catalog/product/list.phtml'}}

O template do bloco de itens randômicos estão localizados em app\code\core\Mage\Catalog\Block\Product\List\Random.php, para chamar no xml de gerenciamento da sua página utilize:

<block type="catalog/product_list_random"
       name="product_random"
       template="catalog/product/list.phtml"/>