[laravel]NEWS APIを使いデータを取得してみた

##はじめに
この記事はプログラミング初学者による備忘録用の記事であり、また、少しでも他の初学者のお役に立てればと思い書いています。

今回は、APIを扱う練習としてlaravelをベースにGuzzleを使い、[NEWS API](https://newsapi.org/)からニュースを取得してみました。

間違いなどがございましたら、ご指摘のほどよろしくお願い致します。


##事前準備
``その1:``
[NEWS API](https://newsapi.org/)にて,APIKeyを取得して下さい。
下記画像の通り、各項目を入力後Submitを押すと取得できます。

![スクリーンショット 2021-04-05 16.18.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/226ecc0c-230f-c4cb-a097-bf6f7dd659cc.png)
``その2:``
Guzzleをインストール

```
composer require guzzlehttp/guzzle
```




##手順

1. 取得したAPIkeyを.envファイルに環境変数として設定
1. NEWS API用のconfigファイルを作成
1. データを取得し表示する為のルーティングを作成
1. ルーティングで設定したコントローラーとメソッドを作成
1. bladeに連想配列として値を渡し取得したデータを表示

##1.取得したAPIKeyを.envファイルに環境変数として設定

```/.env
NEWS_API_KEY=取得したAPIKeyを記述
NEWS_API_URL='https://newsapi.org/v2/'
```

##2.API用のconfigファイルを作成
```config/newsapi.php

<?php

return [
    'news_api_url' => env('NEWS_API_URL', null),
    'news_api_key' => env('NEWS_API_KEY', null),
];
```
``解説``
コントローラーのメソッド内でAPIのurlを設定する時に、``config('newsapi.news_api_url')``のように記述して使用します。``'news_api_key' ``も同様です。

##3.データを取得し表示する為にルーティングを作成

```routes/web.php
<?php

Route::get('/api', 'ApiController@index');

```
##4.ルーティングで設定したコントローラーとメソッドを作成
```ApiController.php
<?php

namespace App\Http\Controllers;


use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Psr7;

class ApiController extends Controller
{
    public function index()
    {

        try {
            $url = config('newsapi.news_api_url') . "top-headlines?country=us&category=business&apiKey=" . config('newsapi.news_api_key');
            $method = "GET";
            $count = 15;

            $client = new Client();
            $response = $client->request($method, $url);

            $results = $response->getBody();
            $articles = json_decode($results, true);

            $news = [];

            for ($id = 0; $id < $count; $id++) {
                array_push($news, [
                    'name' => $articles['articles'][$id]['title'],
                    'url' => $articles['articles'][$id]['url'],
                    'thumbnail' => $articles['articles'][$id]['urlToImage'],
                ]);
            }
        } catch (RequestException $e) {
            echo Psr7\Message::toString($e->getRequest());
            if ($e->hasResponse()) {
                echo Psr7\Message::toString($e->getResponse());
            }
        }
        return view('index', compact('news'));
    }
}
```
``解説1``

```php
use GuzzleHttp\Psr7;
use GuzzleHttp\Exception\RequestException;

try{
//略
}catch (RequestException $e) {
            echo Psr7\Message::toString($e->getRequest());
            if ($e->hasResponse()) {
                echo Psr7\Message::toString($e->getResponse());
            }

```
try catchを使い、リクエストの転送中にスローされる可能性のあるすべての例外をキャッチするようにしています。下記の説明文はGuzzle公式ドキュメントを翻訳して載せています。
>
ネットワークエラー（接続タイムアウト、DNSエラーなど）が発生した場合、GuzzleHttp\RequestExceptionがスローされます。この例外は、GuzzleHttpException\TransferExceptionを継承しています。この例外をキャッチすると、リクエストの転送中にスローされる可能性のあるすべての例外がキャッチされます。
>
``参考:``[Guzzle Docs Quickstart](https://docs.guzzlephp.org/en/stable/quickstart.html#exceptions)

``解説2``

```php
$url = config('newsapi.news_api_url') . "top-headlines?country=us&category=business&apiKey=" . config('newsapi.news_api_key');
$method = "GET";
$count = 15;
```
``$url``と``$method``は、後ほど``$response = $client->request($method, $url);``のように、リクエストを送る際に**メソッド**と**url**として使用するので設定しておきます。

今回は、``$url``を日本のビジネスというカテゴリのヘッドラインニュースを取得するように設定しています。また、先ほど設定したconfigファイルのkey(``'news_api_url'と'news_api_key'``)も使用します。
urlの設定方法は、下記の画像のようにNEWS APIの公式サイトに書かれているurlを参考にしています。
![スクリーンショット 2021-04-05 17.16.21（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/31165c32-5c6d-c864-3d09-c01671c666ab.png)
``$method``はNEWS APIを利用してデータを取得する為に``GETメソッド``を使用します。
``$count``は表示件数を15としたい為、設定しています。

``解説3``

```php

$client = new Client();
$response = $client->request($method, $url);
```
今回は、コードを簡略化して書きたいと思ったので、Guzzle7が提供している``GuzzleHttp\Psr7\Uriクラス``を使用した、インターフェースの実装を試みました。

``参考:``[Guzzle7 Request URI](https://docs.guzzlephp.org/en/stable/psr7.html#request-uri)

```php
$response = $client->request('GET', 'http://httpbin.org/get?q=foo');
```
上記コードの説明
>
The request URI is represented by a Psr\Http\Message\UriInterface object. Guzzle provides an implementation of this interface using the GuzzleHttp\Psr7\Uri class.
When creating a request, you can provide the URI as a string or an instance of Psr\Http\Message\UriInterface.

>



``解説4``

```php
$results = $response->getBody();
```
GETリクエストのレスポンスのボディをgetBody()メソッドで取得しています。
下記リンク先のコードを参考に設定しました。

```php
$body = $response->getBody();
echo $body;
// Cast to a string: { ... }
```
``参考:``[Guzzle7 Responses Body](https://docs.guzzlephp.org/en/stable/psr7.html#id2)

``解説5``

```php
$articles = json_decode($results, true);
```

NEWS APIからレスポンスとして渡ってくるデータはJSON形式なので、``json_decode()メソッド``を使うことでJSONを連想配列にしています。


``解説6``

```php
$news = [];
 for ($id = 0; $id < $count; $id++) {
  array_push($news, [
  'name' => $articles['articles'][$id]['title'],
  'url' => $articles['articles'][$id]['url'],
  'thumbnail' => $articles['articles'][$id]['urlToImage'],
 ]);
}
```
先ほど、``json_decode()メソッド``を使いJSON形式のデータを連想配列に変換したと思います。
``dd($articles);``で中身を見てみると下記のように多次元連想配列となっています。
![スクリーンショット 2021-04-05 18.11.57（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/0827d31f-6c7f-12f7-c4af-df8450813817.png)
従って、``array_push()メソッド``で使いたいkey(データ)を配列に追加するようにします。
流れとしては、``$news = [];``で空の配列を作り、繰り返し処理で先ほど作成した$count変数を使い、表示件数(繰り返される回数)を指定、そして``array_push()メソッド``を使い、対象となる配列を第一引数に、追加する連想配列を第二引数で作成することで空の配列である$news変数にデータが連想配列として格納されます。

##5.bladeに連想配列として値を渡し取得したデータを表示
前提として、コントローラーのメソッド内で``return view('index', compact('news'));``を記述しデータ(連想配列)をbladeに渡します。
そして、下記のように渡ってきた連想配列($news)をforeach文で展開して表示します。

```php
<body>
    <div class="container ">
        @foreach($news as $data)
        <div class="card-body pt-0 pb-2">
            <h3 class="h5 card-title">
                <a href="{{$data['url']}}">{{$data['name']}}</a>
            </h3>
            <div class="card-text">
                <img src="{{$data['thumbnail']}}">
            </div>
        </div>
        @endforeach
    </div>
```

##結果
レイアウトは整えていませんが、下記のように表示できれば成功です。
![スクリーンショット 2021-04-05 18.45.11（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891113/47aa57b5-dd12-383a-927c-3e9345c66e6d.png)

##おわりに
ひとまずエラーなくデータを取得することができたので、次回はこのAPIを活用してニュースアプリを作成してみたいと思います。

最後までお読み頂きありがとうございました。

##参考文献
[phpマニュアル](https://www.php.net/manual/ja/index.php)
[Guzzle7 公式ドキュメント](https://docs.guzzlephp.org/en/stable/index.html)
[Guzzle GitHub](https://github.com/guzzle/guzzle)
[NEWS API](https://newsapi.org/)