👍

Laravel で Web API を使う

2020/12/25に公開

本ドキュメントは次のミラーサイトです。
https://qiita.com/mindwood/items/94869f35b121b188dbed

社内オンプレで動く 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を参考までに挙げておきます。

Laravelでの実装方法

APIが呼ばれる側のサーバ

クラウドに置かれた、APIが呼ばれる側のAPサーバ(勤怠打刻サーバ)です。
まずapi.phpにエンドポイントを定義します。
web.phpにはCSRF保護がありますが、api.phpにはありません。

routes/api.php
Route::get('v1/kintai/{yyyymm}/{id}', 'Api\KintaiController@index');

リクエストを受け取るコントローラを作成します。

app/Http/Controllers/Api/KintaiController.php
<?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
Pythonの場合
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)に渡します。

app/Http/Controllers/KintaiController.php(一部抜粋)
$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 を展開します。

resources\views\kintai.blade.php
<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