PHP
laravel
laravel5

Laravel5 チュートリアル ブログもどきを作る(6) ブログ記事一覧を表示する

前回に引き続いて、今回はブログ記事の一覧画面を作成します。

ルーティングの追加

ブログ記事一覧画面を表示するURL(ルーティング)を設定します。routes/web.php を開いて下記1行を追加します。

routes/web.php
Route::get('admin/list', 'AdminBlogController@list')->name('admin_list');

ところで、これまで設定してきたルーティングについて、全て URL に admin が付いています。なので、これを下記のように編集して、ルートグループとして、まとめてしまいましょう。

routes/web.php
// 下記のようにルーティングをグループ化することもできる
// prefix メソッドを使って、グループ内の全てのルートの URI に admin を付ける
Route::prefix('admin')->group(function() {
    Route::get('form/{article_id?}', 'AdminBlogController@form')->name('admin_form');
    Route::post('post', 'AdminBlogController@post')->name('admin_post');
    Route::post('delete', 'AdminBlogController@delete')->name('admin_delete');
    Route::get('list', 'AdminBlogController@list')->name('admin_list');
});

prefix メソッドの他にも、middleware メソッドでミドルウェアを指定したり、namespace メソッドで、名前空間を指定したりもできます。詳しくは日本語ドキュメントを参照してください。

モデルの編集

ブログ記事一覧を取得するメソッドを追加します。app/Models/Article.php を開いて、下記のメソッドを追加します。

app/Models/Article.php
    /**
     * 記事リストを取得する
     *
     * @param  int $num_per_page 1ページ当たりの表示件数
     * @return Illuminate\Pagination\LengthAwarePaginator
     */
    public function getArticleList($num_per_page = 10)
    {
        // Eloquent モデルはクエリビルダとしても動作するので、orderBy メソッドも paginate メソッドも利用できる
        // paginate メソッドを使うと、ページネーションに必要な全件数の取得やオフセットの指定などは全部やってくれる
        return $this->orderBy('article_id', 'desc')->paginate($num_per_page);
    }

paginate() メソッドは、page パラメータの値から、現在のページが何ページ目かを自動的に判断し、全件数を取得、1ページあたりの表示件数から、オフセットの値を指定し、ページネーションのリンクを生成します。つまり、ページネーションに関することは何でもやってくれます。

コントローラーの編集

ブログ記事一覧画面を表示するためのメソッドをコントローラーに追加します。app/Http/Controllers/AdminBlogController.php を開いて、下記メソッドを追加します。

app/Http/Controllers/AdminBlogController.php
    // 1ページ当たりの表示件数
    const NUM_PER_PAGE = 10;

    ... 中略 ...

    /**
     * ブログ記事一覧画面
     *
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
     */
    public function list()
    {
        $list = $this->article->getArticleList(self::NUM_PER_PAGE);
        return view('admin_blog.list', compact('list'));
    }

先ほど作成した、モデルクラスの getArticleList メソッドを使って記事一覧を取得し、それをテンプレートに渡すだけのシンプルなメソッドですね。

View テンプレートの作成(記事一覧画面)

それでは、取得した記事データとページネーションの結果を表示していきます。新しく記事一覧画面を作るので、resources/views/admin_blog/list.blade.php というファイルを新たに作成して、下記のように編集します。

resources/views/admin_blog/list.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>ブログ記事一覧</title>
    <link rel="stylesheet" href="{{ asset('/css/bootstrap.min.css') }}">
    <link rel="stylesheet" href="{{ asset('/css/blog.css') }}">
</head>

<body>
    <div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <h2>ブログ記事一覧</h2>

                @if (session('message'))
                    <div class="alert alert-success">
                        {{ session('message') }}
                    </div>
                    <br>
                @endif

                <a href="{{ route('admin_form') }}">
                    <span class="btn btn-primary btn-sm">新規記事作成</span>
                </a>
                <br>

                @if (count($list) > 0)
                    <br>

                    {{--links メソッドでページングが生成される。しかも生成されるHTMLは Bootstrap と互換性がある--}}
                    {{ $list->links() }}
                    <table class="table table-striped">
                        <tr>
                            <th width="120px">記事番号</th>
                            <th width="120px">日付</th>
                            <th>タイトル</th>
                        </tr>

                        {{--このまま foreach ループにかけることができる--}}
                        @foreach ($list as $article)
                            <tr>
                                <td>{{ $article->article_id }}</td>
                                <td>{{ $article->post_date_text }}</td>
                                <td>
                                    <a href="{{ route('admin_form', ['article_id' => $article->article_id]) }}">
                                        {{ $article->title }}
                                    </a>
                                </td>
                            </tr>
                        @endforeach
                    </table>
                @else
                    <br>
                    <p>記事がありません。</p>
                @endif
            </div>
        </div>
    </div>
</body>
</html>

通常、ページネーションには苦労することが多いのですが、簡単にできてしまいましたね。少し駆け足で紹介してしまいましたが、ページネーションのURIをカスタマイズしたり、パラメータを付け加えたりすることもできます。詳しくは日本語ドキュメントを参照してください。

コントローラで取得し、View テンプレートに渡した $list ですが、この正体は Illuminate\Pagination\LengthAwarePaginator クラスインスタンスです。このクラスには、ページネーションに関する様々なメソッドが定義されており、links() メソッドで、結果の残りを表示するためのリンクを生成し表示します。生成されるリンクURLには、pageパラメーターが自動的に含まれるため、自分で意識してページング用のパラメーターを付ける必要はありません。そして、このインスタンスはイテレータでもあるため、ループ処理することができます。

ところで、ブログ記事を削除した際、ブログ記事投稿フォームへ戻る仕様にしていましたが、それでは少し不自然なので、ブログ記事一覧画面へリダイレクトされるように変更しておきます。app/Http/Controllers/AdminBlogController.php を開いて delete メソッドを下記のように編集します。

app/Http/Controllers/AdminBlogController.php
    /**
     * ブログ記事削除処理
     *
     * @param AdminBlogRequest $request
     * @return \Illuminate\Http\RedirectResponse
     */
    public function delete(AdminBlogRequest $request)
    {
        ...中略...

        // 記事一覧画面へリダイレクト
        return redirect()->route('admin_list')->with('message', $message);
    }

ここまでできたら、http://{ホスト名}/admin/list にアクセスして、ちゃんと記事一覧が表示されていること、ページネーションが表示されることなどを確認してください。うまくいけば下記のような画面になると思います。(1ページ当たりの表示件数を5件にした時の画像です)

list.png

テンプレートの継承

Blade テンプレートの「継承」という機能を使っていきたいと思います。今、ブログ記事一覧画面と、ブログ記事投稿フォームの2画面ありますが、HTML構造について<body>タグ内以外はほぼ同じです。そこで、似た作りの部分は「親」のテンプレートとして定義してしまい、異なる部分は「親」を継承した「子」テンプレートで表示する、という形にしていきたいと思います。

それではまずば「親」テンプレートから作っていきます。新たに resources/views/admin_blog/app.blade.php というファイルを作成して、下記のように編集します。

resources/views/admin_blog/app.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    {{--@yield ディレクティブは指定したセクションの内容を表示するために使用する--}}
    <title>@yield('title')</title>
    <link rel="stylesheet" href="{{ asset('/css/bootstrap.min.css') }}">
    <link rel="stylesheet" href="{{ asset('/css/blog.css') }}">
</head>

<body>
@yield('body')
</body>
</html>

そして「子」テンプレート側ですが、先ほど作成した resources/views/admin_blog/list.blade.php を開いて、下記のように編集します。

resources/views/admin_blog/list.blade.php
{{--@extends ディレクティブで「継承」するテンプレートを指定--}}
@extends('admin_blog.app')
{{--@section ディレクティブで title セクションを定義--}}
@section('title', 'ブログ記事一覧')

{{--@section ディレクティブで body セクションを定義--}}
@section('body')
    <div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <h2>ブログ記事一覧</h2>

                @if (session('message'))
                    <div class="alert alert-success">
                        {{ session('message') }}
                    </div>
                    <br>
                @endif

                <a href="{{ route('admin_form') }}">
                    <span class="btn btn-primary btn-sm">新規記事作成</span>
                </a>
                <br>

                @if (count($list) > 0)
                    <br>

                    {{--links メソッドでページングが生成される。しかも生成されるHTMLは Bootstrap と互換性がある--}}
                    {{ $list->links() }}
                    <table class="table table-striped">
                        <tr>
                            <th width="120px">記事番号</th>
                            <th width="120px">日付</th>
                            <th>タイトル</th>
                        </tr>

                        {{--このまま foreach ループにかけることができる--}}
                        @foreach ($list as $article)
                            <tr>
                                <td>{{ $article->article_id }}</td>
                                <td>{{ $article->post_date_text }}</td>
                                <td>
                                    <a href="{{ route('admin_form', ['article_id' => $article->article_id]) }}">
                                        {{ $article->title }}
                                    </a>
                                </td>
                            </tr>
                        @endforeach
                    </table>
                @else
                    <br>
                    <p>記事がありません。</p>
                @endif
            </div>
        </div>
    </div>
@endsection

子テンプレート list.blade.php で定義した titlebody の各セクションが、それぞれ、親テンプレート app.blade.php@yield ディレクティブで指定した各箇所に表示されます。

resources/views/admin_blog/form.blade.php も下記のように編集しておきましょう。

resources/views/admin_blog/form.blade.php
@extends('admin_blog.app')
@section('title', 'ブログ記事投稿フォーム')

@section('body')
    ... (<body>タグの中身を記述する)...
@endsection

ここまでできたら、記事一覧画面、記事投稿フォーム画面にそれぞれ遷移して、ちゃんと画面が表示されることを確認してください。

次回は、投稿した記事を表示する表側の画面を実装していきます。

参考資料

Laravel 日本語ドキュメント

プログラムソース(github)