むだいありー

LaavelとContentfulでブログを作り始めた #2

前回

LaavelとContentfulでブログを作り始めた #1

前回ではトップページで呼ばれるControllerでContentfulからブログポストを取得したので、それをbaldeで表示する話。

トップページ

本当はPCとSPで同じソースコードにしたかったけど、事前にデザイン決めてない+スキル不足により断念...。 cssのメディアクエリでdisplayプロパティを切り変えて表示を変更している。

home.blade.php

@extends('layouts.default')

@section('stylesheet')
    <link href="{{ App\Helpers\AssetHelper::assetWithTimestampQuery('css/home/style.css') }}" rel="stylesheet"/>
@endsection

@section('main_content')
    <article>
        <section>
            <div class="content_wrapper">
                <div class="blog_posts pc">
                    @foreach($posts as $post)
                        <div class="blog_card">
                            <div class="hero_img_wrap">
                                <img src="{{ $post['hero_image'] }}"/>
                            </div>
                            <div class="blog_content_wrap">
                                <div class="title">{{ $post['title'] }}</div>
                                <div class="description"><p>{{ $post['description'] }}</p></div>
                                <div class="read_more_button">
                                    <a href="{{ url("posts/${post['slug']}") }}">READ MORE</a>
                                    <span class="publish_date">{{ $post['publish_date'] }}</span>
                                    @if(!empty($post['last_mod_date']))
                                        <span class="last_mod_date">(<img class="time_img" src="{{ asset('images/update-post.svg') }}"/>{{ $post['last_mod_date'] }})</span>
                                    @endif
                                </div>
                            </div>
                        </div>
                    @endforeach
                </div>
                <div class="blog_posts sp">
                    @foreach($posts as $post)
                        <div class="blog_card">
                            <div class="blog_content_wrap">
                                <div class="title">{{ $post['title'] }}</div>
                                <div class="hero_img_wrap">
                                    <img src="{{ $post['hero_image'] }}"/>
                                </div>
                                <div class="description"><p>{{ $post['description'] }}</p></div>
                                <div class="read_more_button">
                                    <a href="{{ url("posts/${post['slug']}") }}">READ MORE</a>
                                </div>
                                <div class="date">
                                    <span class="publish_date">{{ $post['publish_date'] }}</span>
                                        @if(!empty($post['last_mod_date']))
                                            <span class="last_mod_date">(<img class="time_img" src="{{ asset('images/update-post.svg') }}"/>{{ $post['last_mod_date'] }})</span>
                                        @endif
                                </div>
                            </div>
                        </div>
                    @endforeach
                </div>
            </div>
        </section>
    </article>
@endsection

layouts/default.balde.php

<!DOCTYPE html>
<html>
    <head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# {{ $ogpHeadPrefix }}: http://ogp.me/ns/{{ $ogpHeadPrefix }}#">
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
        @if (isset($metaTags))
            @foreach($metaTags as $metaTag)
                <meta name="{{ $metaTag['name'] }}" content="{{ $metaTag['content'] }}">
            @endforeach
        @endif
        @if (isset($ogpTags))
            @foreach($ogpTags as $ogpTag)
                <meta property="{{ $ogpTag['property'] }}" content="{{ $ogpTag['content'] }}">
            @endforeach
        @endif

        <link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet">
        <link href="{{ App\Helpers\AssetHelper::assetWithTimestampQuery('css/app.css') }}" rel="stylesheet"/>
        @yield('stylesheet')

        @hasSection('page_name')
            <title>@yield('page_name') | {{ config('app.name') }}</title>
        @else
            <title>{{ config('app.name') }}</title>
        @endif

        <link rel="apple-touch-icon" type="image/png" href="{{ asset('images//apple-touch-icon-180x180.png') }}">
        <link rel="icon" type="image/png" href="{{ asset('images/icon-192x192.png') }}">
    </head>
    <body>
        @include('parts.header')
        <main>
            @yield('main_content')
            @include('parts.side_content')
        </main>
        @include('parts.footer')
        @yield('script')
        <script src="{{ App\Helpers\AssetHelper::assetWithTimestampQuery('js/app.js') }}" type="text/javascript"></script>
    </body>
</html>

default.blade.phpは他のページでも使うので、渡される変数を後述のViewComposerを作成して用意している。

App\Helpers\AssetHelper::assetWithTimestampQueryはLaravel既存のasset関数をラップしたもので、ファイル更新日のクエリを付与して返すようにしたもの。 キャッシュでスタイル崩れるのを回避。

public static function assetWithTimestampQuery(string $path, bool $secure = null)
    {
        $timestamp = '';
        if (File::exists($path)) {
            $timestamp = File::lastModified($path);
        }
        $fullPath = asset($path, $secure);

        return "${fullPath}?v=${timestamp}";
    }

ページで共通の変数

default.blade.phpで使用する変数をViewComposerで用意する。 先ずは変数を作成する中身を作成。

app/Http/ViewComposers/DefaultLayoutViewComposer.php

<?php

namespace App\Http\ViewComposers;

use Illuminate\Support\Facades\Route;
use Illuminate\View\View;

/**
 * Class DefaultLayoutViewComposer
 * @package App\Http\ViewComposers
 */
class DefaultLayoutViewComposer
{
    protected const OGP_HEAD_PREFIX_WEBSITE = 'website';
    protected const OGP_HEAD_PREFIX_ARTICLE = 'article';

    protected $ogpHeadPrefix;

    public function __construct()
    {
        $this->ogpHeadPrefix = Route::currentRouteName() === 'home' ? self::OGP_HEAD_PREFIX_WEBSITE : self::OGP_HEAD_PREFIX_ARTICLE;
    }

    /**
     * @param View $view
     */
    public function compose(View $view)
    {
        $view->with([
            'ogpHeadPrefix' => $this->ogpHeadPrefix
        ]);
    }
}

こいつでheadタグ内で使う変数を用意。

そして、登録。 app/Providers/ViewComposerServiceProvider.php

<?php

namespace App\Providers;

use App\Http\ViewComposers\DefaultLayoutViewComposer;
use Illuminate\Support\ServiceProvider;

class ViewComposerServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        \View::composers([
            DefaultLayoutViewComposer::class => 'layouts.default'
        ]);
    }
}

bootメソッドで指定したbladeが呼ばれるときは、対応するclassが呼び出されてcomposeメソッドが実行される。 これでOpen Graph Protcol用のタグをページ毎に生成している。

次回

次回は、ブログポスト表示ぺージでContentfulのMarkdownを表示する話。