はじめに
本投稿はPokeAPIを使ってポケモンを検索し、
それをゲットだぜ!することで、データベースに保存する、というアプリを作成していきます。
PokeAPIについて
Poke APIにはポケモン関連のデータが大量にあります。
トップページでAPIを簡単に試せるようになっているので、ぜひ触ってみてください。
なお、今回作るアプリケーションでは取得できるデータのほんの一部しか使いません。
実装
1. Gemfileにgemを追加する
GemfileにFaraday
というGemを入れます。
-
Faradayのざっくり説明
- HTTPクライアントライブラリ。
- Rubyの標準ライブラリのnet/httpでもHTTPリクエストはできますが、Faradayはより簡単にHTTPリクエストを行うことができる。
gem 'faraday' # 追加する
> bundle install
bundle install
した結果👇
> bundle install
.
.
.
Installing faraday 2.7.10
Bundle complete! 19 Gemfile dependencies, 82 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
次に、pokeコントローラを作ります。
コントローラを作成する場合、複数形にすることを忘れないようにしてください。
$ rails g controller pokes index new
これにより、下記のファイルが作成されます。
class PokesController < ApplicationController
def index
end
def new
end
end
次にルーティングを設定します。
今あるget 'pokes/index'
とget 'pokes/new'
を削除して
config/routes.rb
にコードを追加しましょう。
Rails.application.routes.draw do
# ここから追加する
root 'pokes#index'
resources :pokes, :only => [:destroy]
get 'pokes', to: 'pokes#index', as:'pokes_index'
get 'pokes/new', to: 'pokes#new'
post 'pokes', to: 'pokes#create'
# ここまで
# 以下略
end
2. ポケモンを検索する
いろいろなポケモンをゲットするために、検索できるようにします。
app/views/shared
フォルダを新たに作成し、その中に_search.html.erb
を作成し、
下記のコードを書いた上で保存してください。
<div style="margin-bottom: 50px;">
<%= form_with url: path, method: :get, local: true do |form| %>
<%= form.text_field :search %>
<%= form.submit '検索', name: nil %>
<% end %>
</div>
ファイル名の前に_
(アンダースコア)があるファイルは部分テンプレートといい、
複数のビューファイルの共通部分として作成されるファイルのことです。
【Rails】 部分テンプレートの使い方を徹底解説!
form_with
を使って、探したいポケモンをURLパラメータで送ります。
app/views/pokes/new.html.erb
を以下のコードに置き換えてください。
<h1 class="title">ポケモンゲットだぜ!</h1>
<!-- 追加 -->
<%= render "shared/search", { path: pokes_new_path } %>
<div>
<% if @response.present? %>
<table>
<tr>
<td>No.</td>
<td><%= @response["id"] %></td>
</tr>
<tr>
<td>Name:</td>
<td><%= @response["name"] %></td>
</tr>
</table>
<%= image_tag(@response["sprites"]["front_default"], size: "200") %>
<% else %>
<p>検索してね!</p>
<% end %>
</div>
送られてきたパラメータをリクエスト先のURLに入れます。
また、リクエストが失敗した時の処理も追加します。
class PokesController < ApplicationController
def index
end
def new
# ここから追加
return if params[:search].blank?
raw_response = Faraday.get "https://pokeapi.co/api/v2/pokemon/#{params[:search]}"
if raw_response.status == 200
@response = JSON.parse(raw_response.body)
# JSON.parseをすることで、json形式の文字列をRubyオブジェクトに変換することができる。
else
redirect_to pokes_new_path, notice: "#{raw_response.status}エラー!"
end
# ここまで追加
end
end
フラッシュメッセージは別のページなどでも共通して利用される場合があるので、layouts/application.html.erb
に記述しておき、 逐次読み込むようにします。
<!DOCTYPE html>
<html>
<head>
<title>PokemonZukan</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
</head>
<body>
# ここから追加
<% if flash[:notice] %>
<div><%= flash[:notice] %></div>
<% end %>
# ここまで追加
<%= yield %>
</body>
</html>
Railsサーバーを起動しなおして、localhost:3000/pokes/newにアクセスしてください。
これでポケモンの検索ができるようになりました。
3.ポケモンを保存する
ポケモンのデータを保存するためにモデルとテーブルを作成します。
$ rails g model poke order:integer name:string image_url:string
orderは図鑑番号のことです。
生成されたmigrationファイルを開いて、各カラムにnull: false
(NOT NULL制約という。null: false
の前にコンマをつけるの忘れずに)を追加して、rails db:migrate
をします。
NOT NULL制約とは
null: false
を入れると、指定したカラムに空の状態で保存させるのを防ぎます。
migrationファイル内のnull: falseとは
class CreatePokes < ActiveRecord::Migration[5.2]
def change
create_table :pokes do |t|
t.integer :order, null: false
t.string :name, null: false
t.string :image_url, null: false
t.timestamps
end
end
end
$ rails db:migrate
次に空の値が保存されないように、Pokeモデルにバリデーションをかけます。
class Poke < ApplicationRecord
# 以下の3行を追加
validates :order, presence: true
validates :name, presence: true
validates :image_url, presence: true
# ここまで追加する
end
コントローラーのnewアクションに変更を加えていきます。
class PokesController < ApplicationController
def index
end
def new
# ここから変更する
return if params[:search].blank?
raw_response = Faraday.get "https://pokeapi.co/api/v2/pokemon/#{params[:search]}"
if raw_response.status == 200
response = JSON.parse(raw_response.body)
# Pokeインスタンスを生成するようにします。
@poke = Poke.new(order: response["id"], name: response["name"], image_url: response["sprites"]["front_default"])
else
redirect_to pokes_new_path, notice: "#{raw_response.status}エラー!"
end
# ここまで
end
end
poke/new.html.erb
で、form_with
を使って、検索したポケモンを保存(ポケモンゲットだぜ!)できるようにします。
<h1>Pokemon</h1>
<%= render "shared/search", { path: pokes_new_path } %>
<div>
<% if @poke.present? %>
<table>
<tr>
<td>No.</td>
<td><%= @poke.order %></td>
</tr>
<tr>
<td>Name:</td>
<td><%= @poke.name %></td>
</tr>
</table>
<%= image_tag(@poke.image_url, size: "200") %>
<% if Poke.exists?(name: params[:search]) %>
<%= form_with model: @poke, local: true do |f| %>
<%= f.hidden_field :order %>
<%= f.hidden_field :name %>
<%= f.hidden_field :image_url %>
<%= f.submit 'ゲット', name: nil %>
<% end %>
<% else %>
<p>すでに手持ちにいます</p>
<% end %>
<% else %>
<p>検索してね!</p>
<% end %>
</div>
次にapp/controllers/pokes_controller.rb
にcreateアクションを追加します。送られてきたパラメータを処理して、レコードに保存できるようにします。
def create
@poke = Poke.new(poke_params)
if @poke.save
redirect_to pokes_path, notice: "「#{@poke.name}」をゲットしました。"
else
render :new
end
end
private
def poke_params
params.require(:poke).permit(:order, :name, :image_url)
end
pokes/index.html.erb
にレコードに保存されているポケモンを表示できるようにします。
def index
@pokes = Poke.all
end
app/views/pokes/index.html.erb
を編集します。
<h1>Pokemon</h1>
<%= render "shared/search", { path: pokes_new_path } %>
<div>
<% if @poke.present? %>
<% @pokes.each do |poke| %>
<div style="display: inline-block;">
No. <%= poke.order %>,
Name: <%= poke.name %>
<div>
<%= image_tag(poke.image_url, size: "200") %>
</div>
</div>
<% end %>
<% end %>
</div>
また、バリデーションエラーが起きた場合に、エラーメッセージを表示できるようにします。
<h1>Pokemon</h1>
<%= render "shared/search", { path: pokes_new_path } %>
<div>
<% if @poke.present? %>
<!-- 追加 -->
<%= render "shared/error_messages", { object: @poke } %>
<table>
<tr>
<td>No.</td>
<td><%= @poke.order %></td>
</tr>
<tr>
<td>Name:</td>
<td><%= @poke.name %></td>
</tr>
</table>
<%= image_tag(@poke.image_url, size: "200") %>
<% if Poke.exists?(name: params[:search]) %>
<%= form_with model: @poke, local: true do |f| %>
<%= f.hidden_field :order %>
<%= f.hidden_field :name %>
<%= f.hidden_field :image_url %>
<%= f.submit 'ゲット', name: nil %>
<% end %>
<% else %>
<p>すでに手持ちにいます</p>
<% end %>
<% else %>
<p>検索してね!</p>
<% end %>
</div>
> touch app/views/shared/_error_messages.html.erb
<% if object.errors.present? %>
<ul>
<% object.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
<% end %>
課題
逃がす機能
destroyを使用してゲットしたポケモンを逃しましょう。
削除機能を参考にしてみましょう。
リンクは
<%= button_to '逃がす', pokes_path(poke.id),method: :delete %><br>
を使いましょう。
登録したポケモン一覧に逃がすポタンがあり、それを押すと、一覧ページが消えれ(逃せ)ばOKです。
名前を変える機能
上記の記事を参考にしてください。
edit.html.erbは先程作ったnew.html.erbを少しアレンジします。
<h1>ポケモン編集</h1>
<!-- 省略 -->
<%= form_with model: @poke, local: true do |f| %>
<%= f.text_field :name %>
<%= f.submit '名前を変更する', name: nil %>
<% end %>
<% else %>
<% end %>
</div>