Laravelのサービスプロバイダやファサードといった機能を使うと、動的に生成した構造化マークアップのJSON LDデータをページに埋め込むことができます。
OrganizationやWebpageといった全てのページで同じ内容になる構造化データはもちろんのこと、各ページごとに内容が異なるパンくずの構造化マークアップや、Articleなど特定のページにのみで使う構造化データなど、ページ毎に内容を変えたり出し分けることも可能です。
ルーティングやコントローや、ベースとなる共通テンプレートに処理が書けるため、いちいち全てのページにJSON LDを書く必要がなくなります。
ファイルも構造化データとしてまとまるのでメンテナンスも簡単になります。
ここでは、そんな便利なサービスとファサードを使った構造化マークアップを作成する流について解説しています。
サービスとファサードを使った構造化マークアップの流れ
サービスとファサードを使った構造化マークアップの作成の大まかな流れは以下のようになります。
- 構造化マークアップ用のJSON LDデータを作成するクラスの作成
- サービスプロバイダに登録
- ファサードとして登録
- エイリアスを登録
- ヘルパーを作成
- ブレードに追記
処理をブレードに記述しても同じことはできますが、基本、ブレードには処理を極力記述したくないので、サービスとして登録して、どこでも呼び出せるようにしたり、ヘルパーとして別ファイルに処理を記述するようにします。
構造化マークアップ用のJSON LDデータを作成するクラスの作成
クラスを作成する場所はどこでもいいのですが、今回は、app > Services > JsonLd.php に作成します。
中身としては、配列データを便利に処理できるLaravelのコレクションという型を使います。
空のコレクションを格納する変数を用意して、その中に構造化マークアップのデータを追加していく構造です。
<?php
namespace App\Services;
class JsonLd
{
//構造化データを入れる変数を定義
private $collection;
//空のコレクションを格納
public function __construct() {
$this->collection = collect([]);
}
//Collectionへの追加
public function pushCollection($item) {
if (is_array($item)) {
$item = collect($item);
}
$this->collection->push($item);
return $this;
}
//Websiteの構造化データ
public function putWebSite() {
return self::pushCollection([
'@type' => 'WebSite',
'name' => 'サイト名',
'alternateName' => 'サイトの別名(英語表記など)',
'url' => 'https://example.com/',
'description' => 'サイトの説明',
'image' => [
'@type' => 'ImageObject',
'url' => 'サイトロゴのURL'
]
]);
}
//Organizatioの構造化データ
public function putOrganization() {
return self::pushCollection([
'@type' => 'Organization',
'name' => '組織名',
'url' => 'https://example.com/',
'address' => [
'@context' => 'https://schema.org',
'@type' => 'PostalAddress',
'addressLocality' => '新宿区',
'addressRegion' => '東京都',
'postalCode' => '111-1234',
'streetAddress' => '1-11-111, 新宿駅3F',
'addressCountry' => 'JP'
],
'foundingDate' => '2022-01-01(創立日)',
'founder' => '社長名',
'member' => '役員名',
'parentOrganization' => [
'@type' => 'Organization',
'name' => '親会社名',
'url' => '親会社のURL',
],
'description' => 'サイトの説明',
'telephone' => '+81-11-1111-1111',
'logo' => [
'@type' => 'ImageObject',
'url' => '会社のロゴURL',
],
]);
}
//パンくずの構造化データ
public function putBreadcrumbList($breadcrumbs) {
return self::pushCollection([
'@type' => 'BreadcrumbList',
'itemListElement' => $breadcrumbs->map(function($breadcrumb, $i) {
return ['@type'=>'ListItem','position'=>$i+1, 'name'=> $breadcrumb->title, 'item'=>$breadcrumb->url ];
}),
]);
}
//記事の構造化データ
public function putArticle($items) {
return self::pushCollection([
'@type' => 'Article',
'mainEntityOfPage' => [
'@type' => 'WebPage',
'@id' => '記事のURL',
],
'headline' => 'タイトル',
'image' => 'キービジュアルのURL',
'description' => '説明',
'datePublished' => '発行日',
'dateModified' => '更新日',
'author' => '著者',
'publisher' => [
'@type' => 'Organization',
'name' => '組織名',
'logo' => [
'@type' => 'ImageObject',
'url' => '組織のロゴURL',
],
],
]);
}
//コレクションをJsonに変換
public function renderJson() {
return json_encode($this->collection->map(function($item){
$item['@context'] = 'http://schema.org';
return $item;
});
}
}
ここでは例として、(1)Website, (2)Organization, (3)パンくず, (4)記事 の構造化データを作成しています。
他にも構造化データを追加したい場合は、関数を定義して、return self::pushCollection([追加する構造化データ])
としてコレクションの中にデータを追加するようにします。
OrganizationやWebsiteなど構造化データとして追加できるプロパティは他にもあります。そちらは、Schema.orgのページで確認し必要に応じてデータを追加・削除してください。
(参考)Schema.orgページの見方と使い方。入れ子や複数のプロパティを持つ構造化マークアップを実装する方法
このクラスを作成する時の重要なポイントは以下になります。
- 名前空間とファイルのパスを一致させる。
- 構造化データの編集はcollectionで行う。
- 最後に、collectionからJsonに変換する。
特に、名前空間とファイルのパスを一致させないとエラーになるので注意が必要です。詳細は以下をご参考ください。
(参考)【Laravel】クラスが見つからない時の対処法:Class not foundとdoes not comply with psr-4 autoloading standard. Skipping.
もう少し解説すると、上記の処理のコレクション部分は、3つの流れで構成しています。
- コレクションを格納する変数の定義
- 渡されたデータをコレクションとして変数に追加
- コレクションのデータをJSON LDに変換
//構造化データを入れる変数を定義
private $collection;
//空のコレクションを格納
public function __construct() {
$this->collection = collect([]);
}
//Collectionへの追加
public function pushCollection($item) {
if (is_array($item)) {
$item = collect($item);
}
$this->collection->push($item);
return $this;
}
//コレクションをJsonに変換
public function renderJson() {
return json_encode($this->collection->map(function($item){
$item['@context'] = 'http://schema.org';
return $item;
})->values());
}
}
サービスプロバイダに登録
サービスプロバイダーとは、一まとまりの処理を名前を付けて保管できるLaravelの機能です。これを使うと、処理の呼び出しを、自分で設定した名前で簡単に呼び出せます。
よく、サービスやサービスコンテナという言葉と一緒に使われます。この登録した処理がサービスで、そのサービスを保管しているのがサービスコンテナ。サービスを登録するのが、サービスプロバイダーになります。
関連する用語をまとめると以下になります。
用語 | 内容 | 備考 |
---|---|---|
サービス | 登録した処理 | app()->make(‘登録したサービス名’) で呼び出す。登録するのはクラス。 |
サービスコンテナ | サービスが保存されている場所 | Dockerのコンテナとは別物 |
サービスプロバイダ | サービスの登録 | AppServiceProvider.phpなどデフォルトで登録できるファイルがある。自分でも作成可能。 |
ファサード | クラスメソッドをインスタンスメソッドのように使える | インスタンス化の処理をしてくれる。(記述が一部省略できる)。登録時にサービス名を使う。 |
エイリアス | 省略した名前 | クラスをグローバル空間名で呼び出せる。 例 JsonLd:: (または \JsonLd::) |
ヘルパ | 自作のメソッドを登録 | 登録したメソッドはどこからでも呼び出せる。 |
サービスプロバイダを使ってサービスを登録していきます。自分でサービスプロバイダーを作成することもできますが、今回は、既存の app/Providers/AppServiceProvider.php を使います。
AppServiceProvider.phpを開き、register関数に処理を追記します。
public function register()
{
$this->app->singleton('サービス名', function ($app) {
処理;
});
}
ここで指定したサービス名を今後の呼び出しでも使います。
singletonメソッドは、登録したサービスの生成を1つのインスタンスしか行わない方法です。どこで、このサービスを呼び出しても同じインスタンスを参照します。
singletonの他に、bindメソッドもあります。bindメソッドで登録したサービスは、呼び出す毎に新しいインスタンスを生成します。
AppServiceProvider.phpは以下になります。
処理の中でJsonLdクラスのインスタンスを生成するので、上部でuse宣言をしておく必要があります。
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Services\JsonLd;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->app->singleton('JsonLd', function () {
return new JsonLd();
});
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
//
}
}
以上でサービスの登録が完了です。
この段階で、JsonLdを呼び出す場合は、appヘルパのmakeメソッドを使います。生成したインスタンスに対して「->メソッド名()」とすればメソッドを呼び出せます。
$obj = app()->make(‘JsonLd’);
$obj->putBreadcrumbList($breadcrumbs);
この記述では、app()->make(‘メソッド名’) という処理が必要になりますが、次のファサードとして登録することで、 App\Services\JsonLd::メソッド名() のように、よりわかりやすい記述で呼び出せるようになります。
ファサードの登録
ファサードとは、クラスをインスタンス化しなくても、静的(static)メソッドのように使える機能のことです。
名前空間\クラス名::メソッド() で呼び出せるようになります。
ポイントは「インスタンス化不要」という点。いちいちインスタンス化する手間を省けるため、コードの可読性が向上します。
聴き慣れない用語なので難しそうに感じますが、実態は単純な機能です。実際、そこまで大幅に処理を改善してくれる機能でもありません。あれば便利ぐらいのものです。
ちなみに、Facadeとは入り口といった意味。家の中のメソッドにアクセスして呼び出さなくても、入り口にアクセスすれば、そのメソッドを呼び出せるというニュアンスです。
Facadeの登録はとても簡単です。ポイントは以下の3点です。
作成するファイルの場所もファイル名も指定はありません。今回はわかりやすく、
app/Facades/JsonLdFacade.php
とします。(ファイル名は、JsonLdにすると最初に定義したクラスのファイルと被るので、Facadeを付けておくと管理しやすいです。)
名前空間と定義するクラス名は、ファイルのパスに合わせる必要があります。これをしないと、composerのdump-autoloadでエラーが発生しクラスを生成できません。
<?php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class JsonLdFacade extends Facade
{
protected static function getFacadeAccessor()
{
return 'JsonLd';
}
}
これでファサードの登録は完了です。とても簡単ですね。
ここまでの処理で、 App\Services\JsonLd::putBreadcrumbList($breadcrumbs) でメソッドを呼び出せるようになりました。
ただ、まだ長いのでよりシンプルに、JsonLd::メソッド() で呼び出せるようにします。
エイリアスの設定
グローバルな名前空間(クラス名のみ)で呼び出すためにはエイリアスを設定します。この処理もとても簡単です。
config/app.php ファイルの下の方に「’aliase’=>」と書かれた場所があるので、ここに1行追加します。
‘エイリアス名’ => 名前空間::class
'aliases' => [
//省略
'JsonLd' => App\Facades\JsonLdFacade::class,
],
以上で、JsonLd::メソッド() でどこからでも呼び出せるようになりました。記述はとてもシンプルです。
グローバルな名前空間であることを明示する場合は、クラス名の冒頭に「バックスラッシュ \」を付けて記述することもあります。どちらも同じ処理です。
\JsonLd::メソッド()
ここまでの変遷をまとめると以下になります。
項目 | メソッドの呼び出し |
---|---|
サービス登録 | app()->make(‘JsonLd’)->メソッド() |
ファサード登録 | App\Services\JsonLd::メソッド |
エイリアス登録 | JsonLd::メソッド |
ヘルパを作成
ここまで作成できたら、登録したファサードのエイリアス(長いので以下ファサード)を使って、ビューの中に処理を記述することができます。
ただし、ビューにPHPの処理を記述することは極力避けたいので、ヘルパー関数を作って別に切り出すようにします。
ヘルパとは、自分で登録できるメソッドのことです。登録しておけば、どこからでも呼び出すことができます。サイト構築を助けてくれるのでヘルパーです。
WebsiteやOrganizationといった構造化マークアップは全ページ共通で特に処理を加える必要がありません。
ここでは、パンくずを生成する場合の記述を書いていきます。なお、ここではパンくずはLaravel-breadcrumbsを使って、構造化マークアップのデータを生成する方法を記述しています。
ヘルパの作成手順
ヘルパ作成の大まかな流れは以下になります。
- ヘルパ用のファイルを作成(場所や名前は任意)
- 関数を定義する
- composer.jsonのautloadにファイルパスを追記
- dump-autoloadで読み込みを更新
ヘルパ用のファイルを作成
まずは、ヘルパを記述するファイルを作成します。場所や名前はどこでもいいです。今回は、
app > helper.php とします。
関数を定義する
helper.phpの中に関数を定義します。ヘルパの記述は、最初にif文で、これから作成しようとしている関数が既に登録されていなか確認します。
デフォルトで用意されているメソッドも上書きできてしまうので、その回避処理です。
<?php
if (! function_exists('ヘルパ名')) {
function ヘルパ名(引数)
{
//処理;
}
}
今回は、構造化マークアップの処理を記述する。パンくずのみなのでとてもシンプルだが、実際に使うときは、OrganizatinoやArticleなど他の構造化の処理もここに記述する。(例えば、ページによって切り分ける場合は、if文でルート名を指定するなど。)
if(! function_exists( "structured_markup" )){
function structured_markup($breadcrumbs){
//パンくず構造化マークアップ
JsonLd::putBreadcrumbList($breadcrumbs);
}
}
composer.jsonのautloadにファイルパスを追記
作成したファイルがヘルパとして読み込まれるように、composer.jsonのautloadにファイルパスを追記します。
"autoload": {
"files": [
"app/helpers.php"
],
}
dump-autoloadで読み込みを更新
composerのdump-autoloadコマンドを実行して、ファイルをリロードします。
$ composer dump-autoload
以上で、ヘルパーの作成は完了です。
ブレードに追記
最後にブレードで処理を呼び出します。
WebsiteやOrganization、パンくずの構造化マークアップはすべてのページに設置するものなので、共通テンプレートに処理を記述します。
(パンくずでslugを必要とするページがある場合は、if文で条件分岐させる。)
ブレードの例は以下のようになります。
@php
//Websiteの構造化データを追加
\JsonLd::putWebSite();
//Organizationの構造化データを追加
\JsonLd::putOrganization();
//パンくずの生成
$currentRoute = route()->getName();
$breadcrumbsHtml = Breadcrumbs::render( $currentRoute );
$breadcrumbs = Breadcrumbs::generate( $currentRoute );
//Breadcrumbsの構造化データ生成
structured_markup($breadcrumbs);
@endphp
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<meta name="description" content="説明">
<meta name="keywords" content="キーワード">
<title>タイトル</title>
{{-- Structured Markup --}}
<script type="application/ld+json">
{!! JsonLd::renderJson() !!}
</script>
</head>
<body">
@include('layouts.header')
{{ $breadcrumbsHtml }}
<div class="@yield('contentClass')">
@yield('content')
</div>
@include('layouts.footer')
</body>
</html>
{!! JsonLd::renderJson() !!} は、生成した構造化マークアップのコレクションをJSONに変換し出力しています。
{{ $breadcrumbsHtml }} は、パンくずのHTMLを出力しています。
以上で記述は完了です。
構造化マークアップのチェック
実装が完了したら、構造化マークアップが正しく組めているか、Googleのリッチリザルトチェックツールで確認します。
ブラウザをリロードして、ソースコードを開き、script type=”application/ld+json”となっているところをコピーします。
リッチリザルトチェックツールのコード検証に貼り付けて実行する。「リッチリザルトの対象です」と表示され、データが意図した通りに認識されていれば完了です。