コンテンツへ移動

リゾルバーの優先順位

title、description、canonical、robots、画像など、実際に使われるSEOの値はすべて、SEOResolver6つの層をマージして決定します。上位の層が優先され、nullが下位の層の値を上書きすることはないため、各ページに適切な値を出力できます。

6つの層

最下位(常に存在)から最上位(常に優先)までの順序は、次のとおりです。

#取得元主な用途
1サイト設定config/seo.phpsite_nametitle_suffixdefault_og_imagedefault_robotsなど)ブランド全体のデフォルト値
2DBのグローバルデフォルトモデルタイプを指定しないseo_defaultsのレコードデプロイせずに編集できる、サイト全体のデフォルト値
3モデルタイプのデフォルトモデルクラスを対象とするseo_defaultsのレコード「すべての商品にこのOG画像を使う」など
4ルートのデフォルトルート名を対象とするseo_defaultsのレコードモデルのない静的ページ(homecontact
5計算された値モデル自身の属性から取得titleからタイトル、excerptbodyからディスクリプションを取得するなどのフォールバック
6明示的に設定した値モデルのseo_metaレコード(saveSEO()編集者が手動で設定する値
php
use Rankbeam\Seo\Facades\SEO;

$seo = SEO::resolve($post);          // model page: layers 1-3 + 5-6
$seo = SEO::forRoute('contact');     // route page: layers 1-2 + 4

結果は不変のSEOData値オブジェクトとなり、すべてのレンダラー(Blade、配列、Inertia)が利用します。

計算によるフォールバック(第5層)

明示的な値がない場合、リゾルバーはモデルから値を取得します。

  • タイトル — モデルのtitleまたはname属性。
  • ディスクリプションseo.computed.description_fieldsに指定された属性を順に調べ、有効なテキストを含む最初のものを使います。デフォルトの順序は、excerptsummarydescriptionintroleadteasercontentbodytextarticleです。HTMLを除去し、エンティティをデコードして、単語の境界でテキストを切り詰めます。長さはseo.computed.description_max_lengthで指定し、デフォルトは160です。省略記号は追加しません。
  • Robots — モデルのgetSEORobots()フック、またはis_indexable属性から取得します。robotsとインデックス登録可否の制御を参照してください。
  • URLから取得する値getUrlForSEO()からcanonicalとog:urlを取得します。

robotsとインデックス登録可否の制御

モデルごとのnoindexは標準で利用でき、追加パッケージや特別なカラムの準備は不要です。robots用のメソッドは任意なので、HasSEOトレイト自体には宣言されていません。見落としやすい点ですが、リゾルバーはすでに次の3つの取得元に対応しています。優先順位の高い順に示します。

優先順位取得元
1明示的に設定したseo_meta.robots$page->saveSEO(['robots' => 'noindex,follow'])
2モデルの**getSEORobots(): ?stringフック**'noindex, nofollow'を返すか、次の取得元へ進む場合はnullを返す
3is_indexable属性(カラムまたはアクセサー)falsyの場合はnoindex, nofollow、truthyの場合はindex, follow
php
class Page extends Model
{
    use HasSEO;

    // Option A: let the resolver derive robots from a boolean flag.
    //   Schema::table('pages', fn ($t) => $t->boolean('is_indexable')->default(true));

    // Option B: compute it from your own state.
    public function getSEORobots(): ?string
    {
        return $this->status === 'draft' ? 'noindex, nofollow' : null;
    }
}

// Option C: set it explicitly per page (wins over A and B).
$page->saveSEO(['robots' => 'noindex, follow']);

実際に出力される内容

解決されたディレクティブは、<head>に出力される前に出力ポリシーで判定されます。<meta name="robots">タグが出力されるのは、ディレクティブがdefault_robotsと異なる場合だけです。デフォルトはindex,followなので、次のようになります。

  • インデックス登録可能なページ(index, followに解決されるページ)では、robotsタグを出力しません。タグがない状態を、クローラーはindex,followとして読み取ります。
  • インデックス登録不可のページでは、<meta name="robots" content="noindex, nofollow">を出力します。
  • デフォルトと異なるディレクティブ(noindexmax-snippet:-1unavailable_afterなど)は、入力した空白も保持してそのまま出力します。

常にタグを出力するには、seo.robots.emit_default = trueを設定してください。詳細はrobotsの出力ポリシーを参照してください。

値の解決後に適用されるポリシー

これらの処理は、どの層から値を取得したかにかかわらず実行されます。

  • タイトルの接尾辞 — 解決済みのタイトルがすでにtitle_suffixで終わっている場合を除き、接尾辞を追加します。ルートのデフォルトテンプレートにブランド名がすでに含まれる場合は、その接尾辞でテンプレートを終えると、「Brand — X | Brand」のような重複を防げます。
  • 正規URLからのクエリ除去 — モデルのURLや現在のURLから導出した正規URLでは、クエリ文字列を除去します。ただし、canonical.query_whitelistに指定されたキー(ページ分割されたアーカイブのpageなど)は保持されます。明示的に設定した正規URLは、そのまま保持されます。
  • SNS用画像の絶対URL化 — 保存した値が相対パスでも、og:imagetwitter:imageは常に絶対URLとして出力されます。Open Graphの仕様で必要とされる形式です。

どの層の値が採用されたかを確認する

Filamentパッケージでは、フィールドごとに取得元を表示します(手動/コンテンツからのフォールバック/モデル種別のデフォルト/グローバルデフォルト/サイト設定/URL から導出)。コードではSEOWarningEvaluatorが同じ手動値とフォールバック値の区別を提供するため、独自の管理画面インジケーターを作成できます。

rankbeam/laravel-seoはMITライセンスで公開されています。