2019-08-17に更新

LaravelでOGPを作る

TwitterにURLをシェアした時に表示される画像であるOGPは今やWebサービスを作る時に当たり前のものになってきました。しかもWebサービスの場合はだいたい投稿内容に応じて動的に生成を行うため、プログラムによる自動生成が必要となります。

各サービスで色々な作り方をされています。例えばPuppeteerというChromeを用いたライブラリを使用したり、ImageMagickなどのライブラリや、JavaScriptのcanvasを使う方法などです。

Laravel、というか素のPHPにもGDという標準ライブラリがあり、そちらでも簡単に作成することが出来ますのでその方法です。ちなみにこういうOGPを作成します。

要件

サーバーでGDが有効になっている必要があります。また、pngやjpeg等の必要なライブラリも有効になっている必要があり、且つ今回は文字を入れるのでfreetypeも有効になっている必要があります。phpinfoで各サポートが有効になっているかを確認してください。

素材を準備

今回はそれっぽく見せるために最初に背景画像を描画します。そのためまずぱくたそから適当な背景画像をお借りしました。また、フォントも必要ですので無心をお借りしています。

OGPの作り方

コードをそのまま載っけます。普通にPHPで画像を作っているだけです。例えば今回はPostControllerに追記します。

    public function ogp(Post $post)
    {
        // OGPのサイズ
        $w = 600;
        $h = 315;
        // 1行の文字数
        $partLength = 10;

        $fontSize = 30;
        $fontPath = resource_path('font/mushin.otf');

        // 画像を作成
        $image = \imagecreatetruecolor($w, $h);
        // 背景画像を描画
        $bg = \imagecreatefromjpeg(resource_path('image/HIRO95_yuubaenokage_TP_V4.jpg'));
        imagecopyresampled($image, $bg, 0, 0, 0, 0, $w, $h, 800, 533);

        // 色を作成
        $white = imagecolorallocate($image, 255, 255, 255);
        $grey = imagecolorallocate($image, 128, 128, 128);

        // 各行に分割
        $parts = [];
        $length = mb_strlen($post->title);
        for ($start = 0; $start < $length; $start += $partLength) {
            $parts[] = mb_substr($post->title, $start, $partLength);
        }

        // テキストの影を描画
        $this->drawParts($image, $parts, $w, $h, $fontSize, $fontPath, $grey, 3);
        // テキストを描画
        $this->drawParts($image, $parts, $w, $h, $fontSize, $fontPath, $white);

        ob_start();
        imagepng($image);
        $content = ob_get_clean();

        // 画像としてレスポンスを返す
        return response($content)
            ->header('Content-Type', 'image/png');
    }

    /**
     * 各行の描画メソッド
     */
    private function drawParts($image, $parts, $w, $h, $fontSize, $fontPath, $color, $offset = 0)
    {
        foreach ($parts as $i => $part) {
            // サイズを計算
            $box = \imagettfbbox($fontSize, 0, $fontPath, $part);
            $boxWidth = $box[4] - $box[6];
            $boxHeight = $box[1] - $box[7];
            // 位置を計算
            $x = ($w - $boxWidth) / 2;
            $y = $h / 2 + $boxHeight / 2 - $boxHeight * count($parts) * 0.5 + $boxHeight * $i;
            \imagettftext($image, $fontSize, 0, $x + $offset, $y + $offset, $color, $fontPath, $part);
        }
    }

ルーティングを設定

routes/web.phpに画像を表示するためのルーティングを設定します。これで http://localhost:8000/posts/1/ogp.png のようなURLでアクセスできるようになります。

Route::get('posts/{id}/ogp.png', 'PostController@ogp');

メタタグを設定

あとは詳細ページに下記のようなメタタグを追加しておけば詳細ページのURLをツイートするだけで画像が表示されるようになります。

@section('additionalMeta')
<meta property="og:image" content="{{ config('app.url') }}/posts/{{ $post->id }}/ogp.png">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="{{ config('app.url') }}/posts/{{ $post->id }}/ogp.png">
@endsection

(↑この例だと実際には共通レイアウトに @yield('addtionalMeta') を追記する必要があります)

確認

TwitterのCard validatorにそのURLを入力すれば正しく表示されるかテストが出来ます。

まとめ

全部プログラムで描画しているため改造する時のメンテナンス等は大変かもしれませんが、とりあえずぱぱっと作っておきたいときなどは非常に便利です。 Have a happy OGP life!

ツイッターでシェア
みんなに共有、忘れないようにメモ

だら@Crieit開発者

Crieitの開発者です。 Webエンジニアです(在宅)。大体10年ちょい。 記事でわかりにくいところがあればDMで質問していただくか、案件発注してください。 業務依頼、同業種の方からのコンタクトなどお気軽にご連絡ください。 業務経験有:PHP, MySQL, Laravel, React, Flutter, Vue.js, Node, RoR 趣味:Elixir, Phoenix, Nuxt, Express, GCP, AWS等色々 PHPフレームワークちいたんの作者

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント