laravel12.laravel_111
最終投稿日:2026年3月24日
### API設定
API_TIMEOUT=30
BLUE_SKY_URL=https://bsky.social/xrpc/
BLUE_SKY_NAME=xxxx-xxxx.bsky.social
BLUE_SKY_PASS=xxxx-xxxx-xxxx-xxxx
<?php
return [
// API設定情報
'api' => [
'time_out' => (int)env('API_TIMEOUT', 10),
'blue_sky' => [
'url' => env('BLUE_SKY_URL', ''),
'name' => env('BLUE_SKY_NAME', ''),
'password' => env('BLUE_SKY_PASS', ''),
'end_point' => [
'getToken' => 'com.atproto.server.createSession',
'getRefresh' => 'com.atproto.server.refreshSession',
'getSearch' => 'app.bsky.feed.searchPosts',
],
],
],
];
<?php
namespace App\Services;
use Illuminate\Support\Facades\Http;
use Illuminate\Http\Client\RequestException;
use Illuminate\Support\Facades\Session;
class ApiBlueSkyClient {
private bool $reTryAccessJwt = true; // アクセストークン取得リトライフラグ
private bool $reTryRefreshJwt = true; // リフレッシュトークン取得リトライフラグ
private int $exe_code = 500; // API実行結果コード
private string $exe_message; // API実行結果メッセージ
private bool $exe_res = false; // API実行結果
private string $baseUrl; // ベースURL
private string $accessJwt; // アクセストークン
private string $refreshJwt; // リフレッシュトークン
private string $did; // 自分のDID
private string $time_out; // 通信タイムアウト
private string $app_name; // アプリ名
private string $app_pass; // アプリパスワード
// コンストラクタ
public function __construct() {
$this->baseUrl = config('const.api.blue_sky.url');
$this->time_out = config('const.api.time_out');
$this->app_name = config('const.api.blue_sky.name');
$this->app_pass = config('const.api.blue_sky.password');
// トークンがない場合は発行する
if (empty(Session::get('blurSky_accessJwt', ''))) {
$this->getToken();
} else {
// API情報の格納
$this->setApiInfo(null, false);
// API実行結果格納
$this->setApiExeRes(true, 200, '初期化に成功しました。');
}
}
// getter
public function isExeRes() {
return $this->exe_res;
}
public function getExeCode() {
return $this->exe_code;
}
public function getExeMessage() {
return $this->exe_message;
}
// API実行結果格納
private function setApiExeRes(bool $res, int $code, string $msg) {
$this->exe_res = $res;
$this->exe_code = $code;
$this->exe_message = $msg;
}
// API情報の格納
private function setApiInfo($json, $con = true) {
if ($con) {
Session::put('blurSky_accessJwt', $json['accessJwt']);
Session::put('blurSky_refreshJwt', $json['refreshJwt']);
Session::get('blurSky_did', $json['did']);
}
$this->accessJwt = Session::get('blurSky_accessJwt', '');
$this->refreshJwt = Session::get('blurSky_refreshJwt', '');
$this->did = Session::get('blurSky_did', '');
}
// API実行
private function client() {
return Http::acceptJson()->baseUrl($this->baseUrl)->timeout($this->time_out)->throw();
}
// アクセストークン取得
private function getToken() {
// エンドポイント設定
$end_point = config('const.api.blue_sky.end_point.getToken');
// ヘッダー作成
$headers = ['Content-Type' => 'application/json'];
// POST情報作成
$data = [
'identifier' => $this->app_name,
'password' => $this->app_pass,
];
// APIリクエスト
try {
$api_res = $this->client()->withHeaders($headers)->post($end_point, $data);
// 返却情報をセッションに格納
$arrRes = $api_res->json();
// API情報格納
$this->setApiInfo($arrRes);
// API実行結果格納
$this->setApiExeRes(true, 200, '正常にアクセストークンを取得しました。');
return true;
} catch(RequestException $e) {
// API実行結果格納
$this->setApiExeRes(false, 500, 'アクセストークンの取得に失敗しました。');
}
return false;
}
// リフレッシュトークン処理
private function getRefreshToken() {
// エンドポイント設定
$end_point = config('const.api.blue_sky.end_point.getRefresh');
$ch = curl_init($this->baseUrl . $end_point);
// cURL実行(LaravelではPOSTリクエストでNULLのPOSTデータは設定できない)
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->refreshJwt,
'Content-Type: application/json',
],
]);
$api_res = curl_exec($ch);
$api_res = json_decode($api_res, true);
$api_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// 実行結果確認
if ($api_code != 200) {
// API実行結果格納
$this->setApiExeRes(false, $api_code, $api_res['message']);
return false;
}
// API実行結果格納
$this->setApiExeRes(true, 200, 'リフレッシュトークン処理が正常に終了しました。');
// API情報の格納
$this->setApiInfo($api_res);
return true;
}
// 文字列検索
public function getSearch($word, $hash = true, $limit = 10) {
// エンドポイント設定
$end_point = config('const.api.blue_sky.end_point.getSearch');
// キーワード
$keyword = $hash ? '%23' . $word : $word;
// 最大取得件数
$limit = $limit;
// POSTデータ作成
$data['q'] = $keyword;
$data['limit'] = $limit;
// ヘッダー情報作成
$headers = ['Authorization' => 'Bearer ' . $this->accessJwt, 'Content-Type' => 'application/json'];
// APIリクエスト
try {
$api_res = $this->client()->withHeaders($headers)->get($end_point, ['q' => 'reidream', 'limit' => '10'])->throw();
$api_res = $api_res->json();
// API実行結果格納
$this->setApiExeRes(true, 200, '正常に検索APIを実行しました(' . count($api_res) . ')。');
} catch(RequestException $e) {
// API実行結果格納
$this->setApiExeRes(false, 500, '検索APIの処理に失敗しました。');
// APIリトライ判断(リフレッシュトークン)
if ($this->reTryRefreshJwt) {
// リフレッシュトークン処理実施
$this->reTryRefreshJwt = false;
// リフレッシュトークン処理トライ
if ($this->getRefreshToken()) {
// 文字列検索リトライ
$api_res = $this->getSearch($word, $hash, $limit);
}
}
// APIリトライ判断(アクセストークン)
if ($this->reTryAccessJwt) {
// アクセストークン取得実施
$this->reTryAccessJwt = false;
// アクセストークン取得トライ
if ($this->getToken()) {
// 文字列検索リトライ
$api_res = $this->getSearch($word, $hash, $limit);
} else {
// API実行結果格納
$this->setApiExeRes(false, 500, '検索APIの処理(リトライ)に失敗しました。');
return [];
}
}
}
return $api_res;
}
}
サンプルでは例外に『use Illuminate\Http\Client\RequestException』を利用しています。
このクラスから取得できる抜粋を以下に記載します。
マナーとして『アクセストークン』取得、『リフレッシュ』処理の乱発は避けるべきです。
今回のサンプルでは、失敗したら再取得をしていますが、その場合失敗クエストのエラー分が無駄になります。
トークンには『有効期限』があるので、例えばセッションに発行した日時を登録してその差で判断するのも良いかもしれません。
またセッション管理ではなく、DB管理にしても良いかもしれません。
この辺りの設計思想は、上流の醍醐味でもあるので沢山悩みましょう...w