laravelでカバー画像も表示できるブックレビューアプリを作った。※自分の学習用です

#何を作った？
PHP、laravelの勉強として、カバー画像も表示できるブックレビューアプリを作りました。
まずpaizaラーニングを参考に掲示板アプリを作り、そこにGoogleBooksAPIsでマンガのカバー画像を引っ張ってきます。

http://book-review123456.herokuapp.com/articles

基本的な機能はよくある掲示板アプリと同様です。なのでAPIを利用した画像の取得・利用について書きます。

*検索の精度はかなり悪いです。

<details><summary>その他の機能についてはこちらで簡単にまとめました。</summary>[laravelで作った画像も表示できるブックレビューの機能について　※ポートフォリオの解説です](https://qiita.com/Shogo_S/items/7c3273f99075c2d931ed)
</details>

<img width="1440" alt="スクリーンショット 2020-01-19 17.29.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501141/c9df238e-e99b-864b-7b8b-991ef1bbda99.png">　※イメージ画像

##レビューを投稿するまでの流れ(ログイン済みと想定)
①まず検索窓にマンガのタイトルを書きます。
②候補のカバー画像がいくつか出てきます。この画像はボタンになっていて、クリックして選択できます。
③画像を選択したらタイトルと本文を記入し、投稿して完成です。いいね!ボタンもあります。
僕は左側に画像、右にタイトルと本文を配置しました。

##検索フォームを作る

```getCover.blade.php
  {{ Form::model(['route' => ['article.getCover']]) }}
        <div class='form-group'>
            {{ Form::label('bookName', 'Book name:') }}
            {{ Form::text('bookName', null) }}
            {{ Form::submit('検索する', ['class' =>'btn btn-primary'])}}
        </div>
  {{ Form::close() }}

 　<div class = 'form-group'>
          <a href={{ route('article.new') }}>戻る</a>
   </div>
```

bookNameはマンガのタイトル。下記のgetCoverメソッドに送る。

##GoogleBooksAPIsの導入
下記を参考にしました。
[書籍検索APIはGoogleBooksAPIsがオススメ【導入も楽ちんです】](https://manablog.org/google-books-apis/)

[書籍検索APIであるGoogle Books APIsの使い方（PHPでのサンプルコードあり）](https://miyachi-web.com/google-books-apis/)

```PostController.php
public function getCover(Request $request)
    {      
        if($request->filled('bookName')){
          $content= $request->bookName;
          $data = "https://www.googleapis.com/books/v1/volumes?q=" .urlencode($content) ."&maxResults=10";
          $json = file_get_contents($data);
          $json = mb_convert_encoding($json, 'UTF8', 'ASCII,JIS,UTF-8,EUC-JP,SJIS-WIN');
          $json_decode = json_decode($json,true);
        }else {
          $json_decode = [];
        }
```
受け取ったbookNameは\$contentに代入されます。
\$dataはGoogleBooksAPIs。タイトル名をそのまま埋め込むと正しくエンコードされないので、urlencode()に入れます。

JSONデータの取得に関しては下記を参考にさせていただきました。
 [PHPでJSONデータの取得の仕方](https://qiita.com/fantm21/items/603cbabf2e78cb08133e)

<details><summary>変更点</summary><div>
デプロイする段階でエラーが出たため、再度作り直しました。以前はhttpclientを使用していたのですが、検索の精度には影響なしと判断したため、現在は使用していません（デプロイのエラーには影響していないので、使っても使わなくても変わらない、という認識です）。
</div></details>


##画像をボタンとして表示する

```getCover.blade.php
   @if ($json_decode)
      @foreach ($json_decode['items'] as $item)
          <form style="height:200px;width:200px;float:left" action="{{action('ArticleController@create')}}" method="get">
              <input type="hidden" value="{{ $item['volumeInfo']['imageLinks']['thumbnail'] }}" name="url">
              <button type="submit"><img src = "{{ $item['volumeInfo']['imageLinks']['thumbnail'] }}" ></button>
          </form>
      @endforeach
   @endif
```
ボタンを表示します。Formファザード は使っていません。foreachで複数件表示して、それぞれをボタンにします。
配列は入れ子になっていて、画像を表示するURLは\$json_decode['items']の中の['volumeInfo']の中の['imageLinks']の中にある['thumbnail']に格納されています。複数件取得することができるのでforeachを使用しています（['items']以下が複数件取得できます）。
<details><summary>課題</summary>この方法だと、配列が変化するとエラーになってしまいます。例えば、GoogleBooksAPIsを利用してJSONのデータを１０件取得した場合、その中に配列['thumbnail']を含んでいないデータが一つでもある場合はエラーになってしまいます。いくつか方法は試したのですが、まだ解決できていません。</details>

```ArticleController.php
public function create(Request $request)
    { 
        $posts = $request->url;
        $message = 'New article';

        return view('new',['message' =>$message, 'posts' =>$posts]);
    }
```
送られた画像URLを\$postsに格納し、投稿編集画面に送ります。
##タイトルと本文を書く

```ArticleController.php

   <h1>edit review</h1>
   <p>{{$message}}</p>

   //下記に画像URL渡します。表示用。
   <img src={{$posts}}>

   //投稿フォーム
   {{ Form::open(['route' =>'article.store'])}}

//下記にも画像URL渡します。まだ保存はされていないので、このフォームでPOSTしてデータベースに保存します。
      <div class = 'form-group'>       
          {{ Form::hidden('url',$posts)}}
      </div>

       //レビューのタイトル。
      <div class = 'form-group'>
          {{ Form::label('title','Title:')}}
          {{ Form::text('title', null)}}
      </div>

      //レビュー本文。
      <div class = 'form-group'>
          {{ Form::label('content','Content:')}}
          {{ Form::text('content',null)}}
      </div>

      //このレビューを投稿するユーザー。このアプリではログインしたユーザーだけが投稿できます。
      //投稿画面にアクセスできるということは、このユーザーは既にログインしていると言えます。
      //なのでAuth::user()->nameを投稿者として、この記事のユーザー名にします。
      <div class='form-group'>
            {{ Form::label('user_name', Auth::user()->name) }}
            {{ Form::hidden('user_name',Auth::user()->name ) }}
        </div>

      <div class = 'form-group'>
          <a href={{ route('article.getCover') }}>画像検索</a>
      </div>

      <div class = 'form-group'>
          {{ Form::submit('作成する', ['class' =>'btn btn-primary'])}}
          <a href={{ route('article.list') }}>一覧に戻る</a>
      </div>
      {{ Form::close() }}

      //フォームが空欄の場合はフラッシュメッセージを表示します。
      @if (session('flash_message'))
            <div class="flash_message">
                {{ session('flash_message') }}
            </div>
```
##ストアメソッドで保存する

```ArticleController.php
public function store(Request $request, Article $article)
    {
        if($request->content != ""  && $request->title !=""){
          $article = new Article();
          $user = \Auth::user();

          $article->image_url = $request->url;
          $article->title = $request->title;
          $article->content = $request->content;
          $article->user_name = $request->user_name;
          $article->user_id = $user->id;
          $article->save();
          return redirect()->route('article.show',['id'=>$article->id]);
        }else{
          session()->flash('flash_message', '空欄を埋めてください');
          return redirect()->back();
      }
    }
```
上記のstoreメソッドで保存し、\$article->image_urlを表示するviewを作成します。
下記がルートになります。

```web.php

//レビューのリスト。
Route::get('/articles','ArticleController@index')->name('article.list');
//検索ページに飛ぶ
Route::get('/article/getCover','PostController@getCover');
//書籍名を検索する
Route::post('/article/getCover','PostController@getCover')->name('article.getCover');
//選択した画像を編集画面にgetで送る。
Route::get('/article/new','ArticleController@create')->name('article.new');
//新規投稿のレビューをデータベースに保存する。
Route::post('/article', 'ArticleController@store')->name('article.store');
//投稿したレビューを表示する。
Route::get('/article/{id}','ArticleController@show')->name('article.show');
//レビューを削除する
Route::delete('/article/{id}','ArticleController@destroy')->name('article.delete');
//投稿したレビューを再び編集する
Route::get('/article/edit/{id}', 'ArticleController@edit')->name('article.edit');
//@editで編集したデータをポストして保存する
Route::post('/article/update/{id}', 'ArticleController@update')->name('article.update');


```
##課題 / 問題点
- マイグレーションでエラーが発生したため、下記URLを参考にCSRFの仕組みを無効化することで対処しています。なので参考にされる際は注意してもられると。CSRFの仕組みを理解するため、今は生のPHPで掲示板を制作しています。順番おかしいですね。

　[【Laravel】419のHTTPエラーが出た場合の対処法！](https://traveler0401.com/laravel-http-419/)

もっとわかりやすく書いた方がいいし、用語の使い方も修正した方がいいですよね。
もっと勉強して随時アップデートしたいです。