コンテンツへ移動

Bladeのガイド

通常のサーバーサイドレンダリングを行うアプリ向けに、このパッケージは7種類のBladeディレクティブを提供しています。多くの場合、そのうちの@seoだけで対応できます。

まとめて出力するディレクティブ

blade
<!DOCTYPE html>
<html>
<head>
    @seo($post)
</head>

@seo優先順位に従ってモデルの値を解決し、head内に必要な一式を出力します。対象は<title>、meta description、canonicalリンク、robots、Open Graphタグ、Twitter Cardタグ、付随するJSON-LDです。robotsタグはサイトのデフォルト値と異なる場合にのみ出力されます。省略時もindex,followとして扱われるため、冗長なindex,followは出力されません。常に出力するにはseo.robots.emit_defaultを設定してください。詳しくは出力の共通仕様を参照してください。

呼び出し形式:

blade
@seo($post)                  {{-- model page --}}
@seo($seoData)               {{-- a hand-built SEOData (model-less page) --}}
@seo($post, 'blog.show')     {{-- model + route defaults --}}
@seo($post, null, 'fr')      {{-- model + locale --}}
@seo(null)                   {{-- current page, no model --}}

@seoには、Model、手動で作成したSEOData、またはnullを渡せます。ルートとロケールの引数が適用されるのは、Modelまたはnullを渡した場合だけです。手動で作成したSEODataでは、そのオブジェクト自身の値が使われます。

ルートに対応するページ(モデルなし)

静的ページ、アーカイブなど、モデルではなくルートに対応するページでは、次のように呼び出します。

blade
@seoForRoute('pages.about')
@seoForRoute('contact', 'de')   {{-- with locale --}}

ルート用の値は、ルート名を対象とするseo_defaultsのレコードから取得されます。

モデルのないページ:SEODataを手動で作成する

一覧、検索結果、コントローラーで組み立てるページには、対応する単一のモデルがないことがあります。SEODataを作成し、@seoまたはSEOファサードにそのまま渡してください。app(TagRenderer::class)->render(...)を直接呼び出す必要はありません。

php
use Rankbeam\Seo\Data\SEOData;

return view('search.results', [
    'seo' => new SEOData(
        title: "Results for \"{$query}\"",
        description: "Browse {$count} matches for {$query}.",
        ogImage: '/images/search-share.jpg',   // relative is fine — see below
    ),
]);
blade
<head>
    @seo($seo)
</head>

手動で作成したSEODataは、明示的な指定として扱われます。設定した値はすべて保持され、出力時に不足する次の値だけが補われます。

  • canonicalog:urlが未設定の場合は、現在のURLから取得されます。明示的に設定したcanonicalは、クエリ文字列も含めてそのまま保持されます。
  • title_suffixは、タイトルにまだ含まれていない場合にのみ追加されます。タイトルにブランドを表すトークンがすでに含まれている場合は、追加自体が省略されます。title_suffix_skip_when_containsを参照してください。
  • 相対パスのog:imagetwitter:imageは、url()で絶対URLに変換されます。現在のスキームが使われ、HTTPSが強制されるわけではありません
  • og:site_namelocaleは、設定値とアプリのロケールから補われます。

データベース上の優先順位に含まれるグローバル、モデルタイプ、ルート、seo_metaのデフォルト値は、手動で作成したSEODataにはマージされません。渡した値に、上記の不足分だけを補って出力します。

同じオブジェクトをファサードにも渡せます。

php
SEO::render($seoData);     // HTML string
SEO::toArray($seoData);    // Vue/React structure
SEO::forInertia($seoData); // Inertia Head structure

複数のページ種別に対応するレイアウト

モデルのページ、ルートのページ、それ以外のページを1つのレイアウトで扱えます。

blade
<head>
    @if(isset($seoModel))
        @seo($seoModel)
    @elseif(isset($seoRoute))
        @seoForRoute($seoRoute)
    @else
        @seo(null)
    @endif
</head>

コントローラーから'seoModel' => $postまたは'seoRoute' => 'blog.index'を渡すだけで、マークアップを変更する必要はありません。

個別に出力するディレクティブ

他のパッケージの出力と組み合わせる場合など、タグを個別に制御したいときは、次のディレクティブを使います。

ディレクティブ出力内容
@seoTitle($post)<title>のみ
@seoMeta($post)meta descriptionのみ
@seoCanonical($post)canonicalリンクのみ。未設定時は現在のURLを使用
@seoRobots($post)robots metaのみ。明示的に出力を選ぶ呼び出しなので、常に出力されます。@seoとは異なり、デフォルト値との一致による出力抑制は適用されません
@seoSchema($post)JSON-LDの<script>のみ。head内でもbody内でも有効

いずれも@seoと同じ($model, $route, $locale)形式の引数を受け取ります。引数を省略すると、現在のページが対象になります。

hreflangによる代替ページ

HasSEOを使うモデルは、リゾルバーを通じてhreflangリンクを直接提供できます。

php
public function getSEOAlternates(): ?array
{
    return [
        ['hreflang' => 'en', 'href' => route('posts.show', ['locale' => 'en', 'post' => $this])],
        ['hreflang' => 'it', 'href' => route('posts.show', ['locale' => 'it', 'post' => $this])],
    ];
}

絶対URLを使ってください。@seo($post)はこれらのエントリーを解決し、それぞれを<link rel="alternate" hreflang="..." href="...">として出力します。コードは最初にBCP 47形式へ変換されます(it_ITit-IT)。seo.hreflangのポリシーによって、ページ自身への参照とx-defaultを追加することもできます。無料の監査機能は、無効なエントリー、重複、自己参照の欠落を検出します。多言語コンテンツを参照してください。

エスケープと安全性

テキスト値はe()でエスケープされます。JSON-LDはJSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMPを指定してエンコードされるため、ユーザーのコンテンツに</script>が含まれていても、script要素を途中で閉じることはできません。

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