Formulário de busca simples em Ruby on Rails

Tutoriais - 06/Mai/2020 - por André Kanamura

Uma funcionalidade básica super comum em aplicações web é a pesquisa. Essa é uma boa funcionalidade para discutirmos aqui, pois ela é razoavelmente simples e podemos falar sobre criação de formulários, queries e rotas, três assuntos fundamentais para desenvolvimento de aplicações web. Então aqui usaremos como exemplo uma aplicação hipotética em que são catalogados livros e vamos implementar o código da função de pesquisa. Por meio dela, um usuário deve ser capaz de realizar uma busca usando termos parciais tanto para título quanto para autor.

Vamos começar como deve começar qualquer código: pelos testes. Na nossa aplicação hipotética, o usuário poderá fazer uma busca na Home. Note que nesta aplicação já configuramos o root_path para a index do HomeController.

require 'rails_helper'

feature 'Visit home page' do
  context 'and search for book' do
    scenario 'successfully' do
    Book.create(title: 'Animal Farm', author: 'George Orwell')
    Book.create(title: 'Battle Royale', author: 'Koushun Takami')
Book.create(title: 'A to Z Mysteries', author: 'Ron Roy')

    visit root_path
    fill_in 'Busca:', with: 'Roy'
    click_on 'Pesquisar'

    expect(current_path).to eq search_path
    expect(page).to have_content('Battle Royale')
    expect(page).to have_content('Koushun Takami')
expect(page).to have_content('A to Z Mysteries')
    expect(page).to have_content('Ron Roy')
    expect(page).not_to have_content('George Orwell')
    expect(page).not_to have_content('Animal Farm')
     end
  end
end

Nos testes acima são criadas instâncias da classe Book e, após preencher o formulário e clicar no botão "Pesquisar", avaliamos se na página de resultados são renderizados os resultados corretamente. Garantimos nos testes que tanto atributo author quanto title sejam contemplados na funcionalidade. Com essa estrutura de código, sabemos que o primeiro erro que será levantado quando executarmos o comando rspec nos avisará que não existe a classe Book. Para isso, podemos rodar o comando:

$ rails generate model book author:string title:string

Agora precisamos criar o formulário de pesquisa, que nesta aplicação se encontra na Home, ou seja, adicionamos no arquivo app/views/home/index.html.erb o código a seguir:

<h1> Bem vindo </h1>
<%= form_with url: search_path, method: :get do %>
  <%= label_tag :q, 'Busca:' %>
  <%= text_field_tag :q %>
  <%= submit_tag 'Pesquisar' %>
<% end %>

Em Ruby on Rails existe mais de uma maneira de montar formulários. Aqui optamos por usar o método form_with para montar os campos HTML necessários. Perceba que url recebe a rota search_path para qual os parâmetro q (abreviação do inglês "query") será encaminhado. O label_tag e o text_field_tag compõem o campo que receberá o texto a ser pesquisado e armazenado na variável que chamamos de q. O submit_tag compõe o botão que desencadeará a ação de pesquisa enviando o texto pesquisado e armazenado no parâmetro q para a rota search_path. Dessa maneira, sabemos que será necessário modificar nosso arquivo de rotas routes.rb:

Rails.application.routes.draw do
  root to: "home#index"
  get 'search', to:"home#search"
end

A rota search_path é um get que direciona os parâmetros do formulário para a action search no HomeController:

class HomeController < ApplicationController
  def index;end

  def search
    @books = Book.where('title like ? OR author like ?',
"%#{params[:q]}%", "%#{params[:q]}%")
  end
end

O código na action search monta um array com todos os objetos da classe Book que correspondem ao valor da pesquisa. Nessa consulta ao banco utilizamos o marcador % para indicar que procuramos valores que contém o termo buscado. Para conhecer mais sobre marcadores, recomendamos esta documentação. Assim, query do código acima realiza uma busca parcial, ou seja, serão retornados como verdadeiros os resultados com correspondência parcial do termo pesquisado. Caso você queria que os resultados retornados sejam exatos, basta remover os marcadores deixando seu código da query assim:

def search
    @books = Book.where('title = ? OR author = ?',
params[:q], params[:q])
  end

Note que aqui ainda estamos buscando em ambos os atributos da classe Book, no entanto, agora só serão retornados resultados com correspondência exata do termo pesquisado.

Nos resta agora apresentar os livros encontrados na página de resultados em app/views/home/search.html.erb:

<h1>Resultados da busca</h1>
<ul>
  <% @books.each do |book| %>
    <li><%= book.title %>, <%= book.author %></li>
  <% end %>
</ul>

O código acima itera pelo array @books que contém os resultados da query e gera para cada item uma tag <li> com o título e o autor do livro no HTML final da página.

Considerações finais

Neste tutorial passamos pelo processo inicial de construção de um formulário de pesquisa simples em Ruby on Rails utilizando Test Driven Development. Ainda são necessários mais testes para garantir, por exemplo, que alguma mensagem seja renderizada em tela caso não sejam encontrados resultados, entre outras características que possam ser importantes. Além disso, na minha opinião, search_path deveria ser uma rota aninhada parte de resources de books. Aqui a implementamos como parte de home apenas para demonstração, você deve avaliar o que faz mais sentido para sua aplicação.

Referências

Foto de perfil do autor
André Kanamura

Dev na Campus Code