{"id":9343,"date":"2026-08-07T19:00:00","date_gmt":"2026-08-07T10:00:00","guid":{"rendered":"https:\/\/www.wetch.co.jp\/note\/?p=9343"},"modified":"2026-08-06T18:30:41","modified_gmt":"2026-08-06T09:30:41","slug":"%e3%83%90%e3%83%83%e3%82%af%e3%82%a8%e3%83%b3%e3%83%89%e3%81%ae%e4%ba%ba%e3%81%8cnativephp%e3%81%ae%e5%8a%9b%e3%82%92%e5%80%9f%e3%82%8a%e3%81%a6%e5%88%9d%e3%82%81%e3%81%a6%e3%83%a2%e3%83%90%e3%82%a4-2","status":"publish","type":"post","link":"https:\/\/www.wetch.co.jp\/note\/%e3%83%90%e3%83%83%e3%82%af%e3%82%a8%e3%83%b3%e3%83%89%e3%81%ae%e4%ba%ba%e3%81%8cnativephp%e3%81%ae%e5%8a%9b%e3%82%92%e5%80%9f%e3%82%8a%e3%81%a6%e5%88%9d%e3%82%81%e3%81%a6%e3%83%a2%e3%83%90%e3%82%a4-2\/","title":{"rendered":"\u30d0\u30c3\u30af\u30a8\u30f3\u30c9\u306e\u4eba\u304cNativePHP\u306e\u529b\u3092\u501f\u308a\u3066\u521d\u3081\u3066\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u3092\u4f5c\u3063\u3066\u307f\u305f\u306e\u5dfb\uff08\u4e0b\uff09"},"content":{"rendered":"<p><a href=\"https:\/\/www.wetch.co.jp\/note\/\u30d0\u30c3\u30af\u30a8\u30f3\u30c9\u306e\u4eba\u304cnativephp\u306e\u529b\u3092\u501f\u308a\u3066\u521d\u3081\u3066\u30e2\u30d0\u30a4\/\">\u524d\u56de\u306e\u6295\u7a3f<\/a> \u3067\u00a0<a href=\"https:\/\/nativephp.com\/\">NativePHP<\/a> \u306e\u74b0\u5883\u69cb\u7bc9\u304b\u3089\u30a2\u30d7\u30ea\u8d77\u52d5\u3001Welcome \u753b\u9762\u306e\u8868\u793a\u307e\u3067\u3092\u78ba\u8a8d\u3057\u307e\u3057\u305f\u3002<\/p>\n<p>\u4eca\u56de\u306f\u3001\u5f53\u521d\u304a\u8a71\u3057\u3057\u3066\u3044\u305f\u30bf\u30a4\u30e0\u30ec\u30b3\u30fc\u30c0\u30fc\u753b\u9762\u306e\u5b9f\u88c5\u3068\u3001\u6253\u523b\u3092\u53d7\u3051\u53d6\u308b API \u30b5\u30fc\u30d0\u30fc\u5074\u306e\u5b9f\u88c5\u306b\u9032\u307f\u305f\u3044\u3068\u601d\u3044\u307e\u3059\u3002<\/p>\n<h3>\uff11\uff09\u5168\u4f53\u306e\u6d41\u308c<\/h3>\n<p><a href=\"https:\/\/www.wetch.co.jp\/note\/\u30d0\u30c3\u30af\u30a8\u30f3\u30c9\u306e\u4eba\u304cnativephp\u306e\u529b\u3092\u501f\u308a\u3066\u521d\u3081\u3066\u30e2\u30d0\u30a4\/\">\u524d\u56de\u306e\u6295\u7a3f<\/a> \u306b\u66f8\u3044\u305f\u901a\u308a\u3001\u4ee5\u4e0b\u306e\u3088\u3046\u306a\u30bf\u30a4\u30e0\u30ec\u30b3\u30fc\u30c0\u30fc\u30a2\u30d7\u30ea\u3092\u4f5c\u6210\u3057\u3066\u3044\u304d\u307e\u3059\u3002<\/p>\n<p style=\"padding-left: 40px;\">\u30fb\u51fa\u52e4\u30dc\u30bf\u30f3\u3092\u62bc\u3057\u3001\u30b9\u30de\u30db\u306e\u4f4d\u7f6e\u304c\u4f1a\u793e\u306e\u4f4d\u7f6e\u304b\u3089\u534a\u5f84 10m \u4ee5\u5185\u3067\u3042\u308c\u3070\u3001\u51fa\u52e4\u8a18\u9332\u3092\u6253\u523b API \u7d4c\u7531\u3067\u4fdd\u5b58\u3059\u308b<br \/>\u30fb\u9000\u52e4\u30dc\u30bf\u30f3\u3092\u62bc\u3057\u3001\u30b9\u30de\u30db\u306e\u4f4d\u7f6e\u304c\u4f1a\u793e\u306e\u4f4d\u7f6e\u304b\u3089\u534a\u5f84 10m \u4ee5\u5185\u3067\u3042\u308c\u3070\u3001\u9000\u52e4\u8a18\u9332\u3092\u6253\u523b API \u7d4c\u7531\u3067\u4fdd\u5b58\u3059\u308b<br \/>\u30fb\u4f1a\u793e\u306e\u4f4d\u7f6e\u304b\u3089\u534a\u5f84 10m \u3092\u5916\u308c\u3066\u3044\u308b\u5834\u5408\u306f\u3001\u30a8\u30e9\u30fc\u3092\u8868\u793a\u3057\u3001\u8a18\u9332\u3057\u306a\u3044<\/p>\n<p>\u3053\u308c\u3092\u3001NativePHP for Mobile \u306b\u843d\u3068\u3057\u8fbc\u3080\u5834\u5408\u3001\u6574\u7406\u3057\u3066\u3044\u304f\u3068\u4ee5\u4e0b\u306e\u3088\u3046\u306a\u5b9f\u88c5\u306b\u306a\u308a\u307e\u3059\u3002<\/p>\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"730\" src=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-01-1024x730.png\" alt=\"\" class=\"wp-image-9344\" srcset=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-01-1024x730.png 1024w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-01-300x214.png 300w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-01-768x548.png 768w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-01-150x107.png 150w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-01-696x496.png 696w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-01-1068x762.png 1068w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-01.png 1227w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n<ol>\n<li>\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u5074\u306b\u30bf\u30a4\u30e0\u30ec\u30b3\u30fc\u30c0\u30fc\u753b\u9762\uff08 EDGE\u00a0 \/ NativeComponent \uff09\u3092\u4f5c\u308a\u3001\u51fa\u52e4\uff0f\u9000\u52e4\u3092\u30bf\u30c3\u30d7<\/li>\n<li>\u7aef\u672b\u306e\u4f4d\u7f6e\u3092\u53d6\u5f97\uff08\u624b\u52d5 or GPS \uff09<\/li>\n<li>\u4f1a\u793e\u5ea7\u6a19\u304b\u3089\u534a\u5f84\u5185\u304b\u3092\u5224\u5b9a\u3059\u308b<\/li>\n<li>OK\u306e\u5834\u5408\u3001\u6253\u523b API \u306bPOST<\/li>\n<li>\u6253\u523b API \u3068\u540c\u3058 Laravel \u4e0a\u306e <span class=\"font-semibold\" data-streamdown=\"strong\">Web \u4e00\u89a7<\/span>\u3067\u78ba\u8a8d<\/li>\n<\/ol>\n<div>\n<div>\u4eca\u56de\u3001\u6253\u523b API \u306f\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u3068\u306f\u5225\u306b\u7528\u610f\u3057\u305f Laravel\uff08 Docker Desktop + Laravel Sail \uff09\u3067\u52d5\u304b\u3057\u307e\u3059\u3002<\/div>\n<\/div>\n<p>\u4f4d\u7f6e\u60c5\u5831\u306e\u53d6\u5f97\u3092\u884c\u3046\u306b\u306f\u3001Marketplace \u306e\u6709\u6599\u30d7\u30e9\u30b0\u30a4\u30f3\uff08 nativephp\/mobile-geolocation \uff09\u3082\u3042\u308a\u307e\u3059\u304c\u3001\u4eca\u56de\u306f\u307e\u305a \u753b\u9762\u30fb\u4f4d\u7f6e\u306e\u534a\u5f84\u5224\u5b9a\u30fbAPI \u9023\u643a\u00a0\u306b\u7d5e\u308a\u307e\u3059\u3002<\/p>\n<p>\u3053\u3053\u3067\u8a00\u3046\u4f4d\u7f6e\u306e\u534a\u5f84\u5224\u5b9a\u3068\u306f\u3001\u4f1a\u793e\u306e\u7def\u5ea6\u30fb\u7d4c\u5ea6\u3092\u4e2d\u5fc3\u306b\u534a\u5f84\uff08\u4eca\u56de\u306f 10m\uff09\u306e\u5186\u3092\u8003\u3048\u3001\u3044\u307e\u306e\u4f4d\u7f6e\u304c\u305d\u306e\u4e2d\u306a\u3089\u6253\u523b\u3092\u8a31\u53ef\u3057\u3001\u5916\u306a\u3089\u300c\u51fa\u793e\u306e\u78ba\u8a8d\u304c\u3067\u304d\u307e\u305b\u3093\u300d\u3068\u3057\u3066 API \u306b\u306f\u9001\u3089\u306a\u3044\u3001\u3068\u3044\u3046\u3082\u306e\u3067\u3059\u3002\u3053\u306e\u3088\u3046\u306a\u5730\u56f3\u4e0a\u306e\u4eee\u60f3\u7684\u306a\u56f2\u3044\u3092\u30b8\u30aa\u30d5\u30a7\u30f3\u30b9\u3068\u547c\u3073\u307e\u3059\u3002<\/p>\n<p>\u6210\u529f\uff0f\u5931\u6557\u306e\u518d\u73fe\u306f\u624b\u52d5\u5ea7\u6a19\u306e\u65b9\u304c\u691c\u8a3c\u304c\u5b89\u5b9a\u3059\u308b\u305f\u3081\u3001\u4eca\u56de\u306f\u624b\u52d5\u5ea7\u6a19\u3067\u306e\u534a\u5f84\u5224\u5b9a\u3092\u884c\u3044\u307e\u3059\u3002<br \/>\u7aef\u672b GPS \u3092\u4f7f\u3063\u305f\u53d6\u5f97\u306f\u3001\u6b21\u56de\u306e\u6295\u7a3f\u3067\u6271\u3044\u307e\u3059\u3002\u3042\u308f\u305b\u3066\u3001SuperNative \u304b\u3089\u30cd\u30a4\u30c6\u30a3\u30d6\u306e\u4f4d\u7f6e\u53d6\u5f97\u3092\u547c\u3076\u4f8b\u3068\u3057\u3066\u3001\u81ea\u4f5c\u306e\u30ef\u30f3\u30b7\u30e7\u30c3\u30c8\u30d7\u30e9\u30b0\u30a4\u30f3\uff08 simple-geolocation \uff09\u306e\u5b9f\u88c5\u3082\u7d39\u4ecb\u3057\u307e\u3059\u3002<\/p>\n<h3>\uff12\uff09\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u306b\u30bf\u30a4\u30e0\u30ec\u30b3\u30fc\u30c0\u30fc\u753b\u9762\u3092\u5b9f\u88c5<\/h3>\n<p><a href=\"https:\/\/www.wetch.co.jp\/note\/\u30d0\u30c3\u30af\u30a8\u30f3\u30c9\u306e\u4eba\u304cnativephp\u306e\u529b\u3092\u501f\u308a\u3066\u521d\u3081\u3066\u30e2\u30d0\u30a4\/\">\u524d\u56de\u306e\u6295\u7a3f<\/a> \u3067\u306f\u3001Home \u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u3067 Welcome \u753b\u9762\u3092\u8868\u793a\u3057\u307e\u3057\u305f\u3002\u3053\u3053\u3067\u306f\u540c\u3058\u4ed5\u7d44\u307f\u306e\u307e\u307e\u3001\u8d77\u52d5\u76f4\u5f8c\u306e\u753b\u9762\u3092\u30bf\u30a4\u30e0\u30ec\u30b3\u30fc\u30c0\u30fc\u306b\u5dee\u3057\u66ff\u3048\u307e\u3059\u3002<\/p>\n\n\n<pre class=\"wp-block-code\"><code>$ php artisan native:make Timecard<\/code><\/pre>\n\n\n<p>\u30b3\u30de\u30f3\u30c9\u5b9f\u884c\u3067\u751f\u6210\u3055\u308c\u308b\u306e\u306f\u4ee5\u4e0b\u306e NativeComponent \u3067\u3059\u3002<\/p>\n<ul>\n<li>app\/NativeComponents\/Timecard.php : \u753b\u9762\u30ed\u30b8\u30c3\u30af\u3092\u6301\u3064 NativeComponent \u30af\u30e9\u30b9<\/li>\n<li>resources\/views\/native\/timecard.blade.php : SuperNative \u7528\u306e Blade<\/li>\n<\/ul>\n\n\n<pre class=\"wp-block-code\"><code>app\/NativeComponents\/Timecard.php\n\u203b \u63b2\u8f09\u3057\u3066\u3044\u308b\u306e\u306f\u6700\u7d42\u7684\u306a\u5b9f\u88c5\u3067\u3059\u3002API \u9023\u643a\u3084\u534a\u5f84\u5224\u5b9a\u306a\u3069\u3001\u5404\u51e6\u7406\u306e\u8aac\u660e\u306f\u5f8c\u8ff0\u3057\u307e\u3059\u3002\n\n&lt;?php\n\nnamespace App\\NativeComponents;\n\nuse App\\Services\\AttendanceApiClient;\nuse App\\Services\\GeofenceService;\nuse Illuminate\\View\\View;\nuse Local\\SimpleGeolocation\\Facades\\SimpleGeolocation;\nuse Native\\Mobile\\Edge\\NativeComponent;\nuse RuntimeException;\nuse Throwable;\n\nclass Timecard extends NativeComponent\n{\n    public string $employeeName = '\u78ba\u8a8d\u4e2d\u2026';\n\n    public bool $employeeError = false;\n\n    public string $status = '\u5f85\u6a5f\u4e2d\u3067\u3059\u3002\u30dc\u30bf\u30f3\u3092\u62bc\u3057\u3066\u304f\u3060\u3055\u3044\u3002';\n\n    public string $statusKind = 'muted';\n\n    public string $dayHint = '';\n\n    public string $debugLine = '';\n\n    public bool $busy = false;\n\n    public bool $clockInEnabled = true;\n\n    public bool $clockOutEnabled = false;\n\n    public bool $dayComplete = false;\n\n    public bool $hasClockIn = false;\n\n    public bool $hasClockOut = false;\n\n    public bool $useManual = false;\n\n    public string $manualLat = '';\n\n    public string $manualLng = '';\n\n    public float $companyLat = 0.0;\n\n    public float $companyLng = 0.0;\n\n    public float $radiusMeters = 10.0;\n\n    public string $apiUrl = '';\n\n    \/** @var 'clock_in'|'clock_out'|null *\/\n    public ?string $pendingType = null;\n\n    public function mount(): void\n    {\n        $this-&gt;companyLat = (float) config('attendance.company_lat');\n        $this-&gt;companyLng = (float) config('attendance.company_lng');\n        $this-&gt;radiusMeters = (float) config('attendance.geofence_radius_meters', 10);\n        $this-&gt;apiUrl = (string) config('attendance.api_url');\n        $this-&gt;manualLat = (string) $this-&gt;companyLat;\n        $this-&gt;manualLng = (string) $this-&gt;companyLng;\n        $this-&gt;refreshDebug();\n        $this-&gt;loadProfile();\n        $this-&gt;loadToday();\n    }\n\n    public function clockIn(): void\n    {\n        $this-&gt;beginPunch('clock_in');\n    }\n\n    public function clockOut(): void\n    {\n        $this-&gt;beginPunch('clock_out');\n    }\n\n    public function beginPunch(string $type): void\n    {\n        if ($this-&gt;busy) {\n            return;\n        }\n\n        if ($type === 'clock_in' &amp;&amp; ! $this-&gt;clockInEnabled) {\n            return;\n        }\n\n        if ($type === 'clock_out' &amp;&amp; ! $this-&gt;clockOutEnabled) {\n            return;\n        }\n\n        $this-&gt;busy = true;\n        $this-&gt;pendingType = $type;\n        $this-&gt;setStatus('\u4f4d\u7f6e\u60c5\u5831\u3092\u53d6\u5f97\u3057\u3066\u3044\u307e\u3059\u2026');\n\n        if ($this-&gt;useManual) {\n            $lat = (float) $this-&gt;manualLat;\n            $lng = (float) $this-&gt;manualLng;\n\n            if ($this-&gt;manualLat === '' || $this-&gt;manualLng === '' || ! is_finite($lat) || ! is_finite($lng)) {\n                $this-&gt;finishWithError('\u624b\u52d5\u5ea7\u6a19\u304c\u4e0d\u6b63\u3067\u3059\u3002');\n\n                return;\n            }\n\n            $this-&gt;submitPunch($type, $lat, $lng, 0.0);\n\n            return;\n        }\n\n        try {\n            SimpleGeolocation::getCurrentPosition(true)\n                -&gt;id('timecard-punch')\n                -&gt;locationReceived(function ($event) {\n                    if (! ($event-&gt;success ?? false)) {\n                        $this-&gt;finishWithError(\n                            $this-&gt;geolocationErrorMessage($event-&gt;error ?? null)\n                        );\n\n                        return;\n                    }\n\n                    $type = $this-&gt;pendingType;\n\n                    if ($type === null) {\n                        $this-&gt;finishWithError('\u6253\u523b\u7a2e\u5225\u304c\u4e0d\u660e\u3067\u3059\u3002');\n\n                        return;\n                    }\n\n                    $this-&gt;submitPunch(\n                        $type,\n                        (float) $event-&gt;latitude,\n                        (float) $event-&gt;longitude,\n                        isset($event-&gt;accuracy) ? (float) $event-&gt;accuracy : null,\n                    );\n                });\n        } catch (Throwable $e) {\n            report($e);\n            $this-&gt;finishWithError(\n                '\u7aef\u672b\u306e\u4f4d\u7f6e\u60c5\u5831API\u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002\u624b\u52d5\u5ea7\u6a19\u3092\u4f7f\u3046\u304b\u3001simple-geolocation \u30d7\u30e9\u30b0\u30a4\u30f3\u306e\u5c0e\u5165\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002'\n            );\n        }\n    }\n\n    private function submitPunch(string $type, float $latitude, float $longitude, ?float $accuracy): void\n    {\n        $this-&gt;setStatus('\u6253\u523b\u3092\u9001\u4fe1\u3057\u3066\u3044\u307e\u3059\u2026');\n\n        \/** @var GeofenceService $geofence *\/\n        $geofence = app(GeofenceService::class);\n        $check = $geofence-&gt;isWithinRadius($latitude, $longitude);\n\n        $this-&gt;debugLine = sprintf(\n            '\u8ddd\u96e2: %s m \/ \u534a\u5f84: %s m \/ \u7cbe\u5ea6: %s m \/ \u7def\u5ea6\u7d4c\u5ea6: %s, %s',\n            $check&#91;'distance_meters'],\n            $check&#91;'radius_meters'],\n            $accuracy ?? '-',\n            $latitude,\n            $longitude,\n        );\n\n        if (! $check&#91;'allowed']) {\n            $this-&gt;finishWithError('\u51fa\u793e\u306e\u78ba\u8a8d\u304c\u3067\u304d\u307e\u305b\u3093');\n\n            return;\n        }\n\n        try {\n            \/** @var AttendanceApiClient $api *\/\n            $api = app(AttendanceApiClient::class);\n            $result = $api-&gt;punch($type, $latitude, $longitude, $accuracy);\n            $this-&gt;applyToday($result&#91;'data']&#91;'today'] ?? null);\n            $this-&gt;setStatus('\u6253\u523b\u3057\u307e\u3057\u305f', 'ok');\n        } catch (RuntimeException $e) {\n            $this-&gt;setStatus($e-&gt;getMessage(), 'err');\n            $this-&gt;loadToday();\n        } catch (Throwable $e) {\n            report($e);\n            $this-&gt;setStatus('\u6253\u523b\u51e6\u7406\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002', 'err');\n            $this-&gt;loadToday();\n        } finally {\n            $this-&gt;busy = false;\n            $this-&gt;pendingType = null;\n        }\n    }\n\n    private function loadProfile(): void\n    {\n        try {\n            \/** @var AttendanceApiClient $api *\/\n            $api = app(AttendanceApiClient::class);\n            $employee = $api-&gt;profile();\n            $name = $employee&#91;'name'] ?? null;\n\n            if (! is_string($name) || $name === '') {\n                throw new RuntimeException('\u5229\u7528\u8005\u60c5\u5831\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3002');\n            }\n\n            $this-&gt;employeeName = $name;\n            $this-&gt;employeeError = false;\n        } catch (RuntimeException $e) {\n            $this-&gt;employeeName = $e-&gt;getMessage();\n            $this-&gt;employeeError = true;\n        } catch (Throwable $e) {\n            report($e);\n            $this-&gt;employeeName = '\u5229\u7528\u8005\u60c5\u5831\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3002';\n            $this-&gt;employeeError = true;\n        }\n    }\n\n    private function loadToday(): void\n    {\n        try {\n            \/** @var AttendanceApiClient $api *\/\n            $api = app(AttendanceApiClient::class);\n            $this-&gt;applyToday($api-&gt;today());\n        } catch (RuntimeException $e) {\n            $this-&gt;dayHint = $e-&gt;getMessage();\n            $this-&gt;clockInEnabled = false;\n            $this-&gt;clockOutEnabled = false;\n        } catch (Throwable $e) {\n            report($e);\n            $this-&gt;dayHint = '\u672c\u65e5\u306e\u6253\u523b\u72b6\u614b\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3002';\n            $this-&gt;clockInEnabled = false;\n            $this-&gt;clockOutEnabled = false;\n        }\n    }\n\n    private function applyToday(?array $payload): void\n    {\n        if ($payload === null) {\n            return;\n        }\n\n        $this-&gt;clockInEnabled = (bool) ($payload&#91;'clock_in_enabled'] ?? false);\n        $this-&gt;clockOutEnabled = (bool) ($payload&#91;'clock_out_enabled'] ?? false);\n        $this-&gt;dayComplete = (bool) ($payload&#91;'day_complete'] ?? false);\n        $this-&gt;hasClockIn = (bool) ($payload&#91;'has_clock_in'] ?? false);\n        $this-&gt;hasClockOut = (bool) ($payload&#91;'has_clock_out'] ?? false);\n\n        if ($this-&gt;dayComplete) {\n            $this-&gt;dayHint = '\u672c\u65e5\u306e\u51fa\u52e4\u30fb\u9000\u52e4\u306f\u5b8c\u4e86\u3057\u3066\u3044\u307e\u3059\u3002';\n        } elseif ($this-&gt;hasClockIn) {\n            $this-&gt;dayHint = '\u51fa\u52e4\u6e08\u307f\u3067\u3059\u3002\u9000\u52e4\u30dc\u30bf\u30f3\u3067\u9000\u52e4\u6253\u523b\u3067\u304d\u307e\u3059\u3002';\n        } else {\n            $this-&gt;dayHint = '\u672c\u65e5\u307e\u3060\u51fa\u52e4\u3057\u3066\u3044\u307e\u305b\u3093\u3002';\n        }\n    }\n\n    private function setStatus(string $message, string $kind = 'muted'): void\n    {\n        $this-&gt;status = $message;\n        $this-&gt;statusKind = $kind;\n    }\n\n    private function finishWithError(string $message): void\n    {\n        $this-&gt;setStatus($message, 'err');\n        $this-&gt;busy = false;\n        $this-&gt;pendingType = null;\n    }\n\n    private function refreshDebug(): void\n    {\n        $this-&gt;debugLine = sprintf(\n            '\u4f1a\u793e\u5ea7\u6a19: %s, %s \/ \u534a\u5f84 %s m \/ API: %s',\n            $this-&gt;companyLat,\n            $this-&gt;companyLng,\n            $this-&gt;radiusMeters,\n            $this-&gt;apiUrl,\n        );\n    }\n\n    private function geolocationErrorMessage(?string $error): string\n    {\n        if (is_string($error) &amp;&amp; $error !== '') {\n            return $error;\n        }\n\n        return '\u4f4d\u7f6e\u60c5\u5831\u306e\u53d6\u5f97\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002\u624b\u52d5\u5ea7\u6a19\u3092\u4f7f\u3046\u304b\u3001\u7aef\u672b\u306e\u4f4d\u7f6e\u60c5\u5831\u8a2d\u5b9a\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002';\n    }\n\n    public function render(): View\n    {\n        return view('native.timecard');\n    }\n}\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>resources\/views\/native\/timecard.blade.php\n\u203b \u4ee5\u4e0b\u306f\u6700\u7d42\u7684\u306a\u753b\u9762\u5b9f\u88c5\u3067\u3059\u3002\n\n&lt;native:scroll-view class=\"safe-area bg-theme-background\"&gt;\n    &lt;native:column class=\"w-full p-5 gap-4\"&gt;\n        &lt;native:text class=\"text-2xl font-extrabold text-theme-on-background\"&gt;\u7c21\u6613\u30bf\u30a4\u30e0\u30ec\u30b3\u30fc\u30c0\u30fc&lt;\/native:text&gt;\n        &lt;native:text class=\"text-sm text-theme-on-surface-variant\"&gt;\n            \u51fa\u52e4\u30fb\u9000\u52e4\u30dc\u30bf\u30f3\u3067\u4f4d\u7f6e\u3092\u78ba\u8a8d\u3057\u3001\u4f1a\u793e\u304b\u3089\u534a\u5f84 {{ $radiusMeters }}m \u4ee5\u5185\u306a\u3089\u6253\u523b\u3057\u307e\u3059\u3002\n        &lt;\/native:text&gt;\n\n        &lt;native:column class=\"w-full bg-theme-surface rounded-2xl p-4 gap-1\"&gt;\n            &lt;native:text class=\"text-xs text-theme-on-surface-variant\"&gt;\u73fe\u5728\u306e\u5229\u7528\u8005&lt;\/native:text&gt;\n            &lt;native:text class=\"text-lg font-bold {{ $employeeError ? 'text-red-600' : 'text-theme-on-surface' }}\"&gt;\n                {{ $employeeName }}\n            &lt;\/native:text&gt;\n        &lt;\/native:column&gt;\n\n        &lt;native:column class=\"w-full bg-theme-surface rounded-2xl p-4 gap-3\"&gt;\n            &lt;native:pressable\n                @tap=\"clockIn\"\n                class=\"w-full rounded-xl bg-teal-700 py-4 items-center justify-center {{ ($busy || ! $clockInEnabled) ? 'opacity-40' : '' }}\"\n            &gt;\n                &lt;native:text class=\"text-lg font-bold text-white\"&gt;\n                    {{ ($busy &amp;&amp; $pendingType === 'clock_in') ? '\u51e6\u7406\u4e2d\u2026' : '\u51fa\u52e4' }}\n                &lt;\/native:text&gt;\n            &lt;\/native:pressable&gt;\n\n            &lt;native:pressable\n                @tap=\"clockOut\"\n                class=\"w-full rounded-xl bg-slate-600 py-4 items-center justify-center {{ ($busy || ! $clockOutEnabled) ? 'opacity-40' : '' }}\"\n            &gt;\n                &lt;native:text class=\"text-lg font-bold text-white\"&gt;\n                    {{ ($busy &amp;&amp; $pendingType === 'clock_out') ? '\u51e6\u7406\u4e2d\u2026' : '\u9000\u52e4' }}\n                &lt;\/native:text&gt;\n            &lt;\/native:pressable&gt;\n\n            &lt;native:column class=\"w-full rounded-xl p-3 gap-1 {{ $statusKind === 'ok' ? 'bg-green-100' : ($statusKind === 'err' ? 'bg-red-100' : 'bg-theme-surface-variant') }}\"&gt;\n                &lt;native:text class=\"text-sm {{ $statusKind === 'ok' ? 'text-green-800' : ($statusKind === 'err' ? 'text-red-800' : 'text-theme-on-surface-variant') }}\"&gt;\n                    {{ $status }}\n                &lt;\/native:text&gt;\n            &lt;\/native:column&gt;\n\n            &lt;native:text class=\"text-xs text-theme-on-surface-variant\"&gt;{{ $dayHint }}&lt;\/native:text&gt;\n            &lt;native:text class=\"text-xs text-theme-on-surface-variant\"&gt;{{ $debugLine }}&lt;\/native:text&gt;\n        &lt;\/native:column&gt;\n\n        &lt;native:column class=\"w-full bg-theme-surface rounded-2xl p-4 gap-3\"&gt;\n            &lt;native:text class=\"text-sm font-semibold text-theme-on-surface\"&gt;\u691c\u8a3c\u7528\uff1a\u4f4d\u7f6e\u3092\u624b\u52d5\u6307\u5b9a&lt;\/native:text&gt;\n            &lt;native:text class=\"text-xs text-theme-on-surface-variant\"&gt;\n                \u30aa\u30d5\u306b\u3059\u308b\u3068\u7aef\u672b GPS\uff08simple-geolocation \u30d7\u30e9\u30b0\u30a4\u30f3\uff09\u3092\u4f7f\u3044\u307e\u3059\u3002\n            &lt;\/native:text&gt;\n\n            &lt;native:checkbox native:model=\"useManual\" label=\"\u624b\u52d5\u5ea7\u6a19\u3092\u4f7f\u3046\" \/&gt;\n\n            &lt;native:outlined-text-input\n                label=\"\u7def\u5ea6\"\n                native:model=\"manualLat\"\n                keyboard-type=\"decimal\"\n            \/&gt;\n            &lt;native:outlined-text-input\n                label=\"\u7d4c\u5ea6\"\n                native:model=\"manualLng\"\n                keyboard-type=\"decimal\"\n            \/&gt;\n        &lt;\/native:column&gt;\n    &lt;\/native:column&gt;\n&lt;\/native:scroll-view&gt;<\/code><\/pre>\n\n\n<p>\u6b21\u306b\u3001\u30eb\u30fc\u30c8\u3092 Welcome \u753b\u9762\u304b\u3089 Timecard \u306b\u5909\u66f4\u3057\u307e\u3059\u3002<\/p>\n\n\n<pre class=\"wp-block-code\"><code>routes\/mobile.php\n\n&lt;?php\n\nuse App\\NativeComponents\\Timecard;\nuse Illuminate\\Support\\Facades\\Route;\n\nRoute::native('\/', Timecard::class)-&gt;name('timecard.native');\n<\/code><\/pre>\n\n\n<p>bootstrap\/app.php \u3067 routes\/mobile.php \u3092\u8aad\u307f\u8fbc\u3093\u3067\u3044\u308b\u70b9\u306f\u524d\u56de\u306e\u6295\u7a3f\u304b\u3089\u5909\u66f4\u3042\u308a\u307e\u305b\u3093\u3002<\/p>\n<p>\u30bf\u30a4\u30e0\u30ec\u30b3\u30fc\u30c0\u30fc\u753b\u9762\u306e\u8981\u4ef6\u306f\u4ee5\u4e0b\u306e\u901a\u308a\u3067\u3059\u3002<\/p>\n<ul>\n<li class=\"ui-markdown__list-item ui--default-marker ui-exx8yu ui-1xpa7k ui-18d9i69 ui-1uhho1l ui-13faqbe ui-ez2fb\" data-md-list-item=\"\">\u5229\u7528\u8005\u540d\u306e\u8868\u793a<\/li>\n<li class=\"ui-markdown__list-item ui--default-marker ui-exx8yu ui-1xpa7k ui-18d9i69 ui-1uhho1l ui-13faqbe ui-ez2fb\" data-md-list-item=\"\">\u51fa\u52e4\uff0f\u9000\u52e4\u30dc\u30bf\u30f3<\/li>\n<li class=\"ui-markdown__list-item ui--default-marker ui-exx8yu ui-1xpa7k ui-18d9i69 ui-1uhho1l ui-13faqbe ui-ez2fb\" data-md-list-item=\"\">\u51e6\u7406\u7d50\u679c\u306e\u30b9\u30c6\u30fc\u30bf\u30b9\u8868\u793a<\/li>\n<li class=\"ui-markdown__list-item ui--default-marker ui-exx8yu ui-1xpa7k ui-18d9i69 ui-1uhho1l ui-13faqbe ui-ez2fb\" data-md-list-item=\"\">\u691c\u8a3c\u7528\u306e\u300c\u624b\u52d5\u5ea7\u6a19\u300d\u5165\u529b\uff08\u30c1\u30a7\u30c3\u30af\u30dc\u30c3\u30af\u30b9 + \u7def\u5ea6\u30fb\u7d4c\u5ea6\uff09<\/li>\n<\/ul>\n<p>UI \u306f HTML\/JS \u3067\u306f\u306a\u304f\u3001EDGE \u306e\u30cd\u30a4\u30c6\u30a3\u30d6\u90e8\u54c1\u3067\u7d44\u307f\u7acb\u3066\u3066\u3044\u307e\u3059\u3002\u51fa\u52e4\u30fb\u9000\u52e4\u306f native:pressable\u3001\u624b\u52d5\u5ea7\u6a19\u306f native:checkbox \u3068 native:outlined-text-input \u3092\u4f7f\u3063\u3066\u3044\u307e\u3059\u3002@tap=&#8221;clockIn&#8221; \/ @tap=&#8221;clockOut&#8221; \u3067\u3001\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u5074\u306e\u30e1\u30bd\u30c3\u30c9\u306b\u63a5\u7d9a\u3057\u3066\u3044\u307e\u3059\u3002<\/p>\n<p>\u5148\u307b\u3069\u306e\u30bd\u30fc\u30b9\u3092\u53cd\u6620\u3057\u305f\u753b\u9762\u304c\u6b21\u3067\u3059\u3002\u4e0a\u90e8\u306b\u306f\u30bf\u30a4\u30c8\u30eb\u3001\u5229\u7528\u8005\u540d\u3001\u51fa\u52e4\uff0f\u9000\u52e4\u30dc\u30bf\u30f3\u3001\u30b9\u30c6\u30fc\u30bf\u30b9\u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002<\/p>\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"412\" height=\"668\" src=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-05-02.png\" alt=\"\" class=\"wp-image-9345\" srcset=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-05-02.png 412w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-05-02-185x300.png 185w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-05-02-150x243.png 150w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-05-02-300x486.png 300w\" sizes=\"auto, (max-width: 412px) 100vw, 412px\" \/><\/figure>\n\n\n<div>\n<div>\u4e0b\u306b\u30b9\u30af\u30ed\u30fc\u30eb\u3059\u308b\u3068\u3001\u691c\u8a3c\u7528\u306e\u300c\u624b\u52d5\u5ea7\u6a19\u3092\u4f7f\u3046\u300d\u30c1\u30a7\u30c3\u30af\u3068\u7def\u5ea6\u30fb\u7d4c\u5ea6\u306e\u5165\u529b\u304c\u3042\u308a\u307e\u3059\u3002\u4eca\u56de\u306e\u78ba\u8a8d\u3067\u306f\u3001\u3053\u3053\u3092 ON \u306b\u3057\u3066\u534a\u5f84\u5185\uff0f\u5916\u3092\u518d\u73fe\u3057\u307e\u3059\u3002<\/div>\n<\/div>\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"406\" height=\"668\" src=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-06-01.png\" alt=\"\" class=\"wp-image-9346\" srcset=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-06-01.png 406w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-06-01-182x300.png 182w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-06-01-150x247.png 150w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-06-01-300x494.png 300w\" sizes=\"auto, (max-width: 406px) 100vw, 406px\" \/><\/figure>\n\n\n<p>\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\uff08 Timecard \uff09\u3067\u306f\u3001\u5229\u7528\u8005\u540d\u3084\u30b9\u30c6\u30fc\u30bf\u30b9\u3001\u624b\u52d5\u5ea7\u6a19\u306e ON\/OFF \u306a\u3069\u3092\u30d7\u30ed\u30d1\u30c6\u30a3\u3068\u3057\u3066\u6301\u3061\u3001Blade \u304b\u3089\u53c2\u7167\u30fb\u66f4\u65b0\u3057\u307e\u3059\u3002<\/p>\n<p>\u6253\u523b API \u3078\u306e\u9001\u4fe1\u3084\u534a\u5f84\u5224\u5b9a\u306a\u3069\u3001\u30e1\u30bd\u30c3\u30c9\u306e\u4e2d\u8eab\u306f API \u5074\u306e\u6e96\u5099\u306e\u3042\u3068\u306b\u8aac\u660e\u3057\u307e\u3059\u3002<\/p>\n<p>\u5909\u66f4\u3092\u30a8\u30df\u30e5\u30ec\u30fc\u30bf\u306b\u53cd\u6620\u3059\u308b\u3068\u304d\u306f\u3001\u524d\u56de\u3068\u540c\u69d8\u306b\u6b21\u3092\u5b9f\u884c\u3057\u307e\u3059\u3002<\/p>\n\n\n<pre class=\"wp-block-code\"><code>$ php artisan native:run android --no-interaction --no-tty<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"888\" height=\"614\" src=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-05-01.png\" alt=\"\" class=\"wp-image-9352\" srcset=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-05-01.png 888w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-05-01-300x207.png 300w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-05-01-768x531.png 768w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-05-01-150x104.png 150w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-05-01-218x150.png 218w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-05-01-696x481.png 696w\" sizes=\"auto, (max-width: 888px) 100vw, 888px\" \/><\/figure>\n\n\n<h3>\uff13\uff09\u6253\u523b API \u3092 Laravel Sail \u3067\u7528\u610f\u3059\u308b<\/h3>\n<div>\n<div>\u30e2\u30d0\u30a4\u30eb\u5074\u306e\u753b\u9762\u304c\u3067\u304d\u305f\u3089\u3001\u6b21\u306f\u6253\u523b\u7d50\u679c\u3092\u53d7\u3051\u53d6\u308b\u5074\u3067\u3059\u3002\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u672c\u4f53\u3068\u306f\u5225\u306b Laravel \u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3092\u4f5c\u308a\u3001Docker Desktop + Laravel Sail \u3067\u52d5\u304b\u3057\u307e\u3059\u3002<\/div>\n<\/p>\n<div>\u307e\u305a\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3092\u4f5c\u6210\u3057\u3001API \u7528\u306e\u30eb\u30fc\u30c8\u3068 Sail\uff08 MySQL \uff09\u3092\u5165\u308c\u307e\u3059\u3002<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\"><code>$ cd \/Volumes\/\u5916\u4ed8\u3051SSD\/share\n$ composer create-project laravel\/laravel attendance-api --prefer-dist --no-interaction\n$ cd attendance-api\n$ composer require laravel\/sail --dev --no-interaction\n$ php artisan sail:install --with=mysql --no-interaction<\/code><\/pre>\n\n\n<div>\n<div>attendance-api \u306e .env \u306b\u30dd\u30fc\u30c8\u3068\u691c\u8a3c\u7528\u30c8\u30fc\u30af\u30f3\u306a\u3069\u3092\u8a2d\u5b9a\u3057\u307e\u3059\u3002<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\"><code>APP_PORT=8080\nAPP_URL=http:\/\/localhost:8080\n\nATTENDANCE_API_TOKEN=dev-attendance-token\nATTENDANCE_TEST_EMPLOYEE_CODE=TEST001<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"886\" height=\"614\" src=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/20260804\u8ffd\u52a0.png\" alt=\"\" class=\"wp-image-9347\" srcset=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/20260804\u8ffd\u52a0.png 886w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/20260804\u8ffd\u52a0-300x208.png 300w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/20260804\u8ffd\u52a0-768x532.png 768w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/20260804\u8ffd\u52a0-150x104.png 150w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/20260804\u8ffd\u52a0-218x150.png 218w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/20260804\u8ffd\u52a0-696x482.png 696w\" sizes=\"auto, (max-width: 886px) 100vw, 886px\" \/><\/figure>\n\n\n<p>\u6b21\u306b\u3001\u6253\u523b API \u3067\u4f7f\u7528\u3059\u308b DB \u306e\u30c6\u30fc\u30d6\u30eb\u306b\u3064\u3044\u3066\u8aac\u660e\u3057\u307e\u3059\u3002\u6253\u523b API \u3067\u306f\u3001\u6b21\u306e2\u3064\u306e\u30c6\u30fc\u30d6\u30eb\u3092\u4f7f\u3044\u307e\u3059\u3002<\/p>\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>\u30c6\u30fc\u30d6\u30eb<\/td><td>\u5f79\u5272<\/td><\/tr><tr><td>employees<\/td><td>\u5229\u7528\u8005\uff08\u793e\u54e1\u30b3\u30fc\u30c9\u30fb\u6c0f\u540d\u306a\u3069\uff09<\/td><\/tr><tr><td>attendances<\/td><td>\u51fa\u52e4\uff0f\u9000\u52e4\u306e1\u4ef6\u3054\u3068\u306e\u8a18\u9332\uff08\u7a2e\u5225\u30fb\u7def\u5ea6\u7d4c\u5ea6\u30fb\u6253\u523b\u6642\u523b\uff09<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n<p>\u5229\u7528\u8005\u306f\u8907\u6570\u306e\u6253\u523b\u3092\u6301\u3061\u307e\u3059\uff08 Employee hasMany Attendance \uff09\u3002\u6253\u523b\u306b\u306f employee_id \u3067\u5229\u7528\u8005\u3092\u7d10\u3065\u3051\u307e\u3059\u3002<\/p>\n<p>employees \u306b\u306f\u793e\u54e1\u30b3\u30fc\u30c9\uff08employee_code\u30fb\u4e00\u610f\uff09\u3001\u6c0f\u540d\u3001\u6709\u52b9\u30d5\u30e9\u30b0\u3092\u6301\u3061\u307e\u3059\u3002<\/p>\n<p>attendances \u306b\u306f\u7a2e\u5225\uff08clock_in \/ clock_out\uff09\u3001\u7def\u5ea6\u30fb\u7d4c\u5ea6\u3001\u7cbe\u5ea6\uff08\u4efb\u610f\uff09\u3001\u6253\u523b\u6642\u523b\uff08punched_at\uff09\u3092\u6301\u3061\u307e\u3059\u3002<\/p>\n<p>\u4ee5\u4e0b\u306b\u3001Model \u3068 migration \u306e\u30bd\u30fc\u30b9\u3092\u63b2\u8f09\u3057\u307e\u3059\u3002<\/p>\n\n\n<pre class=\"wp-block-code\"><code>app\/Models\/Employee.php\n\n&lt;?php\n\nnamespace App\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Database\\Eloquent\\Relations\\HasMany;\n\nclass Employee extends Model\n{\n    protected $fillable = &#91;\n        'employee_code',\n        'name',\n        'is_active',\n    ];\n\n    protected function casts(): array\n    {\n        return &#91;\n            'is_active' =&gt; 'boolean',\n        ];\n    }\n\n    public function attendances(): HasMany\n    {\n        return $this-&gt;hasMany(Attendance::class);\n    }\n}<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>app\/Models\/Attendance.php\n\n&lt;?php\n\nnamespace App\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Database\\Eloquent\\Relations\\BelongsTo;\n\nclass Attendance extends Model\n{\n    protected $fillable = &#91;\n        'employee_id',\n        'type',\n        'latitude',\n        'longitude',\n        'accuracy',\n        'punched_at',\n    ];\n\n    protected function casts(): array\n    {\n        return &#91;\n            'latitude' =&gt; 'float',\n            'longitude' =&gt; 'float',\n            'accuracy' =&gt; 'float',\n            'punched_at' =&gt; 'datetime',\n        ];\n    }\n\n    public function employee(): BelongsTo\n    {\n        return $this-&gt;belongsTo(Employee::class);\n    }\n}<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>database\/migrations\/XXXX_XX_XX_000000_create_employees_table.php\n\n&lt;?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Facades\\Schema;\n\nreturn new class extends Migration\n{\n    public function up(): void\n    {\n        Schema::create('employees', function (Blueprint $table) {\n            $table-&gt;id();\n            $table-&gt;string('employee_code')-&gt;unique();\n            $table-&gt;string('name');\n            $table-&gt;boolean('is_active')-&gt;default(true);\n            $table-&gt;timestamps();\n        });\n    }\n\n    public function down(): void\n    {\n        Schema::dropIfExists('employees');\n    }\n};<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>database\/migrations\/XXXX_XX_XX_000001_create_attendances_table.php\n\n&lt;?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Facades\\Schema;\n\nreturn new class extends Migration\n{\n    public function up(): void\n    {\n        Schema::create('attendances', function (Blueprint $table) {\n            $table-&gt;id();\n            $table-&gt;string('type', 32); \/\/ clock_in | clock_out\n            $table-&gt;decimal('latitude', 10, 7);\n            $table-&gt;decimal('longitude', 10, 7);\n            $table-&gt;decimal('accuracy', 8, 2)-&gt;nullable();\n            $table-&gt;timestamp('punched_at');\n            $table-&gt;timestamps();\n        });\n    }\n\n    public function down(): void\n    {\n        Schema::dropIfExists('attendances');\n    }\n};<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>database\/migrations\/XXXX_XX_XX_000002_add_employee_id_to_attendances_table.php\n\n&lt;?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Facades\\Schema;\n\nreturn new class extends Migration\n{\n    public function up(): void\n    {\n        Schema::table('attendances', function (Blueprint $table) {\n            $table-&gt;foreignId('employee_id')\n                -&gt;nullable()\n                -&gt;after('id')\n                -&gt;constrained()\n                -&gt;nullOnDelete();\n        });\n    }\n\n    public function down(): void\n    {\n        Schema::table('attendances', function (Blueprint $table) {\n            $table-&gt;dropConstrainedForeignId('employee_id');\n        });\n    }\n};<\/code><\/pre>\n\n\n<p>\u7d9a\u3051\u3066 Docker \u30b3\u30f3\u30c6\u30ca\u3092\u8d77\u52d5\u3057\u3001\u30de\u30a4\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3068\u30b7\u30fc\u30c0\u30fc\u3092\u6d41\u3057\u307e\u3059\u3002<\/p>\n<p>\u30b7\u30fc\u30c0\u30fc\u3067\u306f\u3001\u691c\u8a3c\u7528\u306e\u5229\u7528\u8005\u300c\u30c6\u30b9\u30c8\u4e00\u90ce\u300d\uff08\u793e\u54e1\u30b3\u30fc\u30c9 TEST001 \uff09\u3092\u767b\u9332\u3057\u307e\u3059\u3002<\/p>\n\n\n<pre class=\"wp-block-code\"><code>database\/seeders\/DatabaseSeeder.php\n\n&lt;?php\n\nnamespace Database\\Seeders;\n\nuse App\\Models\\Employee;\nuse Illuminate\\Database\\Console\\Seeds\\WithoutModelEvents;\nuse Illuminate\\Database\\Seeder;\n\nclass DatabaseSeeder extends Seeder\n{\n    use WithoutModelEvents;\n\n    \/**\n     * Seed the application's database.\n     *\/\n    public function run(): void\n    {\n        Employee::query()-&gt;updateOrCreate(&#91;\n            'employee_code' =&gt; 'TEST001',\n        ], &#91;\n            'name' =&gt; '\u30c6\u30b9\u30c8\u4e00\u90ce',\n            'is_active' =&gt; true,\n        ]);\n    }\n}<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>$ .\/vendor\/bin\/sail up -d\n$ .\/vendor\/bin\/sail artisan migrate --seed<\/code><\/pre>\n\n\n<p>\u4eca\u56de\u306e\u691c\u8a3c\u3067\u306f\u3001\u30ed\u30b0\u30a4\u30f3\u3084\u7aef\u672b\u3054\u3068\u306e\u5229\u7528\u8005\u767b\u9332\u306f\u307e\u3060\u5165\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u305d\u306e\u305f\u3081 API \u5074\u306f\u3001\u30c8\u30fc\u30af\u30f3\u3067\u4fdd\u8b77\u3057\u305f\u3046\u3048\u3067\u3001\u6253\u523b\u306e\u8a18\u9332\u306f\u3001\u3053\u306e\u30c6\u30b9\u30c8\u5229\u7528\u8005\u306b\u7d10\u3065\u304f\u5f62\u3067\u6b8b\u3057\u3066\u3044\u307e\u3059\u3002\u30a2\u30d7\u30ea\u306e\u300c\u73fe\u5728\u306e\u5229\u7528\u8005\u300d\u306b\u8868\u793a\u3055\u308c\u308b\u540d\u524d\u3082\u3001\u3053\u3053\u304b\u3089\u53d6\u5f97\u3057\u3066\u3044\u307e\u3059\u3002<\/p>\n<p>\u6253\u523b\u7d50\u679c\u306f \u30d6\u30e9\u30a6\u30b6\u304b\u3089\u78ba\u8a8d\u3067\u304d\u308b\u3088\u3046\u306b\u3057\u3066\u3044\u307e\u3059\u3002\u4eca\u56de\u306e\u691c\u8a3c\u3067\u306f\u30ed\u30b0\u30a4\u30f3\u306a\u3057\u306e\u7c21\u6613\u30da\u30fc\u30b8\u3067\u3001GET \/attendances \u304c\u6253\u523b\u8a18\u9332\u4e00\u89a7\u3067\u3059\u3002<\/p>\n<p>Controller \u3067\u306f\u3001\u6253\u523b\u3092\u65b0\u3057\u3044\u9806\u306b\u53d6\u5f97\u3057\u3001\u5229\u7528\u8005\uff08 employee \uff09\u3092\u4e00\u7dd2\u306b\u8aad\u307f\u8fbc\u307f\u307e\u3059\u3002\u8868\u793a\u306f 20 \u4ef6\u305a\u3064\u30da\u30fc\u30b8\u30cd\u30fc\u30b7\u30e7\u30f3\u3057\u307e\u3059\u3002<\/p>\n\n\n<pre class=\"wp-block-code\"><code>app\/Http\/Controllers\/AttendanceWebController.php\n\n&lt;?php\n\nnamespace App\\Http\\Controllers;\n\nuse App\\Models\\Attendance;\nuse Illuminate\\View\\View;\n\nclass AttendanceWebController extends Controller\n{\n    public function index(): View\n    {\n        $attendances = Attendance::query()\n            -&gt;select(&#91;\n                'id',\n                'employee_id',\n                'type',\n                'latitude',\n                'longitude',\n                'accuracy',\n                'punched_at',\n            ])\n            -&gt;with('employee:id,employee_code,name')\n            -&gt;orderByDesc('punched_at')\n            -&gt;orderByDesc('id')\n            -&gt;paginate(20);\n\n        return view('attendances.index', compact('attendances'));\n    }\n}<\/code><\/pre>\n\n\n<p>\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u306f routes\/web.php\u00a0\u3067\u3059\u3002<\/p>\n\n\n<pre class=\"wp-block-code\"><code>routes\/web.php\n\n&lt;?php\n\nuse App\\Http\\Controllers\\AttendanceWebController;\nuse Illuminate\\Support\\Facades\\Route;\n\nRoute::get('\/', function () {\n    return redirect()-&gt;route('attendances.index');\n});\n\nRoute::get('\/attendances', &#91;AttendanceWebController::class, 'index'])\n    -&gt;name('attendances.index');\n<\/code><\/pre>\n\n\n<p>\u30d3\u30e5\u30fc\u306f resources\/views\/attendances\/index.blade.php \u3067\u3059\u3002<\/p>\n<div>\n<div>\u30ec\u30b3\u30fc\u30c9\u304c\u306a\u3051\u308c\u3070\u300c\u6253\u523b\u8a18\u9332\u306f\u307e\u3060\u3042\u308a\u307e\u305b\u3093\u3002\u300d\u3001\u3042\u308c\u3070\u5f93\u696d\u54e1\u540d\u30fb\u533a\u5206\u30fb\u6253\u523b\u65e5\u6642\uff08JST\uff09\u30fb\u7def\u5ea6\u7d4c\u5ea6\u306a\u3069\u3092\u8868\u5f62\u5f0f\u3067\u51fa\u3057\u307e\u3059\u3002\u65e5\u6642\u306f Asia\/Tokyo \u3067\u6574\u5f62\u3057\u3066\u3044\u307e\u3059\u3002<\/div>\n<\/p>\n<div>\u4ee5\u4e0b\u306b Blade \u306e\u30bd\u30fc\u30b9\u3092\u63b2\u8f09\u3057\u307e\u3059\u3002<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\"><code>resources\/views\/attendances\/index.blade.php\n\n&lt;!DOCTYPE html&gt;\n&lt;html lang=\"ja\"&gt;\n&lt;head&gt;\n    &lt;meta charset=\"utf-8\"&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"&gt;\n    &lt;title&gt;\u6253\u523b\u8a18\u9332\u4e00\u89a7 | {{ config('app.name') }}&lt;\/title&gt;\n    &lt;style&gt;\n        :root {\n            color-scheme: light;\n            font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n            color: #1d2b26;\n            background: #f3f7f5;\n        }\n\n        * {\n            box-sizing: border-box;\n        }\n\n        body {\n            margin: 0;\n        }\n\n        main {\n            width: min(1180px, calc(100% - 32px));\n            margin: 48px auto;\n        }\n\n        h1 {\n            margin: 0;\n            font-size: clamp(1.5rem, 4vw, 2rem);\n        }\n\n        .summary {\n            margin: 8px 0 24px;\n            color: #60706a;\n        }\n\n        .table-wrap {\n            overflow-x: auto;\n            border: 1px solid #d9e3df;\n            border-radius: 14px;\n            background: #fff;\n            box-shadow: 0 10px 30px rgb(31 58 48 \/ 8%);\n        }\n\n        table {\n            width: 100%;\n            min-width: 900px;\n            border-collapse: collapse;\n        }\n\n        th,\n        td {\n            padding: 14px 16px;\n            border-bottom: 1px solid #e8eeeb;\n            text-align: left;\n            white-space: nowrap;\n        }\n\n        th {\n            color: #52635d;\n            background: #f8faf9;\n            font-size: 0.8rem;\n            font-weight: 700;\n        }\n\n        tbody tr:last-child td {\n            border-bottom: 0;\n        }\n\n        tbody tr:hover {\n            background: #f8fbfa;\n        }\n\n        .employee-code,\n        .subtle {\n            color: #71807b;\n            font-size: 0.85rem;\n        }\n\n        .badge {\n            display: inline-block;\n            min-width: 52px;\n            padding: 5px 10px;\n            border-radius: 999px;\n            color: #fff;\n            text-align: center;\n            font-size: 0.8rem;\n            font-weight: 700;\n        }\n\n        .clock-in {\n            background: #27836f;\n        }\n\n        .clock-out {\n            background: #4d6f9d;\n        }\n\n        .empty {\n            padding: 56px 24px;\n            color: #71807b;\n            text-align: center;\n        }\n\n        nav {\n            display: flex;\n            align-items: center;\n            justify-content: space-between;\n            gap: 16px;\n            margin-top: 20px;\n        }\n\n        .page-link {\n            display: inline-block;\n            padding: 10px 16px;\n            border: 1px solid #cbd8d3;\n            border-radius: 9px;\n            color: #236d5d;\n            background: #fff;\n            text-decoration: none;\n        }\n\n        .page-link:hover {\n            background: #eaf4f0;\n        }\n\n        .page-link.disabled {\n            color: #9aa6a2;\n            background: #eef2f0;\n            pointer-events: none;\n        }\n\n        @media (max-width: 600px) {\n            main {\n                width: min(100% - 20px, 1180px);\n                margin: 24px auto;\n            }\n        }\n    &lt;\/style&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n&lt;main&gt;\n    &lt;h1&gt;\u6253\u523b\u8a18\u9332\u4e00\u89a7&lt;\/h1&gt;\n    &lt;p class=\"summary\"&gt;\u5168 {{ number_format($attendances-&gt;total()) }} \u4ef6\uff0f\u6253\u523b\u65e5\u6642\u306e\u65b0\u3057\u3044\u9806\uff08\u65e5\u672c\u6642\u9593\uff09&lt;\/p&gt;\n\n    &lt;div class=\"table-wrap\"&gt;\n        @if ($attendances-&gt;isEmpty())\n            &lt;div class=\"empty\"&gt;\u6253\u523b\u8a18\u9332\u306f\u307e\u3060\u3042\u308a\u307e\u305b\u3093\u3002&lt;\/div&gt;\n        @else\n            &lt;table&gt;\n                &lt;thead&gt;\n                &lt;tr&gt;\n                    &lt;th&gt;ID&lt;\/th&gt;\n                    &lt;th&gt;\u5f93\u696d\u54e1&lt;\/th&gt;\n                    &lt;th&gt;\u533a\u5206&lt;\/th&gt;\n                    &lt;th&gt;\u6253\u523b\u65e5\u6642\uff08JST\uff09&lt;\/th&gt;\n                    &lt;th&gt;\u7def\u5ea6&lt;\/th&gt;\n                    &lt;th&gt;\u7d4c\u5ea6&lt;\/th&gt;\n                    &lt;th&gt;\u7cbe\u5ea6&lt;\/th&gt;\n                &lt;\/tr&gt;\n                &lt;\/thead&gt;\n                &lt;tbody&gt;\n                @foreach ($attendances as $attendance)\n                    &lt;tr&gt;\n                        &lt;td&gt;{{ $attendance-&gt;id }}&lt;\/td&gt;\n                        &lt;td&gt;\n                            &lt;div&gt;{{ $attendance-&gt;employee?-&gt;name ?? '\u524a\u9664\u6e08\u307f\u306e\u5f93\u696d\u54e1' }}&lt;\/div&gt;\n                            @if ($attendance-&gt;employee)\n                                &lt;div class=\"employee-code\"&gt;{{ $attendance-&gt;employee-&gt;employee_code }}&lt;\/div&gt;\n                            @endif\n                        &lt;\/td&gt;\n                        &lt;td&gt;\n                            &lt;span class=\"badge {{ $attendance-&gt;type === 'clock_in' ? 'clock-in' : 'clock-out' }}\"&gt;\n                                {{ $attendance-&gt;type === 'clock_in' ? '\u51fa\u52e4' : '\u9000\u52e4' }}\n                            &lt;\/span&gt;\n                        &lt;\/td&gt;\n                        &lt;td&gt;{{ $attendance-&gt;punched_at?-&gt;timezone('Asia\/Tokyo')-&gt;format('Y\/m\/d H:i:s') ?? '\uff0d' }}&lt;\/td&gt;\n                        &lt;td&gt;{{ number_format($attendance-&gt;latitude, 7) }}&lt;\/td&gt;\n                        &lt;td&gt;{{ number_format($attendance-&gt;longitude, 7) }}&lt;\/td&gt;\n                        &lt;td&gt;\n                            {{ $attendance-&gt;accuracy !== null ? number_format($attendance-&gt;accuracy, 1).' m' : '\uff0d' }}\n                        &lt;\/td&gt;\n                    &lt;\/tr&gt;\n                @endforeach\n                &lt;\/tbody&gt;\n            &lt;\/table&gt;\n        @endif\n    &lt;\/div&gt;\n\n    @if ($attendances-&gt;hasPages())\n        &lt;nav aria-label=\"\u30da\u30fc\u30b8\u79fb\u52d5\"&gt;\n            &lt;a\n                class=\"page-link {{ $attendances-&gt;onFirstPage() ? 'disabled' : '' }}\"\n                href=\"{{ $attendances-&gt;previousPageUrl() ?? '#' }}\"\n            &gt;\u524d\u306e\u30da\u30fc\u30b8&lt;\/a&gt;\n            &lt;span class=\"subtle\"&gt;{{ $attendances-&gt;currentPage() }} \/ {{ $attendances-&gt;lastPage() }} \u30da\u30fc\u30b8&lt;\/span&gt;\n            &lt;a\n                class=\"page-link {{ $attendances-&gt;hasMorePages() ? '' : 'disabled' }}\"\n                href=\"{{ $attendances-&gt;nextPageUrl() ?? '#' }}\"\n            &gt;\u6b21\u306e\u30da\u30fc\u30b8&lt;\/a&gt;\n        &lt;\/nav&gt;\n    @endif\n&lt;\/main&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/code><\/pre>\n\n\n<p>Sail \u8d77\u52d5\u306e\u69d8\u5b50\u3067\u3059\u3002<\/p>\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"889\" height=\"614\" src=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-02-1.png\" alt=\"\" class=\"wp-image-9350\" srcset=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-02-1.png 889w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-02-1-300x207.png 300w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-02-1-768x530.png 768w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-02-1-150x104.png 150w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-02-1-218x150.png 218w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-02-1-696x481.png 696w\" sizes=\"auto, (max-width: 889px) 100vw, 889px\" \/><\/figure>\n\n\n<div>\n<div>HTTP \u30dd\u30fc\u30c8\u306f 8080 \u3067\u3059\uff08 API \u5074 .env \u306e APP_PORT \uff09\u3002<\/div>\n<\/div>\n<p>\u8d77\u52d5\u76f4\u5f8c\u306f\u307e\u3060\u6253\u523b\u304c\u306a\u3044\u306e\u3067\u3001\u4e00\u89a7\u306f\u7a7a\u306e\u72b6\u614b\u3067\u3059\u3002\u30d6\u30e9\u30a6\u30b6\u3067\u6b21\u3092\u958b\u3051\u3070\u3001API \u5074\u304c\u52d5\u3044\u3066\u3044\u308b\u3053\u3068\u3092\u78ba\u8a8d\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n<pre class=\"wp-block-code\"><code>http:&#47;&#47;localhost:8080\/attendances<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"598\" src=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-06-02-1024x598.png\" alt=\"\" class=\"wp-image-9348\" srcset=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-06-02-1024x598.png 1024w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-06-02-300x175.png 300w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-06-02-768x448.png 768w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-06-02-150x88.png 150w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-06-02-696x406.png 696w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-06-02-1068x624.png 1068w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-06-02.png 1526w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n<div>\n<div>\u30d6\u30e9\u30a6\u30b6\u306e\u4e00\u89a7\u306f\u78ba\u8a8d\u7528\u3067\u3001\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u304c\u5b9f\u969b\u306b\u53e9\u304f\u306e\u306f JSON API \u3067\u3059\u3002\u8a8d\u8a3c\u306f Bearer \u30c8\u30fc\u30af\u30f3\uff08 .env \u306e ATTENDANCE_API_TOKEN \uff09\u3067\u3001\u4e3b\u306a\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u306f\u6b21\u306e\u3068\u304a\u308a\u3067\u3059\u3002<\/div>\n<\/div>\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>\u30e1\u30bd\u30c3\u30c9<\/td><td>\u30d1\u30b9<\/td><td>\u7528\u9014<\/td><\/tr><tr><td>GET<\/td><td>\/api\/me<\/td><td>\u5229\u7528\u8005\u60c5\u5831\uff08\u753b\u9762\u306e\u540d\u524d\u8868\u793a\u7528\uff09<\/td><\/tr><tr><td>GET<\/td><td>\/api\/attendance\/today<\/td><td>\u672c\u65e5\u306e\u6253\u523b\u72b6\u614b\u30fb\u30dc\u30bf\u30f3\u53ef\u5426<\/td><\/tr><tr><td>POST<\/td><td>\/api\/attendance<\/td><td>\u51fa\u52e4\uff0f\u9000\u52e4\u306e\u8a18\u9332<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n<div>\n<div>\u4f4d\u7f6e\u306e\u534a\u5f84\u5224\u5b9a\uff08\u30b8\u30aa\u30d5\u30a7\u30f3\u30b9\uff09\u306f API \u3067\u306f\u884c\u3044\u307e\u305b\u3093\u3002\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u5074\u3067\u5224\u5b9a\u3057\u3001OK \u306e\u3068\u304d\u3060\u3051 POST \u3057\u307e\u3059\u3002<\/div>\n<\/p>\n<div>bootstrap\/app.php \u3067 routes\/api.php \u3092\u8aad\u307f\u8fbc\u3093\u3067\u3044\u307e\u3059\u3002\u30d1\u30b9\u306b\u306f\u81ea\u52d5\u3067 \/api \u304c\u4ed8\u304d\u307e\u3059\u3002<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\"><code>routes\/api.php\n\n&lt;?php\n\nuse App\\Http\\Controllers\\Api\\AttendanceController;\nuse App\\Http\\Controllers\\Api\\EmployeeController;\nuse App\\Http\\Middleware\\VerifyAttendanceApiToken;\nuse Illuminate\\Support\\Facades\\Route;\n\nRoute::middleware(VerifyAttendanceApiToken::class)-&gt;group(function () {\n    Route::get('\/me', &#91;EmployeeController::class, 'show']);\n    Route::get('\/attendance\/today', &#91;AttendanceController::class, 'today']);\n    Route::post('\/attendance', &#91;AttendanceController::class, 'store']);\n});<\/code><\/pre>\n\n\n<div>\n<div>\u30c8\u30fc\u30af\u30f3\u691c\u8a3c\u306f middleware \u3067\u3059\u3002Authorization: Bearer &#8230; \u304c .env \u306e\u5024\u3068\u4e00\u81f4\u3057\u306a\u3044\u3068 401 \u3092\u8fd4\u3057\u307e\u3059\u3002<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\"><code>app\/Http\/Middleware\/VerifyAttendanceApiToken.php\n\n&lt;?php\n\nnamespace App\\Http\\Middleware;\n\nuse Closure;\nuse Illuminate\\Http\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\nclass VerifyAttendanceApiToken\n{\n    public function handle(Request $request, Closure $next): Response\n    {\n        $expected = (string) config('services.attendance.token', '');\n\n        if ($expected === '') {\n            return response()-&gt;json(&#91;\n                'message' =&gt; 'ATTENDANCE_API_TOKEN is not configured on the server.',\n            ], 500);\n        }\n\n        $provided = (string) $request-&gt;bearerToken();\n\n        if (! hash_equals($expected, $provided)) {\n            return response()-&gt;json(&#91;\n                'message' =&gt; 'Unauthorized.',\n            ], 401);\n        }\n\n        return $next($request);\n    }\n}<\/code><\/pre>\n\n\n<div>\n<div>\u30c8\u30fc\u30af\u30f3\u3068\u56fa\u5b9a\u5229\u7528\u8005\u30b3\u30fc\u30c9\u306f config\/services.php \u7d4c\u7531\u3067\u8aad\u307f\u307e\u3059\u3002<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\"><code>config\/services.php\n\n&lt;?php\n\nreturn &#91;\n\n    \/*\n    |--------------------------------------------------------------------------\n    | Third Party Services\n    |--------------------------------------------------------------------------\n    |\n    | This file is for storing the credentials for third party services such\n    | as Mailgun, Postmark, AWS and more. This file provides the de facto\n    | location for this type of information, allowing packages to have\n    | a conventional file to locate the various service credentials.\n    |\n    *\/\n\n    'postmark' =&gt; &#91;\n        'key' =&gt; env('POSTMARK_API_KEY'),\n    ],\n\n    'resend' =&gt; &#91;\n        'key' =&gt; env('RESEND_API_KEY'),\n    ],\n\n    'ses' =&gt; &#91;\n        'key' =&gt; env('AWS_ACCESS_KEY_ID'),\n        'secret' =&gt; env('AWS_SECRET_ACCESS_KEY'),\n        'region' =&gt; env('AWS_DEFAULT_REGION', 'us-east-1'),\n    ],\n\n    'slack' =&gt; &#91;\n        'notifications' =&gt; &#91;\n            'bot_user_oauth_token' =&gt; env('SLACK_BOT_USER_OAUTH_TOKEN'),\n            'channel' =&gt; env('SLACK_BOT_USER_DEFAULT_CHANNEL'),\n        ],\n    ],\n\n    'attendance' =&gt; &#91;\n        'token' =&gt; env('ATTENDANCE_API_TOKEN'),\n        'employee_code' =&gt; env('ATTENDANCE_TEST_EMPLOYEE_CODE', 'TEST001'),\n    ],\n\n];<\/code><\/pre>\n\n\n<div>\n<div>\n<div>\n<div>\u5229\u7528\u8005\u60c5\u5831\uff08GET \/api\/me\uff09\u306f\u3001\u3055\u304d\u307b\u3069\u30b7\u30fc\u30c0\u30fc\u3067\u5165\u308c\u305f\u30c6\u30b9\u30c8\u5229\u7528\u8005\uff08 ATTENDANCE_TEST_EMPLOYEE_CODE \uff09\u3092\u8fd4\u3057\u307e\u3059\u3002\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u306e\u300c\u73fe\u5728\u306e\u5229\u7528\u8005\u300d\u8868\u793a\u306b\u4f7f\u3044\u307e\u3059\u3002<\/div>\n<\/div>\n<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\"><code>app\/Http\/Controllers\/Api\/EmployeeController.php\n\n&lt;?php\n\nnamespace App\\Http\\Controllers\\Api;\n\nuse App\\Http\\Controllers\\Controller;\nuse App\\Models\\Employee;\nuse Illuminate\\Http\\JsonResponse;\n\nclass EmployeeController extends Controller\n{\n    public function show(): JsonResponse\n    {\n        $employee = Employee::query()\n            -&gt;where('employee_code', config('services.attendance.employee_code'))\n            -&gt;where('is_active', true)\n            -&gt;firstOrFail();\n\n        return response()-&gt;json(&#91;\n            'data' =&gt; &#91;\n                'id' =&gt; $employee-&gt;id,\n                'employee_code' =&gt; $employee-&gt;employee_code,\n                'name' =&gt; $employee-&gt;name,\n            ],\n        ]);\n    }\n}<\/code><\/pre>\n\n\n<p>\u672c\u65e5\u306e\u6253\u523b\u72b6\u614b\uff08GET \/api\/attendance\/today \uff09\u306f\u3001\u672c\u65e5\u306e\u51fa\u52e4\uff0f\u9000\u52e4\u6e08\u307f\u304b\u3001\u30dc\u30bf\u30f3\u3092\u62bc\u305b\u308b\u304b\u3092\u8fd4\u3057\u307e\u3059\u3002\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u5074\u306e\u51fa\u52e4\u30fb\u9000\u52e4\u30dc\u30bf\u30f3\u306e\u6709\u52b9\uff0f\u7121\u52b9\u306b\u4f7f\u3044\u307e\u3059\u3002<\/p>\n<p>\u6253\u523b\u306e\u8a18\u9332\uff08POST \/api\/attendance\uff09\u306f\u3001type\uff08clock_in \/ clock_out\uff09\u3068\u7def\u5ea6\u30fb\u7d4c\u5ea6\u306a\u3069\u3092\u53d7\u3051\u53d6\u308a\u3001\u6253\u523b\u30921\u4ef6\u4f5c\u6210\u3057\u307e\u3059\u3002\u3069\u3061\u3089\u3082\u56fa\u5b9a\u306e\u30c6\u30b9\u30c8\u5229\u7528\u8005\u306b\u7d10\u3065\u3051\u307e\u3059\u3002\u4e8c\u91cd\u6253\u523b\u306a\u3069\u306f TodayAttendanceStatus \u3067\u6b62\u3081\u3001409 \u3092\u8fd4\u3057\u307e\u3059\u3002<\/p>\n\n\n<pre class=\"wp-block-code\"><code>app\/Http\/Controllers\/Api\/AttendanceController.php\n\u203b1 \u672c\u65e5\u306e\u6253\u523b\u72b6\u614b\u306b\u3064\u3044\u3066\u306f today \u30e1\u30bd\u30c3\u30c9\u304c\u8a72\u5f53\n\u203b2 \u6253\u523b\u306e\u8a18\u9332\u306b\u3064\u3044\u3066\u306f store \u30e1\u30bd\u30c3\u30c9\u304c\u8a72\u5f53\n\n&lt;?php\n\nnamespace App\\Http\\Controllers\\Api;\n\nuse App\\Http\\Controllers\\Controller;\nuse App\\Models\\Attendance;\nuse App\\Models\\Employee;\nuse App\\Services\\TodayAttendanceStatus;\nuse Illuminate\\Http\\JsonResponse;\nuse Illuminate\\Http\\Request;\n\nclass AttendanceController extends Controller\n{\n    public function today(TodayAttendanceStatus $status): JsonResponse\n    {\n        $employee = $this-&gt;testEmployee();\n\n        return response()-&gt;json(&#91;\n            'data' =&gt; $status-&gt;forEmployee($employee),\n        ]);\n    }\n\n    public function store(Request $request, TodayAttendanceStatus $status): JsonResponse\n    {\n        $validated = $request-&gt;validate(&#91;\n            'type' =&gt; &#91;'required', 'in:clock_in,clock_out'],\n            'latitude' =&gt; &#91;'required', 'numeric', 'between:-90,90'],\n            'longitude' =&gt; &#91;'required', 'numeric', 'between:-180,180'],\n            'accuracy' =&gt; &#91;'nullable', 'numeric', 'min:0'],\n            'punched_at' =&gt; &#91;'nullable', 'date'],\n        ]);\n\n        $employee = $this-&gt;testEmployee();\n        $status-&gt;assertCanPunch($employee, $validated&#91;'type']);\n\n        $attendance = Attendance::query()-&gt;create(&#91;\n            'employee_id' =&gt; $employee-&gt;id,\n            'type' =&gt; $validated&#91;'type'],\n            'latitude' =&gt; $validated&#91;'latitude'],\n            'longitude' =&gt; $validated&#91;'longitude'],\n            'accuracy' =&gt; $validated&#91;'accuracy'] ?? null,\n            'punched_at' =&gt; $validated&#91;'punched_at'] ?? now(),\n        ]);\n\n        $label = $validated&#91;'type'] === 'clock_in' ? '\u51fa\u52e4' : '\u9000\u52e4';\n        $today = $status-&gt;forEmployee($employee);\n\n        return response()-&gt;json(&#91;\n            'message' =&gt; \"{$label}\u3092\u6253\u523b\u3057\u307e\u3057\u305f\",\n            'data' =&gt; &#91;\n                'id' =&gt; $attendance-&gt;id,\n                'employee' =&gt; &#91;\n                    'id' =&gt; $employee-&gt;id,\n                    'employee_code' =&gt; $employee-&gt;employee_code,\n                    'name' =&gt; $employee-&gt;name,\n                ],\n                'type' =&gt; $attendance-&gt;type,\n                'latitude' =&gt; $attendance-&gt;latitude,\n                'longitude' =&gt; $attendance-&gt;longitude,\n                'accuracy' =&gt; $attendance-&gt;accuracy,\n                'punched_at' =&gt; $attendance-&gt;punched_at?-&gt;toIso8601String(),\n                'today' =&gt; $today,\n            ],\n        ], 201);\n    }\n\n    private function testEmployee(): Employee\n    {\n        return Employee::query()\n            -&gt;where('employee_code', config('services.attendance.employee_code'))\n            -&gt;where('is_active', true)\n            -&gt;firstOrFail();\n    }\n}<\/code><\/pre>\n\n\n<p>\u672c\u65e5\u306e\u51fa\u52e4\u30fb\u9000\u52e4\u30dc\u30bf\u30f3\u3092\u62bc\u305b\u308b\u304b\u306e\u5224\u5b9a\u306b\u3064\u3044\u3066\u306f\u3001\u6b21\u306e\u30b5\u30fc\u30d3\u30b9\u306b\u5207\u308a\u51fa\u3057\u3066\u3044\u307e\u3059\u3002<\/p>\n\n\n<pre class=\"wp-block-code\"><code>app\/Services\/TodayAttendanceStatus.php\n\n&lt;?php\n\nnamespace App\\Services;\n\nuse App\\Models\\Attendance;\nuse App\\Models\\Employee;\nuse Carbon\\Carbon;\nuse Illuminate\\Http\\Exceptions\\HttpResponseException;\nuse Illuminate\\Support\\Collection;\n\nclass TodayAttendanceStatus\n{\n    \/**\n     * @return array{\n     *     date: string,\n     *     has_clock_in: bool,\n     *     has_clock_out: bool,\n     *     day_complete: bool,\n     *     clock_in_enabled: bool,\n     *     clock_out_enabled: bool\n     * }\n     *\/\n    public function forEmployee(Employee $employee): array\n    {\n        $punches = $this-&gt;todayPunches($employee);\n\n        $hasClockIn = $punches-&gt;contains(fn (Attendance $a) =&gt; $a-&gt;type === 'clock_in');\n        $hasClockOut = $punches-&gt;contains(fn (Attendance $a) =&gt; $a-&gt;type === 'clock_out');\n        $dayComplete = $hasClockIn &amp;&amp; $hasClockOut;\n\n        return &#91;\n            'date' =&gt; $this-&gt;today()-&gt;toDateString(),\n            'has_clock_in' =&gt; $hasClockIn,\n            'has_clock_out' =&gt; $hasClockOut,\n            'day_complete' =&gt; $dayComplete,\n            \/\/ \u51fa\u52e4\u5f8c\u306f\u51fa\u52e4\u30de\u30b9\u30af\u3002\u51fa\u52e4\u30fb\u9000\u52e4\u304c\u63c3\u3063\u305f\u3089\u4e21\u65b9\u30de\u30b9\u30af\u3002\n            'clock_in_enabled' =&gt; ! $hasClockIn &amp;&amp; ! $dayComplete,\n            'clock_out_enabled' =&gt; $hasClockIn &amp;&amp; ! $hasClockOut,\n        ];\n    }\n\n    public function assertCanPunch(Employee $employee, string $type): void\n    {\n        $status = $this-&gt;forEmployee($employee);\n\n        if ($type === 'clock_in' &amp;&amp; ! $status&#91;'clock_in_enabled']) {\n            throw new HttpResponseException(response()-&gt;json(&#91;\n                'message' =&gt; $status&#91;'day_complete']\n                    ? '\u672c\u65e5\u306e\u51fa\u9000\u52e4\u306f\u5b8c\u4e86\u3057\u3066\u3044\u307e\u3059\u3002'\n                    : '\u672c\u65e5\u306f\u3059\u3067\u306b\u51fa\u52e4\u6253\u523b\u6e08\u307f\u3067\u3059\u3002',\n                'data' =&gt; $status,\n            ], 409));\n        }\n\n        if ($type === 'clock_out' &amp;&amp; ! $status&#91;'clock_out_enabled']) {\n            throw new HttpResponseException(response()-&gt;json(&#91;\n                'message' =&gt; $status&#91;'day_complete']\n                    ? '\u672c\u65e5\u306e\u51fa\u9000\u52e4\u306f\u5b8c\u4e86\u3057\u3066\u3044\u307e\u3059\u3002'\n                    : '\u5148\u306b\u51fa\u52e4\u6253\u523b\u3092\u884c\u3063\u3066\u304f\u3060\u3055\u3044\u3002',\n                'data' =&gt; $status,\n            ], 409));\n        }\n    }\n\n    \/**\n     * @return Collection&lt;int, Attendance&gt;\n     *\/\n    public function todayPunches(Employee $employee): Collection\n    {\n        $start = $this-&gt;today()-&gt;copy()-&gt;startOfDay()-&gt;utc();\n        $end = $this-&gt;today()-&gt;copy()-&gt;endOfDay()-&gt;utc();\n\n        return Attendance::query()\n            -&gt;where('employee_id', $employee-&gt;id)\n            -&gt;whereBetween('punched_at', &#91;$start, $end])\n            -&gt;orderBy('punched_at')\n            -&gt;get();\n    }\n\n    public function today(): Carbon\n    {\n        return now('Asia\/Tokyo');\n    }\n}<\/code><\/pre>\n\n\n<p>\u300c\u65e5\u672c\u6642\u9593\u306e\u4eca\u65e5\u300d\u3092\u57fa\u6e96\u306b\u3001\u51fa\u52e4\u5f8c\u306f\u51fa\u52e4\u30dc\u30bf\u30f3\u3092\u6b62\u3081\u3001\u9000\u52e4\u5f8c\u306f\u4e21\u65b9\u6b62\u3081\u308b\u3001\u3068\u3044\u3063\u305f\u5236\u5fa1\u3092\u884c\u306a\u3063\u3066\u3044\u307e\u3059\u3002<\/p>\n<h3>\uff14\uff09\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u5074\u306e\u63a5\u7d9a\u8a2d\u5b9a<\/h3>\n<div>\n<div>API \u5074\u306e\u6e96\u5099\u304c\u3067\u304d\u305f\u3089\u3001\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\uff08 native-test \uff09\u304b\u3089\u3064\u306a\u304e\u307e\u3059\u3002<\/div>\n<div>\u8a2d\u5b9a\u306f \u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u5074\u306e .env \u3067\u3059\u3002<\/div>\n<\/div>\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"889\" height=\"614\" src=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-03.png\" alt=\"\" class=\"wp-image-9351\" srcset=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-03.png 889w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-03-300x207.png 300w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-03-768x530.png 768w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-03-150x104.png 150w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-03-218x150.png 218w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-03-696x481.png 696w\" sizes=\"auto, (max-width: 889px) 100vw, 889px\" \/><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>COMPANY_LAT=XX.XXXXXX\nCOMPANY_LNG=XXX.XXXXXX\nGEOFENCE_RADIUS_METERS=10\n\nATTENDANCE_API_URL=http:\/\/10.0.2.2:8080\/api\nATTENDANCE_API_TOKEN=dev-attendance-token<\/code><\/pre>\n\n\n<p>\u305d\u308c\u305e\u308c\u306e\u5185\u5bb9\u306b\u3064\u3044\u3066\u306f\u4ee5\u4e0b\u306e\u901a\u308a\u3067\u3059\u3002<\/p>\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>\u30ad\u30fc<\/td><td>\u610f\u5473<\/td><\/tr><tr><td>COMPANY_LAT \/ COMPANY_LNG<\/td><td>\u4f1a\u793e\uff08\u6253\u523b\u8a31\u53ef\u30a8\u30ea\u30a2\uff09\u306e\u4e2d\u5fc3\u5ea7\u6a19<\/td><\/tr><tr><td>GEOFENCE_RADIUS_METERS<\/td><td>\u534a\u5f84\uff08\u30e1\u30fc\u30c8\u30eb\uff09\u3002\u4eca\u56de\u306f 10m<\/td><\/tr><tr><td>ATTENDANCE_API_URL<\/td><td>\u6253\u523b API \u306e\u30d9\u30fc\u30b9 URL<\/td><\/tr><tr><td>ATTENDANCE_API_TOKEN<\/td><td>Bearer \u30c8\u30fc\u30af\u30f3\uff08API \u5074\u3068\u540c\u3058\u5024\uff09<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n<p>10.0.2.2 \u306f\u3001Android \u30a8\u30df\u30e5\u30ec\u30fc\u30bf\u304b\u3089\u30db\u30b9\u30c8\u30de\u30b7\u30f3\u306e localhost \u3092\u6307\u3059\u7279\u5225\u306a\u30a2\u30c9\u30ec\u30b9\u3067\u3059\u3002\u30db\u30b9\u30c8\u3067\u306f http:\/\/localhost:8080\u3001\u30a8\u30df\u30e5\u30ec\u30fc\u30bf\u304b\u3089\u306f http:\/\/10.0.2.2:8080 \u306b\u306a\u308a\u307e\u3059\u3002\u672b\u5c3e\u306e \/api \u306f\u3001\uff13\uff09\u3067\u7528\u610f\u3057\u305f JSON API \u306e\u30d7\u30ec\u30d5\u30a3\u30c3\u30af\u30b9\u3067\u3059\u3002<\/p>\n<p>\u30c8\u30fc\u30af\u30f3\u306f\u3001API \u5074 .env \u306e ATTENDANCE_API_TOKEN \u3068\u63c3\u3048\u3066\u304f\u3060\u3055\u3044\u3002\u305a\u308c\u3066\u3044\u308b\u3068 401 \u306b\u306a\u308a\u307e\u3059\u3002<\/p>\n<div>\n<div>\u3053\u308c\u3089\u306e\u5024\u306f config\/attendance.php \u7d4c\u7531\u3067\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u304b\u3089\u8aad\u307f\u307e\u3059\u3002<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\"><code>config\/attendance.php\n\n&lt;?php\n\nreturn &#91;\n    'company_lat' =&gt; env('COMPANY_LAT', ZZ.ZZZZZZ),\n    'company_lng' =&gt; env('COMPANY_LNG', ZZZ.ZZZZZZ),\n    'geofence_radius_meters' =&gt; env('GEOFENCE_RADIUS_METERS', 10),\n    'api_url' =&gt; env('ATTENDANCE_API_URL', 'http:\/\/10.0.2.2:8080\/api'),\n    'api_token' =&gt; env('ATTENDANCE_API_TOKEN', 'dev-attendance-token'),\n];<\/code><\/pre>\n\n\n<div>\n<div>.env \u3092\u5909\u3048\u305f\u3042\u3068\u306f\u3001\u8a2d\u5b9a\u3092\u8aad\u307f\u76f4\u3057\u307e\u3059\u3002<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\"><code>$ php artisan config:clear<\/code><\/pre>\n\n\n<h3>\uff15\uff09\u534a\u5f84\u5224\u5b9a\u3068 API \u547c\u3073\u51fa\u3057\uff08 GeofenceService \/ AttendanceApiClient \/ Timecard \u306e\u6253\u523b\u51e6\u7406\uff09<\/h3>\n<div>\n<div>\uff12\uff09\u3067\u8aac\u660e\u3057\u305f Timecard \u306b\u3064\u3044\u3066\u3001\u3053\u3053\u3067\u306f\u6253\u523b\u30dc\u30bf\u30f3\u3092\u62bc\u3057\u305f\u3042\u3068\u306b\u4f55\u304c\u8d77\u304d\u308b\u304b\u3092\u8aac\u660e\u3057\u307e\u3059\u3002<\/div>\n<p><\/p>\n<div>\u6d41\u308c\u306f\u6b21\u306e\u3068\u304a\u308a\u3067\u3059\u3002<\/div>\n<ol>\n<li>\u3044\u307e\u306e\u4f4d\u7f6e\u3092\u6c7a\u3081\u308b\uff08\u4eca\u56de\u306f\u753b\u9762\u4e0a\u306e\u624b\u52d5\u5ea7\u6a19\u3002GPS \u306f\u6b21\u56de\uff09<\/li>\n<li>\u4f1a\u793e\u306e\u4f4d\u7f6e\u304b\u3089\u3069\u308c\u304f\u3089\u3044\u96e2\u308c\u3066\u3044\u308b\u304b\u3092\u8a08\u7b97\u3059\u308b\uff08GeofenceService\uff09<\/li>\n<li>\u534a\u5f84\u306e\u5185\u5074\u306a\u3089\u3001\u6253\u523b API \u3078\u8a18\u9332\u3092\u9001\u308b\uff08AttendanceApiClient\uff09<\/li>\n<li>\u753b\u9762\u306e\u30e1\u30c3\u30bb\u30fc\u30b8\u3068\u3001\u51fa\u52e4\uff0f\u9000\u52e4\u30dc\u30bf\u30f3\u306e\u72b6\u614b\u3092\u66f4\u65b0\u3059\u308b<\/li>\n<\/ol>\n<div>\n<div>GeofenceService\uff08\u534a\u5f84\u5224\u5b9a\uff09\u306f\u3001\u4f1a\u793e\u306e\u7def\u5ea6\u30fb\u7d4c\u5ea6\u3092\u4e2d\u5fc3\u306b\u5186\u3092\u8003\u3048\u3001\u3044\u307e\u306e\u4f4d\u7f6e\u304c\u305d\u306e\u5186\u306e\u5185\u5074\u304b\u3069\u3046\u304b\u3092\u5224\u5b9a\u3057\u307e\u3059\u30022\u70b9\u9593\u306e\u8ddd\u96e2\u306f\u3001\u5730\u7403\u3092\u7403\u3068\u307f\u306a\u3057\u3066\u6c42\u3081\u308b <a href=\"https:\/\/en.wikipedia.org\/wiki\/Haversine_formula\">Haversine \u306e\u516c\u5f0f<\/a>\u3092\u4f7f\u3063\u3066\u3044\u307e\u3059\u3002Web \u3084\u30a2\u30d7\u30ea\u3067\u3088\u304f\u4f7f\u308f\u308c\u308b\u65b9\u6cd5\u3067\u3001\u5b9f\u88c5\u304c\u30b7\u30f3\u30d7\u30eb\u306a\u3046\u3048\u3001\u4eca\u56de\u306e\u3088\u3046\u306a\u6570\u5341\u30e1\u30fc\u30c8\u30eb\u898f\u6a21\u306e\u5224\u5b9a\u306b\u3082\u5341\u5206\u306a\u7cbe\u5ea6\u3067\u3059\u3002<\/div>\n<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\"><code>app\/Services\/GeofenceService.php\n\n&lt;?php\n\nnamespace App\\Services;\n\nclass GeofenceService\n{\n    \/**\n     * Haversine formula: distance in meters between two WGS84 points.\n     *\/\n    public function distanceMeters(float $lat1, float $lng1, float $lat2, float $lng2): float\n    {\n        $earthRadius = 6371000;\n\n        $latFrom = deg2rad($lat1);\n        $lonFrom = deg2rad($lng1);\n        $latTo = deg2rad($lat2);\n        $lonTo = deg2rad($lng2);\n\n        $latDelta = $latTo - $latFrom;\n        $lonDelta = $lonTo - $lonFrom;\n\n        $a = sin($latDelta \/ 2) ** 2\n            + cos($latFrom) * cos($latTo) * sin($lonDelta \/ 2) ** 2;\n\n        return 2 * $earthRadius * asin(min(1, sqrt($a)));\n    }\n\n    public function isWithinRadius(float $latitude, float $longitude): array\n    {\n        $companyLat = (float) config('attendance.company_lat');\n        $companyLng = (float) config('attendance.company_lng');\n        $radius = (float) config('attendance.geofence_radius_meters', 10);\n\n        $distance = $this-&gt;distanceMeters($latitude, $longitude, $companyLat, $companyLng);\n\n        return &#91;\n            'allowed' =&gt; $distance &lt;= $radius,\n            'distance_meters' =&gt; round($distance, 2),\n            'radius_meters' =&gt; $radius,\n            'company_lat' =&gt; $companyLat,\n            'company_lng' =&gt; $companyLng,\n        ];\n    }\n}<\/code><\/pre>\n\n\n<div>\n<div>isWithinRadius \u30e1\u30bd\u30c3\u30c9\u304c\u8fd4\u3059\u4e3b\u306a\u5024\u306f\u3001\u6b21\u306e\u3068\u304a\u308a\u3067\u3059\u3002<\/div>\n<\/div>\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>\u30ad\u30fc<\/td><td>\u610f\u5473<\/td><\/tr><tr><td>allowed<\/td><td>\u534a\u5f84\u5185\u306a\u3089\u6253\u523b\u3057\u3066\u3088\u3044<\/td><\/tr><tr><td>distance_meters<\/td><td>\u4f1a\u793e\u304b\u3089\u306e\u8ddd\u96e2\uff08\u30e1\u30fc\u30c8\u30eb\uff09<\/td><\/tr><tr><td>radius_meters<\/td><td>\u8a31\u53ef\u3059\u308b\u534a\u5f84\uff08\u4eca\u56de\u306f 10m\uff09<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n<p>\u534a\u5f84 10m \u3088\u308a\u5916\u5074\u3060\u3063\u305f\u5834\u5408\u306f\u6253\u523b API \u306b\u306f\u9001\u3089\u305a\u3001\u753b\u9762\u306b\u300c\u51fa\u793e\u306e\u78ba\u8a8d\u304c\u3067\u304d\u307e\u305b\u3093\u300d\u3068\u8868\u793a\u3057\u307e\u3059\u3002<\/p>\n<p>AttendanceApiClient \u306f\u3001\uff13\uff09\u3067\u7528\u610f\u3057\u305f JSON API \u3092\u3001\u30a2\u30d7\u30ea\u304b\u3089\u547c\u3073\u51fa\u3059\u305f\u3081\u306e\u30b5\u30fc\u30d3\u30b9\u30af\u30e9\u30b9\u3067\u3059\u3002\u63a5\u7d9a\u5148\u306e URL \u3068\u30c8\u30fc\u30af\u30f3\u306f\u3001\uff14\uff09\u306e config\/attendance.php \u304b\u3089\u8aad\u307f\u307e\u3059\u3002<\/p>\n\n\n<pre class=\"wp-block-code\"><code>app\/Services\/AttendanceApiClient.php\n\n&lt;?php\n\nnamespace App\\Services;\n\nuse Illuminate\\Http\\Client\\RequestException;\nuse Illuminate\\Support\\Facades\\Http;\nuse RuntimeException;\n\nclass AttendanceApiClient\n{\n    public function profile(): array\n    {\n        $response = $this-&gt;request()-&gt;get($this-&gt;url('\/me'))-&gt;throw();\n\n        return $response-&gt;json('data') ?? &#91;];\n    }\n\n    public function today(): array\n    {\n        try {\n            $response = $this-&gt;request()-&gt;get($this-&gt;url('\/attendance\/today'))-&gt;throw();\n        } catch (RequestException $e) {\n            throw new RuntimeException('\u672c\u65e5\u306e\u6253\u523b\u72b6\u614b\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3002', previous: $e);\n        }\n\n        return $response-&gt;json('data') ?? &#91;];\n    }\n\n    public function punch(string $type, float $latitude, float $longitude, ?float $accuracy = null): array\n    {\n        try {\n            $response = $this-&gt;request()\n                -&gt;post($this-&gt;url('\/attendance'), &#91;\n                    'type' =&gt; $type,\n                    'latitude' =&gt; $latitude,\n                    'longitude' =&gt; $longitude,\n                    'accuracy' =&gt; $accuracy,\n                ])\n                -&gt;throw();\n        } catch (RequestException $e) {\n            $message = $e-&gt;response?-&gt;json('message');\n\n            throw new RuntimeException(\n                is_string($message) &amp;&amp; $message !== ''\n                    ? $message\n                    : '\u6253\u523bAPI\u3078\u306e\u9001\u4fe1\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002',\n                previous: $e,\n            );\n        }\n\n        return $response-&gt;json() ?? &#91;];\n    }\n\n    private function request()\n    {\n        $token = (string) config('attendance.api_token');\n\n        if ($token === '') {\n            throw new RuntimeException('ATTENDANCE_API_TOKEN \u304c\u672a\u8a2d\u5b9a\u3067\u3059\u3002');\n        }\n\n        return Http::timeout(10)\n            -&gt;acceptJson()\n            -&gt;withToken($token);\n    }\n\n    private function url(string $path): string\n    {\n        $baseUrl = rtrim((string) config('attendance.api_url'), '\/');\n\n        if ($baseUrl === '') {\n            throw new RuntimeException('ATTENDANCE_API_URL \u304c\u672a\u8a2d\u5b9a\u3067\u3059\u3002');\n        }\n\n        return $baseUrl.$path;\n    }\n}<\/code><\/pre>\n\n\n<p>\u3053\u306e\u30af\u30e9\u30b9\u304c\u884c\u3046\u3053\u3068\u306f\u3001\u6b21\u306e3\u3064\u3067\u3059\u3002<\/p>\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>\u30e1\u30bd\u30c3\u30c9<\/td><td>\u5bfe\u5fdc\u3059\u308b API<\/td><td>\u4f55\u3092\u3059\u308b\u304b<\/td><\/tr><tr><td>profile()<\/td><td>GET \/api\/me<\/td><td>\u5229\u7528\u8005\u306e\u540d\u524d\u3092\u53d6\u5f97<\/td><\/tr><tr><td>today()<\/td><td>GET \/api\/attendance\/today<\/td><td>\u672c\u65e5\u3001\u51fa\u52e4\uff0f\u9000\u52e4\u3067\u304d\u308b\u304b\u306e\u72b6\u614b\u3092\u53d6\u5f97<\/td><\/tr><tr><td>punch()<\/td><td>POST \/api\/attendance<\/td><td>\u51fa\u52e4\uff0f\u9000\u52e4\u3092\u8a18\u9332\u3059\u308b<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n<p>Timecard \u6253\u523b\u51e6\u7406\u306e\u753b\u9762\u30af\u30e9\u30b9\u81ea\u4f53\u306f\u3001\uff12\uff09\u3067\u63b2\u8f09\u6e08\u307f\u306a\u306e\u3067\u3001\u3053\u3053\u3067\u306f\u6253\u523b\u306b\u95a2\u4fc2\u3059\u308b\u52d5\u304d\u3060\u3051\u8ffd\u3044\u307e\u3059\u3002<\/p>\n<p>mount\uff08\u30a2\u30d7\u30ea\u8d77\u52d5\u6642\uff09\u306f\u3001\u4f1a\u793e\u306e\u5ea7\u6a19\u30fb\u534a\u5f84\u30fbAPI \u306e URL \u3092\u8aad\u307f\u8fbc\u307f\u307e\u3059\u3002\u3042\u308f\u305b\u3066\u3001\u5229\u7528\u8005\u540d\uff08 loadProfile \uff09\u3068\u3001\u672c\u65e5\u306e\u6253\u523b\u72b6\u614b\uff08 loadToday \uff09\u3082\u53d6\u5f97\u3057\u307e\u3059\u3002<\/p>\n<p>clockIn \/ clockOut \u2192 beginPunch\uff08\u51fa\u52e4\uff0f\u9000\u52e4\u30dc\u30bf\u30f3\uff09\u306f\u3001\u3059\u3067\u306b\u51e6\u7406\u4e2d\u306e\u3068\u304d\u3084\u3001\u30dc\u30bf\u30f3\u304c\u62bc\u305b\u306a\u3044\u72b6\u614b\u306e\u3068\u304d\u306f\u4f55\u3082\u3057\u307e\u305b\u3093\u3002\u300c\u624b\u52d5\u5ea7\u6a19\u3092\u4f7f\u3046\u300d\u304c ON \u306a\u3089\u3001\u5165\u529b\u3057\u305f\u7def\u5ea6\u30fb\u7d4c\u5ea6\u3067 submitPunch\uff08\u6253\u523b\u5b9f\u884c\uff09\u306b\u9032\u307f\u307e\u3059\u3002OFF \u306e\u3068\u304d\u306f\u7aef\u672b\u306e GPS\uff08 simple-geolocation \uff09\u3092\u4f7f\u3044\u307e\u3059\u304c\u3001\u4eca\u56de\u306e\u78ba\u8a8d\u306f\u624b\u52d5\u5ea7\u6a19\u3092\u524d\u63d0\u306b\u3057\u3066\u3044\u307e\u3059\u3002GPS \u306e\u8a71\u306f\u6b21\u56de\u306b\u56de\u3057\u307e\u3059\u3002<\/p>\n<p>submitPunch\uff08\u6253\u523b\u5b9f\u884c\uff09\u306f\u3001\u00a0<\/p>\n<ol>\n<li>GeofenceService \u3067\u3001\u4f1a\u793e\u304b\u3089\u534a\u5f84\u5185\u304b\u3092\u898b\u308b<\/li>\n<li>\u5916\u5074\u306a\u3089\u300c\u51fa\u793e\u306e\u78ba\u8a8d\u304c\u3067\u304d\u307e\u305b\u3093\u300d\u3068\u51fa\u3057\u3066\u7d42\u4e86\u3059\u308b\uff08API \u306f\u547c\u3070\u306a\u3044\uff09<\/li>\n<li>\u5185\u5074\u306a\u3089 AttendanceApiClient \u3067\u6253\u523b\u3092\u9001\u308b<\/li>\n<li>\u8fd4\u3063\u3066\u304d\u305f\u672c\u65e5\u306e\u72b6\u614b\u3067\u30dc\u30bf\u30f3\u3092\u66f4\u65b0\u3057\u3001\u300c\u6253\u523b\u3057\u307e\u3057\u305f\u300d\u3068\u8868\u793a\u3059\u308b<\/li>\n<\/ol>\n<p>\u753b\u9762\u306e\u30c7\u30d0\u30c3\u30b0\u884c\u306b\u306f\u3001\u8ddd\u96e2\u3084\u534a\u5f84\u3082\u51fa\u3057\u3066\u3044\u307e\u3059\u3002\u691c\u8a3c\u306e\u3068\u304d\u3001\u300c\u4f55\u30e1\u30fc\u30c8\u30eb\u96e2\u308c\u3066\u3044\u305f\u304b\u300d\u3092\u305d\u306e\u5834\u3067\u78ba\u8a8d\u3067\u304d\u307e\u3059\u3002<\/p>\n<h3>\uff16\uff09\u52d5\u4f5c\u691c\u8a3c<\/h3>\n<p>\u3053\u3053\u307e\u3067\u306e\u5b9f\u88c5\u3092\u3001\u5b9f\u969b\u306e\u753b\u9762\u3067\u78ba\u8a8d\u3057\u3066\u3044\u304d\u307e\u3059\u3002\u4eca\u56de\u306f GPS \u3067\u306f\u306a\u304f\u3001\u753b\u9762\u4e0b\u306e\u300c\u624b\u52d5\u5ea7\u6a19\u3092\u4f7f\u3046\u300d\u3092 ON \u306b\u3057\u3066\u9032\u3081\u307e\u3059\u3002<\/p>\n<p>\u524d\u63d0\u306f\u6b21\u306e\u3068\u304a\u308a\u3067\u3059\u3002<\/p>\n<ul>\n<li>\u6253\u523b API\uff08 Sail \uff09\u304c\u8d77\u52d5\u3057\u3066\u3044\u308b<\/li>\n<li>\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u5074 .env \u306e\u8a2d\u5b9a\u304c\u8aad\u307f\u8fbc\u307e\u308c\u3066\u3044\u308b<\/li>\n<li>\u300c\u624b\u52d5\u5ea7\u6a19\u3092\u4f7f\u3046\u300d\u306b\u30c1\u30a7\u30c3\u30af\u304c\u5165\u3063\u3066\u3044\u308b<\/li>\n<\/ul>\n<h4>\u2460 \u534a\u5f84\u5185\u3067 \u51fa\u52e4 \u2192 \u9000\u52e4 \u2192 \u6253\u523b\u4e00\u89a7\u306e\u78ba\u8a8d<\/h4>\n<p>\u307e\u305a\u3001\u624b\u52d5\u306e\u7def\u5ea6\u30fb\u7d4c\u5ea6\u3092\u3001\u4f1a\u793e\u5ea7\u6a19\u3068\u540c\u3058\u5024\u306b\u3057\u307e\u3059\u3002\u3053\u308c\u3067\u8ddd\u96e2\u306f\u307b\u307c 0m \u306b\u306a\u308a\u3001\u534a\u5f84 10m \u306e\u5185\u5074\u3068\u3057\u3066\u6271\u308f\u308c\u307e\u3059\u3002\u51fa\u52e4\u3092\u30bf\u30c3\u30d7\u3059\u308b\u3068\u3001\u300c\u6253\u523b\u3057\u307e\u3057\u305f\u300d\u3068\u8868\u793a\u3055\u308c\u3001\u672c\u65e5\u306e\u72b6\u614b\u3082\u300c\u51fa\u52e4\u6e08\u307f\u300d\u306b\u5909\u308f\u308a\u307e\u3059\u3002<\/p>\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"410\" height=\"666\" src=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-06-03.png\" alt=\"\" class=\"wp-image-9353\" srcset=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-06-03.png 410w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-06-03-185x300.png 185w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-06-03-150x244.png 150w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-06-03-300x487.png 300w\" sizes=\"auto, (max-width: 410px) 100vw, 410px\" \/><\/figure>\n\n\n<p>\u7d9a\u3051\u3066\u9000\u52e4\u3092\u30bf\u30c3\u30d7\u3059\u308b\u3068\u3001\u9000\u52e4\u3082\u8a18\u9332\u3055\u308c\u3001\u672c\u65e5\u306e\u51fa\u9000\u52e4\u304c\u5b8c\u4e86\u3057\u305f\u72b6\u614b\u306b\u306a\u308a\u307e\u3059\u3002<\/p>\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"412\" height=\"662\" src=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-07-01.png\" alt=\"\" class=\"wp-image-9354\" srcset=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-07-01.png 412w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-07-01-187x300.png 187w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-07-01-150x241.png 150w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-07-01-300x482.png 300w\" sizes=\"auto, (max-width: 412px) 100vw, 412px\" \/><\/figure>\n\n\n<p>\u30d6\u30e9\u30a6\u30b6\u306e\u6253\u523b\u4e00\u89a7\u3092\u958b\u304f\u3068\u3001\u3044\u307e\u6253\u3063\u305f\u51fa\u52e4\u30fb\u9000\u52e4\u304c\u4e26\u3073\u307e\u3059\u3002<\/p>\n\n\n<pre class=\"wp-block-code\"><code>\u6253\u523b\u4e00\u89a7 : http:\/\/localhost:8080\/attendances<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"597\" src=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-07-02-1024x597.png\" alt=\"\" class=\"wp-image-9356\" srcset=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-07-02-1024x597.png 1024w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-07-02-300x175.png 300w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-07-02-768x448.png 768w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-07-02-150x87.png 150w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-07-02-696x406.png 696w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-07-02-1068x623.png 1068w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-07-02.png 1528w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n<h4>\u2461 \u534a\u5f84\u5916\u3067\u306f\u30a8\u30e9\u30fc\u306b\u306a\u308b\u3053\u3068<\/h4>\n<p>\u6b21\u306b\u3001\u624b\u52d5\u5ea7\u6a19\u3092\u4f1a\u793e\u304b\u3089\u5341\u5206\u96e2\u3057\u305f\u5024\u306b\u5909\u3048\u307e\u3059\u3002\u51fa\u52e4\u3092\u30bf\u30c3\u30d7\u3059\u308b\u3068\u3001\u6253\u523b API \u306b\u306f\u9001\u3089\u305a\u3001\u300c\u51fa\u793e\u306e\u78ba\u8a8d\u304c\u3067\u304d\u307e\u305b\u3093\u300d\u3068\u8868\u793a\u3055\u308c\u307e\u3059\u3002<\/p>\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"409\" height=\"666\" src=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-09.png\" alt=\"\" class=\"wp-image-9357\" srcset=\"https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-09.png 409w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-09-184x300.png 184w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-09-150x244.png 150w, https:\/\/www.wetch.co.jp\/note\/wp-content\/uploads\/2026\/08\/SN2-09-300x489.png 300w\" sizes=\"auto, (max-width: 409px) 100vw, 409px\" \/><\/figure>\n\n\n<p>\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u5074\u306e\u52d5\u4f5c\u304c\u3001\u4e00\u901a\u308a\u78ba\u8a8d\u3067\u304d\u308b\u3068\u3053\u308d\u307e\u3067\u6765\u307e\u3057\u305f\u3002\u4eca\u56de\u306f\u753b\u9762\u30fb\u6253\u523b API\u30fb\u534a\u5f84\u5224\u5b9a\u307e\u3067\u6271\u3063\u305f\u306e\u3067\u3001\u968f\u5206\u3068\u9577\u304f\u306a\u3063\u3066\u3057\u307e\u3044\u307e\u3057\u305f\u3002<\/p>\n<p>\u6b21\u56de\u306f\u7279\u5225\u7de8\u3068\u3057\u3066\u3001\u7aef\u672b\u306e GPS\uff08\u81ea\u4f5c\u306e simple-geolocation \u30d7\u30e9\u30b0\u30a4\u30f3\uff09\u3067\u4f4d\u7f6e\u3092\u53d6\u308b\u8a71\u3092\u66f8\u304f\u4e88\u5b9a\u3067\u3059\u3002<\/p>","protected":false},"excerpt":{"rendered":"<p>\u524d\u56de\u306e\u6295\u7a3f \u3067\u00a0NativePHP \u306e\u74b0\u5883\u69cb\u7bc9\u304b\u3089\u30a2\u30d7\u30ea\u8d77\u52d5\u3001Welcome \u753b\u9762\u306e\u8868\u793a\u307e\u3067\u3092\u78ba\u8a8d\u3057\u307e\u3057\u305f\u3002 \u4eca\u56de\u306f\u3001\u5f53\u521d\u304a\u8a71\u3057\u3057\u3066\u3044\u305f\u30bf\u30a4\u30e0\u30ec\u30b3\u30fc\u30c0\u30fc\u753b\u9762\u306e\u5b9f\u88c5\u3068\u3001\u6253\u523b\u3092\u53d7\u3051\u53d6\u308b API \u30b5\u30fc\u30d0\u30fc\u5074\u306e\u5b9f\u88c5\u306b\u9032\u307f\u305f\u3044\u3068\u601d [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":9358,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","_wp_rev_ctl_limit":""},"categories":[210],"tags":[],"class_list":["post-9343","post","type-post","status-publish","format-standard","has-post-thumbnail","category-210"],"_links":{"self":[{"href":"https:\/\/www.wetch.co.jp\/note\/wp-json\/wp\/v2\/posts\/9343","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.wetch.co.jp\/note\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.wetch.co.jp\/note\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.wetch.co.jp\/note\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/www.wetch.co.jp\/note\/wp-json\/wp\/v2\/comments?post=9343"}],"version-history":[{"count":0,"href":"https:\/\/www.wetch.co.jp\/note\/wp-json\/wp\/v2\/posts\/9343\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.wetch.co.jp\/note\/wp-json\/wp\/v2\/media\/9358"}],"wp:attachment":[{"href":"https:\/\/www.wetch.co.jp\/note\/wp-json\/wp\/v2\/media?parent=9343"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.wetch.co.jp\/note\/wp-json\/wp\/v2\/categories?post=9343"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.wetch.co.jp\/note\/wp-json\/wp\/v2\/tags?post=9343"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}