Halu_wimps
@Halu_wimps (シガラキ)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Laravel・APIでの値の受け渡しについて

解決したいこと

LaravelでAPI連携を行い、Viewの入力フォームから値を取得してAPIで検索を行う。
取得したデータをViewに渡す処理をしています。
GuzzleHTTPを使用していてJson形式で受け取ったあと配列に変換しようとしているのですがうまくいきません。
アドバイスや修正点などご教授をいただきたいです。

発生している問題・エラー

Cannot access offset of type string on string

View(入力フォーム)

cocktails.blade.ph
  <form action="{{ url('/search') }}" method="">
  {{ csrf_field() }}
    <div class="freeword mt-5">
        <p class="text-light">フリーワード</p>
        <input id="word_form" type="text" name="keyword" maxlength="20" placeholder="ジン、ウォッカ、オレンジ、、、etc">
    </div>

    <div class="selecter mt-5">
        <div>
            <p class="text-light">お酒のベース</p>
                <select id="select_form" name="base">
                    <option value="" selected>選択無し</option>
                    <option value="0">ノンアルコール</option>
                    <option value="1">ジン</option>
                    <option value="2">ウォッカ</option>
                    <option value="3">テキーラ</option>
                    <option value="4">ラム</option>
                    <option value="5">ウイスキー</option>
                    <option value="6">ブランデー</option>
                    <option value="7">リキュール</option>
                    <option value="8">ワイン</option>
                    <option value="9">ビール</option>
                    <option value="10">日本酒</option>
                </select>
        </div>
        <div>
            <p class="text-light">甘口・辛口</p>
            <select id="select_form" name="taste">
                <option value="" selected>選択無し</option>
                <option value="1">甘口</option>
                <option value="2">中甘口</option>
                <option value="3">ジン中口</option>
                <option value="4">中辛口</option>
                <option value="5">辛口</option>
            </select>
        </div>
        <div>
            <p class="text-light">アルコール度数</p>
            <select id="select_form" name="alcohol">
                <option value="" selected>選択無し</option>
                <option value="0">ノンアルコール</option>
                <option value="1-10">1~10() 弱め</option>
                <option value="11-20">11~20() 普通</option>
                <option value="21-30">21~30() やや強い</option>
                <option value="31-40">31~40() 強い</option>
                <option value="40-">40~() とても強い</option>
            </select>
        </div>
    </div>

    <input type="submit" class="btn btn-neutral btn-secondary btn-lg mt-5" value="お酒を検索する"></input>

  </form>

Controller(APIとのやりとり)

CocktailController.php
    public function search(Request $request){

        // GuzzleHTTPのインスタンスを作成
        $client = new Client();

        //入力フォームから値を受け取る
        $keyword = $request->input('keyword','');
        $base = $request->input('base','');
        $taste = $request->input('taste','');
        $alcohol = $request->input('alcohol','');

        //API先にデータを飛ばして所得する
        $response = $client->request('GET','https://cocktail-f.com/api/v1/cocktails',[
            'query' => [
                'keyword' => $keyword,
                'base' => $base,
                'taste' => $taste,
                'alcohol' => $alcohol,
            ]
        ]);

        //取得したデータを入れる
        $body = $response->getBody();

        //データを配列に変換
        $cocktails = json_decode($body, true);

        //表示するためにViewにデータを渡す
        return view('search',[
            'cocktails' => $cocktails
        ]);
    }

View(取得データの表示)

search.blade.ph
<div>
    @foreach($cocktails as $cocktail)
    <table>
        <tr>
            <th>{{ $cocktail['cocktail_name'] }}</th>
            <td>{{ $cocktail['cocktail_name_english'] }}</td>
            <td>{{ $cocktail['cocktail_digest'] }}</td>
            <td>{{ $cocktail['base_name'] }}</td>
            <td>{{ $cocktail['taste_name'] }}</td>
            <td>{{ $cocktail['alcohol'] }}</td>
        </tr>
    </table>
    @endforeach
</div>


自分で試したこと

エラー内容を僕が見た感じだとちゃんとJson形式のデータが配列に変換できていないのかな?って思ったのですが、
json_decode($body, true);を使ってもエラーが変わらずtrueを外しても同じエラーが表示されます。
dd()を使って確認したところ下の表示になりデータの取得はできているのかな?って認識です。

