Itens ativos
Pesquisa
Login do usuário
Assine o RSS do Drupal-BR
Prover campos personalizados para o Views
Hoje vou mostrar como criar um pequeno módulo com um campo que é gravado em uma tabela própria, e em seguida disponibilizar esse campo para filtragem e ordenação no módulo Views.
Antes de começarmos, prcisamos nos ambientar. Para quem nunca criou um módulo para o Drupal, recomendo, antes de ler esse tutorial, ler essa documentação: http://drupal.org/node/508
Um outro requisito para entender plenamente esse tutorial(sua segunda parte, mais especificamente), é um pouco de conhecimento do módulo Views. Para baixar e testar esse módulo, acesse http://drupal.org/project/views Uma documentação inicial está disponível em http://drupal.org/node/47412
Ao final desse artigo, você poderá baixar o módulo de teste que vamos criar.
Sabendo essas coisas vamos lá.
Parte 1 - Criação do módulo
Para esse tutorial vamos assumir alguns padrões:
- O nome do módulo será "modteste" (sem aspas, obviamente);
- O nome do campo adicional será "meucampo" e seu label será "Meu campo";
- O nome das tabelas que iremos criar são: modteste e modteste_options
Antes de mais nada precisamos criar um módulo e, para isso, precisamos criar uma pasta com o nome do nosso módulo.
Assim, acesse a pasta modules da sua instalação do Drupal e crie uma pasta chamada modteste. Dentro dessa pasta crie, a princípio, 3 arquivos: modteste.info modteste.install modteste.module
Cada arquivo desse tem um propósito:
- modteste.info: Esse arquivo informa ao Drupal qual a versão, subgrupo e outras informações de instalação do seu módulo;
- modteste.install: Aqui temos a criação e remoção de tabelas, parâmetros de configuração etc. Se você planeja alguma funcionalidade para o momento da instalação ou remoção do seu módulo, você deve definr aqui;
- modteste.module: Esse é o módulo em sí. É aqui que toda a magia acontece;
Abaixo temos um exemplo do arquivo modteste.info (clique na imagem para ampliar)

Explicando:
name = modteste: Esse é o nome do módulo
description = Permite criar um tipo de teste: Essa é a descrição do módulo. Ela será mostrada na área de instalação de módulos do Drupal
package = Testes: Esse é o pacote do módulo. Na área de instalação de módulos do Drupal, esse será o nome do separador (fieldset) que conterá o seu módulo
version = VERSION: Aqui é a versão do seu módulo. Se você deixar VERSION ele pegará a versão atual do Drupal. É recomendável que você defina números de versões coerentes com o Drupal que você está utilizando. Assim, se a série do Drupal para a qual o seu módulo foi feito é a 5, utilize algo como 5.x-0.1, onde: 5.x é a série para a qual o seu módulo foi feito e 0.1 é efetivamente a versão do seu módulo. Uma coisa importante sobre as versões é notar que mesmo que você faça um módulo quando o Drupal estava na versão 5.3, utilize 5.x pois você está fazendo o módulo para essa série, e não para a versão específica, uma vez que o que vem depois do ponto é só o número de bugfixes da série.
A seguir o arquivo modteste.install (clique na imagem para ampliar):

