他のLaravel SEOパッケージからの移行
すでに別のSEOパッケージを使っている場合も、Rankbeamへの切り替えは全面的な書き直しではなく、1日で進められる作業を目指しています。このガイドでは、よく使われるパッケージのAPIと保存方式を、Rankbeamの2つの基本機能、HasSEOトレイトとsaveSEO()に対応付けます。モデル単位でSEOデータを保存するパッケージ向けには、1コマンドで使えるインポーターも用意しています。
WordPressから移行する場合
コンテンツサイトをWordPress(YoastまたはRank Math)から移行する場合は、専用のWordPressからの移行ガイドを参照してください。CSVインポーターと稼働中のデータベースを読み取る方法を説明しています。
| 移行元 | データの保存先 | 移行方法 |
|---|---|---|
ralphjsmit/laravel-seo | seoのポリモーフィックテーブル | **php artisan seo:import-from ralphjsmit**とトレイトの置き換え |
artesaos/seotools | なし(実行時の値と設定) | コードを置き換え、saveSEO()または算出用ゲッターで値を設定 |
spatie/* | なし(schema-org / サイトマップのビルダー) | 補完する機能は残し、それ以外をRankbeamへ移す |
この中でSEOデータをデータベースのテーブルに永続化するのはralphjsmitだけなので、一括インポートするデータがあるのもこれだけです。他は実行時のタグビルダーであり、読み取るテーブルはありません。リクエストごとの呼び出しを、保存済みのseo_metaに置き換えます。
ralphjsmit/laravel-seoからの移行
ralphjsmit/laravel-seoは、モデルごとにポリモーフィックな行を1つ、seoテーブルに保存します。その構造はRankbeamのseo_metaに近く、整合性を保った冪等な一括インポートが可能です。
1. Rankbeamを併せてインストール
composer require rankbeam/laravel-seo
php artisan vendor:publish --tag=seo-config
php artisan vendor:publish --tag=seo-migrations
php artisan migrate移行中は両パッケージを併用できます。テーブル(seoとseo_meta)もトレイトの名前空間も異なります。
設定ファイルは共用です
ralphjsmit/laravel-seoから公開したconfig/seo.phpがアプリに残っていると、Rankbeamの設定より優先されます。両方がseo設定キーを使うためです。バックアップして削除し、Rankbeamの設定を再公開してください:php artisan vendor:publish --tag=seo-config。
2. インポーターを実行
# Preview first — writes nothing
php artisan seo:import-from ralphjsmit --dry-run
# Then import for real
php artisan seo:import-from ralphjsmitインポーターはralphjsmitのseoテーブルを読み取り、各行に対応する実際のEloquentモデルを解決して、seo_metaへ書き込みます。
| オプション | 動作 |
|---|---|
--dry-run | インポート予定の内容を報告し、何も書き込みません。 |
--model="App\Models\Post" | 1つ以上のモデルクラスに限定します。繰り返し指定できます。 |
--locale=fr | 指定ロケールの行として書き込みます。デフォルトはアプリのロケールです。 |
--table=legacy_seo | 名前を変更した移行元テーブルを読み取ります。 |
--connection=legacy | 別のデータベース接続から移行元テーブルを読み取ります。 |
--limit=100 | 最大N行をインポートします。段階的な移行に使えます。 |
--overwrite | 既存の空でない値を置き換えます。デフォルトでは空のフィールドだけを埋めます。 |
--json | 機械可読のレポートを出力します。 |
--force | 確認プロンプトを省きます。スクリプトやCI向けです。 |
処理は冪等です。再実行しても同じ行を更新し、重複を作りません。また、デフォルトでは空のフィールドを埋めるだけで、Rankbeamに設定済みのSEOデータを上書きしません。既存値をインポート値で置き換える場合は、--overwriteを指定してください。
3. モデルのトレイトを置き換え
ralphjsmitのトレイトをRankbeamのものに置き換えます。メソッド名は少し異なり、トレイトが読み取るテーブルはseo_metaになります。
// Before
use RalphJSmit\Laravel\SEO\Support\HasSEO;
// After
use Rankbeam\Seo\Traits\HasSEO;ralphjsmitのgetDynamicSEOData()でSEOデータをカスタマイズしていた場合は、そのロジックをRankbeamのフィールド別の算出用ゲッター(getSEOTitle()、getSEODescription()、getSEOImage()、getUrlForSEO()、getSEOAlternates())へ移します。クイックスタートを参照してください。保存する上書き値にはsaveSEO()を使います。
$post->saveSEO([
'title' => 'A hand-written SEO title',
'description' => 'A hand-written meta description.',
'canonical' => 'https://example.com/posts/my-post',
'robots' => 'noindex, nofollow',
'og_image' => 'https://example.com/og/my-post.jpg',
]);フィールドの対応
インポーターはフィールドを明示的に対応付けます。Core 3のスキーマに存在しないカラムを、無条件にコピーすることはありません。
ralphjsmitのseo | Rankbeamのseo_meta | 補足 |
|---|---|---|
model_type / model_id | seoable_type / seoable_id | そのままコピーせず、現在のモデルから再解決します。下記を参照してください。 |
title | title | 70文字(seo_metaカラムの長さ)に切り詰めます。超過した値は報告します。 |
description | description | 160文字に切り詰めます。超過した値は報告します。 |
canonical_url | canonical | |
robots | robots | 50文字に切り詰めます。 |
image | og_image | twitter:imageはリゾルバーを通して自動で引き継ぎます。 |
author | インポート対象外 | Core 3のseo_metaには著者カラムがありません。記事の著者は、保存するソーシャルメタデータではなくリゾルバー層で扱います。著者を持つ行を集計して報告するため、移行先(たとえばgetSEODataのような算出値)を判断できます。 |
id、created_at、updated_at | インポート対象外 | 構造上のフィールドです。 |
morph型を再解決する理由。 各移行元行から実際のモデルを解決し、モデル自身のgetMorphClass()からseoableキーを取得します。ralphjsmitが別の規則で保存していても、アプリの現在のmorphマップに従ってリレーションを維持できます。また、すでにモデルが削除された行はスキップして報告し、参照先のない行として書き込むことはありません。
レポートの内容
--jsonを使わない実行では、結果の表と、確認用の3つのセクションを表示します。
- Truncated(切り詰め) —
seo_metaカラムに収めるため短くした値です。確認してください。 - Not imported(未インポート) — 値はあったものの、Core 3に保存先がない移行元カラムです。
authorなどが該当します。 - Skipped rows by reason(理由別のスキップ行) — 空の移行元行、削除済みモデル、解決できないモデル型です。
検証
php artisan seo:audit # confirm the imported metadata looks right結果に問題がないことを確認したら、ralphjsmit/laravel-seoと、そのseoテーブルを削除します。
artesaos/seotoolsからの移行
artesaos/seotoolsは実行時のタグビルダーです。config/seotools.phpのデフォルトを基に、通常はコントローラー内で、SEOMeta、OpenGraph、TwitterCard、JsonLdファサードを通してリクエストごとに値を設定します。モデルごとの保存はないため、インポートするテーブルもありません。リクエストごとの呼び出しを、保存値または算出値へ移します。
| artesaos/seotoolsの呼び出し | Rankbeamでの対応 |
|---|---|
SEOMeta::setTitle($t) | saveSEO(['title' => $t])またはgetSEOTitle() |
SEOMeta::setDescription($d) | saveSEO(['description' => $d])またはgetSEODescription() |
SEOMeta::setCanonical($u) | saveSEO(['canonical' => $u])またはgetUrlForSEO() |
SEOMeta::addKeyword(...) | 対応するkeywordsメタタグはありません。フォーカスキーワードは内部の編集チェック用です。saveSEO(['focus_keywords' => [...]])(監査を参照) |
OpenGraph::setTitle / setDescription / addImage | saveSEO(['og_title' => …, 'og_description' => …, 'og_image' => …]) |
TwitterCard::setType / setTitle / setImage | saveSEO(['twitter_card' => …, 'twitter_title' => …, 'twitter_image' => …]) |
JsonLd::setType(...) / JsonLdMulti | JSON-LDスキーマグラフ |
config/seotools.phpのデフォルト | config/seo.phpのサイトデフォルトとリゾルバーの優先順位 |
レイアウトの{!! SEO::generate() !!} | @seo($model)(Bladeを参照) |
考え方が変わります。各コントローラーでタグを逐次設定する代わりに、SEOデータをモデルごとにseo_metaへ一度保存し、Rankbeamのリゾルバーが出力します。config/seotools.phpにあったサイト全体のフォールバックは、Rankbeamの設定デフォルトへ移します。ルートごとの静的ページには@seoForRoute()を使います。
Spatieパッケージからの移行
メタデータを保存するspatie/laravel-seoパッケージはないため、インポートするものはありません。SEOと組み合わせて使われるSpatieパッケージは補完的なビルダーであり、部分的に残したり置き換えたりできます。
spatie/schema-org— メソッドチェーンで使うJSON-LDビルダーです。Rankbeamにも、型付きのArticle、FAQPage、Product、BreadcrumbList、LocalBusiness、Organizationビルダーを持つスキーマグラフがあります。seo_meta.schema_jsonldに保存し、重複排除して出力します。手作業で組み立てたspatie/schema-orgオブジェクトは、その->toArray()出力をsaveSEO(['schema_jsonld' => $array])に渡すか、Rankbeamのビルダーで書き直せます。spatie/laravel-sitemap— サイトマップジェネレーターです。Rankbeamのサイトマップレジストリはこれを基にしています。モデルをソースとして登録し、Rankbeamに統合サイトマップを出力させることも、既存のSpatieサイトマップを残してRankbeamのルートを無効にすることもできます。
別の実行時・構造体ベースのメタデータビルダー、romanzipp/laravel-seoを使っていた場合も、artesaosと同じ方法です。リクエストごとのsetTitle/addMeta呼び出しを、saveSEO()または算出用ゲッターに移します。
インポーターの拡張
seo:import-fromコマンドの基盤は、Rankbeam\Seo\Importing\Contracts\Importer実装の小さなレジストリです。コマンドを変更せず、新しいソースを追加できます。組み込みソースは現在、ralphjsmitとWordPressインポーター(wordpress-csv、yoast、rank-math。WordPressからの移行を参照)です。独自のソースはサービスプロバイダーに登録します。
use Rankbeam\Seo\Importing\ImporterRegistry;
$this->app->afterResolving(ImporterRegistry::class, function (ImporterRegistry $registry) {
$registry->register('my-source', \App\Seo\MyImporter::class);
});