Laravelでブログ記事の投稿、編集、削除等の機能を実装する【第3回マッヒーの勉強部屋】

こんにちは、新人エンジニアの「マッヒー」です。
弊社「インスパイアデザイン」では、月に1回勉強会が開催されており、社長のマーチンさん、ベテランエンジニアのトミーさんからのご指導のもと日々勉強させていただいております。

現在私はPHPフレームフレームであるLaravelの学習中であり、WordPressと同じブログサイトの構築を目指しています。
そこで今回は「記事の投稿機能を実装をしよう」というお題をいただきましたので、実行したことを本記事にまとめていきたいと思います。
前回は、ユーザー認証機能を利用して管理画面の実装を行いました。よろしければぜひご覧ください。
https://inspire-design.net/blog/laravel1/

ページの設計

まずは、今回どのようなページが必要かどうか予め検討しておきたいと思います。

必要なページはWordPressを真似するのが早そうなので確認してみます。
WordPressの記事に関するページは下記3つであり、削除機能は一覧ページから行えるので、Laravelでも同じように作成することにします。

  • 記事の投稿画面
  • 記事の編集画面(投稿画面と同じ)
  • 記事の一覧表示画面

また、記事の投稿画面と編集画面はデザインが同じなので、同じ PHPファイルで済みそうです。
ということで、今回は
・記事の投稿&編集画面のPHPファイル
・記事の一覧表示画面のPHPファイル
上記2つのviewファイルを作成します。

記事の一覧表示画面
記事の投稿&編集画面

HTMLとCSSで上記2ページを作成しました。
※ソースコードは割愛します。

ルーティングの設計

続いてルーティングの設計をしていきます。
以下の表のようなページとURIにしたいと思います。

ページmethodURI
記事の一覧表示画面GET/admin/post
記事の新規投稿画面GET/admin/post/add
記事の新規投稿POST/admin/post/add
記事の編集画面GET/admin/post/edit
記事の編集(更新)POST/admin/post/edit
記事の削除GET/admin/post/delete_one

これで web.php に何を記述すれば良いのかが把握できたので早速書いていきます。

// ログイン後の処理
Route::group(['middleware' => ['auth:admin']], function () {

            // 記事の新規投稿画面
            Route::get('admin/post/add', [PostController::class, 'add'])->name('admin.post.add');
            // 記事の投稿
            Route::post('admin/post/add', [PostController::class, 'addPost']);
            // 記事の編集画面
            Route::get('admin/post/edit', [PostController::class, 'edit'])->name('admin.post.edit');
            // 記事の更新
            Route::post('admin/post/edit', [PostController::class, 'editPost']);
            // 記事の削除
            Route::get('admin/post/delete_one', [PostController::class, 'deleteOne'])->name('admin.post.delete');

});

今回の記事に関するページは、全て管理画面内で行いたいため、authミドルウェアを使用してログイン後のユーザーしかアクセスできないようにしています。
また、使用するコントローラーは「PostController」です。(後ほど作成します)

データベース設計

続いてはデータベースの設計を行います。
投稿した記事を保存しておくためのテーブルが1つあれば良さそうです。
そこで、今回は以下のような「entries」デーブルを作成することにします。

カラム名IDtitlecontentcreated_atupdated_at
データ型intstringstringtimestamptimestamp
テーブル名:entries

マイグレーションの作成・実行

使用するテーブルが決まったので、早速マイグレーションの作成をしていきます。

$ php artisan make:migration create_entries_table

作成されたマイグレーションファイルに以下を記述します。

public function up()
{
    Schema::create('entries', function (Blueprint $table) {
        $table->increment('id');
        $table->string('title');
        $table->string('content')->nullable();
        $table->timestamps();
    });
}

1点だけ、contentカラムに関しては、nullable() を指定しています。
これは、null を許すという意味です。
WordPressでは、記事の内容を記述しなくても投稿ができるのでその点を真似して見ました。
これで、仮に記事内容を未入力のまま投稿したとしてもエラーになることがなくなります。

最後に下記コマンドでマイグレーションを実行して、テーブルを生成させます。

$ php artisan migrate

モデルの作成

ついでにモデルの作成も行ってしまいます。
下記コマンドを実行します。

$ php artisan make:model Entry

テーブル名が、「entries」に対して、モデル名は「Entry」(entriesの単数形)であるため、Laravelが自動で関連付けを行ってくれます。

作成されたモデルクラス内に以下のように $guarded を指定し、idカラム以外をLaravelで操作可能にします。

class Entry extends Model
{
    use HasFactory;

    protected $guarded = array('id');
}

これでデータベースに関する準備は完了です。

コントローラーの設計

MVCモデルである、M(モデル)とV(ビュー)の部分は完成しました。
今度はC(コントローラー)の部分を設計していきます。

コントローラーの作成

下記コマンドで「PostController」を作成します。

$ php artisan make:controller PostController

コントローラーとモデルの連携

作成されたコントローラーに、下記のコンストラクタを追加することによって、
$this->entry と指定するだけで、データベースの操作が可能になります。
entriesテーブルの全てのレコードを取得する場合は、
$this->entry->all(); となります。

use App\Models\Entry;

class PostController extends Controller
{
    public function __construct( Entry $entry )
    {
        $this->entry = $entry;
    }
}