Explicando:
Esse arquivo conta, essencialmente com duas funções:
modteste_install(): função que realiza o procedimento de instalação. É executada quando se marca o checkbox e envia o formulário na área de instalação de módulos do Drupal.
modteste_uninstall(): função que realiza o procedimento de desinstalação. É executada quando o módulo é desabilitado na área de instalação de módulos do Drupal, e em seguida marcado o checkbox e enviado o formulário na aba Desinstalar, também na área de instalação de módulos do Drupal.
Note que, no exemplo, só foram feitas a criação e exclusão de tabelas. No entanto pode-se realizar qualquer ação nesse momento. Isso vai depender do que o seu módulo precisa para cada momento. Esse arquivo só é necessário, efetivamente, se você vai realizar alguma configuração no momento da instalação/desinstalação.
Por fim temos o arquivo modteste.module. Vamos ver os pontos chave desse arquivo. Pretendo escrever um outro tutorial explicando, mais detalhadamente, como criar um módulo.
Primeiro a função modteste_uninstall() que montra o formulário em si:
* Implementação do hook_form().
*/
function modteste_form(&$node) {
$type = node_get_types('type', $node);
$form['title'] = array(
'#type' => 'textfield',
'#title' => check_plain($type->title_label) ,
'#required' => TRUE,
'#default_value' => $node->title,
'#weight' => -5
);
$form['meucampo'] = array(
'#type'=>'radios',
'#title'=> t('Um campo a mais'),
'#default_value' => $node->meucampo,
'#options'=> _modteste_options() );
$form['body_filter']['body'] = array(
'#type' => 'textarea',
'#title' => check_plain($type->body_label) ,
'#default_value' => $node->body,
'#rows' => 20,
'#required' => TRUE);
$form['body_filter']['filter'] = filter_form($node->format);
return $form;
}
Essa função implementa 3 campos. Dois desses campos são padrão para qualquer tipo de conteúdo no Drupal(node): o título e o corpo(title e body).
Todo tipo no Drupal possui um título e um copro. Esses dados são, por padrão, gravados na tabela node(somente o title) e node_revisions(title e body).
O terceiro campo do formulário é o nosso campo personalizado. Esse campo será, na verdade, um conjunto de radio buttons onde podemos escolher um valor.
Um princípio importante na hora de trabalhar com o Drupal é não mexer nos módulos e tabelas que ele cria, pois essas mudanças podem se perder quando você atualizar o Drupal na próxima vez. Só mexa no que o Drupal já traz se você estiver corrigindo um bug(correção que você deve enviar para os desenvolvedores do Drupal, para poder melhorá-lo para a sua próxima versão), caso contrário evite.
Tendo isso em mente, criamos uma tabela com o mesmo nome do nosso módulo (modteste). Essa tabela terá dois campos: um será o nosso campo efetivamente, e o segundo o nid(node id) que é o "elo"(foreign key) entre o nosso campo e o node dele.
Se você prestou atenção no arquivo modteste.install verá que ele cria uma segunda tabela, e já a popula. Essa é uma tabela auxiliar para o nosso módulo e será nela que iremos guardar os valores possíveis para o nosso campo.
Como já foi dito esse terceiro campo(meucampo) é um conjunto de radio buttons. Para tanto, a sua definição pede uma opção chamada "#options" que nada mais é que um array contendo o par chave/valor para cada um dos radio buttons.
No nosso caso, esses valores vêm de uma tabela auxiliar chamada modteste_options então fiz uma pequena função para carregar esses valores:
$result = db_query("SELECT * FROM {modteste_options}");
while ($option = db_fetch_array($result)) {
$options[$option['mid']] = $option['name'];
}
return $options;
}
Note que essa função começa com o caractere _ .Essa é uma convenção do Drupal que significa que é uma função interna para esse módulo.
Por fim temos as seguintes funções:
if (user_access('criar um teste')) {
db_query("INSERT INTO {modteste} (nid, meucampo) VALUES (%d, %d)", $node->nid, $node->meucampo);
}
}
function modteste_update($node) {
if (user_access('alterar seu próprio teste')) {
db_query("UPDATE {modteste} SET nid=%d, meucampo=%d WHERE nid=%d", $node->nid, $node->meucampo, $node->nid);
}
}
function modteste_delete($node) {
if (user_access('alterar seu próprio teste')) {
db_query("DELETE FROM {modteste} WHERE nid=%d", $node->nid);
}
}
Essas três funções fazem, respectivamente, a inserção, alteração e remoção do nosso campo na tabela auxiliar.
Note que não precisamos escrever nenhuma instrução SQL para a tabela node ou node_revisions. Isso é feito internamente pelo Drupal.
Esses são os pontos chave para o nosso módulo. Como dito antes, você pode baixar o módulo no final desse artigo e ver como é feito o restante do módulo.
Parte 2 - Integração com o módulo Views
Agora vamos ver como integrar nosso módulo com o módulo Views.
Nesse artigo não vamos cobrir o módulo Views, apenas a itegração do nosso módulo com ele.
O módulo Views permite criar visualizações personalizadas para determinadas situações. Existem diversas opções para criar Views, recomendo você dar uma olhada com mais calma nesse módulo. Em breve devo lançar um screencast só com o módulo views.
O módulo Views, assim como o Drupal inteiro, trabalha com hooks que nada mais são do que funções que podem ser extendidas.
Assim, o módulo Views implementa algumas funções que podemos extender para habilitar o nosso campo personalizado para construção de views. Vamos ver 2 dessas funções:
hook_views_tables(): Nessa função são definidas tabelas usadas para o relacionamento entr o módulo Views e o seu módulo
hook_views_arguments(): Nessa função são definidos os campos que podem ser usados como argumentos(via URL) para uma view.
Abaixo temos o código dessas duas funções:
$tables['modteste'] = array(
'name' => 'modteste',
'provider' => 'internal',
'join' => array(
'left' => array(
'table' => 'node',
'field' => 'nid'
),
'right' => array(
'field' => 'nid'
)
),
"filters" => array(
'meucampo' => array(
'name' => t('Teste: Meu campo'),
'list' => 'views_handler_filter_modteste_meu_campo',
'operator' => 'views_handler_operator_andor',
'help' => t('Permite você filtrar pelo Meu Campo'),
),
),
'sorts' => array(
'meucampo' => array(
'name' => t('Teste: Meu Campo'),
'handler' => 'views_handler_sort_modteste_sort',
'help' => t('Ordenar pelo meu campo'),
),
),
);
return $tables;
}
function modteste_views_arguments() {
$arguments = array(
'modteste_meucampo' => array(
'name' => t("Teste: Meu campo"),
'handler' => "views_handler_arg_modteste_meucampo",
),
);
return $arguments;
}
Na função modteste_views_tables() nós definimos qual o nome da tabela que vamos utilizar para o relacionamento e como esse relacionamento se dará(veja o array join nessa função). Também definimos os filtros(filters) e as ordenações(sorts). Para esses dois ultimos, o módulo faz uso de funções callback, que nada mais são funções que são chamadas para executar e retornar um valor(ou conjunto de valores) sem precisarmos escrevê-la no local onde a chamamos.
É nessas funções callback que está o código a ser executado quando filtrarmos ou ordenarmos um campo.
A função modteste_views_arguments() tem um funcionamento semelhante. Ela especifica o nome de um campo(no caso acima modteste_meucampo) e uma função callback que será usada para executar a filtragem. Aparentemente ela é semelhante ao filters da outra função, mas a diferença essêncial é que ela implementa o nosso campo como um argumento para filtragem pela URL. Assim, eu posso chamar minha view, passando por argumento na URL o valor(no caso) do Meu Campo e a view fará a filtragem por nodes que tenham Meu Campo com esse valor.
Por fim, para manter as coisas organizadas, criamos um arquivo chamado views_modteste.inc e colocamos todas as funções de view nesse arquivo(as duas funções que estamos extendendo e suas callback). No arquivo modteste.module criamos então um mecanismo para que o Drupal encontre esse arquivo, criando uma função hook_init() como a abaixo:
if (module_exists('views')) {
include_once('./'. drupal_get_path('module', 'modteste') .'/views_modteste.inc');
}
}
Essa função irá carregar as nossas funções de views, somente se exisiter o módulo views (e esse estiver habilitado).
Assim concluímos esse tutorial.
Espero que ele sirva para você.
Esteja à vontade para comentar.
Abraço
- Versão para impressão
- Por favor, se logue ou se registre para poder enviar comentários
- 554 leituras




