Laravel5 チュートリアル ブログもどきを作る(8) カテゴリー機能を追加する

[前回](https://qiita.com/yumgoo17/items/c44f43cf74f718db0376)はフロントのトップページを実装しましたが、今回はカテゴリー機能を追加していきます。ユーザーが設定したカテゴリー毎に、ブログ記事を分類する、ブログによくある機能のことです。それでは管理画面から実装していきます。



## テーブルの追加・カラムの追加
それではまずは、カテゴリーを保存する categories テーブルを作成します。

### テーブルスキーマ
下記のスキーマで categories テーブルを作成します。

| 物理名        	| 論理名       	| 型                   	| NOT NULL 	| デフォルト 	| 備考               	|
|---------------	|--------------	|----------------------	|----------	|------------	|--------------------	|
| category_id   	| カテゴリーID 	| int(10) unsigned    	| YES      	|            	| auto_increment, PK 	|
| name          	| カテゴリー名 	| varchar(255)         	| YES      	|            	|                    	|
| display_order 	| 表示順       	| smallint(5) unsigned 	| YES      	|            	|                    	|
| deleted_at    	| 削除日時     	| timestamp            	| NO       	| NULL       	| 論理削除用カラム   	|
| created_at    	| 作成日時     	| timestamp            	| NO       	| NULL       	|                    	|
| updated_at    	| 更新日時     	| timestamp            	| NO       	| NULL       	|                    	|

### カラム追加
articles テーブルに category_id カラムを追加します。category_id カラム追加後のスキーマは下記のようになります。

| 物理名     	| 論理名   	| 型               	| NOT NULL 	| デフォルト 	| 備考               	|
|------------	|----------	|------------------	|----------	|------------	|--------------------	|
| article_id 	| 記事ID   	| int(10) unsigned 	| YES       	|       	| auto_increment, PK 	|
| caetgory_id 	| カテゴリーID   	| int(10) unsigned 	| YES       	|       	| 新しく追加              |
| post_date  	| 投稿日   	| date             	| YES       	|       	|                    	|
| title      	| タイトル 	| varchar(255)     	| YES       	|       	|                    	|
| body       	| 本文     	| text             	| YES       	|       	|                    	|
| deleted_at 	| 削除日時 	| timestamp        	| NO      	| NULL       	| 論理削除用カラム   	|
| created_at 	| 作成日時 	| timestamp        	| NO      	| NULL       	|                    	|
| updated_at 	| 更新日時 	| timestamp        	| NO      	| NULL       	|                    	|


### マイグレーションファイル作成
続いて categories テーブルを作るためのマイグレーションファイルを作成すると同時に、Category モデルクラスも同時に作成します。Laravel ホームディレクトリに移動して、下記コマンドを実行します。

```
php artisan make:model Category --migration
```

そうすると、`database/migrations/{$timestamp}_create_categories_table.php` というファイルができるので、それを下記のように編集します。

```database/migrations/{$timestamp}_create_categories_table.php
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateCategoriesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('categories', function (Blueprint $table) {
            $table->increments('category_id');
            $table->string('name');
            $table->unsignedSmallInteger('display_order');
            $table->softDeletes();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('categories');
    }
}
```

続いて articles テーブルに category_id カラムを追加するためのマイグレーションファイルを作成します。articles テーブルを作ったときに作成したマイグレーションファイルを編集するのではなく、新しくマイグレーションファイルを作ります（私は、そうとは知らずにハマったことがあります・・・）。
Laravel ホームディレクトリで下記コマンドを打って、カラム追加用のマイグレーションファイルを作成します。

```
php artisan make:migration add_category_id_column_to_articles_table
```

そうすると、`database/migrations/{$timestamp}_add_category_id_column_to_articles_table.php` というファイルができるので、それを開いて下記のように編集します。

```database/migrations/{$timestamp}_add_category_id_column_to_articles_table.php
class AddCategoryIdColumnToArticlesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        // 既に存在するテーブルの更新には table メソッドを使う
        Schema::table('articles', function(Blueprint $table) {
            // after メソッドでカラムを追加する位置を指定できる（MySQLのみ）
            $table->unsignedInteger('category_id')->after('article_id');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('articles', function(Blueprint $table) {
            // カラムを削除するときは dropColumn メソッドを使う
            $table->dropColumn('category_id');
        });
    }
}
```

編集ができたら、下記コマンドを叩いて、マイグレーションを実行します。

```
php artisan migrate
```

実行できたら、意図した通りに categories テーブルができているか、articles テーブルに category_id が追加されているか確認してください。



## ルーティングの追加
管理画面のカテゴリ一覧画面、追加・編集、削除処理のルーティングを追加します。今回は、追加・編集と削除処理についてはAPIを作成し、それを利用してDBにカテゴリーデータを保存・削除します。`routes/web.php` を開いて、下記のように編集します。

```routes/web.php
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');

    Route::get('category', 'AdminBlogController@category')->name('admin_category');
    Route::post('category/edit', 'AdminBlogController@editCategory')->name('admin_category_edit');
    Route::post('category/delete', 'AdminBlogController@deleteCategory')->name('admin_category_delete');
});
```

下から3つが今回追加したルーティングになります。
（今回はカテゴリー一覧画面を作成します。カテゴリーの追加・編集と削除処理については次回になります）



## モデルの作成
続いて、先ほど作った categories テーブルのモデルクラス Cateogry を作成します。マイグレーションファイルを作るとき、同時に `app/Category.php` が作られていると思いますので、それを Models ディレクトリに移動し `app/Models/Category.php` として、このファイルを下記のように編集します。

```app/Models/Category.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Category extends Model
{
    use SoftDeletes;

    protected $primaryKey = 'category_id';
    protected $fillable = ['name', 'display_order'];
    protected $dates = ['deleted_at', 'created_at', 'updated_at'];

    /**
     * カテゴリリストを取得する
     *
     * @param int    $num_per_page 1ページ当たりの表示件数
     * @param string $order        並び順の基準となるカラム
     * @param string $direction    並び順の向き asc or desc
     * @return mixed
     */
    public function getCategoryList(int $num_per_page = 0, string $order = 'display_order', string $direction = 'asc')
    {
        $query = $this->orderBy($order, $direction);
        if ($num_per_page) {
            return $query->paginate($num_per_page);
        }
        return $query->get();
    }
}
```

特に目新しいことはしていません。ソフトデリート（論理削除）の設定、各プロパティの設定の他に、カテゴリ一覧を取得するメソッド getCategoryList() を実装しています。



## コントローラーの編集
コントローラーを編集して、カテゴリ一覧画面を表示するためのメソッドを追加します。`app/Http/Controllers/AdminBlogController.php` を開いて、下記の category メソッドを追加します。

```app/Http/Controllers/AdminBlogController.php
<?php

namespace App\Http\Controllers;

use App\Http\Requests\AdminBlogRequest;
use App\Models\Article;
use App\Models\Category;

class AdminBlogController extends Controller
{
    /** @var Article */
    protected $article;
    /** @var Category */
    protected $category;

    // 1ページ当たりの表示件数
    const NUM_PER_PAGE = 10;

    function __construct(Article $article, Category $category)
    {
        $this->article = $article;
        $this->category = $category;
    }

    ...（中略）...

    /**
     * カテゴリ一覧画面
     *
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
     */
    public function category()
    {
        $list = $this->category->getCategoryList(self::NUM_PER_PAGE);
        return view('admin_blog.category', compact('list'));
    }
```

まず、コンストラクターインジェクションで Category モデルクラスのインスタンスを生成します。そして、category メソッドは、先ほど作った getCategoryList() を使ってカテゴリリストを取得し、それを View に渡すだけのシンプルな作りですね。



## Viewテンプレートの作成
続いて `resources/views/admin_blog/` 配下に `category.blade.php` ファイルを新規に作成して、カテゴリ一覧画面を作っていきます。

```html:resources/views/admin_blog/category.blade.php
@extends('admin_blog.app')
@section('title', 'カテゴリ一覧')

@section('body')
    <div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <h2>カテゴリ一覧</h2>
                <br>

                @if (count($list) > 0)
                    <br>
                    {{ $list->links() }}
                    <table class="table table-striped">
                        <tr>
                            <th width="120px">カテゴリ番号</th>
                            <th>カテゴリ名</th>
                            <th width="60px">表示順</th>
                        </tr>

                        @foreach ($list as $category)
                            <tr data-category_id="{{ $category->category_id }}">
                                <td>
                                    <span class="category_id">{{ $category->category_id }}</span>
                                </td>
                                <td>
                                    <span class="name">{{ $category->name }}</span>
                                </td>
                                <td>
                                    <span class="display_order">{{ $category->display_order }}</span>
                                </td>
                            </tr>
                        @endforeach
                    </table>
                @else
                    <br>
                    <p>カテゴリがありません。</p>
                @endif

            </div>
        </div>
    </div>
@endsection
```

ここまでできたら、実際に画面を表示させて、カテゴリー一覧がちゃんと表示されることを確認してください（カテゴリーデータが無いと思うので、手動でinsert文を発行して、データを作成してください）。
うまくできたら、下記のような画面になっていると思います。

<img width="777" alt="category_list.png" src="https://qiita-image-store.s3.amazonaws.com/0/252417/b8c48f74-b8f1-9f6c-185e-4f5bb8ff9396.png">

[次回](https://qiita.com/yumgoo17/items/9376bf1c851c4c003253)は、カテゴリーの追加・編集、削除APIを実装します。



## 参考資料
### Laravel 日本語ドキュメント
* [マイグレーション](https://readouble.com/laravel/5.5/ja/migrations.html)
* [ルーティング](https://readouble.com/laravel/5.5/ja/routing.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)