<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <atom:link href="https://invokable.net/feed" rel="self" type="application/rss+xml" />
        <title><![CDATA[Invokable]]></title>
        <link><![CDATA[https://invokable.net/feed]]></link>
        <description><![CDATA[]]></description>
        <language>ja</language>
        <pubDate>Fri, 03 Apr 2026 18:38:20 +0900</pubDate>

                    <item>
                <title><![CDATA[Laravel13.x リリースノート]]></title>
                <link>https://invokable.net/article/laravel13-release-notes</link>
                <description><![CDATA[<p>いつもの。「毎週の新バージョンリリース時に気になる変更があればコメントする」使い方。とはいえ最近は <strong>フレームワーク本体</strong> には大きな変更は少ないので書くことがない。</p>
<ul>
<li>リリース日：2026年3月17日</li>
<li>バグ修正：2027年9月まで</li>
<li>セキュリティ修正：2028年3月まで</li>
</ul>
<ol class="rounded-sm border bg-indigo-50 dark:bg-black text-xs list-inside">
<li><a href="#ドキュメント" target="_self">ドキュメント</a></li>
<li><a href="#必要なphpバージョン" target="_self">必要なPHPバージョン</a></li>
</ol>
<h2><a id="ドキュメント" href="#ドキュメント" class="mr-2" aria-hidden="true" title="Permalink">_</a>ドキュメント</h2>
<ul>
<li><div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://laravel.com/docs/13.x/releases" target="_blank">
            Release Notes | Laravel 13.x - The clean stack for Artisans and agents
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        Laravel is a PHP web application framework with expressive, elegant syntax. We&#039;ve already laid the foundation — freeing you to create without sweating the small things.
    </div>
    <div>
        <a href="https://laravel.com/docs/13.x/releases" target="_blank" class="text-sm">
            laravel.com/docs/13.x/releases
        </a>
    </div>
</div>
</li>
<li><div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://laravel.com/docs/13.x/upgrade" target="_blank">
            Upgrade Guide | Laravel 13.x - The clean stack for Artisans and agents
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        Laravel is a PHP web application framework with expressive, elegant syntax. We&#039;ve already laid the foundation — freeing you to create without sweating the small things.
    </div>
    <div>
        <a href="https://laravel.com/docs/13.x/upgrade" target="_blank" class="text-sm">
            laravel.com/docs/13.x/upgrade
        </a>
    </div>
</div>
</li>
</ul>
<h2><a id="必要なphpバージョン" href="#必要なphpバージョン" class="mr-2" aria-hidden="true" title="Permalink">_</a>必要なPHPバージョン</h2>
<ul>
<li>8.3以上 <div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://www.php.net/releases/8.3/ja.php" target="_blank">
            PHP 8.3 Released
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        PHP8.3は、PHP言語のメジャーアップデートです。クラス定数の型付け、読み取り専用プロパティのクローン、ランダム機能追加など、多くの新機能が含まれています。さらにパフォーマンスの向上、バグフィックス、コードのクリーンナップも行われました。
    </div>
    <div>
        <a href="https://www.php.net/releases/8.3/ja.php" target="_blank" class="text-sm">
            www.php.net/releases/8.3/ja.php
        </a>
    </div>
</div>
</li>
</ul>
]]></description>
                <author><![CDATA[Invokable]]></author>
                <pubDate>Fri, 03 Apr 2026 18:38:20 +0900</pubDate>
                            </item>
                    <item>
                <title><![CDATA[GitHub Copilot SDK for Laravel]]></title>
                <link>https://invokable.net/article/laravel-copilot-sdk</link>
                <description><![CDATA[<p>他言語版の公式SDKを元にLaravel版もさっと開発できた。</p>
<p><div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://github.com/invokable/laravel-copilot-sdk" target="_blank">
            GitHub - invokable/laravel-copilot-sdk: Laravel version of GitHub Copilot SDK
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        Laravel version of GitHub Copilot SDK. Contribute to invokable/laravel-copilot-sdk development by creating an account on GitHub.
    </div>
    <div>
        <a href="https://github.com/invokable/laravel-copilot-sdk" target="_blank" class="text-sm">
            github.com/invokable/laravel-copilot-sdk
        </a>
    </div>
</div>
</p>
<p>公式SDKの使い方そのままではLaravelらしくないのでFacadeを使ったLaravel流の使い方ができるようにしている。</p>
<pre><code class="language-php">use Revolution\Copilot\Facades\Copilot;

$response = Copilot::run(prompt: 'What is 2 + 2?');

echo $response-&gt;content();
</code></pre>
<pre><code class="language-php">use Revolution\Copilot\Contracts\CopilotSession;
use Revolution\Copilot\Facades\Copilot;

Copilot::start(function (CopilotSession $session) {
    echo 'Starting Copilot session: '.$session-&gt;id();

    $response = $session-&gt;sendAndWait(prompt: 'Tell me something about PHP.');
    echo $response-&gt;content();

    $response = $session-&gt;sendAndWait(prompt: 'How about Laravel?');
    echo $response-&gt;content();
});
</code></pre>
<ol class="rounded-sm border bg-indigo-50 dark:bg-black text-xs list-inside">
<li><a href="#fake" target="_self">fake()</a></li>
</ol>
<h2><a id="fake" href="#fake" class="mr-2" aria-hidden="true" title="Permalink">_</a>fake()</h2>
<p>いつからかLaravelで使われ出して最近はサードパーティパッケージでも普及している<code>fake()</code>メソッドでのモック。<br />
お馴染みMockeryの<code>shouldReceive()</code>、<code>expects()</code>はFacadeを使えば自動的に対応するので簡単だけど<code>fake()</code>を真似するのは中々大変。</p>
<p>こんなテストを書けるようにしたいのでがんばって対応。</p>
<pre><code class="language-php">use Revolution\Copilot\Facades\Copilot;

Copilot::fake('4');

$response = Copilot::run(prompt: 'What is 2 + 2?');

expect($response-&gt;content())-&gt;toBe('4');

Copilot::assertPrompt('What is *');
Copilot::assertPromptCount(1);
</code></pre>
]]></description>
                <author><![CDATA[Invokable]]></author>
                <pubDate>Sat, 07 Mar 2026 11:56:30 +0900</pubDate>
                            </item>
                    <item>
                <title><![CDATA[Laravel Cloudにはルート権限がない]]></title>
                <link>https://invokable.net/article/laravel-cloud-root</link>
                <description><![CDATA[<p>最近はフィードリーダー用にいろいろ作っている。フィードリーダー本体はInertia+Reactなのであまり関わってないけど関連する <strong>RSSフィードがないサイトからフィードを生成する</strong> や <strong>記事から全文取得する</strong> パッケージは担当。</p>
<p><div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://github.com/invokable/feedable" target="_blank">
            GitHub - invokable/feedable
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        Contribute to invokable/feedable development by creating an account on GitHub.
    </div>
    <div>
        <a href="https://github.com/invokable/feedable" target="_blank" class="text-sm">
            github.com/invokable/feedable
        </a>
    </div>
</div>
</p>
<p><div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://github.com/invokable/feedable-core" target="_blank">
            GitHub - invokable/feedable-core: Feedable: core and built-in drivers
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        Feedable: core and built-in drivers. Contribute to invokable/feedable-core development by creating an account on GitHub.
    </div>
    <div>
        <a href="https://github.com/invokable/feedable-core" target="_blank" class="text-sm">
            github.com/invokable/feedable-core
        </a>
    </div>
</div>
</p>
<p><div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://github.com/invokable/laravel-fullfeed" target="_blank">
            GitHub - invokable/laravel-fullfeed: Extracts the main content from web pages for use in feed reader...
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        Extracts the main content from web pages for use in feed readers - invokable/laravel-fullfeed
    </div>
    <div>
        <a href="https://github.com/invokable/laravel-fullfeed" target="_blank" class="text-sm">
            github.com/invokable/laravel-fullfeed
        </a>
    </div>
</div>
</p>
<p>LaravelのHttpクライアントでhtmlを取得できるなら簡単で何も難しくない。今はJavaScriptで描写してることも多いのでリアルブラウザでのアクセスが必要な場合が難しい。<br />
ここに関しても何年も前にLaravel Duskを参考にして作ってたパッケージがあった。リアルブラウザの管理が手間でアーカイブにして終了してたけど最近調べてPlaywrightが使えそうだったので復活させた。</p>
<p><div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://github.com/invokable/salvager" target="_blank">
            GitHub - invokable/salvager
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        Contribute to invokable/salvager development by creating an account on GitHub.
    </div>
    <div>
        <a href="https://github.com/invokable/salvager" target="_blank" class="text-sm">
            github.com/invokable/salvager
        </a>
    </div>
</div>
</p>
<p>つい最近Vercelからagent-browserが出てこれが使えないか試していた。</p>
<p>ここまで前振り。</p>
<p>Laravel Cloudならagent-browserをインストールできるだろうと試したらできなかった。<br />
デプロイ時のBuild commandsで<code>composer install</code>や<code>npm ci</code>を実行してるけどここはルート環境ではない。<br />
<code>sudo apt-get install</code>で追加インストールしようとしてもsudoコマンドがなくて不可能。<br />
まさかのルート権限がない。<br />
Laravel ForgeならできるはずなのでこれもLaravel Cloudの制限。<br />
システムへのインストールが必要なのでユーザーランドでの簡単な解決方法はなさそう。</p>
<p>引き続き調査。</p>
]]></description>
                <author><![CDATA[Invokable]]></author>
                <pubDate>Sat, 24 Jan 2026 09:56:17 +0900</pubDate>
                            </item>
                    <item>
                <title><![CDATA[Laravel Cloud]]></title>
                <link>https://invokable.net/article/laravel-cloud</link>
                <description><![CDATA[<p>Laravel Forge+Laravel VPSに移行してたけど結局すぐにLaravel Cloudに再度全部移行。<br />
小規模な利用ならCloudが一番安そう。</p>
<p>Laravel VPSはスペックを上げると下げられない。EC2の感覚で使えない。<br />
Cloudは柔軟に変更できるのでこっちのほうが良さそう。</p>
<p>自由に使える度はForge＞Cloud＞Vaporの順。<br />
Laravelの普通の使い方の範囲からタスクスケジュール、キュー程度ならどれでも使える。</p>
<p>Forgeはサーバー1台を丸ごと管理してるので大体なんでも可能。BlueskyのLabelerのようなnginxの設定変更が必要なことをするならForgeでしかできない。</p>
<p>VaporはAWS Lambdaの制約に縛られるのでできないことが多い。タスクスケジュールとキューはVapor側の機能でなんとか実現できているけど他のバックグラウンドプロセスを動かし続けることはできない。</p>
<p>CloudはForgeとVaporの中間。バックグラウンドプロセスを長時間動かせるけどForgeほどの自由はない。Laravelの一般的な使い方の範囲ならCloudで困らない。</p>
<p><div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://laravel.com/blog/one-minute-or-less-how-we-built-laravel-clouds-architecture" target="_blank">
            How We Built Laravel Cloud’s Architecture | Laravel - The clean stack for Artisans and agents
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        A trip behind the scenes of Laravel Cloud&#039;s architecture: how we built a scalable, multi-tenant platform using Kubernetes, Cloudflare, and custom operators.
    </div>
    <div>
        <a href="https://laravel.com/blog/one-minute-or-less-how-we-built-laravel-clouds-architecture" target="_blank" class="text-sm">
            laravel.com/blog/one-minute-or-less-how-we-built-laravel-clouds-architecture
        </a>
    </div>
</div>
</p>
<ol class="rounded-sm border bg-indigo-50 dark:bg-black text-xs list-inside">
<li><a href="#データベースはパブリックアクセス可能" target="_self">データベースはパブリックアクセス可能</a></li>
<li><a href="#休止中はタスクスケジュールやキューは動かない" target="_self">休止中はタスクスケジュールやキューは動かない</a></li>
<li><a href="#コスト" target="_self">コスト</a></li>
</ol>
<h2><a id="データベースはパブリックアクセス可能" href="#データベースはパブリックアクセス可能" class="mr-2" aria-hidden="true" title="Permalink">_</a>データベースはパブリックアクセス可能</h2>
<p>Laravel CloudのデータベースはMySQL(休止なし)とPostgreSQL(休止あり)が選べてどちらもパブリックアクセス可能にできる。<br />
複数アプリで共有可能。<br />
Cloudを使うのさえもったいない規模のアプリはDBだけCloudを使ってVercelなどの他のサーバーから接続するのもあり。</p>
<h2><a id="休止中はタスクスケジュールやキューは動かない" href="#休止中はタスクスケジュールやキューは動かない" class="mr-2" aria-hidden="true" title="Permalink">_</a>休止中はタスクスケジュールやキューは動かない</h2>
<p>Laravel Cloudは自動休止で余計な費用を抑えるようになっているけどその間はタスクスケジュールやキューも動かない。<br />
小規模アプリの用途なんてタスクスケジュールをきっかけにした自動化なので期待通りに動かないこともある。<br />
1日1回の低頻度ならGitHub Actionsなどの外部サービスで実行するのもあり。DBと合わせればRDSに接続していた以前と同じことができる。</p>
<p>逆にずっと動かしていたいアプリは休止機能を無効に。</p>
<h2><a id="コスト" href="#コスト" class="mr-2" aria-hidden="true" title="Permalink">_</a>コスト</h2>
<p>最低限の月額で計算。</p>
<ul>
<li>Forge: 12ドル+VPS(RAM1GB)6ドル=18ドル</li>
<li>Vapor 39ドル+AWS費用。AWS使ってる人なら分かるようにRDSが一番高い。</li>
<li>Cloud: Starterプラン0ドル+App cluster(Ohio, 1flex, RAM256MiB)4ドル+DB(MySQL, 5GB)5ドル = 9ドル</li>
</ul>
<p>Growthプランなら20ドル。Starterで十分なので現実的なRAM1GBにしても7ドル。1アプリならCloudが一番安い。<br />
ただしForgeはVPS1台に複数アプリを載せられるのでアプリ数が多い場合はForgeのほうが安くなる可能性がある。<br />
CloudはアプリごとにApp clusterが必要。代わりに休止機能で費用を抑える設計。</p>
<p>Laravel VPSはRAM2GBに上げると13ドル。上で書いたように下げられない。<br />
Cloudはアプリごとに別サーバーで動いてるので少ないRAMでも大丈夫。<br />
Laravel VPSで2GB必要だったアプリがCloudでは1GBで十分。</p>
<p>VirginiaとOhioのアメリカリージョンだけ少し安い。今後日本リージョンに対応しても若干高いはず。</p>
]]></description>
                <author><![CDATA[Invokable]]></author>
                <pubDate>Sat, 07 Mar 2026 11:53:37 +0900</pubDate>
                            </item>
                    <item>
                <title><![CDATA[Laravel ForgeとLaravel VPSに移行中]]></title>
                <link>https://invokable.net/article/laravel-forge-vps</link>
                <description><![CDATA[<p>しばらくはForge+EC2とLaravel Vaporを併用していたけど急にVaporが決済に失敗するようになったのでとりあえず全部Forgeに移行中。</p>
<ol class="rounded-sm border bg-indigo-50 dark:bg-black text-xs list-inside">
<li><a href="#新しいforge" target="_self">新しいForge</a></li>
<li><a href="#laravel-vps" target="_self">Laravel VPS</a></li>
<li><a href="#メールはaws-ses" target="_self">メールはAWS SES</a></li>
<li><a href="#ストレージはs3互換のcloudflare-r2" target="_self">ストレージはS3互換のCloudflare R2</a></li>
<li><a href="#キャッシュもセッションもキューもdatabaseドライバー" target="_self">キャッシュもセッションもキューもdatabaseドライバー</a></li>
</ol>
<h2><a id="新しいforge" href="#新しいforge" class="mr-2" aria-hidden="true" title="Permalink">_</a>新しいForge</h2>
<p>今年リニューアルしたForgeはゼロダウンタイムデプロイに対応してるけど新しく登録したサイトでしか有効化できないのでこの機会に全サイトを新サーバーで再登録。</p>
<h2><a id="laravel-vps" href="#laravel-vps" class="mr-2" aria-hidden="true" title="Permalink">_</a>Laravel VPS</h2>
<p>Forgeからのみ起動できるVPS。実体はDigitalOceanなので日本リージョンがない。<br />
若干の遅さはCloudflareを挟んでなんとか誤魔化す。</p>
<p>今まではRDSでデータベースを分けてたけど今回はVPS内にDBも入れて、複数VPSでDBを分ける構成に。<br />
RDSが単一障害点になってたので分散。<br />
DBのレコードが多いサイトはVPS1台に1サイトで割り当てて、軽いサイトは複数まとめて1台に。</p>
<p>今後また変えるかもしれないのでこれでいいだろう。</p>
<p>Laravel CloudはAWS EC2+Cloudflare。こっちも日本リージョンには未対応だけどAWSなら対応の可能性はあるのでいずれはLaravel Cloudかも。</p>
<h2><a id="メールはaws-ses" href="#メールはaws-ses" class="mr-2" aria-hidden="true" title="Permalink">_</a>メールはAWS SES</h2>
<p>これだけAWS。</p>
<h2><a id="ストレージはs3互換のcloudflare-r2" href="#ストレージはs3互換のcloudflare-r2" class="mr-2" aria-hidden="true" title="Permalink">_</a>ストレージはS3互換のCloudflare R2</h2>
<p>Laravelのドキュメントでは.envで環境変数設定すれば使えるように書いてるけど他のAWSサービスと併用する場合はAWSの環境変数をそのままは使いにくい。<br />
<code>config/filesystems.php</code>にR2用の設定を増やして環境変数も変える。どこにも情報ないので分かりにくいけどregionは<code>auto</code>にする。</p>
<pre><code class="language-php">        'r2' =&gt; [
            'driver' =&gt; 's3',
            'key' =&gt; env('R2_AWS_ACCESS_KEY_ID'),
            'secret' =&gt; env('R2_AWS_SECRET_ACCESS_KEY'),
            'region' =&gt; env('R2_AWS_DEFAULT_REGION', 'auto'),
            'bucket' =&gt; env('R2_AWS_BUCKET'),
            'url' =&gt; env('R2_AWS_URL'),
            'endpoint' =&gt; env('R2_AWS_ENDPOINT'),
            'use_path_style_endpoint' =&gt; env('R2_AWS_USE_PATH_STYLE_ENDPOINT', false),
            'throw' =&gt; false,
            'report' =&gt; false,
        ],
</code></pre>
<h2><a id="キャッシュもセッションもキューもdatabaseドライバー" href="#キャッシュもセッションもキューもdatabaseドライバー" class="mr-2" aria-hidden="true" title="Permalink">_</a>キャッシュもセッションもキューもdatabaseドライバー</h2>
<p>最近のLaravelは<code>database</code>がデフォルト。速度は遅いはずだけどそこまで気にする必要がないなら全部databaseドライバーにすると管理しやすい。</p>
<p>Horizonを使っている場合のみredis。</p>
]]></description>
                <author><![CDATA[Invokable]]></author>
                <pubDate>Sun, 04 Jan 2026 10:23:10 +0900</pubDate>
                            </item>
                    <item>
                <title><![CDATA[Code Climate Qualityの後継はQlty]]></title>
                <link>https://invokable.net/article/qlty</link>
                <description><![CDATA[<p>しばらく気付いてなかったけどQltyが生まれていた。<br />
機能的には同じで良くなってる部分もあるので使ってる人は移行しよう。</p>
<p><div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://qlty.sh/" target="_blank">
            Qlty - Code Quality and Coverage
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        Code quality and coverage done right. Linting, formatting, security, maintainability, and coverage all in a free, open Rust CLI.
    </div>
    <div>
        <a href="https://qlty.sh/" target="_blank" class="text-sm">
            qlty.sh/
        </a>
    </div>
</div>
<br />
<div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://docs.qlty.sh/migration/overview" target="_blank">
            Migration Overview | Qlty Docs
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        
    </div>
    <div>
        <a href="https://docs.qlty.sh/migration/overview" target="_blank" class="text-sm">
            docs.qlty.sh/migration/overview
        </a>
    </div>
</div>
</p>
<ol class="rounded-sm border bg-indigo-50 dark:bg-black text-xs list-inside">
<li><a href="#openid-connect-oidcでの認証" target="_self">OpenID Connect (OIDC)での認証</a></li>
</ol>
<h2><a id="openid-connect-oidcでの認証" href="#openid-connect-oidcでの認証" class="mr-2" aria-hidden="true" title="Permalink">_</a>OpenID Connect (OIDC)での認証</h2>
<p>一番便利なのはこれで、Code Climate QualityをGitHub Actionsで使うにはリポジトリごとに<code>CC_TEST_REPORTER_ID</code>を設定する作業が必要だった。一度だけとはいえ毎回は手間。</p>
<pre><code class="language-yaml">      - name: Test &amp; publish code coverage
        uses: paambaati/codeclimate-action@v9
        env:
          CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
</code></pre>
<p>これがQltyでは不要になった。GitHub Actionsのワークフローファイルに<code>id-token: write</code>や<code>oidc: true</code>を書いておくだけで自動で認証される。<br />
ここの説明通り。<br />
<div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://docs.qlty.sh/coverage/quickstart" target="_blank">
            Coverage Quick Start | Qlty Docs
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        Get started with code coverage reporting in Qlty
    </div>
    <div>
        <a href="https://docs.qlty.sh/coverage/quickstart" target="_blank" class="text-sm">
            docs.qlty.sh/coverage/quickstart
        </a>
    </div>
</div>
</p>
<pre><code class="language-yaml">permissions:
  contents: read
  id-token: write
</code></pre>
<pre><code class="language-yaml">- name: Test
  run: vendor/bin/phpunit
- name: Code Coverage
  uses: qltysh/qlty-action/coverage@v1
  with:
    oidc: true
    files: build/logs/clover.xml
</code></pre>
]]></description>
                <author><![CDATA[Invokable]]></author>
                <pubDate>Wed, 04 Jun 2025 19:08:21 +0900</pubDate>
                            </item>
                    <item>
                <title><![CDATA[Laravel12.x リリースノート]]></title>
                <link>https://invokable.net/article/laravel12-release-notes</link>
                <description><![CDATA[<p>いつものページをLaravel12用に準備。「毎週の新バージョンリリース時に気になる変更があればコメントする」使い方。とはいえ最近は大きな変更は少ない。</p>
<ul>
<li>リリース日：2025年2月24日</li>
<li>バグ修正：2026年8月まで</li>
<li>セキュリティ修正：2027年2月まで</li>
</ul>
<ol class="rounded-sm border bg-indigo-50 dark:bg-black text-xs list-inside">
<li><a href="#ドキュメント" target="_self">ドキュメント</a></li>
<li><a href="#必要なphpバージョン" target="_self">必要なPHPバージョン</a></li>
</ol>
<h2><a id="ドキュメント" href="#ドキュメント" class="mr-2" aria-hidden="true" title="Permalink">_</a>ドキュメント</h2>
<ul>
<li><div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://laravel.com/docs/12.x/releases" target="_blank">
            Release Notes | Laravel 12.x - The clean stack for Artisans and agents
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        Laravel is a PHP web application framework with expressive, elegant syntax. We&#039;ve already laid the foundation — freeing you to create without sweating the small things.
    </div>
    <div>
        <a href="https://laravel.com/docs/12.x/releases" target="_blank" class="text-sm">
            laravel.com/docs/12.x/releases
        </a>
    </div>
</div>
</li>
<li><div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://laravel.com/docs/12.x/upgrade" target="_blank">
            Upgrade Guide | Laravel 12.x - The clean stack for Artisans and agents
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        Laravel is a PHP web application framework with expressive, elegant syntax. We&#039;ve already laid the foundation — freeing you to create without sweating the small things.
    </div>
    <div>
        <a href="https://laravel.com/docs/12.x/upgrade" target="_blank" class="text-sm">
            laravel.com/docs/12.x/upgrade
        </a>
    </div>
</div>
</li>
</ul>
<h2><a id="必要なphpバージョン" href="#必要なphpバージョン" class="mr-2" aria-hidden="true" title="Permalink">_</a>必要なPHPバージョン</h2>
<ul>
<li>8.2以上 <div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://www.php.net/releases/8.2/ja.php" target="_blank">
            PHP 8.2 Released
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        PHP 8.2 は、PHP 言語のメジャーアップデートです。読み取り専用クラス、独立した型 null, true, false、動的なプロパティの非推奨化などの機能や、パフォーマンスの向上が含まれています。
    </div>
    <div>
        <a href="https://www.php.net/releases/8.2/ja.php" target="_blank" class="text-sm">
            www.php.net/releases/8.2/ja.php
        </a>
    </div>
</div>
</li>
</ul>
]]></description>
                <author><![CDATA[Invokable]]></author>
                <pubDate>Fri, 07 Mar 2025 11:29:22 +0900</pubDate>
                            </item>
                    <item>
                <title><![CDATA[Bluesky]]></title>
                <link>https://invokable.net/article/laravel-bluesky</link>
                <description><![CDATA[<p>個別のコメント書く場所を用意。</p>
<p><div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://github.com/kawax/laravel-bluesky" target="_blank">
            GitHub - invokable/laravel-bluesky
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        Contribute to invokable/laravel-bluesky development by creating an account on GitHub.
    </div>
    <div>
        <a href="https://github.com/kawax/laravel-bluesky" target="_blank" class="text-sm">
            github.com/kawax/laravel-bluesky
        </a>
    </div>
</div>
</p>
<p><div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://github.com/kawax/atproto-lexicon-contracts" target="_blank">
            GitHub - invokable/atproto-lexicon-contracts
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        Contribute to invokable/atproto-lexicon-contracts development by creating an account on GitHub.
    </div>
    <div>
        <a href="https://github.com/kawax/atproto-lexicon-contracts" target="_blank" class="text-sm">
            github.com/kawax/atproto-lexicon-contracts
        </a>
    </div>
</div>
</p>
]]></description>
                <author><![CDATA[Invokable]]></author>
                <pubDate>Sun, 24 Nov 2024 18:08:06 +0900</pubDate>
                            </item>
                    <item>
                <title><![CDATA[Laravel11 Sail+Reverbでのブロードキャスト機能開発環境の構築]]></title>
                <link>https://invokable.net/article/laravel11-sail-reverb-broadcast</link>
                <description><![CDATA[<p>Laravel Reverbが登場して<code>beyondcode/laravel-websockets</code>が終了。2022年の記事が古くなってたので書き直し。<br />
<div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://invokable.net/article/sail-horizon-broadcast" target="_blank">
            Laravel9 Sail+Horizonでのブロードキャスト機能開発環境の構築
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        
    </div>
    <div>
        <a href="https://invokable.net/article/sail-horizon-broadcast" target="_blank" class="text-sm">
            invokable.net/article/sail-horizon-broadcast
        </a>
    </div>
</div>
</p>
<p>あくまで開発環境までの話。本番環境で動かす話は含まない。</p>
<ol class="rounded-sm border bg-indigo-50 dark:bg-black text-xs list-inside">
<li><a href="#環境" target="_self">環境</a></li>
<li><a href="#新規プロジェクト作成" target="_self">新規プロジェクト作成</a></li>
<li><a href="#breezeインストール" target="_self">Breezeインストール</a></li>
<li><a href="#docker-composeymlでreverbも起動" target="_self">docker-compose.ymlでReverbも起動</a></li>
<li><a href="#envの変更" target="_self">.envの変更</a></li>
<li><a href="#sail起動" target="_self">Sail起動</a></li>
<li><a href="#reverbはwebsocketサーバー" target="_self">ReverbはWebSocketサーバー</a></li>
<li><a href="#新規ユーザーを作成" target="_self">新規ユーザーを作成</a></li>
<li><a href="#presencechannel" target="_self">PresenceChannel</a></li>
<li><a href="#privatechannel" target="_self">PrivateChannel</a></li>
<li><a href="#最後の動作確認" target="_self">最後の動作確認</a></li>
<li><a href="#片付け" target="_self">片付け</a></li>
<li><a href="#終わり" target="_self">終わり</a></li>
</ol>
<h2><a id="環境" href="#環境" class="mr-2" aria-hidden="true" title="Permalink">_</a>環境</h2>
<ul>
<li>Laravel 11.x</li>
<li>PHP 8.3</li>
<li>Sail 1.x</li>
<li>Breeze 2.x Inertia(Vue)</li>
<li>Reverb 1.x</li>
</ul>
<p>Horizonは開発環境では使いにくいのでなし。</p>
<p>将来のバージョンではこのまま真似しても動かない可能性が高いので注意。</p>
<h2><a id="新規プロジェクト作成" href="#新規プロジェクト作成" class="mr-2" aria-hidden="true" title="Permalink">_</a>新規プロジェクト作成</h2>
<pre><code class="language-shell">curl -s &quot;https://laravel.build/sail-reverb-project&quot; | bash

cd sail-reverb-project
</code></pre>
<p>Laravel11ではブロードキャスト機能はデフォルトでは無効なのでインストールして有効化。ここでReverbも一緒にインストールされる。</p>
<pre><code class="language-shell">php artisan install:broadcasting
</code></pre>
<h2><a id="breezeインストール" href="#breezeインストール" class="mr-2" aria-hidden="true" title="Permalink">_</a>Breezeインストール</h2>
<p>PrivateChannelやPresenceChannelも試したいのでスターターキットをインストール。今回はBreezeのVue版。好きなスターターキットを使えばいい。ReactならEchoを使うのでほとんど同じ。Livewireを使うのが最も簡単。</p>
<pre><code class="language-shell">composer require laravel/breeze --dev

php artisan breeze:install vue
</code></pre>
<h2><a id="docker-composeymlでreverbも起動" href="#docker-composeymlでreverbも起動" class="mr-2" aria-hidden="true" title="Permalink">_</a>docker-compose.ymlでReverbも起動</h2>
<pre><code class="language-yml">    reverb:
        image: sail-8.3/app
        ports:
            - '${REVERB_PORT:-8080}:8080'
        environment:
            WWWUSER: '${WWWUSER}'
            LARAVEL_SAIL: 1
        volumes:
            - '.:/var/www/html'
        command: php artisan reverb:start --debug
        restart: always
        networks:
            - sail
        depends_on:
            - mysql
            - redis
</code></pre>
<h2><a id="envの変更" href="#envの変更" class="mr-2" aria-hidden="true" title="Permalink">_</a>.envの変更</h2>
<p>Sailで起動するなら重要。</p>
<p>REVERB_HOSTは<code>docker-compose.yml</code>のサービス名と合わせる。<br />
VITE_REVERB_HOSTはDockerの外から接続する用なのでlocalhost。</p>
<pre><code>REVERB_HOST=&quot;reverb&quot;
VITE_REVERB_HOST=&quot;localhost&quot;
</code></pre>
<p><code>DB_HOST=mysql</code>と同様<code>reverb</code>の名前で接続できる。内部ではLaravelの<code>laravel.test</code>からReverbの<code>reverb:8080</code>にリクエストを送っている。REVERB_HOSTを変更してないと<code>laravel.test</code>から<code>localhost:8080</code>に送ろうとしてエラーになる。</p>
<h2><a id="sail起動" href="#sail起動" class="mr-2" aria-hidden="true" title="Permalink">_</a>Sail起動</h2>
<p>migrateしてviteのdevサーバー起動。</p>
<pre><code class="language-shell">sail up -d

sail art migrate
</code></pre>
<pre><code class="language-shell">sail npm run dev
</code></pre>
<p>Sail外でもいい。</p>
<pre><code class="language-shell">npm run dev
</code></pre>
<h2><a id="reverbはwebsocketサーバー" href="#reverbはwebsocketサーバー" class="mr-2" aria-hidden="true" title="Permalink">_</a>ReverbはWebSocketサーバー</h2>
<p>Pusherや<code>beyondcode/laravel-websockets</code>使ってた部分の代わりがReverb。前回の記事でのこの辺りの作業が全部不要になった。</p>
<h2><a id="新規ユーザーを作成" href="#新規ユーザーを作成" class="mr-2" aria-hidden="true" title="Permalink">_</a>新規ユーザーを作成</h2>
<p>ここからはブロードキャスト機能の確認。</p>
<p>簡易的なのでチャットっぽいものを作るだけ。DBにログは残さずその場だけのチャット。</p>
<ul>
<li>PresenceChannel : ログイン中のユーザー共通のチャット。</li>
<li>PrivateChannel : 自分だけのチャット。</li>
</ul>
<p>前回の記事と同じなのでコピーして最新のInertiaで動くように修正しただけ。</p>
<h2><a id="presencechannel" href="#presencechannel" class="mr-2" aria-hidden="true" title="Permalink">_</a>PresenceChannel</h2>
<p>長くなったのでコードはGitHubで。</p>
<p><div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://github.com/kawax/sail-reverb-project/blob/main/resources/js/Pages/PresenceChannel.vue" target="_blank">
            sail-reverb-project/resources/js/Pages/PresenceChannel.vue at main · kawax/sail-reverb-project
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        Contribute to kawax/sail-reverb-project development by creating an account on GitHub.
    </div>
    <div>
        <a href="https://github.com/kawax/sail-reverb-project/blob/main/resources/js/Pages/PresenceChannel.vue" target="_blank" class="text-sm">
            github.com/kawax/sail-reverb-project/blob/main/resources/js/Pages/PresenceChannel.vue
        </a>
    </div>
</div>
<br />
<div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://github.com/kawax/sail-reverb-project/blob/main/app/Http/Controllers/PresenceController.php" target="_blank">
            sail-reverb-project/app/Http/Controllers/PresenceController.php at main · kawax/sail-reverb-project
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        Contribute to kawax/sail-reverb-project development by creating an account on GitHub.
    </div>
    <div>
        <a href="https://github.com/kawax/sail-reverb-project/blob/main/app/Http/Controllers/PresenceController.php" target="_blank" class="text-sm">
            github.com/kawax/sail-reverb-project/blob/main/app/Http/Controllers/PresenceController.php
        </a>
    </div>
</div>
<br />
<div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://github.com/kawax/sail-reverb-project/blob/main/app/Events/PresenceSubmit.php" target="_blank">
            sail-reverb-project/app/Events/PresenceSubmit.php at main · kawax/sail-reverb-project
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        Contribute to kawax/sail-reverb-project development by creating an account on GitHub.
    </div>
    <div>
        <a href="https://github.com/kawax/sail-reverb-project/blob/main/app/Events/PresenceSubmit.php" target="_blank" class="text-sm">
            github.com/kawax/sail-reverb-project/blob/main/app/Events/PresenceSubmit.php
        </a>
    </div>
</div>
</p>
<h3>処理の流れ</h3>
<ol>
<li>DashboardのPresenceChannel.vueを表示したら<code>Echo.join('presence')</code>でPresenceChannelに参加。</li>
<li>フォームからチャットを送信。</li>
<li>Laravel側のPresenceController→PresenceSubmitイベントと進んでPresenceSubmitからブロードキャスト。</li>
<li>フロント側に戻ってきて<code>.listen('PresenceSubmit',</code>で受信。</li>
</ol>
<p>VueかReactで変わる表示部分は重要ではないのでこの流れを理解。</p>
<h2><a id="privatechannel" href="#privatechannel" class="mr-2" aria-hidden="true" title="Permalink">_</a>PrivateChannel</h2>
<p>PresenceChannelとほとんど同じなのでファイルをコピーしてPrivateChannel用を作成。</p>
<h2><a id="最後の動作確認" href="#最後の動作確認" class="mr-2" aria-hidden="true" title="Permalink">_</a>最後の動作確認</h2>
<p>2ユーザー作ってPresenceChannelには両方のチャットが表示、PrivateChannelには送信したユーザーだけ表示されることを確認。</p>
<h2><a id="片付け" href="#片付け" class="mr-2" aria-hidden="true" title="Permalink">_</a>片付け</h2>
<p>終了時は</p>
<pre><code class="language-shell">sail down
</code></pre>
<p>viteはCtrl+Cで終了。</p>
<h2><a id="終わり" href="#終わり" class="mr-2" aria-hidden="true" title="Permalink">_</a>終わり</h2>
<p>ここまでできれば後はLaravelのドキュメントを見てブロードキャスト機能を使っていける。</p>
]]></description>
                <author><![CDATA[Invokable]]></author>
                <pubDate>Sun, 28 Jul 2024 12:11:07 +0900</pubDate>
                            </item>
                    <item>
                <title><![CDATA[Threads APIの使い方]]></title>
                <link>https://invokable.net/article/laravel-threads</link>
                <description><![CDATA[<p>いつものように通知を使いたいのがメインなので最低限のクライアント（機能を増やしたいならmacroで拡張）。<br />
Socialiteも入れたけど公式が対応した場合は削除する。<br />
<div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://github.com/invokable/laravel-threads" target="_blank">
            GitHub - invokable/laravel-threads
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        Contribute to invokable/laravel-threads development by creating an account on GitHub.
    </div>
    <div>
        <a href="https://github.com/invokable/laravel-threads" target="_blank" class="text-sm">
            github.com/invokable/laravel-threads
        </a>
    </div>
</div>
</p>
<ol class="rounded-sm border bg-indigo-50 dark:bg-black text-xs list-inside">
<li><a href="#ドキュメント" target="_self">ドキュメント</a></li>
<li><a href="#meta-for-developersでアプリを作るまで" target="_self">Meta for Developersで「アプリ」を作るまで</a></li>
<li><a href="#ユースケースのカスタマイズ" target="_self">ユースケースのカスタマイズ</a></li>
<li><a href="#socialite用のclient-idとclient-secret" target="_self">Socialite用のClient IDとClient Secret</a></li>
<li><a href="#threadsのトークン" target="_self">Threadsのトークン</a></li>
<li><a href="#トークンの使用" target="_self">トークンの使用</a></li>
<li><a href="#threadsテスターに追加した自分のアカウントに投稿" target="_self">Threadsテスターに追加した自分のアカウントに投稿</a></li>
<li><a href="#自分の投稿を取得" target="_self">自分の投稿を取得</a></li>
<li><a href="#socialiteで認証" target="_self">Socialiteで認証</a></li>
<li><a href="#長期トークンの更新" target="_self">長期トークンの更新</a></li>
<li><a href="#更新" target="_self">更新</a></li>
</ol>
<h2><a id="ドキュメント" href="#ドキュメント" class="mr-2" aria-hidden="true" title="Permalink">_</a>ドキュメント</h2>
<p><div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://developers.facebook.com/docs/threads" target="_blank">
            Threads API - Documentation - Meta for Developers
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        The Threads API enables developers to build their own unique integrations, and helps creators and brands manage their Threads presence at scale and easily share inspiring content with their communities.
    </div>
    <div>
        <a href="https://developers.facebook.com/docs/threads" target="_blank" class="text-sm">
            developers.facebook.com/docs/threads
        </a>
    </div>
</div>
</p>
<h2><a id="meta-for-developersでアプリを作るまで" href="#meta-for-developersでアプリを作るまで" class="mr-2" aria-hidden="true" title="Permalink">_</a>Meta for Developersで「アプリ」を作るまで</h2>
<p><div class="p-3 my-3 border border-indigo-500 shadow-sm bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-black overflow-x-auto">
    <div class="font-bold">
        <a href="https://developers.facebook.com/" target="_blank">
            Social technologies | Meta for Developers
        </a>
    </div>
    <div class="text-gray-500 dark:text-gray-200 text-sm">
        Use Meta&#039;s developer tools and resources to build solutions that help people connect and grow businesses.
    </div>
    <div>
        <a href="https://developers.facebook.com/" target="_blank" class="text-sm">
            developers.facebook.com/
        </a>
    </div>
</div>
</p>
<ul>
<li>Facebookアカウントが必要なはず</li>
<li>「アプリを作成」</li>
<li>「ビジネスポートフォリオをリンクしない」</li>
<li>ユースケースでThreads APIを選択</li>
</ul>
<h2><a id="ユースケースのカスタマイズ" href="#ユースケースのカスタマイズ" class="mr-2" aria-hidden="true" title="Permalink">_</a>ユースケースのカスタマイズ</h2>
<p>アプリのダッシュボードまで進んだら、ユースケースからThreads APIの設定をカスタマイズ。</p>
<h3>アクセス許可</h3>
<p><code>threads_basic</code>は必須なので最初から有効。投稿のために<code>threads_content_publish</code>、削除用に<code>threads_delete</code>、検索用に<code>threads_keyword_search</code>も有効化する。</p>
<h3>設定</h3>
<p>Threadsテスターに自分のThreadsアカウントを追加。Threadsのウェブサイトで許可。トークンを生成。<strong>自分のアカウントに投稿するだけ</strong>ならこのトークンだけでいい。</p>
<p>Socialite/OAuthで認証してトークンを取得するならコールバックURLの設定も必要。これは後からでいい。</p>
<h2><a id="socialite用のclient-idとclient-secret" href="#socialite用のclient-idとclient-secret" class="mr-2" aria-hidden="true" title="Permalink">_</a>Socialite用のClient IDとClient Secret</h2>
<p>アプリ設定→ベーシックの「ThreadsアプリID」と「Threads App Secret」を使う。</p>
<p>上側に「アプリID」と「app secret」があるけどこっちは違う。</p>
<h2><a id="threadsのトークン" href="#threadsのトークン" class="mr-2" aria-hidden="true" title="Permalink">_</a>Threadsのトークン</h2>
<p>短期トークンと長期トークンがある。</p>
<p>Socialiteで取得できるのは短期トークン。短期トークンを長期トークンに変換すれば60日か90日間有効なトークンを得られる。長期トークンを更新すれば有効期限が伸びた新しい長期トークンを得られるので更新しながら使えばずっと使える。</p>
<p>Threadsテスターで生成されるのは最初から長期トークンなのでSocialiteは不要。</p>
<p>長期トークンの更新が必要なので<code>.env</code>に書いたままずっと使うことはできない。一人分だとしてもDBかキャッシュに保存して更新できるようにしておく。</p>
<p>APIの利用に必須なのは長期トークンだけなのでトークンについて理解できれば十分。</p>
<h2><a id="トークンの使用" href="#トークンの使用" class="mr-2" aria-hidden="true" title="Permalink">_</a>トークンの使用</h2>
<p>ドキュメントではクエリパラメータで使用しているけど</p>
<pre><code>&amp;access_token=&lt;ACCESS_TOKEN&gt;
</code></pre>
<p>AuthorizationヘッダーでもいいのでLaravelのHttpクライアントならwithToken()が使える。ちょっと投稿するだけならパッケージ使う必要はなくHttpクライアントでいい。</p>
<pre><code class="language-php">Http::withToken('')-&gt;post();
</code></pre>
<h2><a id="threadsテスターに追加した自分のアカウントに投稿" href="#threadsテスターに追加した自分のアカウントに投稿" class="mr-2" aria-hidden="true" title="Permalink">_</a>Threadsテスターに追加した自分のアカウントに投稿</h2>
<p>トークンはプロフィールページに入力欄作ってusersテーブルに保存して<code>$user-&gt;threads_token</code>で使える、と仮定。この辺は好きなように。</p>
<p>Threadsへの投稿は「text, image, videoを投稿する」と「publishで公開する」の2段階の工程が必要。</p>
<pre><code class="language-php">use Revolution\Threads\Facades\Threads;

Threads::token($user-&gt;threads_token);

$id = Threads::createText('test');
Threads::publish($id)
</code></pre>
<p>imageやvideoは公開されてるURLが必要。直接アップロードはできない。<br />
Storage内のファイルを使うならurlで指定する。</p>
<pre><code class="language-php">use Revolution\Threads\Facades\Threads;
use Illuminate\Support\Facades\Storage;

Threads::token($user-&gt;threads_token);

$id = Threads::createImage(url: Storage::url('cat.png'), text: 'test');
Threads::publish($id)
</code></pre>
<p>動画の場合は30秒待ったほうがいいらしいのでpublish時に何秒sleepを入れるか指定できる。publish時に待ってもいいし、<code>Threads::status()</code>でチェックしてもいいし、キューで30秒遅らせてもいい。</p>
<pre><code class="language-php">use Revolution\Threads\Facades\Threads;
use Illuminate\Support\Facades\Storage;

Threads::token($user-&gt;threads_token);

$id = Threads::createVideo(url: Storage::url('dog.mov'), text: 'test');
Threads::publish($id, sleep: 30)
</code></pre>
<p>2段階に分かれてるのは複数のimage, videoをまとめて投稿できるカルーセル機能のためと予想。</p>
<pre><code class="language-php">use Revolution\Threads\Facades\Threads;
use Illuminate\Support\Facades\Storage;

Threads::token($user-&gt;threads_token);

$id1 = Threads::createImage(url: Storage::url('cat1.png'), is_carousel: true);
$id2 = Threads::createImage(url: Storage::url('cat2.png'), is_carousel: true);
$id = Threads::createCarousel(children: [$id1, $id2], text: 'test');
Threads::publish($id)
</code></pre>
<p>一時変数を使うのはLaravelらしくない使い方になるけどカルーセルのことを考えるとこれが無難。</p>
<h2><a id="自分の投稿を取得" href="#自分の投稿を取得" class="mr-2" aria-hidden="true" title="Permalink">_</a>自分の投稿を取得</h2>
<pre><code class="language-php">use Revolution\Threads\Facades\Threads;

$posts = Threads::token($user-&gt;threads_token)-&gt;posts()-&gt;json('data');
</code></pre>
<p>v0.3以降、<code>posts()</code>の返り値は<code>Illuminate\Http\Client\Response</code>に変更。</p>
<pre><code class="language-php">        $posts-&gt;each(function (array $post) {
            //dump($post);
            // $post['text'] ?? '';
            // Arr::get($post, 'text');
        });
</code></pre>
<p>投稿データは「画像だけの投稿にはtextがない」とか意外と項目の抜けがあるので注意が必要。</p>
<p>取得件数のデフォルトは25。上限は100。limitで指定できる。</p>
<pre><code class="language-php">use Revolution\Threads\Facades\Threads;

$posts = Threads::token($user-&gt;threads_token)-&gt;posts(limit: 50);
</code></pre>
<p>全部省略できるけど<code>posts()</code>は引数が多くなった。<br />
名前付き引数が登場してから「配列で渡す」や「専用のクラスを渡す」よりも「省略すればいいだけなので引数を増やす」傾向が強くなった。</p>
<h2><a id="socialiteで認証" href="#socialiteで認証" class="mr-2" aria-hidden="true" title="Permalink">_</a>Socialiteで認証</h2>
<p>configと.envに追加。</p>
<p><code>config/services.php</code></p>
<pre><code class="language-php">    'threads' =&gt; [
        // Threads App ID
        'client_id' =&gt; env('THREADS_CLIENT_ID'),
        // Threads App Secret
        'client_secret' =&gt; env('THREADS_CLIENT_SECRET'),
        'redirect' =&gt; env('THREADS_REDIRECT_URL', '/threads/callback'),
    ],
</code></pre>
<p><code>.env</code></p>
<pre><code>THREADS_CLIENT_ID=
THREADS_CLIENT_SECRET=
THREADS_REDIRECT_URL=/threads/callback
</code></pre>
<p>ユースケースのコールバックURLのリダイレクト先を設定。<br />
httpsが必須。localhostでも保存はできるけど実際には動かないかもしれない。<br />
面倒なので最初からサーバーに公開して動かすのが早い。</p>
<p>コールバックURLリダイレクト：<code>https://example.com/threads/callback</code><br />
他2つは同じでも<code>https://example.com/</code>でもいい。必要なら後で設定。</p>
<p><code>routes/web.php</code>での例。</p>
<pre><code class="language-php">use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Laravel\Socialite\Facades\Socialite;
use Revolution\Threads\Facades\Threads;

Route::get('threads/redirect', function () {
    return Socialite::driver('threads')-&gt;redirect();
})-&gt;middleware('auth');

Route::get('threads/callback', function (Request $request) {
    if ($request-&gt;missing('code')) {
        dd($request);
    }

    /** @var \Laravel\Socialite\Two\User $user */
    $user = Socialite::driver('threads')-&gt;user();

    dump($user);

    $long_token = Threads::exchangeToken($user-&gt;token, config('services.threads.client_secret'))['access_token'];
    dump($long_token);

    $request-&gt;user()-&gt;fill(['threads_token' =&gt; $long_token])-&gt;save();

})-&gt;middleware('auth');
</code></pre>
<p>いつものSocialiteだけど<code>Threads::exchangeToken()</code>で短期トークンから長期トークンへの変換が必要。<br />
長期トークンさえ取得できれば後はThreadsテスターと同じ。</p>
<h2><a id="長期トークンの更新" href="#長期トークンの更新" class="mr-2" aria-hidden="true" title="Permalink">_</a>長期トークンの更新</h2>
<p>ここまでは省略してるけど使う時に毎回更新するか、タスクスケジュールで定期的に更新する。</p>
<pre><code class="language-php">use Revolution\Threads\Facades\Threads;

$token = Threads::token($user-&gt;threads_token)-&gt;refreshToken()['access_token'] ?? null;
$user-&gt;fill(['threads_token' =&gt; $token])-&gt;save();
</code></pre>
<h2><a id="更新" href="#更新" class="mr-2" aria-hidden="true" title="Permalink">_</a>更新</h2>
<ul>
<li>2025/06 v0.3以降のパッケージでは返り値をarrayから<code>Illuminate\Http\Client\Response</code>に変更しているので記事内のコードは古いかもしれない。</li>
</ul>
]]></description>
                <author><![CDATA[Invokable]]></author>
                <pubDate>Mon, 09 Jun 2025 11:47:11 +0900</pubDate>
                            </item>
            </channel>
</rss>
