【Rails TMDB】APIで映画情報を取得しよう【保存版】

# 【Rails TMDB】APIで映画情報を取得しよう

:::note warn
警告
だいぶ昔に書いた記事なので、現時点ではエラーや動作がしないなどの事象が起こる可能性があります泣
ちゃんと書き直すので少々お待ちを！！
取り急ぎ、問題があれば、twitter( https://twitter.com/kazuhitonakayam )までご連絡ください🙏
:::

## なぜこの記事を僕が書くのか
端的に、Railsを用いてAPI経由で映画情報を取得できるようになる、参考記事がなかったから。

## この記事のゴール
TMDBのAPIを用いて世界範囲で映画情報を取得し、そのデータを自分のオリジナルサービスで扱えるようになる。
![スクリーンショット 2020-10-20 2.13.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411851/ff58538d-2e6d-c2e7-b302-3b20e0603f3d.png)


## この記事で扱わないこと
・APIとは何か
・TMDBのAPIで、映画以外の情報を取得する方法


## そもそもTMDBとは
![スクリーンショット 2020-10-20 0.21.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411851/dfd5a46d-a231-39b9-1d5d-0334f997971d.png)
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）は、コミュニティで構築された映画およびTVデータベースです。すべてのデータは、2008年にさかのぼる私たちの素晴らしいコミュニティによって追加されました。

とのことです！コミュティ型で成長させてきたプロダクトのようですね
だからなのか、結構APIも容易に取得できました

## TMDBに登録（サインアップ）& API取得
APIの登録の時に、プロダクトのURLが必要なので、Railsアプリはリリースしておいてくださいね(^○^)/
（厳密にいくと、別にリリースする必要はないのですが、リリースしておいた方がスムーズなので今回は、リリースしてる前提で進めていきます！）※[リリースしたい方](https://qiita.com/kazukimatsumoto/items/a0daa7281a3948701c39)

[この記事](https://weblion303.net/1262)を参考に、TMDBに開発者用のAPIの使用許可を得ましょう！


## themoviedb-apiというGemをインストール
[こちら](https://rubygems.org/gems/themoviedb-api)から、themoviedb-apiというGemの情報をご覧ください！

>Provides a simple and intuitive interface for the Movie Database API making use of OpenStruct.

何やら直感的なインターフェイスを提供してくれるそう笑。
開発者の方、感謝します

>OpenStructとは
→OpenStructは、要素を動的に追加・削除できる手軽な構造体を提供する、rubyの標準ライブラリ。

例としてはこんな感じ。

```rb:index.rb
require 'ostruct'
# 構造体を作って動的にpropertyを追加
son = OpenStruct.new
son.name = "Thomas"
son.age = 3
```
わかんない笑
一旦先に進みましょう
んじゃあ早速インストール

```ruby:Gemfile
#(省略)　以下の1行をGemfileの最後に書きましょう！
gem 'themoviedb-api', '~> 1.3'
```

```terminal:terminal
> bundle install --without production # 別にbundle installだけでも良いです
```

これで、TMDBを利用するために必要な、直感的なインターフェイスを提供してくれるGemはインストールできた。

## 自分のAPIを自分のアプリに記述する
自分の場合、movies_controllerを作成し、searchアクション、並びにshowアクションを用意しました。
モデルは、映画情報を引っ張ってくるだけの範囲ならば、こちらの方でモデルの作成は不要です。

```terminal:terminal
> rails g controller movies search show
```

```ruby:movies_controller
class MoviesController < ApplicationController
  require 'themoviedb-api'
  Tmdb::Api.key("ご自身のAPI Key")
  Tmdb::Api.language("ja") # こちらで映画情報の表示の際の言語設定を日本語にできます

  def search
  end
  
  def show
  end
end
```
一旦、この辺で、これらの変更を本番環境にあげましょうか

```terminal:terminal
> git add .
> git commit -m "commit"
> git push heroku master #masterで打ってできなかったらmainで打ってみてくださいな
```

## 現時点で何ができるか
ここまでできると、自分のターミナル上で、映画情報が取得できます

```rb:terminal
> heroku run rails console
#対話モードになる

> Tmdb::Search.movie("Harry") 
#Tmdb::Search.movie()で、()に書いた文字列で映画情報を検索できる。
```
上のコマンドでこのようなアウトプットが出ます

![スクリーンショット 2020-10-20 0.54.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411851/c5f7e3bf-aa50-c7e9-e752-80add351b1e4.png)

先ほど書きましたが、このアウトプットはOpenStructというRubyライブラリによって、OpenStruct型で出力されていて、馴染みが僕にはなかったので、いったんjson形式に変換します。

[to_json](https://docs.ruby-lang.org/ja/latest/method/Struct/i/to_json.html)によって、実現しています。

```
(Tmdb::Search.movie("Harry")).to_json #まだターミナルに打たないでね
```

次に、[`JSON.parse`](https://docs.ruby-lang.org/ja/latest/class/JSON.html)を用いて、与えられた JSON 形式の文字列を Ruby オブジェクトに変換して返します。

```
JSON.parse((Tmdb::Search.movie('Harry')).to_json)
```
このコマンドでアウトプットは以下のようになります。
![スクリーンショット 2020-10-20 1.26.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411851/57365af2-c96a-9cf6-2343-3da85e383c14.png)

分かりにくい笑
冒頭だけみますね
Before
![スクリーンショット 2020-10-20 1.28.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411851/7eb60785-bed5-a456-4d6b-8f9e401e9cba.png)

After
![スクリーンショット 2020-10-20 1.29.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411851/e23a7ea7-590b-0125-5392-5f36d1b266e2.png)
分かりますでしょうか
データの対応関係がわかりやすくなってたでしょう

最後に、さらにこれをみやすくするために、
[`JSON.pretty_generate`メソッド](https://docs.ruby-lang.org/ja/latest/method/JSON/m/pretty_generate.html)を使ってみやすくします

```rb:
# 一旦moviedataという変数にアウトプットを格納
> moviedata = JSON.parse((Tmdb::Search.movie("Harry")).to_json)
# そして
> puts JSON.pretty_generate(moviedata)
```
これによってアウトプットが劇的に見易くなります泣

![スクリーンショット 2020-10-20 1.37.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411851/b319d89d-d18f-8a35-e21e-499b4eb3d3cb.png)
データのまとまりが視覚的によくわかりますね。
最終的にはjson形式でデータが整形されましたね笑
自分的にはjson形式が一番分かりやすかったのでこうしました！
もっと効率的な方法があれば教えてくださいね笑

## あとはViewにアウトプットを表示するだけ
さて終盤です。

ポイントはjson形式のデータの取り出し方ですね
[この記事](http://tatsuya1970.com/?p=8575)をご覧になればわかると思います👍
例えば以下のアウトプットから、映画のタイトルの情報を取得したいならば、


![スクリーンショット 2020-10-20 1.44.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411851/9a9663b0-3cab-ce09-b8c3-ebb37ce59037.png)

```rb
moviedata['table']['results'][0]['table']['title']
```
と書きます👍
確かに取得できていますね🎉
![スクリーンショット 2020-10-20 1.48.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411851/9c333e05-2df0-ce81-5e57-50217afa85aa.png)

これを参考にViewを作っていきましょう！
仕様としては、
・検索バーに検索したい映画を入力すれば、目的の映画情報が取得、表示できる
・検索してない時（アプリに最初アクセスする時）には、人気映画を取得、表示する
というものです！

```erb:search.html.erb
<h1>映画検索</h1>

<div>
  <%= form_tag(root_path, method: :get) do %>
    <%= search_field_tag :looking_for, nil, placeholder: 'Movie Title...' %>
    <%= submit_tag '検索' %>
  <% end %>
</div>

<%# 検索された時にコンテンツを表示する %>
<%if params[:looking_for].present? %>
    <% movieinfo = JSON.parse((Tmdb::Search.movie(params[:looking_for])).to_json) %>
    <h2><%= params[:looking_for] %>の検索結果</h2>
    =====================
    <% i = 0 %>
    <div class="card-wrapper">
        <% while i < movieinfo['table']['results'].count %>
            <div class="card">
                <div class="card-upperinfo">
                    <%if movieinfo['table']['results'][i]['table']['title'].present?%>
                        <h3><%= link_to movieinfo['table']['results'][i]['table']['title'],detail_path(movieinfo['table']['results'][i]['table']['id']) %></h3>
                    <%end%>

                    <% if movieinfo['table']['results'][i]['table']['release_date'].present? %>
                        <p class="release-date"><%= movieinfo['table']['results'][i]['table']['release_date'] %></p>
                    <%end%>
                </div>


                <% if movieinfo['table']['results'][i]['table']['poster_path'].present? %>
                    <p><%= image_tag 'https://image.tmdb.org/t/p/w1280' + movieinfo['table']['results'][i]['table']['poster_path'],class: "card-img" %></p>
                <%end%>
            </div>
            <% i += 1%>
        <%end%>
    </div>
<%else%>
    <%# 検索されていない時にトレンドコンテンツを表示する %>
    <h2>本日の世界のトレンド</h2>
    ====================
    <% movieinfo = JSON.parse((Tmdb::Movie.popular).to_json) %>
    <% i = 0 %>
    <div class="card-wrapper">
        <% while i < movieinfo['table']['results'].count %>
            <div class="card">
                <div class="card-upperinfo">
                    <%if movieinfo['table']['results'][i]['table']['title'].present?%>
                        <h3><%= link_to movieinfo['table']['results'][i]['table']['title'], detail_path(movieinfo['table']['results'][i]['table']['id'])%></h3>
                    <%end%>

                    <% if movieinfo['table']['results'][i]['table']['release_date'].present? %>
                        <p class="release-date"><%= movieinfo['table']['results'][i]['table']['release_date'] %></p>
                    <%end%>
                </div>

                <% if movieinfo['table']['results'][i]['table']['poster_path'].present? %>
                    <p><%= image_tag 'https://image.tmdb.org/t/p/w1280' + movieinfo['table']['results'][i]['table']['poster_path'],class: "card-img" %></p>
                <%end%>
            </div>

            <% i += 1%>
        <%end%>
    </div>
<%end%>
```

CSSは、参考程度に記しておきますね

```css:movie.scss
.card {
    // box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.5);
    border-radius: 10px;
    width: 28%;
    margin-bottom: 100px;
    padding: 20px;
    box-sizing: border-box;
}

.card-wrapper {
    width: 80%;
    margin: auto;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-evenly;
}

.release-date {
    text-align: right;
}

.card-img {
    width: 100%;
    border-radius: 16px;
}

.card-upperinfo {
    height: 139px;
    overflow-y: scroll;
}
```
今回は、
・検索したい映画情報で映画情報を取得するのに、

```rb
Tmdb::Search.movie()
```
・世界のトレンド映画情報を取得する際に、

```rb
Tmdb::Movie.popular
```
を使いましたが他にも様々な魔法があるので、ぜひ以下よりご覧ください！
[htemoviedb-api Githubレポジトリ](https://github.com/18Months/themoviedb-api/)

できなかったりエラーが出たりしたらいつでもご連絡ください🙇‍♂️
[Twitter](https://twitter.com/kazuhitonakayam)
[Github](https://github.com/kazuhitonakayama)


## 参考
[OpenStructとは](https://madogiwa0124.hatenablog.com/entry/2019/07/21/153114)