GuzzleHttp\Psr7\Stream {#336 ▼ // app\Http\Controllers\CocktailController.php:31
  -stream: stream resource @483 ▶}
  -size: null
  -seekable: true
  -readable: true
  -writable: true
  -uri: "php://temp"
  -customMetadata: []
}
true // app\Http\Controllers\CocktailController.php:31

解決できました!!

Viewでのキーの指定の問題でした!
今回取得しているデータが多次元連想配列だったのに単純配列での指定をしていたのが問題でした。

search.blade.ph
        @foreach($cocktails['cocktails'] as $cocktail)
        <tr>
            <th>カクテル名</th>
            <th>英語名</th>
            <th>説明</th>
            <th>ベース</th>
            <th>味わい</th>
            <th>アルコール度数</th>
        </tr>
        <tr>
            <td>{{ $cocktail['cocktail_name'] }}</td>
            <td>{{ $cocktail['cocktail_name_english'] }}</td>
            <td>{{ $cocktail['cocktail_digest'] }}</td>
            <td>{{ $cocktail['base_name'] }}</td>
            <td>{{ $cocktail['taste_name'] }}</td>
            <td>{{ $cocktail['alcohol'] }}</td>
        </tr>
        @endforeach

foreachのキーの指定を多次元用に入り込んだら取得できました!
コメントなどでデバックのやり方を教えていただき勉強になりました、ありがとうございます!!

0

2Answer

とりあえずviewのtableタグがループ内に含まれてるのが気になりました。

search.blade.php
<div>
    <table>
        <tr>
            <th>カクテル名</th>
            <th>英語名</th>
            <th>説明</th>
            <th>ベース</th>
            <th>味わい</th>
            <th>アルコール度数</th>
        </tr>
        @foreach($cocktails as $cocktail)
        <tr>
            <td>{{ $cocktail['cocktail_name'] }}</td>
            <td>{{ $cocktail['cocktail_name_english'] }}</td>
            <td>{{ $cocktail['cocktail_digest'] }}</td>
            <td>{{ $cocktail['base_name'] }}</td>
            <td>{{ $cocktail['taste_name'] }}</td>
            <td>{{ $cocktail['alcohol'] }}</td>
        </tr>
        @endforeach
    </table>
</div>

エラーに関しては、

json_decode() で変換する前に、$body 変数の値を確認したり、

CocktailController.php
// データを配列に変換
$body = $response->getBody();
dd($body);
$cocktails = json_decode($body, true);

json_last_error() を使って、変換処理でエラーが発生していないか確認したり、

CocktailController.php
// データを配列に変換
$body = $response->getBody();
$cocktails = json_decode($body, true);
if (json_last_error() !== JSON_ERROR_NONE) {
    // エラー処理
}

json_decodeが失敗して、代わりにJSON文字列そのものを返していないか確認したりしてみるといいんじゃないでしょうかね?

CocktailController.php
$cocktails = json_decode($body, true);

if($cocktails === null) {
    dd(json_last_error_msg()); // json_decodeが失敗した場合にエラーメッセージを出力する
}

return view('search',[
    'cocktails' => $cocktails
]);

他にもAPIから返されるJSONデータのキーと、配列にアクセスする際のキーが一致してないときとかもそのエラー出ちゃうみたいですねー

0Like

Comments

  1. @Halu_wimps

    Questioner

    tableに関してはごめんなさいすぐ修正します!!
    なるほどそうやって調べることもできるんですね。
    ネットでネットでddを使うってのは見つけられてたけどこんな風に使えばよかったんですね!!すぐ試しますありがとうございます!!

まずは取得したレスポンスのbodyやjson_decodeした結果を実際に確認してみましょう。
試しにAPIの結果を見てみると次のようなデータ構造になっています。

{
  "status": "0000",
  "total_pages": 14,
  "current_page": 1,
  "cocktails": []
}

実際にデータを見てみると、statusなどいくつかの要素と同じ階層にcocktailsがあることがわかります。

0Like

Comments

  1. 失念していたのですが、`getBody`はコンテンツのデータ(今回の場合はJSON文字列)を直接返すものではなかった気がします。
    「dd()を使って確認した」というのが`getBody`の結果でしょうか?

    https://docs.guzzlephp.org/en/stable/quickstart.html#using-responses

    ドキュメントにあるように、文字列にキャストしたり、`getContents`メソッドで取得できると思います。
  2. @Halu_wimps

    Questioner

    おっしゃる通りgetBodyをdd()で確認してました!
    ドキュメントを読むことも今後意識付けます!
    ありがとうございます!!

Your answer might help someone💌