【Ruby on Rails】ポケモンゲットだぜ！！

## はじめに
本投稿はPokeAPIを使ってポケモンを検索し、
それをゲットだぜ！することで、データベースに保存する、というアプリを作成していきます。


# PokeAPIについて
[Poke API](https://pokeapi.co/)にはポケモン関連のデータが大量にあります。
トップページでAPIを簡単に試せるようになっているので、ぜひ触ってみてください。
なお、今回作るアプリケーションでは取得できるデータのほんの一部しか使いません。


## 実装
## 1. Gemfileにgemを追加する
Gemfileに`Faraday`というGemを入れます。

- [Faraday](https://github.com/lostisland/faraday)のざっくり説明
  - HTTPクライアントライブラリ。
  - Rubyの標準ライブラリのnet/httpでもHTTPリクエストはできますが、Faradayはより簡単にHTTPリクエストを行うことができる。


```ruby:Gemfile
gem 'faraday' # 追加する
```

```terminal:コマンドプロンプト
> bundle install
```

<font color="gray">`bundle install`した結果👇</font>
```terminal:コマンドプロンプト
> 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**コントローラを作ります。
コントローラを作成する場合、**複数形**にすることを忘れないようにしてください。
```terminal:terminal
$ rails g controller pokes index new 
```
これにより、下記のファイルが作成されます。

```rb:app/controllers/pokes_controller.rb
class PokesController < ApplicationController
  def index
  end

  def new
  end
end
```

次にルーティングを設定します。
今ある`get 'pokes/index'`と`get 'pokes/new'`を削除して
`config/routes.rb`にコードを追加しましょう。
```rb: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`を作成し、
下記のコードを書いた上で保存してください。



```erb: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>
```

::: note

ファイル名の前に`_`(アンダースコア)があるファイルは<font color="green">**部分テンプレート**</font>といい、
複数のビューファイルの共通部分として作成されるファイルのことです。
[【Rails】 部分テンプレートの使い方を徹底解説！](https://pikawaka.com/rails/partial_template)
:::

`form_with`を使って、探したいポケモンをURLパラメータで送ります。
`app/views/pokes/new.html.erb`を以下のコードに置き換えてください。

```erb: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に入れます。
また、リクエストが失敗した時の処理も追加します。

```ruby:app/controllers/pokes_controller.rb
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`
に記述しておき、　逐次読み込むようにします。

```erb:app/views/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にアクセスしてください。
これでポケモンの検索ができるようになりました。

![poke_search.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344685/0d13f5ba-188f-7014-a8d0-308c2955d607.gif)


### 3.ポケモンを保存する

ポケモンのデータを保存するためにモデルとテーブルを作成します。

```terminal:terminal
$ rails g model poke order:integer name:string image_url:string
```
orderは図鑑番号のことです。

生成されたmigrationファイルを開いて、各カラムに`null: false`(**NOT NULL制約という。`null: false`の前にコンマをつけるの忘れずに**)を追加して、`rails db:migrate`をします。


::: note
**NOT NULL制約とは**
`null: false`を入れると、指定したカラムに空の状態で保存させるのを防ぎます。
[migrationファイル内のnull: falseとは](https://qiita.com/ryuuuuuuuuuu/items/2841cddf8b541c336ebd)
:::


```ruby:db/migrate/作成日時_create_pokes.rb
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
```

```terminal:terminal
$ rails db:migrate
```

　
次に空の値が保存されないように、Pokeモデルにバリデーションをかけます。

```ruby:poke.rb
class Poke < ApplicationRecord
  # 以下の3行を追加
  validates :order, presence: true
  validates :name, presence: true
  validates :image_url, presence: true
  # ここまで追加する
end
```

コントローラーのnewアクションに変更を加えていきます。

```ruby:pokes_controller.rb
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`を使って、検索したポケモンを保存(ポケモンゲットだぜ！)できるようにします。

```erb:poke/new.html.erb
<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**アクションを追加します。送られてきたパラメータを処理して、レコードに保存できるようにします。

```ruby:pokes_controller.rb
  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`にレコードに保存されているポケモンを表示できるようにします。

```ruby:pokes_controller.rb
def index
  @pokes = Poke.all
end
```

`app/views/pokes/index.html.erb`を編集します。


```erb: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>
```
　
また、バリデーションエラーが起きた場合に、エラーメッセージを表示できるようにします。

```erb:pokes/new.html.erb
<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>
```

```sh
> touch app/views/shared/_error_messages.html.erb
```

```erb:shared/_error_messages.html.erb
<% if object.errors.present? %>
  <ul>
    <% object.errors.full_messages.each do |message| %>
      <li><%= message %></li>
    <% end %>
  </ul>
<% end %>
```


![poke.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344685/cb2da9c4-9c3f-b169-3fed-0188f3779bc3.gif)

# 課題

## 逃がす機能
destroyを使用してゲットしたポケモンを逃しましょう。
[削除機能](https://zenn.dev/odentravel/books/e69a157daeecb3/viewer/700a22)を参考にしてみましょう。

リンクは
```erb
<%= button_to '逃がす', pokes_path(poke.id),method: :delete %><br>
```
を使いましょう。
登録したポケモン一覧に逃がすポタンがあり、それを押すと、一覧ページが消えれ（逃せ）ばOKです。


## 名前を変える機能
https://zenn.dev/odentravel/books/e69a157daeecb3/viewer/e203ab

上記の記事を参考にしてください。

edit.html.erbは先程作ったnew.html.erbを少しアレンジします。
```erb:edit.html.erb
<h1>ポケモン編集</h1>
<!-- 省略　-->

    <%= form_with model: @poke, local: true do |f| %>
      <%= f.text_field :name %>
      <%= f.submit '名前を変更する', name: nil %>
    <% end %>
  <% else %>
  <% end %>
</div>
```