各メソッドの作成

    /**
     * 記事の一覧取得
     * 
     * @return view
     */
    public function index()
    {
        // 記事の取得
        $entries = $this->entry->orderBy('created_at', 'desc')->paginate(5);

        // viewに渡す値
        $params = [
            "entries" => $entries,
        ];

        return view('admin.post.index', $params);
    }

    /**
     * 記事の新規画面
     * 
     * @return view
     */
    public function add()
    {
        return view('admin.post.input');
    }

    /**
     * 記事の投稿(POST)
     *
     * @param Illuminate\Http\Request $request
     * @return redirect
     */
    public function addPost(Request $request)
    {
        // フォームの入力内容を全て取得
        $entries = $request->all();

        // DBに格納する値(記事)
        $entry = [
            'title' => $entries['title'],
            'content' => $entries['content'],
        ];

        // 記事をDBに格納
        $this->entry->fill($entry)->save();

        return redirect( route('admin.post.index') );
    }

    /**
     * 記事の編集画面
     * 
     * @param Illuminate\Http\Request $request
     * @return view
     */
    public function edit(Request $request)
    {
        // 記事IDを取得
        $post_id = $request->post_id;
        $request->merge( ['id' => $post_id] );

        // 記事を取得
        $entry = $this->entry->find($post_id);

        $params = [
            'entry' => $entry,
            'request' => $request,
        ];

        return view('admin.post.input', $params);
    }

    /**
     * 記事の更新(POST)
     * 
     * @param Illuminate\Http\Request $request
     * @return redirect
     */
    public function editPost(Request $request)
    {
        // 記事IDを取得
        $post_id = $request->post_id;

        // フォームの入力内容を全て取得
        $entries = $request->all();

        // DBを更新(記事)
        $entry = $this->entry->find( $entries['post_id'] );
        $entry->title = $request->title;
        $entry->content = $request->content;
        $entry->save();

        // メッセージ
        $message = '投稿が更新されました。';

        return redirect()->route('admin.post.edit', ['post_id' => $post_id])->with('update_message', $message);
    }

    /**
     * 記事の削除
     * 
     * @param
     * @return
     */
    public function deleteOne(Request $request)
    {
        if ( empty($request->post_id) ) {
            return redirect()->route('admin.post.index');
        }
        $this->entry->deleteOne($request->post_id);
        return redirect()->route('admin.post.index')->with('delete_message', '記事を1件削除しました。');
    }

indexメソッドについて

indexメソッドは、記事一覧表示用のアクションメソッドになります。
entriesテーブルから、投稿日の降順にソートした5件のレコードを取得して、viewに渡しています。
また、paginateメソッドを呼び出すことによって、ページネーションでの表示が可能になります。

これで、「/admin/post」にアクセスすると、記事の一覧が表示されるようになりました。

記事の一覧表示画面

addメソッドについて

addメソッドは、記事の新規投稿画面で使用するアクションメソッドになります。
今回はデータベースからデータを引っ張ってくる必要もありませんので、
非常にシンプルにViewファイルを呼び出しているだけです。

「/admin/post/add」にアクセスすると、以下の画像のように記事の新規投稿画面が表示されるようになりました。

記事の新規投稿画面

addPostメソッドについて

addPostメソッドは、記事を投稿するためのアクションメソッドになります。
リクエストから、フォームの入力内容を全て取得して、データベースに格納するための配列 $paramsにまとめています。
そして、Eloquentのfill、saveメソッドを用いてentriesテーブルに新たなレコードを格納しています。
最後に記事一覧表示画面にリダイレクトされて、先程登録した記事が一番上に追加されていることが確認できるかと思います。

editメソッドについて

editメソッドは、記事の編集画面で使用するアクションメソッドになります。
記事編集画面ではURLに「admin/post/edit?post_id=記事ID」のようにパラメータに記事IDを渡すようにします。
なので、$request->post_id とすることによって、記事IDを取得できます。
取得した記事IDを findメソッドの引数に渡し、記事のタイトルと内容を取得し、viewに渡します。
これで、「admin/post/edit?post_id=記事ID」にアクセスすると、記事のタイトルと内容が既に入力されているページへ遷移します。

記事の編集画面

editPostメソッドについて

editPostメソッドは、記事の更新を行うためのアクションメソッドになります。
フォームの入力内容を取得し、その値を以下のように $entry インスタンスにセットしています。
$entry->title = $request->title;
$entry->content = $request->content;
値セットした後、saveメソッドを呼び出せばデータベースが更新されます。
また、redirectメソッドに更にwithメソッドを呼び出すことによって、フラッシュメッセージを渡すことができます。
以下の画像のように、記事を更新したことろ緑色の「投稿が更新されました。」というメッセージが表示されます。

記事を更新した際の画面

deleteOneメソッドについて

deleteOneメソッドは、記事を1件削除するためのアクションメソッドになります。
「/admin/post/delete_one?post_id=記事ID」というURLでアクセスすると、指定の記事が削除されるといった仕様になります。
こちらは記事一覧表示画面の「削除」というボタンをクリックするこによって実行されます。
試しにクリックしてみると、1件の記事がなくなり、「記事を1件削除しました。」というフラッシュメッセージが表示されるようになりました。

最後に

大変長くなってしまいましたが、これでようやく記事の投稿、編集、削除機能の実装ができました。
次回は、今回作成した記事にカテゴリー、タグの紐付けを可能にしていきたいと思います。

この記事を書いた人

峯岸真弘