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

[前回](https://qiita.com/yumgoo17/items/e51823f1f9f49db82867)に引き続いて、今回はブログ記事の一覧画面を作成します。



## ルーティングの追加
ブログ記事一覧画面を表示する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 メソッドで、名前空間を指定したりもできます。詳しくは[日本語ドキュメント](https://readouble.com/laravel/5.5/ja/routing.html)を参照してください。



## モデルの編集
ブログ記事一覧を取得するメソッドを追加します。`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` というファイルを新たに作成して、下記のように編集します。

```html: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をカスタマイズしたり、パラメータを付け加えたりすることもできます。詳しくは[日本語ドキュメント](https://readouble.com/laravel/5.5/ja/pagination.html)を参照してください。

コントローラで取得し、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](https://qiita-image-store.s3.amazonaws.com/0/252417/23f9ca98-19a7-9292-d395-aa4dc6da050b.png)



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

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

```html: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` を開いて、下記のように編集します。

```html: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` で定義した `title` と `body` の各セクションが、それぞれ、親テンプレート `app.blade.php` の `@yield` ディレクティブで指定した各箇所に表示されます。

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

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

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

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

[次回は、投稿した記事を表示する表側の画面を実装していきます。](https://qiita.com/yumgoo17/items/c44f43cf74f718db0376)


## 参考資料
### Laravel 日本語ドキュメント
* [ルーティング](https://readouble.com/laravel/5.5/ja/routing.html)
* [ペジネーション](https://readouble.com/laravel/5.5/ja/pagination.html)
* [Eloquentの準備](https://readouble.com/laravel/5.5/ja/eloquent.html)
* [コントローラ](https://readouble.com/laravel/5.5/ja/controllers.html)
* [Bladeテンプレート](https://readouble.com/laravel/5.5/ja/blade.html)

### プログラムソース（github）
* [github yumgoo17/like_blog](https://github.com/yumgoo17/like_blog)