やりたいこと
Google Sheet APIを通して、Laravelからスプレットシートの情報を取得し、表示させる!!
なぜやりたいか?
今回は100以上の項目のある測定テストを作成して、回答者が自分が回答した問題をRadar Chartで見せたい。ユーザー側は自分の情報をいつでも見れるように、Laravel Breeze
を使って、ログインし、自分が回答した結果を見れる仕組みにしたい。
作りたい仕組みはこちら:
つまり、LaravelでAPIを扱うことに
LaravelのView画面からJavaScriptでAPIを叩いたことありますが、今回はコントローラからAPI情報を入れてみました。
理由はGoogleスプレットシートの共有部分に、共有するメールアドレスが必要(後ほど説明!)のと、JavaScript以外のやり方にチャレンジしたかったです。
やってみたこと:
① Google Cloud PlatformでSheetAPIを有効にし、サービスキーを作成する
② Laravelのコントローラから認証情報を取得
③ Chart.jsでデータを処理して反映する
③はChart.jsを調べればすぐできますので、今回は認証情報とLaravelで苦戦したところを共有します。
今回使ったChart.jsのサンプルコードはこちら
苦戦した部分
苦戦の連続でした。主に情報取得と引き渡し、そして本番環境でめちゃくちゃ詰んだ。
苦戦①:どのキーを使えば情報を取得できるの?
SheetAPIを動かしたいので、どんな認証方法がいいの?APIキーでもいいの?
正直、イマイチ理解してなかった。GCPを開いたら、3つもある!!
- API キー
- OAuth 2.0 クライアント ID
- サービス アカウント
公式から、細かく説明があります。
ざっくりと言いますと、今回はスプレットシートの情報を取得したいので、スプレットシートの権限をもらう必要があります。だから、スプレットシートから共有が必要ですので、サービスアカウントキーが最も簡単で便利です。(セキュリティも悪くないようです。)
補足: なぜAPIキーはダメでしたか?
公式ドキュメンテーションによると、多くのAPIがAPIキーに対応しないそうです。つまり、コードを書けたら、OAuthがいい、スプレットシートくらいなら、サービスアカウントだったら手っ取り早い!!
手順:
② サービスキーのJSONファイルをダウンロード
詳しい設定はこちらの記事を参考しました。設定時ダウンロードしないと、2度と取得できないようです。
③ サービスキーファイルをStorageに保存
※ここは結構大事です!! めちゃくちゃ詰んだ部分です。
いろいろ試したあと、Storage/App/private/
の下で配置するようにしました。理由はセキュリティ上、アクセスしにくい場所がいい。そして、.envファイルのように、本番環境で多量の手打ちすることもない。
苦戦②:情報をどう取得すればいいの?
認証取得だけで、二日ほど苦戦しました。見飽きたエラーはこちらです。
データの取得に失敗しました SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
データ取得できない時によく出るSyntaxエラーらしい。このエラーが登場する時、考えられる可能性は、
- 認証情報が間違ってる。
- 設定ミス
- サービスキーのファイルはあっていますが、パスが通ってない。
実際コントローラで書いたコードはこんな感じです。
$client = new Google_Client();
$key_file = storage_path('app/private/credential.json');
$client->setAuthConfig($key_file);
$client->setScopes([
'https://www.googleapis.com/auth/spreadsheets'
]);
先ほど話したサービスキーファイルを storage/app/private/credential.json
で保存します。パスの通し方はこんな感じ:storage_path('app/private/credential.json');
あとはわりとシンプルです。スプレットシートの情報を取得し、response経由でviewの方に引き渡すだけです。
$sheetId = '1ml44HFe1tGhl7_eJgIbB4EQjDusAami3gRUTA64zdps';
$sheetService = new Google_Service_Sheets($client);
$range = 'Sheet1'; // Sheet1の内容全てを取得
$response = $sheetService->spreadsheets_values->get($sheetId, $range);
$values = $response->getValues();//データを配列で取得
// return response()->json($values);// 取得したデータをチェックするときはこちらを
return response()->view('googleSheet',compact('values'));//viewの方に取得したデータを引き渡し
苦戦③:本番環境で詰んだ
今回はさくらサーバーでデプロイしました。デプロイした瞬間、動いてたChart.jsが動かなくなりました。なぜだ!!!!!
可能性をいろいろ模索しました。やってみてもうまくいかなかったこと:
・.envファイルでのphpMyAdmin設定
・google clould jsonキーは本番環境に入れる
そして、同期からもらったアドバイス:
・セキュリティー関連のパーミッション問題
・ローカルと本番のギャップによるマシン問題
・そもそも認証が間違ってる
などなど
ほぼ総当てして、最終的に動かせた。
正直どれか正解なのかわかりませんが、最後に試したことはこれです。
fetch("{{ route('sheet') }}?email=" + email,{
//本番環境入ってから追加項目
headers : {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
'Content-Type': 'application/json'
を明記することによって、引き渡されたファイルはちゃんとJSONファイルですよ〜ということらしいです。
本番環境はローカル環境より厳格ということかな?
viewのコードはこちら:
<body>
//認証機能
@auth
<div class="flex justify-center">
<input type="hidden" id="email" name="email" value="{{ auth()->user()->email }}">
<x-primary-button id="fetchData" class="mt-4 mb-4 py-2 px-4 bg-blue-500 text-black rounded-lg">結果表示</x-primary-button>
</div>
@endauth
//canvasを使ってChart.jsを表示・描画
<canvas id="radarChart"></canvas>
<script>
// データ取得用のAjaxリクエストを送信する関数
function fetchData() {
const email = document.getElementById('email').value;
// Ajaxリクエストを送信
fetch("{{ route('sheet') }}?email=" + email,{
headers : {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
.then(response => response.json())
.then(data => {
// データを取得してレーダーチャートを描画する関数を呼び出す
drawRadarChart(data);
})
.catch(error => {
console.error("データの取得に失敗しました", error);
});
}
// レーダーチャートを描画する関数
function drawRadarChart(data) {
//引き渡されたデータの処理は割愛。こっからはChart.jsのサンプルコードを参照
const canvas = document.getElementById('radarChart');
new Chart
(canvas, {
type: 'radar',
data: {
labels: [
'label1',
'label2',
'label3',
'label4',
'label5',
'label6',
'label7',
],
datasets: [{
label: `${timestamp}`,
data: [Q1result,Q2result,Q3result,Q4result,Q5result,Q6result,Q7result],
fill: true,
backgroundColor: 'rgba(75, 192, 192, 0.2)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1
}]
},
options: {
scale: {
ticks: {
beginAtZero: true,
max: 100
}
}
}
});
}
// データ取得ボタンのクリックイベントを設定
document.getElementById('fetchData').addEventListener('click', fetchData);
</script>
</body>
引き渡されたデータをJavaScriptで処理して、完成!!
感想
本番環境でもチャートが表示されました!!! 歓喜!!!!!
いろんな人の協力を得て、なんとかできました。これからは複数のデータを重ねていく仕組みを考えます。
引き続き、GoogleAPIサービスを使っていきたいと思います^^
最後に、、、最近の私。
携わっているByNameがリリースして、私のプロフィールもぜひみてもらいたいと思ってます!!