Filamentの管理画面フィールド
無料のrankbeam/laravel-seo-filamentパッケージは、FilamentリソースのフォームにSEOセクションを追加します。リソースごとに2行で組み込めます。Filament 4.xと5.x(Livewire 3と4)に対応しています。メタデータの編集は無料です。Proを追加すると、スキャンと下の例にあるスコアを利用できます。
前提条件
既存のFilament 4または5のパネルと、CoreのHasSEOトレイトを使うモデルが必要です。エディターを追加する前に、マイグレーションと出力設定を含めてCoreのクイックスタートを完了してください。
インストール
composer require rankbeam/laravel-seo-filamentリソースが扱うモデルは、CoreのHasSEOトレイトを使う必要があります。
リソースにセクションを追加
use Rankbeam\Seo\Filament\Concerns\HasSEOFields;
class PostResource extends Resource
{
use HasSEOFields; // 1
public static function form(Schema $schema): Schema
{
return $schema->components([
TextInput::make('title'),
// ...
static::seoSection(), // 2
]);
}
}保存結果を確認
既存のレコードを開き、SEOディスクリプションを入力して保存し、フォームを再読み込みします。値が保存され、プレビューに表示され、値の取得元が手動(英語の画面ではManual)になっていることを確認してください。出力されたページの<head>も確認し、同じディスクリプションが訪問者に届いていることを確かめます。

Merchantデモの例です。フィールドにはパネルのテーマが適用されます。利用できる操作と文字数の目安は、インストール済みのバージョンと設定によって異なります。
セクションには次の項目があります。
- タイトルとディスクリプションには、入力に合わせて更新される文字数カウンターがあります。目安は、入力中の文字体系に応じたCoreの文字数ポリシーに従います。ラテン文字は60/160、CJKは約30/80で、書記素単位で数えます。
- フォーカスキーワードはタグ入力です。通常のキーワードを入力すると、Coreの構造化された
[{keyword, is_primary}]形式で保存されます。先頭が主キーワードで、getPrimaryKeyword()とSEODataはそのまま読み取れます。seo.keywords.enabledを有効にすると、seo:auditコマンドとProスキャンが、キーワードのないページを指摘します。デフォルトでは無効で、両方を1つの設定で制御します。設定を参照してください。 - 正規URL。空欄なら自動で決まり、クエリ文字列は除去されます。
- Robotsの選択。空欄ならサイトのデフォルトを使います。
- ソーシャル共有画像のアップロード(og:image / twitter:image)。Filamentのデフォルトディスクの
seo/に保存されます。 - 検索スニペットのプレビュー。入力中も、リゾルバーのフォールバック連鎖を反映します。
- 取得元の表示。各フィールドの実効値を決めたリゾルバーの層を示します。手動、コンテンツからのフォールバック、モデル種別のデフォルト、グローバルデフォルト、サイト設定、URLから導出です。
表示フィールドを限定
static::seoSection(['title', 'description'])title、description、focus_keywords、canonical、robots、og_imageから任意の組み合わせを指定できます。
トレイトを使わない場合は、SEOFields::make(?array $only)が同じセクションを直接返します。
値の保存方法
セクションはseo_metaの状態グループにバインドされ、CoreのseoMeta()リレーションを通して更新または作成します。アプリ自身のテーブルにカラムを追加する必要はありません。保存値は直ちにリゾルバーの第6層(明示的な値)になります。
複数の言語
Coreはモデルとロケールの組み合わせごとにseo_meta行を1つ保持します。ページを公開するロケールを渡すと、セクションは言語ごとに1つのタブを表示します(Filament 1.9)。
static::seoSection(locales: ['en', 'it', 'ja']);
// or, without the trait
SEOFields::make(locales: ['en', 'it', 'ja']);すべてのリソースに一括で適用する場合は、パッケージ設定で指定します。
php artisan vendor:publish --tag=seo-filament-config// config/seo-filament.php
'locales' => ['en', 'it', 'ja'],各タブは自分の行を編集し、次の表示も個別に持ちます。
- カウンターは、その言語の文字体系に応じた文字数ポリシーを使います。同じページでも、空の日本語タイトルは
0 / 30、英語タブは0 / 60と表示されます。 - プレビュー(SERP / ソーシャルカード)は、そのロケールの解決済みの値から出力します。
- フォールバックの表示は、そのロケールの行について説明します。
- バッジは、その言語版で設定済みのフィールド数を示し、未入力の翻訳を見つけやすくします。
ext-intlが読み込まれていれば、タブにはパネルの表示言語で言語名(Italiano / Italian)を表示します。それ以外はコードを表示します。すべてのタブをまとめて検証し、保存します。何も入力していない言語に、空の仮行を作ることはありません。
フォームの状態を独自にバインドする場合
複数ロケールの状態パスはseo_meta.{locale}.titleです。1ロケールではseo_meta.titleのままです。独自のフォームアクションでは、対応するパスを使ってください。