Legal Rafael!Tá muito bom
Legal Rafael!
Tá muito bom o tutorial, parabéns. Isso é uma coisa que eu tava precisando muito.
Baixei o código aqui para dar uma olhada também, agora estou tentando implementar essas api's do módulo view aqui.
Surgiu uma dúvida,
no hook_views_arguments() eu posso ter dois parâmetros?
Faço algo desse tipo?
$arguments['arg1'] = array(
'modteste_arg1' => array(
'name' => t('ModTeste: Arg 1'),
'handler' => 'views_handler_arg_modteste'
)
);
$arguments['arg2'] = array(
'modteste_arg2' => array(
'name' => t('ModTeste: Arg 2'),
'handler' => 'views_handler_arg_modteste'
)
);
return $arguments;
}
Ambos os argumentes apontam para o mesmo handler, no caso views_handler_arg_modteste?
Valeu...
Abração.
No handler
No handler views_handler_arg_modteste para criar a função para pegar ambos os valores eu tenho que fazer algo desse tipo? Segue abaixo:
...
}
Fica dessa forma?
Eu preciso trabalhar com dois parâmetros.
Obrigado.
Abração.
Rafael,Tem como você
Rafael,
Tem como você explicar um pouco mais sobre as funções abaixo? O que está acontecendo internamente dentro das funções.
switch ($op)
{
case 'summary' :
$query->ensure_table("modteste_meucampo_node");
$query->add_field("nid");
$query->add_field("meucampo", "modteste");
$query->add_field("title", "modteste_meucampo_node");
$query->add_field("nid", "modteste_meucampo_node", "pnid");
$query->add_where("modteste_meucampo_node.nid IS NOT NULL");
$fieldinfo['field'] = "modteste_meucampo_node.title";
return $fieldinfo;
break;
case 'sort':
$query->add_orderby('modteste', 'weight', $argtype);
$query->add_orderby('modteste_meucampo_node', 'title', $argtype);
break;
case 'filter' :
$query->ensure_table("modteste");
$query->add_where("modteste.meucampo = '%s'", $arg);
$query->add_where("modteste.nid = node.nid");
break;
case 'link' :
return l($query->title, "$arg/$query->pnid");
case 'title' :
if ($query)
{
$term = db_fetch_object(db_query("SELECT title FROM {node} WHERE nid = '%d'", $query));
return check_plain($term->title);
}
}
}
function views_handler_filter_modteste_meu_campo() {
$opts = array();
$result = db_query("SELECT mid,name FROM {modteste_options} ORDER BY name");
while ($obj = db_fetch_object($result)) {
$opts[$obj->mid] = "$obj->name";
}
return $opts;
}
Muito obrigado.
Abração.
Patrick, O lance dessa
Patrick,
O lance dessa função é o seguinte:
Quando ela é chamada, automaticamente são passados os campos que ela espera, e um desses campos, o $op, determina qual operação está sendo executada. Assim, quando você cria um argumento, e passa ele por parâmetro, por exemplo, a operação é 'filter'. Quando você usa o argumento em uma ordenação, é 'sort' e assim vai. Essa função vai processar cada casao, dependendo da operação.
Você perguntou em outro post acima se dava para passar mais um argumento. Por padrão, para esse handler, não dá(até onde eu consegui encontrar informação). Mas dá para vc fazer algo que o taxonomy faz: passar um parâmetro composto, tipo algo como: http://seusisite/suaview/1+2
Com isso você está passando dois argumentos em um. Aí você quebra esses arqumentos(com split, por exemplo) e usa dentro do bloco "case 'filter' :" como você quiser.
Essa é uma opção que eu consideraria, pode ter outro jeito, mas eu ainda não descobri.
Abraço,
--
Rafael Ferreira Silva
http://www.rafaelsilva.net
Obrigado por responder. Fiz
Obrigado por responder.
Fiz uma porção de perguntas, hehehe.. foi mal tomar o seu tempo.
Consegui fazer funcionar aqui, funcionou redondinho. Esse seu tutorial me salvou muito massa. Legal mesmo...
Eu acabei fazendo esse esquema do mais sem querer, tentei fazer de varias formas e não deu, ae fiz com um split e foi.. huahauh eu achei que tava fazendo errado, mas é desse jeito mesmo.. heheh
Ei Rafael, sobre o $op == 'summary'
O que essa opção, só isso q não entendi muito bem, li o manual inteiro da api da views lá no drupal.org, mas não entendi qual é a função dessa parte. Você tirar mais essa dúvida?
Obrigado.
Grande abraço.
Fala patrick, Que bom que
Fala patrick,
Que bom que ajudou :-)
Bom, o summary é mais fácil um exemplo:
Você já deve ter visto em blogs, por exemplo, um bloco lateral que traz a quantidade de posts sob uma categoria certo?
Então; quando você cria, por exemplo, um bloco e usa como argumento um campo, uma das opções desse argumento é 'summary, unsorted'. Nessa opção você pode fazer um agrupamento de um campo (no caso do tutorial não foi usado, mas seria com $query->add_groupby) e usá-lo para essa contagem.
Espero ter esclarecido.
Abraço,
--
Rafael Ferreira Silva
http://www.rafaelsilva.net
Rafael,Então acho que é
Rafael,
Então acho que é bem o $op == 'summary' que vou precisar usar agora. Vou explicar o que estou querendo fazer.
no hook_menu do meu módulo eu construi isso:
'path' => 'product',
'title' => t('Product'),
'callback' => 'product_get_content',
'callback arguments' => array(),
'access' => TRUE
);
Quando eu acesso a url: http://localhost/drupal/product/3835730+3835731 estou montando o breadcumb e definindo o titulo com o nome do produto, através do drupal_set_title
O que eu estou tentando fazer é construir uma view do tipo bloco, nessa view quero mostrar a descrição do produto, pegando o código 3835730+3835731, passado via url, e passar para o bloco mostrar a descrição do produto.
Quando eu acesso a view na construção: http://localhost/drupal/admin/build/views/product_description_block/3835... ele consegue puxar a descrição certinho e funciona como eu quero, mas quando eu coloco o bloco dentro da página http://localhost/drupal/product/3835730+3835731 ele não consegue puxar a descrição, sempre retorna o empty text.
Então é no summary que eu tenho que criar as coisas para o bloco funcionar certinho?
Abração.
Rafael,Conseguiii fazer
Rafael,
Conseguiii fazer funcionar!!!
Deu certo pegar o parâmetro da URL e passar para o bloco, não sei se isso é o melhor jeito, mas funcinou. Tá valendo ;-)
Eu fiz o seguinte:
Dentro do fieldset arguments eu fui no Argument Handling Code e coloquei o seguinte código:
return $args;
Dessa forma eu crio o vetor de argumentos para o bloco, ou seja eu pego a url http://localhost/drupal/product/3835730+3835731
e através do arg(1) eu pego somente 3835730+3835731 e passo para o bloco.
Funcionou legallllll
Valeu..
Abração.
Muito bom o tutorial, estava
Muito bom o tutorial, estava procurando algo assim.
Mas não consigo colocar esse módulo de teste no meu drupal.
"This version is incompatible with the 6.1 version of Drupal core."
Não sei porque não está aceitando a versão do módulo.
Uso o drupal 6.1
Esse tutorial foi feito para
Esse tutorial foi feito para a versão 5 do Drupal.
Não sei se já tem o Views para o Drupal 6, e também ainda não testei pra ver se esse tutorial vale para a versão 6 do Drupal.
Abraço,
--
Rafael Ferreira Silva
http://www.rafaelsilva.net
Estou tento problemas com o
Estou tento problemas com o 6 por falta de compatibilidade com muitos módulos.
Agora vou trabalhar com o 5 mesmo.
Depois, com mais tempo, volto da mexer no 6.