【Rails】映画の公式情報を取得・投稿できるようにする。「TMDb API」

# 経緯
やっぱり映画関連のアプリ作るなら公式情報を利用して作りたいよね～
てことで、[「The Movie DataBase(TMDB)」](https://www.themoviedb.org/ "「The Movie DataBase(TMDB)」")のAPIを使っていきます。
初学者ゆえ、かなり脳筋な実装となっております。

# TMDBって？

https://www.themoviedb.org/about?language=ja-JP
>Let's talk about TMDB
The Movie Database (TMDB) is a community built movie and TV database. Every piece of data has been added by our amazing community dating back to 2008. TMDB's strong international focus and breadth of data is largely unmatched and something we're incredibly proud of. Put simply, we live and breathe community and that's precisely what makes us different.

要はコミュニティによって作られた映画やテレビ番組のデータベースという感じですかね。
このTMDBが出しているAPIを使って映画の公式情報を取得していきます。

# この記事でできる事
1.映画の情報を取得＆検索できる。
2.選択した映画の情報を利用しながら感想などを付け加えて投稿できる
3.映画の取得件数の調整やページネーション

# 完成イメージ
### 映画関連のMVCとしてMovieを用意
本記事で取得する映画情報は以下の通り
（取得したい情報→`それに対応するAPI内の映画情報`）
タイトル→`title`
ポスター→`poster_path`
あらすじ→`overview`
公開日→`release_date`

その他に欲しい映画情報は以下を参照するなり用意してください。

https://developers.themoviedb.org/3/movies/get-movie-details

### 選択した映画を投稿するMVCとしてPostを用意
本記事ではAPIで取得した映画情報を投稿する為にそれぞれに対応したカラムを用意して投稿できる仕様なので、以下の様なカラムを用意。
公開日の投稿はしない事にしました。
（API内の映画情報→`対応させるため用意したカラム名:データ型`）
title→`title:string`
poster_path→`image:string`
overview→`overview:text`
これらのカラムに加え映画に自分のコメントを残せるよう、`contents:text`も用意した。
# 手順
１.APIの登録
２.映画用のMVC作成
３.投稿用のMVC作成

## 1-1.APIの登録
**こちらにてアカウント登録とAPIの登録（申請をしましょう）**

https://www.themoviedb.org/

**APIの登録は以下を参考にしてみてください**
※すべて英語ですがスクショ等あるので翻訳機能等を活用しましょう

https://developers.themoviedb.org/3/getting-started/introduction

古い記事ですがこちらも参考までに

https://weblion303.net/1262

**API登録に関してポイント**
１．`Application URL`は適当に入れていても恐らく問題ないが、心配だったら先にリリースしてURLを貼っておく。
２．`Application Summary`は英語で書く。
もしかしたらテキトーに登録してもいけるかもですが、一応ね！

**取得できたAPIキーは忘れずメモしておきましょう!**
サイトからAPIキーを確認したい時は **「右上ユーザーアイコン→プロフィール編集→左側サイドバーのAPI」** をクリックすると確認できます。
## 1-2.APIの登録(環境変数にする)
APIキーが外部に公開されるのを防ぐために
gemの`dotenv-rails`を利用します。

**①gemfileに以下を追加・gemのインストール**
```diff_ruby:Gemfile
    gem 'dotenv-rails'
```
```:ターミナル
bundle install
```

**②app直下の階層に`.env`ファイルを作成**
![qiita-tmdb-env.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1874115/dded7716-af5e-1f9b-69db-fbd0d5b28ed2.png)
**③作成した.envファイルに追記**
```:.env
    TMDB_API=取得した自分のTMDB APIキー
```
`TMDB_API`に自分のAPIキーを代入しています。
スペースを空けずに先ほど取得したAPIキーをコピペしましょう。
**④.gitignoreに追記**
```:.gitignore
    /.env
```
## ２.映画用のMVC作成

**①moviesコントローラーの作成**
```:ターミナル
    rails g controller movies index show
```

**②routes.rbの修正**
※-と書かれてる列は削除　+と書かれている列は追記の意
rootは別途で作成している場合は追記しなくてもよい
```diff_ruby:routes.rb
Rails.application.routes.draw do
- get 'movies/index'
- get 'movies/show'
+ resources :movies, only: [:index ,:show]
+ root 'movies#index'
end
```

**③movies_controller.rbの記述**
```diff_ruby:movies_controller.rb
class MoviesController < ApplicationController
  def index
    if params[:looking_for]
      movie_title = params[:looking_for]
      url = "https://api.themoviedb.org/3/search/movie?api_key=#{ENV['TMDB_API']}&language=ja&query=" + URI.encode_www_form_component(movie_title)
    else
      url = "https://api.themoviedb.org/3/movie/popular?api_key=#{ENV['TMDB_API']}&language=ja"
    end
    @movies = JSON.parse(Net::HTTP.get(URI.parse(url)))
  end

  def show
    movie_id = params[:id]
    url = "https://api.themoviedb.org/3/movie/#{movie_id}?api_key=#{ENV['TMDB_API']}&language=ja"
    @movie = JSON.parse(Net::HTTP.get(URI.parse(url)))
  end
end

```
詳しい説明は書き始めると長くなるのでここでは省きます。
気になる方はこちらを参考にしてみてください。

https://qiita.com/jnchito/items/dc9285884b2349833fad

https://docs.ruby-lang.org/ja/3.0/class/Net=3a=3aHTTPResponse.html

**④index.html.erbの記述**
映画の一覧と検索フォームの記述です
```diff_erb:views/movies/index.html.erb
<div class="search">
    <%= form_tag(root_path, method: :get) do %>
        <%= search_field_tag :looking_for, nil, placeholder: 'Movie Title...' , :size => "40*40" %>
        <%= submit_tag 'search' %>
    <% end %>
</div>
<div class = "movies-container">
    <% @movies.each do |movie| %>
        <div class = "movie-data">
            <p><%= movie['title'] %></p>
            <%= link_to movie_path(movie['id']) do %>
                <% if movie['poster_path'] %>
                    <p><%= image_tag 'https://image.tmdb.org/t/p/w200' + movie['poster_path'],class: "card-img" %></p>
                <% end %>
            <% end %>
            <p><%= link_to "詳細へ", movie_path(movie['id']) %></p>
        </div>
    <% end %>
</div>
```
**⑤show.html.erbの記述**
投稿ページへのリンクは現地点ではエラーが出るので、コメントアウトしています。
リンク先のcrudが完成したらコメントアウトを外してください。
```diff_erb:views/movies/show.html.erb
<div class="movie-detail">
    <p><%= @movie['title'] %></p>
    <p><%= @movie['release_date'] %></p>
    <% if @movie['poster_path'] %>
        <p><%= image_tag 'https://image.tmdb.org/t/p/w200' + @movie['poster_path'],class: "card-size" %></p>
    <% end %>
    <p><%= @movie['overview'] %></p>
    <%#= link_to "投稿ページへ", new_post_path(movie_id: @movie['id']) %>
</div>
```

## ３.投稿用のMVC作成
通常のCRUDを実装しましょう。

:::note warn
カラムの関係上Postモデルの作成コマンドは表記していますが、Postモデルに対応するpostsコントローラーやルーティングの記述もCRUDが問題なく作動するように完成している前提で以降の説明は進めます。
:::

**①Postモデルの作成**
```:ターミナル
rails g model post title:string image:string overview:text contents:text 
```
**②Postsコントローラーのnewアクションに追記**
```diff_ruby:posts_controller.rb
class PostsController < ApplicationController
    def index
        @posts = Post.all
    end

    def new
        @post = Post.new
+       movie_id = params[:movie_id]
+       url = "https://api.themoviedb.org/3/movie/#{movie_id}?api_key=#{ENV['TMDB_API']}&language=ja"
+       @movie = JSON.parse(Net::HTTP.get(URI.parse(url)))
    end

    def create
        post = Post.new(post_params)
        if post.save
            redirect_to :action => "index"
        else
            redirect_to :action => "new"
        end
    end

    def show
        @post = Post.find(params[:id])  
    end

    def edit
        @post = Post.find(params[:id])
    end

    def update
        post = Post.find(params[:id])
        if post.update(post_params)
            redirect_to :action => "show", :id => post.id
        else
            redirect_to :action => "new"
        end
    end

    def destroy
        post = Post.find(params[:id])
        post.destroy
        redirect_to action: :index
    end

    private
    def post_params
        params.require(:post).permit(:title, :contents ,:image ,:overview)
    end
end

```
**③new.html.erbの記述**
取得した映画情報が変更されないよう`hidden_field`を使います。
```diff_erb:views/posts/new.html.erb
<h1>posts#new</h1>
<%= form_for @post do |f| %>
    <div class="field">
        <p><%= f.label :title %>:<%= @movie['title']%></p>
        <p><%= f.hidden_field :title, :size => 140, value: @movie['title']%></p>

        <p><%= image_tag 'https://image.tmdb.org/t/p/w200' + @movie['poster_path'] %></p>
        <p><%= f.hidden_field :image, value: 'https://image.tmdb.org/t/p/w200' + @movie['poster_path'] %></p>

        <p><%= f.label :overview %>:<%= @movie['overview']%></p>
        <p><%= f.hidden_field :overview, :size => 140, value: @movie['overview'] %></p>
        <%= f.label :contents,"感想" %>
        <p><%= f.text_area :contents, :size => "20x5"%></p>
    </div>
    <%= f.submit "postする" %>
<% end %>
<%= link_to "post一覧に戻る", posts_path %>
```
以上で投稿まで実装できているかと思います。

# おまけ
実は今の状態だと映画は20件までしか取得できていない状態です。
100件ぐらいは取得しときたいですよね！
ついでに`gem kaminari`を用いて映画一覧のページネーションも実装しちゃいましょう！

**①gemfileに以下を追加・gemのインストール**
```diff_ruby:Gemfile
    gem 'kaminari'
```
```:ターミナル
bundle install
```
**②movies_controller.rbのindexアクションの内容を変更**
下記内容に全て書き換えましょう

```diff_ruby:movies_controller.rb
def index
    if params[:looking_for]
      movie_title = params[:looking_for]
      movies = []
      (1..5).each do |page|
        url = "https://api.themoviedb.org/3/search/movie?api_key=#{ENV['TMDB_API']}&language=ja&query=" + URI.encode_www_form_component(movie_title) + "&page=#{page}"
        response = Net::HTTP.get_response(URI.parse(url))
        if response.code == "200"
          result = JSON.parse(response.body)
          movies.concat(result["results"])
        end
      end
    else
      movies = []
      (1..5).each do |page|
        url = "https://api.themoviedb.org/3/movie/popular?api_key=#{ENV['TMDB_API']}&language=ja&page=#{page}"
        response = Net::HTTP.get_response(URI.parse(url))
        if response.code == "200"
          result = JSON.parse(response.body)
          movies.concat(result["results"])
        end
      end
    end
    @movies = Kaminari.paginate_array(movies).page(params[:page]).per(20)
  end
```

作品数を変えたい場合は`(1..5).each do |page|`の`(1..5)`の数字を変更してください。
1ページあたり20作品、本記事では5ページ目までと指定しているので100作品分取得しているという感じです。

kaminariの仕様については以下を参照してください。

https://github.com/kaminari/kaminari

## 以上です！
ここまでの差分はこちら

https://github.com/okok111/rails_movie_app_TMDB/tree/6620002524f6b53323fdce8e522f7c83dee46e73

# 参考記事
今回もたくさんの先人たちのおかげで実装することが出来ました。
>https://qiita.com/kazuhito_nakayama/items/c9cf578058d1e4f078a1
https://weblion303.net/1262
https://developers.themoviedb.org/3
https://github.com/kaminari/kaminari
https://docs.ruby-lang.org/ja/3.0/class/Net=3a=3aHTTPResponse.html