2026年9月9日のMerchantデモ。locales: ['en', 'it', 'ja']を指定しています。空の日本語タブは専用のカウンターを使います。ここに表示される英語タイトルは、デモモデルのコンテンツからのフォールバックです。言語タブを追加しても、コンテンツは翻訳されません。フィールド上部のProスコアはレコードの最後のスキャン結果であり、言語タブごとの個別スコアではありません。
翻訳対応プラグインとの併用
lara-zeus/spatie-translatableの1.xをFilament 4で、または2.xをFilament 5で使う場合は、編集・作成ページにRankbeamのページアダプターを使ってください。置き換えるのはページのトレイトのインポートだけです。プラグインのリソース・一覧用トレイト、パネルプラグイン、LocaleSwitcherアクションは保持します。
// In your EditPost page:
use Rankbeam\Seo\Filament\Resources\Pages\EditRecord\Concerns\Translatable;
// In your CreatePost page (a separate file):
use Rankbeam\Seo\Filament\Resources\Pages\CreateRecord\Concerns\Translatable;各ページのクラス内には、引き続きuse Translatable;を宣言します。プラグインは任意のアプリ依存関係です。最新の修正版を使ってください。ローカルの統合テスト用構成では、プラグイン1.0.4 / Filament 4.13.1と、プラグイン2.0.1 / Filament 5.8.1を検証しています。
言語を切り替えても、未保存の親コンテンツ、SEOメタデータ、構造化データの下書きをエディター内に保持します。保存時は、編集で開いたすべての言語を検証し、データベーストランザクション内でまとめて保存します。検証エラーがあれば、対応が必要な言語を開きます。アップロードは保存時に格納されます。ページを離れたり再読み込みしたりすると、未保存の下書きは失われます。下書きを保存しても、欠けているコンテンツは翻訳されません。
アダプターは、通常の前後フックとフォームデータのミューテーターを保持します。ページでhandleRecordCreation()、handleRecordUpdate()、callHook()またはトランザクション関連メソッドを上書きしている場合は、そのカスタマイズにアダプターの動作を組み込み、保存の流れをテストしてください。データベーストランザクションはファイルシステムへの書き込みを巻き戻しません。アプリでは、従来どおり参照されなくなったファイルを削除する処理を維持してください。
Livewire 3で独自のライブテキストフィールドを使う場合は、明示的なdebounce指定より->live()または->live(onBlur: true)を推奨します。明示的な指定はローカルのモデル状態の更新を遅らせ、素早く言語を切り替えると末尾の入力が失われることがあります。Rankbeamのタイトルとディスクリプションは、リクエストのデフォルトのdebounceを使います。
上流プラグインのページトレイトだけでは、言語切り替え時にフォームを再入力します。Rankbeamは、その処理による意図しないメタデータの書き込みを防ぎますが、それらのトレイトはSEOの下書きを保持しません。編集・作成ページをアダプターへ移行してください。明示的なlocales:タブは引き続き共有エディターとなり、ページの言語切り替えより優先されます。
明示的なロケール一覧もページのロケールもない場合、セクションはアプリのロケールを編集します。
構造化データ(schema.org)
任意の構造化データセクションでは、編集者がコードを触らずにリッチリザルト用のJSON-LDスキーマを付けられます。SEOセクションの横に追加します。
public static function form(Schema $schema): Schema
{
return $schema->components([
// ... your fields ...
static::seoSection(),
static::seoSchemaSection(), // optional
]);
}トレイトなしで、SEOSchemaFields::make()を直接使うこともできます。
このセクションはCoreのseo_meta.schema_jsonldカラムに書き込みます。スキーマレンダラーが出力するものと同じ値です。役割はUIのバインドだけで、各ドキュメントはCoreのスキーマビルダーが生成し、保存前にCoreのSchemaValidatorが検証します。独自のスキーマロジックは追加しません。
セクションには次の機能があります。
- 自動パンくずリストは、設定不要で始められる先頭の切り替え項目です。
BreadcrumbSchema::fromModelAncestors()を通して、レコードの親の連鎖からBreadcrumbListを導出します。入力する項目はなく、モデルの祖先をたどります。 - スキーマブロックは繰り返し入力です。各ブロックはFAQ(質問と回答の組 →
FAQPage)またはProduct(名前、説明、画像、ブランド、SKU、価格と通貨、在庫状況 →Product)で、CoreのFAQSchema/ProductSchemaビルダーが生成します。
検証
不正なJSON-LDになるブロックは、Coreのバリデーターのメッセージとともに保存時に拒否されます。たとえば、回答のないFAQ項目、画像やオファーのないProductです。このビルダーではそれらのフィールドが必須ですが、GoogleのすべてのProduct検索機能の要件を網羅した説明ではありません。空のままのブロックは無視します。
保存される内容
schema_jsonldには、生成したドキュメントを保持します。1つなら単一オブジェクト、複数ならJSON配列で、パンくずリスト、その後に指定したブロックの順です。どちらも有効なJSON-LDであり、@seo / renderSchema()を通してそのまま出力されます。
このエディターで管理しないスキーマ
コードで作成した、このエディターでは表現できないスキーマはそのまま保持します。手書きの@graph、特殊な@type、フォームにないフィールド(レビュー、評価、GTIN/MPN)を持つProductなどです。フォームを開いて保存しても、これらを上書きして壊すことはありません。
トラブルシューティング
- **保存したフィールドがページにない:**同じレコードとロケールに対して、テンプレートが
@seo($model)を出力しているか確認します。 - **フィールドが引き続きフォールバックを使う:**現在の言語に、そのフィールドの上書き値が保存されているか確認します。取得元の表示で、解決された層を特定できます。
- **言語タブがない:**明示的な
locales:引数、パッケージ設定、ページ単位の翻訳切り替えを確認します。優先順位は上記のとおりです。
Testbenchで独自のパネルをテストする場合
orchestra/testbench内でFilamentを起動する場合は、FilamentのSupportServiceProviderをLivewireServiceProviderより前に登録してください。FilamentはLivewireのDataStoreを再バインドするため、順序が逆だとすべてのLivewireテストがViewErrorBag::put(): ... null givenで失敗します。実際のアプリには影響しません。パッケージ検出がプロバイダーを正しく並べます。