2007 09/01
Criando um formulário de contato simples usando o ActiveRecord
Se você já utilizou o Rails alguma vez, certamente também usou (e provavelmente adorou) a implementação Model-View-Controller (MVC) que existe a sua disposição.
Nela podemos representar facilmente uma tabela em um banco de dados como um objeto qualquer. Esta transformação permite a você manipular, editar, consultar, listar, etc... com facilidade as informações contidas na sua tabela.
Eu estive pensando em criar um formulário de contato simples. Porém, sem usar uma tabela no banco de dados (que não faz sentido algum nesse caso) como eu poderia me aproveitar das vantagens do ActiveRecord?
Só pra dar uma visão geral sobre o ActiveRecord: é ele quem vai dar a você todos os facilitadores na hora de trabalhar com um modelo que representa uma tabela em seu banco de dados.
Como você já deve estar imaginando, estas facilidades só estarão disponíveis, por padrão, se os seus dados estiverem dentro de uma tabela para que o Rails possa ler eles e transformá-los num objeto para você.
Pensando nisso, fiz uma pesquisa e encontrei uma solução que permite criar um modelo que define os campos diretamente, sem a necessidade de uma tabela real. Eu achei um PDF com o artigo, a URL indicada no final do PDF não existe mais. Apenas encontrei referências em um fórum.
Criando um modelo que apenas simula uma tabela
O primeiro passo é criarmos um modelo que nos vai permitir criar outros modelos que não estejam ligados a tabela alguma. O modelo é simples e não precisa de alteração alguma:
class TablelessForms < ActiveRecord::Base
def self.columns()
@columns ||= [];
end
def self.column(name, sql_type = nil, default = nil, null = true)
columns << ActiveRecord::ConnectionAdapters::Column.new(
name.to_s, default, sql_type.to_s, null
)
end
# override the save method to prevent exceptions.
def save(validate = true)
validate ? valid? : true
end
end
Basta salvá-lo dentro da sua pasta de modelos (app/models) e pronto. Agora você pode criar um outro modelo para representar sua pseudo-tabela:
class ContactForm < TablelessForms
column :name, :string
column :email, :string
column :site, :string
column :subject, :string
column :message_text, :text
validates_presence_of :name,
:message => 'Seu nome (campo obrigatório)'
validates_format_of :email,
:with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i,
:message => 'Seu e-mail (formato inválido)'
validates_presence_of :subject,
:message => 'O assunto (campo obrigatório)'
validates_presence_of :message_text,
:message => 'Sua mensagem (campo obrigatório)'
end
Repare que eu criei ali meus campos usando o column. Criei de acordo com os campos que eu queria em meu formulário.
Aproveitei e adicionei também todos os métodos de validação necessários ao meu formulário. Pronto, temos o nosso modelo pronto para uso.
Usando o modelo em uma action
Agora a parte mais fácil. Se você já usou algum modelo para manipular dados com o Rails vai notar que o funcionamento é basicamente o mesmo, a diferença é que os dados não irão para o banco. =)
Vamos criar uma action para mostrar o nosso formulário ou enviar os dados (caso o formulário seja enviado):
def contato
# se o método é get, o formulário ainda não foi enviado, mostramos o form vazio
if request.get?
@contact = ContactForm.new
else
# aqui recebemos o formulário por post, vamos criar o nosso objeto
# usando os parâmetros vindos do formulário
@contact = ContactForm.new( params['contact'] )
# o método save (que foi sobreescrito) vai ativar a validação
if @contact.save
# se está tudo OK com os dados, vamos enviar o e-mail e...
GenericMailer.deliver_contact_me @contact
# ... redirecionar mostrando uma mensagem
display_message 'Sua mensagem foi enviada com sucesso!', 'app-ok', '/mostrar/contato'
end
end
end
O template de contato ficou assim (apenas a parte relevante, claro):
<%= form_tag( { :method => 'post' }, :id => 'contact-me-form' )%>
<%= error_messages_for 'contact' %>
<!--[form:contact]-->
<p class="hint-form">
* campos obrigatórios
</p>
<div class="fields">
<label class="labels" for="contact_name">
Nome <small>*</small>:
</label>
<%= text_field 'contact', 'name', :class => 'data-field' %>
</div>
<div class="fields">
<label class="labels" for="contact_site">
Site:
</label>
<%= text_field 'contact', 'site', :class => 'data-field' %>
</div>
<div class="fields">
<label class="labels" for="contact_email">
E-mail <small>*</small>:
</label>
<%= text_field 'contact', 'email', :class => 'data-field' %>
</div>
<div class="fields">
<label class="labels" for="contact_subject">
Assunto <small>*</small>:
</label>
<%= text_field 'contact', 'subject', :class => 'data-field' %>
</div>
<div class="fields">
<label class="labels" for="contact_message_text">
Mensagem <small>*</small>:
</label>
<%= text_area 'contact', 'message_text', :class => 'post-field' %>
</div>
<!--[eoform:comment]-->
<p class="actions">
<%= submit_tag 'Enviar' %>
</p>
<%= end_form_tag %>
E aquele método save ali?
Se você reparar no código da classe TablelessForms, ali o método save é sobreescrito.
Ele apenas vai fazer a validação sem tentar fazer alteração alguma no banco (o que geraria erros pois o que temos é uma pseudo-tabela, não uma tabela real).
Mas e o envio do e-mail?
Enviar e-mails é bastante descomplicado mas, por questão de organização, vou deixar isso como assunto para um próximo post. =)
Resumindo
Usar a estrutura do ActiveRecord para uma pseudo-tabela foi bem tranquilo de se conseguir. E com os facilitadores que o ActiveRecord permite, gerar o formulário de contato simples foi realmente simples! (com o perdão da redundância! =)