Laravel で Web API を使う
本ドキュメントは次のミラーサイトです。
社内オンプレで動く Laravel と、クラウドで動く Laravel を、Web API で連携させてみました。
概念図
やりたいこと
社内オンプレの給与明細APサーバにアクセスすると、クラウドの勤怠打刻APサーバのデータも併せて取得し、給与明細と合わせて勤怠実績明細も表示できるようにしたい。
シーケンス図
Web API の設計
設計時に決めておくべき項目を挙げます。
-
HTTPメソッド
GET -
エンドポイント
/api/v1/kintai/{yyyyMM}/{勤怠個人番号}
-
機能概要
従業員の勤怠実績データを返す -
リクエスト
-
yyyyMM
・・・ 年月("last" と指定されたら最終年月) -
勤怠個人番号
・・・ 従業員の勤怠個人番号(Active Directory に照会すること)
-
-
レスポンス
- ステータスコード
-
200
・・・ 成功 -
404
・・・ リクエストされた勤怠実績が存在しなかった
-
- 応答形式 ・・・ JSON
- ステータスコード
REST APIはステートレスなので、認証にCookieやセッションは使えません。
APIではトークン認証が一般的ですが、社内でしか使わないので、認証自体を省略しました。
API仕様書の書き方には一定のパターンがあります。
他社人事労務クラウドAPIのドキュメントURLを参考までに挙げておきます。
- 人事労務freee API ~ https://developer.freee.co.jp/docs/hr
- SmartHR API ~ https://developer.smarthr.jp/api/index.html
- ジョブカン経費精算 API ~ https://ssl.wf.jobcan.jp/api_doc
Laravelでの実装方法
APIが呼ばれる側のサーバ
クラウドに置かれた、APIが呼ばれる側のAPサーバ(勤怠打刻サーバ)です。
まずapi.php
にエンドポイントを定義します。
web.php
にはCSRF保護がありますが、api.php
にはありません。
Route::get('v1/kintai/{yyyymm}/{id}', 'Api\KintaiController@index');
リクエストを受け取るコントローラを作成します。
<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Models\Result;
use Carbon\Carbon;
use App\Http\Controllers\Controller;
/*
給与明細サーバから呼ばれるクラス
*/
class KintaiController extends Controller
{
public function index(Request $request, $yyyymm, $syaincd)
{
if ($yyyymm == 'last') { // last指定時は最終年月を仮定
$yyyymm = Carbon::yesterday()->format('Ym');
}
$baseDate = Carbon::create(substr($yyyymm, 0, 4), substr($yyyymm, 4, 2), 1); // 基準日の設定
// 勤怠実績データベースの読み込み
$query = Result::where('syaincd', $syaincd)
->whereBetween('kintaijissekiymd', array($baseDate->copy()->startOfMonth()->format('Ymd'), $baseDate->copy()->endOfMonth()->format('Ymd')))
->orderBy('kintaijissekiymd');
// レコード件数が0より大きければデータを返し、そうでなければエラーレスポンスとする
if ($query->count() > 0) {
return response()->json([
'code' => 200,
'contents' => $query->get()
], 200);
} else {
return response()->json([
'code' => 404,
'message' => 'Not Found'
], 404);
}
}
}
Eloquentモデルでは、文字列にキャストされるときにJSONに変換されるため、Eloquentオブジェクトをそのまま返しても大丈夫です。
コマンドからAPIをテスト
curl
コマンドで得られるJSONデータをjq
コマンドで整形して確認します。
curl -s https://example.com/api/v1/kintai/202006/12765 | jq
import requests
res = requests.get('https://example.com/api/v1/kintai/202006/12765')
print(res.json())
{
"code": 200,
"contents": [
{
"syaincd": 12765,
"syainmei": "山田 太郎",
"kanrisyouninkengencode": "一般社員",
"kintaijissekiymd": "20200625",
"syugyozikantaikaishihhmi": "0850",
"syugyozikantaisyuryohhmi": "1730",
"meisyo": "出勤",
"barcodesyukkinhhmi": "0842",
"barcodetaikinhhmi": "1738",
:
APIを呼ぶ側のサーバ
社内オンプレに置かれた、APIを呼ぶ側のAPサーバ(給与明細サーバ)です。
APIで取得したデータはフロントエンドの Vue.js や React にレンダリングを任せるのが通常ですが、ここではLaravelで受けてビュー(Blade)に渡します。
$context = stream_context_create(array(
'http' => array('ignore_errors' => true)
));
$kintaicd = $request->session()->get('user')->kintaicd;
// APIを呼び出す
$json = file_get_contents("https://example.com/api/v1/kintai/$yyyymm/$kintaicd", false, $context);
$ary = json_decode($json); // JSONを配列に
// 正常なら "contents" をViewに渡す
if ($ary->code == 200) {
$reports = $ary->contents;
} else {
$reports = null;
}
return view('kintai', compact('reports'))
->with('spec_type', 'kintai')
->with('ym', substr($yyyymm, 0, 4) . '年' . ltrim(substr($yyyymm, 4, 2), '0') . '月')
->with('prev_month', $dt->copy()->subMonth(1)->format('Ym'))
->with('next_month', $dt->copy()->addMonth(1)->format('Ym'));
ビュー(Blade)で $reports を展開します。
<div class="table-responsive">
<table class="table text-nowrap table-bordered table-striped table-hover table-condensed">
<thead>
<tr>
<td rowspan="2">日付</td>
<td rowspan="2">勤怠種別</td>
<td colspan="2">打刻</td>
: 省略
</tr>
<tr>
<td>出勤</td>
<td>退勤</td>
<td>就業開始</td>
: 省略
</tr>
</thead>
<tbody>
@foreach ($reports as $report)
<tr>
<td>{{ $report->kintaijissekiymd }}</td>
<td>{{ $report->syukkinhhmi }}</td>
<td>{{ $report->taikinhhmi }}</td>
: 省略
</tr>
@endforeach
</tbody>
</table>
</div>
以上。参考になれば幸いです。
Discussion