{"id":1264,"date":"2024-08-16T11:55:16","date_gmt":"2024-08-16T02:55:16","guid":{"rendered":"https:\/\/shinke1987.net\/?p=1264"},"modified":"2024-08-16T11:56:10","modified_gmt":"2024-08-16T02:56:10","slug":"laravel11%e3%81%a7web%e3%83%96%e3%83%a9%e3%82%a6%e3%82%b6%e3%81%ae%e3%83%97%e3%83%83%e3%82%b7%e3%83%a5%e9%80%9a%e7%9f%a5%e3%81%ae%e5%8b%95%e4%bd%9c%e7%a2%ba%e8%aa%8d","status":"publish","type":"post","link":"https:\/\/shinke1987.net\/?p=1264","title":{"rendered":"Laravel11\u3067Web\u30d6\u30e9\u30a6\u30b6\u306e\u30d7\u30c3\u30b7\u30e5\u901a\u77e5\u306e\u52d5\u4f5c\u78ba\u8a8d"},"content":{"rendered":"\n<h2 id=\"toc0\" class=\"wp-block-heading\">\u6982\u8981<\/h2>\n\n\n\n<p>Laravel11 \u3068 laravel-notification-channels\/webpush \u3092\u5229\u7528\u3057\u3066Web\u30d6\u30e9\u30a6\u30b6\u306e\u30d7\u30c3\u30b7\u30e5\u901a\u77e5\u306e\u52d5\u4f5c\u78ba\u8a8d\u3092\u884c\u3046\u3002<\/p>\n\n\n\n<h2 id=\"toc1\" class=\"wp-block-heading\">\u53c2\u8003\u8cc7\u6599<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/readouble.com\/laravel\/11.x\/ja\/notifications.html\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">11.x \u901a\u77e5 Laravel<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/laravel-notification-channels.com\/webpush\/#installation\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">Laravel Notification Channels | Laravel Notification Channels<\/a><\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-wp-embed is-provider-shinke-1987-net wp-block-embed-shinke-1987-net\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"wp-embedded-content\" data-secret=\"LxvVRfxUMp\"><a href=\"https:\/\/shinke1987.net\/?p=1253\">PHP\u3067Web\u30d6\u30e9\u30a6\u30b6\u306e\u30d7\u30c3\u30b7\u30e5\u901a\u77e5\u306e\u52d5\u4f5c\u78ba\u8a8d<\/a><\/blockquote><iframe loading=\"lazy\" class=\"wp-embedded-content\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; visibility: hidden;\" title=\"&#8220;PHP\u3067Web\u30d6\u30e9\u30a6\u30b6\u306e\u30d7\u30c3\u30b7\u30e5\u901a\u77e5\u306e\u52d5\u4f5c\u78ba\u8a8d&#8221; &#8212; shinke1987.net\" src=\"https:\/\/shinke1987.net\/?p=1253&#038;embed=true#?secret=Ae4IE3wcie#?secret=LxvVRfxUMp\" data-secret=\"LxvVRfxUMp\" width=\"500\" height=\"282\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\"><\/iframe>\n<\/div><\/figure>\n\n\n\n<h2 id=\"toc2\" class=\"wp-block-heading\">\u74b0\u5883<\/h2>\n\n\n\n<p>Laravel Sail\u3092\u5229\u7528\u3057\u305f\u3002<\/p>\n\n\n\n<p>MacOS\uff1a14.5 Sonoma<\/p>\n\n\n\n<p>PHP\uff1a8.3.10<\/p>\n\n\n\n<p>Laravel\uff1a11.20.0<\/p>\n\n\n\n<h2 id=\"toc3\" class=\"wp-block-heading\">\u52d5\u4f5c\u78ba\u8a8d\u306e\u6e96\u5099<\/h2>\n\n\n\n<h3 id=\"toc4\" class=\"wp-block-heading\">Laravel Sail \u306e\u57fa\u672c\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7<\/h3>\n\n\n\n<p>\u4e0b\u8a18\u30b3\u30de\u30f3\u30c9\u3092\u5b9f\u884c\u3059\u308b\u3002<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n\/\/ \u4f5c\u696d\u30d5\u30a9\u30eb\u30c0\u3078\u79fb\u52d5\u3002\n% cd \u4f5c\u696d\u30d5\u30a9\u30eb\u30c0\n\n\/\/ Laravel\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u4f5c\u6210\u3002\n% curl -s &quot;https:\/\/laravel.build\/webpush-test&quot; | bash\n\n\/\/ Sail\u3092\u8d77\u52d5\u3002\n% cd webpush-test\n% .\/vendor\/bin\/sail up -d\n\n\/\/ DB\u306e\u30de\u30a4\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u5b9f\u884c\u3002\n% .\/vendor\/bin\/sail artisan migrate\n\n\/\/ laravel-notification-channels\/webpush \u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3002\n% .\/vendor\/bin\/sail composer require laravel-notification-channels\/webpush\n<\/pre><\/div>\n\n\n<h3 id=\"toc5\" class=\"wp-block-heading\">app\/Models\/User.php \u7de8\u96c6<\/h3>\n\n\n\n<p>\u6b21\u306b app\/Models\/User.php \u3092\u6b21\u306e\u3088\u3046\u306b\u7de8\u96c6\u3059\u308b\u3002\uff082\u884c\u3060\u3051\u7de8\u96c6\uff09<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: php; title: ; notranslate\" title=\"\">\n&lt;?php\n\nnamespace App\\Models;\n\n\/\/ use Illuminate\\Contracts\\Auth\\MustVerifyEmail;\nuse Illuminate\\Database\\Eloquent\\Factories\\HasFactory;\nuse Illuminate\\Foundation\\Auth\\User as Authenticatable;\nuse Illuminate\\Notifications\\Notifiable;\n\n\/\/ \u203b HasPushSusbscriptions \u3092\u8ffd\u52a0\u3002\nuse NotificationChannels\\WebPush\\HasPushSubscriptions;\n\nclass User extends Authenticatable\n{\n    \/\/ \u203b HasPushSusbscriptions \u3092\u8ffd\u52a0\u3002\n    use HasFactory, Notifiable, HasPushSubscriptions;\n\n    \/**\n     * The attributes that are mass assignable.\n     *\n     * @var array&lt;int, string&gt;\n     *\/\n    protected $fillable = &#x5B;\n        &#039;name&#039;,\n        &#039;email&#039;,\n        &#039;password&#039;,\n    ];\n<\/pre><\/div>\n\n\n<h3 id=\"toc6\" class=\"wp-block-heading\">WebPush\u7528\u30de\u30a4\u30b0\u30ec\u30fc\u30c8\u7b49\u5b9f\u884c<\/h3>\n\n\n\n<p>\u6b21\u306b\u4e0b\u8a18\u30b3\u30de\u30f3\u30c9\u3092\u5b9f\u884c\u3059\u308b\u3002<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n\/\/ \u30de\u30a4\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u30d5\u30a1\u30a4\u30eb\u751f\u6210\u3002\n% .\/vendor\/bin\/sail artisan vendor:publish --provider=&quot;NotificationChannels\\WebPush\\WebPushServiceProvider&quot; --tag=&quot;migrations&quot;\n\n\/\/ \u30de\u30a4\u30b0\u30ec\u30fc\u30c8\u5b9f\u884c\u3002\n% .\/vendor\/bin\/sail artisan migrate\n\n\/\/ VAPID\u3067\u5229\u7528\u3059\u308b\u516c\u958b\u9375\u3068\u79d8\u5bc6\u9375\u3092\u751f\u6210\u3057\u3066 .env \u3078\u8ffd\u52a0\u3002\n% .\/vendor\/bin\/sail artisan webpush:vapid\n<\/pre><\/div>\n\n\n<h3 id=\"toc7\" class=\"wp-block-heading\">.env \u7de8\u96c6<\/h3>\n\n\n\n<p>\u6b21\u306b .env\u30d5\u30a1\u30a4\u30eb\u306b\u3066\u3001<br>Mac\u306eSafari\u3067\u3082\u30d7\u30c3\u30b7\u30e5\u901a\u77e5\u3092\u8868\u793a\u3055\u305b\u308b\u305f\u3081\u306b\u3001subject\u306e\u8a2d\u5b9a\u3068\u3001<br>xDebug\u3092\u52d5\u304b\u3059\u305f\u3081\u306e\u8a2d\u5b9a\u3092\u884c\u3046\u3002\uff08\u5408\u8a082\u884c\u8ffd\u8a18\uff09<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>xDebug\u3092\u5229\u7528\u3059\u308b\u306b\u306fIDE\u3068php.ini\u306e\u7de8\u96c6\u3082\u5fc5\u8981\u306b\u306a\u308b\u3002<\/p>\n\n\n\n<p>php.ini \u306b\u3064\u3044\u3066\u306f\u3001Docker\u30b3\u30f3\u30c6\u30ca\u5185\u306b\u5165\u308a\u3001\u30c6\u30ad\u30b9\u30c8\u30a8\u30c7\u30a3\u30bf\u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3057\u3001\/etc\/php\/8.3\/cli\/conf.d\/20-xdebug.ini \u3092\u7de8\u96c6\u3059\u308c\u3070\u826f\u3044\u3002<\/p>\n\n\n\n<p>IDE\u306fPHPStorm\u3084VSCode\u3067\u624b\u9806\u304c\u5909\u308f\u308b\u306e\u3067\u3001\u9069\u5b9c\u8abf\u3079\u308b\u3068\u826f\u3044\u3002<\/p>\n\n\n\n<p>\uff08\u3053\u306e\u52d5\u4f5c\u78ba\u8a8d\u3067\u306fPHPStorm\u3092\u5229\u7528\u3057\u3066\u3044\u308b\uff09<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>\u203b .\/vendor\/bin\/sail up \u30b3\u30de\u30f3\u30c9\u3092\u5229\u7528\u3057\u306a\u3044\u3068\u3001xDebug\u3092\u5229\u7528\u3067\u304d\u306a\u3044\u306e\u3067\u6ce8\u610f\u3002<\/p>\n\n\n\n<p>\uff08Docker\u304b\u3089\u30b3\u30f3\u30c6\u30ca\u8d77\u52d5\u3059\u308b\u3068\u3001xDebug\u5229\u7528\u3067\u304d\u306a\u3044\uff09<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nAPP_NAME=Laravel\nAPP_ENV=local\nAPP_KEY=base64:Y0EgytJvxhFQn7RpK0y36Y3K09nYR+LSf\/\/QICzIq0Q=\nAPP_DEBUG=true\nAPP_TIMEZONE=UTC\nAPP_URL=http:\/\/localhost\n\nAPP_LOCALE=en\nAPP_FALLBACK_LOCALE=en\nAPP_FAKER_LOCALE=en_US\n\nAPP_MAINTENANCE_DRIVER=file\n# APP_MAINTENANCE_STORE=database\n\nBCRYPT_ROUNDS=12\n\nLOG_CHANNEL=stack\nLOG_STACK=single\nLOG_DEPRECATIONS_CHANNEL=null\nLOG_LEVEL=debug\n\nDB_CONNECTION=mysql\nDB_HOST=mysql\nDB_PORT=3306\nDB_DATABASE=laravel\nDB_USERNAME=sail\nDB_PASSWORD=password\n\nSESSION_DRIVER=database\nSESSION_LIFETIME=120\nSESSION_ENCRYPT=false\nSESSION_PATH=\/\nSESSION_DOMAIN=null\n\nBROADCAST_CONNECTION=log\nFILESYSTEM_DISK=local\nQUEUE_CONNECTION=database\n\nCACHE_STORE=database\nCACHE_PREFIX=\n\nMEMCACHED_HOST=127.0.0.1\n\nREDIS_CLIENT=phpredis\nREDIS_HOST=redis\nREDIS_PASSWORD=null\nREDIS_PORT=6379\n\nMAIL_MAILER=smtp\nMAIL_HOST=mailpit\nMAIL_PORT=1025\nMAIL_USERNAME=null\nMAIL_PASSWORD=null\nMAIL_ENCRYPTION=null\nMAIL_FROM_ADDRESS=&quot;hello@example.com&quot;\nMAIL_FROM_NAME=&quot;${APP_NAME}&quot;\n\nAWS_ACCESS_KEY_ID=\nAWS_SECRET_ACCESS_KEY=\nAWS_DEFAULT_REGION=us-east-1\nAWS_BUCKET=\nAWS_USE_PATH_STYLE_ENDPOINT=false\n\nVITE_APP_NAME=&quot;${APP_NAME}&quot;\n\nSCOUT_DRIVER=meilisearch\nMEILISEARCH_HOST=http:\/\/meilisearch:7700\n\nMEILISEARCH_NO_ANALYTICS=false\n\n\/\/ \u203b \u4e0b\u8a18\u306e1\u884c\u3092\u8ffd\u8a18\u3002\nVAPID_SUBJECT=&quot;mailto:\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9&quot;\nVAPID_PUBLIC_KEY=BCe3IPJTeTMABsuWjtq3i-uxAgWXtfZ93m_nm3TU7YiIhqmKZMt5hHi5lJD3fdaq5Awif3m9Y-mEdeUvmC1WqhU\nVAPID_PRIVATE_KEY=wYKHvNOQ469B75RXHwnXVKvYFA7Dq7Q6B-FIUdDYbkw\n\n\/\/ \u203b \u4e0b\u8a18\u306e1\u884c\u3092\u8ffd\u8a18\u3002\nSAIL_XDEBUG_MODE=develop,debug\n<\/pre><\/div>\n\n\n<h3 id=\"toc8\" class=\"wp-block-heading\">app\/Notifications\/TestPush.php \u4f5c\u6210\u3068\u7de8\u96c6<\/h3>\n\n\n\n<p>\u6b21\u306b\u4e0b\u8a18\u30b3\u30de\u30f3\u30c9\u3092\u5b9f\u884c\u3059\u308b\u3002<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n% .\/vendor\/bin\/sail artisan make:notification TestPush\n<\/pre><\/div>\n\n\n<p>\u6b21\u306b app\/Notifications\/TestPush.php \u3092\u6b21\u306e\u3088\u3046\u306b\u7de8\u96c6\u3059\u308b\u3002<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: php; title: ; notranslate\" title=\"\">\n&lt;?php\n\nnamespace App\\Notifications;\n\nuse Illuminate\\Bus\\Queueable;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Notifications\\Messages\\MailMessage;\nuse Illuminate\\Notifications\\Notification;\nuse Minishlink\\WebPush\\WebPush;\nuse NotificationChannels\\WebPush\\WebPushChannel;\nuse NotificationChannels\\WebPush\\WebPushMessage;\n\nclass TestPush extends Notification\n{\n    use Queueable;\n\n    private $title;\n    private $body;\n    private $url;\n\n    \/**\n     * Create a new notification instance.\n     *\/\n    public function __construct(string $title = &quot;&quot;, string $body = &quot;&quot;, string $url = &quot;&quot;)\n    {\n        $this-&gt;title = $title;\n        $this-&gt;body = $body;\n        $this-&gt;url = $url;\n    }\n\n    \/**\n     * Get the notification&#039;s delivery channels.\n     *\n     * @return array&lt;int, string&gt;\n     *\/\n    public function via(object $notifiable): array\n    {\n\/\/        return &#x5B;&#039;mail&#039;];\n        return &#x5B;WebPushChannel::class];\n    }\n\n    public function toWebPush($notifiable, $notification)\n    {\n        return (new WebPushMessage())\n            -&gt;title($this-&gt;title)\n            -&gt;body($this-&gt;body)\n            -&gt;data(&#x5B;&#039;url&#039; =&gt; $this-&gt;url]);\n    }\n\n    \/**\n     * Get the array representation of the notification.\n     *\n     * @return array&lt;string, mixed&gt;\n     *\/\n    public function toArray(object $notifiable): array\n    {\n        return &#x5B;\n            \/\/\n        ];\n    }\n}\n<\/pre><\/div>\n\n\n<h3 id=\"toc9\" class=\"wp-block-heading\">Breeze \u30bb\u30c3\u30c8\u30a2\u30c3\u30d7<\/h3>\n\n\n\n<p>\u6b21\u306e\u30b3\u30de\u30f3\u30c9\u3092\u5b9f\u884c\u3059\u308b\u3002<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n\/\/ Breeze \u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3002\n% .\/vendor\/bin\/sail composer require laravel\/breeze\n% .\/vendor\/bin\/sail artisan breeze:install\n\n\/\/ \u8272\u3005\u805e\u304b\u308c\u308b\u304c\u3001\u3068\u308a\u3042\u3048\u305a\u300cBlade with Alpine\u300d\u3092\u9078\u629e\u3059\u308b\u3002\n&gt; Blade with Alpine\n\n\/\/ Would you like dark mode support ? \u3068\u805e\u304b\u308c\u308b\u304c\u3001\u3069\u3061\u3089\u3092\u9078\u629e\u3057\u3066\u3082\u826f\u3044\u3002\n&gt; No\n\n\/\/ Which testing framework do you prefer ? \u3068\u805e\u304b\u308c\u308b\u304c\u3001\u3069\u3061\u3089\u3092\u9078\u629e\u3057\u3066\u3082\u826f\u3044\u3002\n&gt; PHPUnit\n<\/pre><\/div>\n\n\n<h3 id=\"toc10\" class=\"wp-block-heading\">\u30a2\u30ab\u30a6\u30f3\u30c8\u4f5c\u6210<\/h3>\n\n\n\n<p>\u6b21\u306b\u6700\u521d\u304b\u3089\u5b58\u5728\u3059\u308bWelcome\u30da\u30fc\u30b8\u304b\u3089\u3001\u30a2\u30ab\u30a6\u30f3\u30c8\u30921\u500b\u4f5c\u6210\u3059\u308b\u3002<\/p>\n\n\n\n<p>\uff08\u4eca\u56de\u306f\u3053\u306e\u30a2\u30ab\u30a6\u30f3\u30c8\u306b\u7d10\u4ed8\u3051\u3066\u30d7\u30c3\u30b7\u30e5\u901a\u77e5\u3092\u884c\u3046\uff09<\/p>\n\n\n\n<p>Web\u30d6\u30e9\u30a6\u30b6\u3067\u30af\u30ea\u30c3\u30af\u3068\u5165\u529b\u3057\u3066Register\u62bc\u3059\u3060\u3051\u306a\u306e\u3067\u3001\u624b\u9806\u306f\u7701\u7565\u3059\u308b\u3002<\/p>\n\n\n\n<p>\u540d\u524d\u3084\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u3084\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u63a7\u3048\u308b\u5fc5\u8981\u306f\u7121\u3044\u3002<\/p>\n\n\n\n<h3 id=\"toc11\" class=\"wp-block-heading\">app\/Http\/Controllers\/WebPushController.php \u4f5c\u6210\u3068\u7de8\u96c6<\/h3>\n\n\n\n<p>\u6b21\u306b\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u3092\u4f5c\u6210\u3059\u308b\u3002<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n% .\/vendor\/bin\/sail artisan make:controller WebPushController\n<\/pre><\/div>\n\n\n<p>\u6b21\u306b app\/Http\/Controllers\/WebPushController.php \u3092\u6b21\u306e\u3088\u3046\u306b\u7de8\u96c6\u3059\u308b\u3002<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: php; title: ; notranslate\" title=\"\">\n&lt;?php\n\nnamespace App\\Http\\Controllers;\n\nuse App\\Models\\User;\nuse App\\Notifications\\TestPush;\nuse Illuminate\\Http\\JsonResponse;\nuse Illuminate\\Http\\Request;\n\nclass WebPushController extends Controller\n{\n    public function setSubscription(Request $request): JsonResponse\n    {\n        $user = User::find(1);\n        $tempData = $request-&gt;json()-&gt;all();\n        $user-&gt;updatePushSubscription(\n            $tempData&#x5B;&#039;endpoint&#039;],\n            $tempData&#x5B;&#039;keys&#039;]&#x5B;&#039;p256dh&#039;],\n            $tempData&#x5B;&#039;keys&#039;]&#x5B;&#039;auth&#039;],\n            $tempData&#x5B;&#039;contentEncoding&#039;]\n        );\n        return response()-&gt;json();\n    }\n\n    public function send(Request $request)\n    {\n        $user = User::find(1);\n        $user-&gt;notify(new TestPush(&#039;\u30bf\u30a4\u30c8\u30eb&#039;, &#039;\u5185\u5bb9&#039;, &#039;https:\/\/google.co.jp&#039;));\n    }\n}\n<\/pre><\/div>\n\n\n<h3 id=\"toc12\" class=\"wp-block-heading\">routes\/web.php \u7de8\u96c6<\/h3>\n\n\n\n<p>\u6b21\u306b routes\/web.php \u3092\u6b21\u306e\u3088\u3046\u306b\u7de8\u96c6\u3059\u308b\u3002<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: php; title: ; notranslate\" title=\"\">\n&lt;?php\n\nuse App\\Http\\Controllers\\ProfileController;\nuse Illuminate\\Support\\Facades\\Route;\nuse App\\Http\\Controllers\\WebPushController;\n\n\/\/ \u3053\u3053\u304b\u3089\nRoute::view(&#039;\/webpush&#039;, &#039;webpush&#039;);\n\nRoute::post(&#039;\/set-subscription&#039;, &#x5B;WebPushController::class, &#039;setSubscription&#039;])\n    -&gt;withoutMiddleware(\\Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken::class);\n\nRoute::get(&#039;\/send&#039;, &#x5B;WebPushController::class, &#039;send&#039;]);\n\/\/ \u3053\u3053\u307e\u3067\u8ffd\u8a18\u3002\n\nRoute::get(&#039;\/&#039;, function () {\n    return view(&#039;welcome&#039;);\n});\n\nRoute::get(&#039;\/dashboard&#039;, function () {\n    return view(&#039;dashboard&#039;);\n})-&gt;middleware(&#x5B;&#039;auth&#039;, &#039;verified&#039;])-&gt;name(&#039;dashboard&#039;);\n\nRoute::middleware(&#039;auth&#039;)-&gt;group(function () {\n    Route::get(&#039;\/profile&#039;, &#x5B;ProfileController::class, &#039;edit&#039;])-&gt;name(&#039;profile.edit&#039;);\n    Route::patch(&#039;\/profile&#039;, &#x5B;ProfileController::class, &#039;update&#039;])-&gt;name(&#039;profile.update&#039;);\n    Route::delete(&#039;\/profile&#039;, &#x5B;ProfileController::class, &#039;destroy&#039;])-&gt;name(&#039;profile.destroy&#039;);\n});\n\nrequire __DIR__.&#039;\/auth.php&#039;;\n<\/pre><\/div>\n\n\n<h3 id=\"toc13\" class=\"wp-block-heading\">\u30d3\u30e5\u30fc\u306e\u7de8\u96c6<\/h3>\n\n\n\n<p>\u6b21\u306b resources\/views\/webpush.blade.php \u3092\u4f5c\u6210\u3057\u3001\u4e0b\u8a18\u306e\u3088\u3046\u306b\u7de8\u96c6\u3059\u308b\u3002<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta charset=&quot;UTF-8&quot;&gt;\n    &lt;title&gt;webpush.blade.php&lt;\/title&gt;\n    &lt;script src=&quot;app.js&quot;&gt;&lt;\/script&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n\nwebpush.blade.php&lt;br&gt;\n&lt;br&gt;\n&lt;button\n    type=&quot;button&quot;\n    onclick=&quot;getPermission()&quot;\n&gt;\n    \u901a\u77e5\u306e\u8a31\u53ef\u3092\u53d6\u5f97\n&lt;\/button&gt;\n&lt;br&gt;&lt;br&gt;\n&lt;button\n    type=&quot;button&quot;\n    onclick=&quot;updateSw()&quot;\n&gt;\n    \u30b5\u30fc\u30d3\u30b9\u30ef\u30fc\u30ab\u30fc\u3092\u66f4\u65b0\n&lt;\/button&gt;\n\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre><\/div>\n\n\n<h3 id=\"toc14\" class=\"wp-block-heading\">public\/app.js \u7de8\u96c6<\/h3>\n\n\n\n<p>\u6b21\u306b public\/app.js \u3092\u4f5c\u6210\u3057\u3001\u4e0b\u8a18\u306e\u3088\u3046\u306b\u7de8\u96c6\u3059\u308b\u3002<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n\/\/ \u516c\u958b\u9375\u3002\n\/\/ \u203b .env\u30d5\u30a1\u30a4\u30eb\u306e\u516c\u958b\u9375\u3092\u30b3\u30d4\u30da\u3059\u308c\u3070\u826f\u3044\u3002\nconst applicationServerKey = &#039;BCe3IPJTeTMABsuWjtq3i-uxAgWXtfZ93m_nm3TU7YiIhqmKZMt5hHi5lJD3fdaq5Awif3m9Y-mEdeUvmC1WqhU&#039;;\n\n\/\/ \u516c\u958b\u9375\u3092\u7de8\u96c6\u3059\u308b\u95a2\u6570\u3002minishlink\/web-push\u304b\u3089\u30b3\u30d4\u30da\u3057\u305f\u3002\nfunction urlBase64ToUint8Array (base64String) {\n    const padding = &#039;=&#039;.repeat((4 - (base64String.length % 4)) % 4);\n    const base64 = (base64String + padding).replace(\/\\-\/g, &#039;+&#039;).\n        replace(\/_\/g, &#039;\/&#039;);\n\n    const rawData = window.atob(base64);\n    const outputArray = new Uint8Array(rawData.length);\n\n    for (let i = 0; i &lt; rawData.length; ++i) {\n        outputArray&#x5B;i] = rawData.charCodeAt(i);\n    }\n    return outputArray;\n}\n\nasync function getPermission() {\n    \/\/ \u901a\u77e5\u306e\u8a31\u53ef\u3092\u53d6\u5f97\u3002\n    const result = await Notification.requestPermission();\n\n    if (result !== &#039;granted&#039;) {\n        \/\/ \u8a31\u53ef\u4ee5\u5916\u3060\u3063\u305f\u5834\u5408\u306f\u4f55\u3082\u3057\u306a\u3044\u3002\n        return;\n    }\n\n    \/\/ \u30b5\u30fc\u30d3\u30b9\u30ef\u30fc\u30ab\u30fc\u3092\u767b\u9332\u3002\n    await navigator.serviceWorker.register(&#039;webpush_sw.js&#039;);\n\n    \/\/ \u30a2\u30af\u30c6\u30a3\u30d6\u306b\u306a\u3063\u305f\u30b5\u30fc\u30d3\u30b9\u30ef\u30fc\u30ab\u30fc\u3092\u53d6\u5f97\u3002\n    const swr = await navigator.serviceWorker.ready;\n\n    \/\/ \u30d7\u30c3\u30b7\u30e5\u30b5\u30fc\u30d0\u3078\u767b\u9332\u3002\n    const subscription = await swr.pushManager.subscribe({\n        userVisibleOnly: true,\n        applicationServerKey: urlBase64ToUint8Array(applicationServerKey)\n    });\n\n    \/\/ \u6697\u53f7\u5316\u65b9\u5f0f\u3092\u78ba\u8a8d\u3002\n    let contentEncoding = &#039;aesgcm&#039;;\n    if (PushManager.supportedContentEncodings?.includes(&#039;aes128gcm&#039;)) {\n        contentEncoding = &#039;aes128gcm&#039;;\n    }\n\n    \/\/ \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30b5\u30fc\u30d0\u3078subscription\u306e\u60c5\u5831\u3092\u4fdd\u5b58\u3002\n    await fetch(&#039;set-subscription&#039;, {\n        method: &#039;POST&#039;,\n        headers: {&#039;Content-Type&#039;: &#039;application\/json&#039;},\n        body: JSON.stringify(Object.assign(subscription.toJSON(), {contentEncoding}))\n    });\n\n    \/\/ \u5b8c\u4e86\u3057\u305f\u65e8\u8868\u793a\u3002\n    alert(&#039;\u30d7\u30c3\u30b7\u30e5\u30b5\u30fc\u30d0\u306e\u767b\u9332\u307e\u3067\u5b8c\u4e86&#039;);\n}\n\nasync function updateSw() {\n    const swr = await navigator.serviceWorker.ready;\n    swr.update();\n}\n<\/pre><\/div>\n\n\n<h3 id=\"toc15\" class=\"wp-block-heading\">\u30b5\u30fc\u30d3\u30b9\u30ef\u30fc\u30ab\u30fc\uff08public\/webpush_sw.js\uff09\u306e\u7de8\u96c6<\/h3>\n\n\n\n<p>\u6b21\u306b public\/webpush_sw.js \u3092\u4f5c\u6210\u3057\u3001\u4e0b\u8a18\u306e\u3088\u3046\u306b\u7de8\u96c6\u3059\u308b\u3002<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n\/\/ \u901a\u77e5\u3092\u30af\u30ea\u30c3\u30af\u3057\u305f\u6642\u306b\u8868\u793a\u3059\u308bURL\u3002\nlet url = null;\n\nself.addEventListener(&#039;push&#039;, async function (event) {\n    const jsonData = event.data.json();\n\n    url = jsonData.data.url;\n\n\n    event.waitUntil(\n        self.registration.showNotification(jsonData.title, {\n            body: jsonData.body,\n        }),\n    );\n});\n\n\/\/ \u901a\u77e5\u3092\u30af\u30ea\u30c3\u30af\u3055\u308c\u305f\u6642\u306e\u30a4\u30d9\u30f3\u30c8\u3002\nself.addEventListener(&#039;notificationclick&#039;, function (event) {\n    event.notification.close();\n    clients.openWindow(url);\n});\n<\/pre><\/div>\n\n\n<h2 id=\"toc16\" class=\"wp-block-heading\">\u52d5\u4f5c\u78ba\u8a8d<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u203b xDebug\u3092\u5229\u7528\u3059\u308b\u306a\u3089\u3001WebPushController\u306esetSubscription\u30e1\u30bd\u30c3\u30c9\u306e\u6700\u521d\u306e\u884c\u306b\u30d6\u30ec\u30a4\u30af\u30dd\u30a4\u30f3\u30c8\u3092\u8a2d\u7f6e\u3059\u308b\u3002<\/li>\n\n\n\n<li>Web\u30d6\u30e9\u30a6\u30b6\u3067 http:\/\/localhost\/webpush \u3078\u30a2\u30af\u30bb\u30b9\u3057\u3001\u300c\u901a\u77e5\u306e\u8a31\u53ef\u3092\u53d6\u5f97\u300d\u30dc\u30bf\u30f3\u3092\u62bc\u4e0b\u3059\u308b\u3002<\/li>\n\n\n\n<li>Web\u30d6\u30e9\u30a6\u30b6\u3067\u00a0http:\/\/localhost\/send\u00a0\u3078\u30a2\u30af\u30bb\u30b9\u3057\u3001\u901a\u77e5\u304c\u8868\u793a\u3055\u308c\u308b\u3053\u3068\u3092\u78ba\u8a8d\u3059\u308b\u3002<\/li>\n\n\n\n<li>\u901a\u77e5\u3092\u30af\u30ea\u30c3\u30af\u3059\u308b\u3068\u3001Google\u306e\u691c\u7d22\u30da\u30fc\u30b8\u304cWeb\u30d6\u30e9\u30a6\u30b6\u3067\u8868\u793a\u3055\u308c\u308b\u3053\u3068\u3092\u78ba\u8a8d\u3059\u308b\u3002<\/li>\n<\/ol>\n\n\n\n<p><\/p>\n\n\n\n<p>\u4e0a\u8a18\u4ee5\u5916\u306b\u3082\u3001<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u9014\u4e2d\u3067app.js \u306e applicationServerKey \u3092\u5909\u66f4\u3059\u308b\u3068\u3069\u3046\u306a\u308b\u306e\u304b\uff1f<\/li>\n\n\n\n<li>\u8907\u6570\u306e\u30e6\u30fc\u30b6\u3067\u3001\u540c\u4e00\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u540c\u4e00Web\u30d6\u30e9\u30a6\u30b6\u3067\u30b5\u30d6\u30b9\u30af\u30ea\u30d7\u30b7\u30e7\u30f3\u3092\u767b\u9332\u3059\u308b\u3068\u3069\u3046\u306a\u308b\u306e\u304b\uff1f<\/li>\n\n\n\n<li>push_subscriptions\u30c6\u30fc\u30d6\u30eb\u306b\u3069\u3046\u3044\u3063\u305f\u30c7\u30fc\u30bf\u304c\u5165\u3063\u3066\u3044\u308b\u306e\u304b\uff1f<\/li>\n<\/ul>\n\n\n\n<p>\u3068\u3044\u3063\u305f\u3053\u3068\u3092\u78ba\u8a8d\u3059\u308b\u3068\u826f\u3044\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u6982\u8981 Laravel11 \u3068 laravel-notification-channels\/webpush \u3092\u5229\u7528\u3057\u3066Web\u30d6\u30e9\u30a6\u30b6\u306e\u30d7\u30c3\u30b7\u30e5\u901a\u77e5\u306e\u52d5\u4f5c\u78ba\u8a8d\u3092\u884c\u3046\u3002 \u53c2\u8003\u8cc7\u6599 \u74b0\u5883 Laravel Sail\u3092\u5229\u7528\u3057\u305f\u3002 M [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[96],"tags":[88],"class_list":["post-1264","post","type-post","status-publish","format-standard","hentry","category-laravel","tag-laravel"],"_links":{"self":[{"href":"https:\/\/shinke1987.net\/index.php?rest_route=\/wp\/v2\/posts\/1264","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/shinke1987.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/shinke1987.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/shinke1987.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/shinke1987.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1264"}],"version-history":[{"count":4,"href":"https:\/\/shinke1987.net\/index.php?rest_route=\/wp\/v2\/posts\/1264\/revisions"}],"predecessor-version":[{"id":1268,"href":"https:\/\/shinke1987.net\/index.php?rest_route=\/wp\/v2\/posts\/1264\/revisions\/1268"}],"wp:attachment":[{"href":"https:\/\/shinke1987.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1264"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/shinke1987.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1264"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/shinke1987.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1264"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}