楽天ブックス書籍検索APIで検索した商品の一つをDBに保存する

#楽天APIで取得した商品をDBに保存する
商品を検索して、表示された商品の中から一つをデータベースに保存したかったのだが、クリティカルな記事を見つけることができずに軽くハマってしまったため自分で記事にしてみる。

使うAPIは楽天ブックス書籍検索API
APIの使い方は他の方々が丁寧にまとめてくださっているので省略。ここではあくまでもAPIの実装と保存までをテーマとする。

```ruby:items_controller.rb
class ItemsController < ApplicationController
  def new
    @items = []
    
    @title = params[:title]
    if @title.present?
      results = RakutenWebService::Books::Book.search({
        title: @title,
        booksGenreId: '001004',
        hits: 20,
      })
      
      results.each do |result|
        item = Item.new(read(result))
        @items << item
      end
    end
  end
end
  
```
検索フォームを実装している。
@itemを初期化して最初に検索フォームが空になっているようにしておく。
ちなみにここでは書籍タイトルから本を検索している。

```ruby:items_controller.rb
results.each do |result|
  item = Item.new(read(result))
  @items << item
end
```

ここでread(result)メソッドが使われているが、そんなメソッドは存在しないので同コントローラー内でprivateで定義しておく。

```ruby:items_controller.rb

private
  
  def read(result)
    title = result['title']
    url = result['itemUrl']
    isbn = result['isbn']
    image_url = result['mediumImageUrl'].gsub('?_ex=120x120', '')
    
    {
      title: title,
      url: url,
      isbn: isbn,
      image_url: image_url,
    }
    
  end
end
```
read(result)でやっているのは、ざっくり言うとデータの整形。
APIの中から本当に欲しい情報だけを抜き出している。
普通に表示だけさせると複雑な検索結果になるので、必要な情報だけに限定させる。
つまり、title, itemUrl, isbn, imageUrlの四つだけ。
titleは言わずもがな。itemUrlは楽天の商品ページへリンクを貼ることを想定して。isbnはcreateアクションで保存する商品を特定するため、imageUrlは画像を表示させるためにそれぞれ必要。
この四つの情報をそれぞれ変数に代入し、ハッシュにしてそれぞれの商品に渡している。

```ruby:index.rb
<%= form_tag(new_item_path, method: :get, class: 'form-inline') do %>
  <%= text_field_tag :title, @title, class: 'form-control', placeholder:'書籍タイトル', size: 40 %>
  <%= submit_tag '商品を検索', class: 'btn'%>
<% end %>
<% if @items %>
  <% @items.each do |item| %>
    <img src="<%= @item.image_url %>" alt="">
    <p><%= item.title %></p>
    <%= form_tag(items_path) do %>
      <%= hidden_field_tag :isbn, @item.isbn %>
      <%= submit_tag 'save' %>
    <% end %>
  <% end %>
<% end %>

```
コードが真っ青になってることが気になる。何でだろう？
体裁を整えないと表示の仕方が変になるけど、上のコードでも表示自体はされるはず。

```ruby:index.rb
 <%= form_tag(items_path) do %>
    <%= hidden_field_tag :isbn, @item.isbn %>
    <%= submit_tag 'save' %>
 <% end %>
```
items#createに保存する商品の情報を渡す。
商品には先ほどreadメソッドでやったようにtitle, itemUrl, isbn, imageUrlの四つの情報が入っている。
商品(書籍)を特定するにはisbnを使うのでhidden_field_tagを<input name="isbn" value="その商品のisbnコード">となるようにしておき、ボタンを押すと保存するようにする。
これでitems#createでparams[:isbn]で保存したい商品の情報を取り出せる。

```ruby:items_controller.rb
@item = Item.find_or_initialize_by(isbn: params[:isbn])
#その商品を検索し、保存されていなければItem.newを実行する。
```

```ruby:items_controller.rb
unless @item.persisted?
#@itemが保存されていなければ、先に＠@itemを保存する。
```
これを踏まえて、できたitems#createがこれ。

```ruby:items_controller.rb
 def create
    @item = Item.find_or_initialize_by(isbn: params[:isbn])
    
    unless @item.persisted?
      results = RakutenWebService::Books::Book.search(isbn: @item.isbn)
      @item = Item.new(read(results.first))
      @item.save
    end
 end
```

ビューから受け取ったisbnでもう一度商品を検索して保存する商品を特定して、それを保存するという流れ。
もう一度readメソッドが登場するも、items#newとやっていることは同じ。違うのは、一番最初の商品を取得するというfirstがくっついているだけ。isbnで検索すれば合致する商品は一つしかないにも関わらずわざわざfirstをつけてるのは、JSONからRubyの配列へ変換するため。そうしないと商品のデータを扱えない。

###終わり！
どこがおかしなところがあればご指摘お願いします。