LoginSignup
26
36

More than 3 years have passed since last update.

ゼロから始めるPHP ~Laravelを使って掲示板を作る~

Last updated at Posted at 2019-05-19

というわけでLaravelを使って掲示板を作ってみました。
いきなり何のガイドもなしに作れるわけがないので、掲示板を作るチュートリアル通りに作ってみました。

【Laravel 5.7対応】掲示板を作成するチュートリアル
他にも同じようなチュートリアルはありましたが、このサイトのチュートリアルが凄くわかりやすかったです。

初心者でもすぐに作れると思ったのですが、なかなかそう上手くはいきませんでした。
色々と躓いたり何回もやり直したりをして完成しました。

実際に作成した掲示板

Laravel掲示板.gif

↑こんな掲示板を作成できます。

この掲示板の特徴

  • 新規記事の投稿
  • 投稿した記事の編集・削除
  • 長文記事の「続きを読む」機能
  • 各記事にコメントが出来る
    • コメント数の表示
  • 投稿日時の表示
  • ページネーション

身に付くスキル

  • CRUD処理(クラッド処理) ⇨ 「Create(生成)、Read(読み取り)、Update(更新)、Delete(削除)」
  • ルーティング ⇨ 作成した機能のアクセスを制御する
  • コントローラ ⇨ ルートから受け取った情報をモデルに処理・モデルから受け取った情報をビューに表示する
  • バリデーション ⇨ 入力チェック
  • EloquentORM ⇨ データベースとモデルを関連付け、柔軟なデータ操作を行う
  • ページネーション ⇨ 1つのページを複数のWebページに分割し、各ページへのリンクを並べる
  • withメソッドを利用してクエリのチューニング ⇨ 大量に実行されたクエリを高速化する

実際に掲示板を作ってみる

基本的には【Laravel 5.7対応】掲示板を作成するチュートリアルに従って行います。
意味が分からない部分があっても、このチュートリアル通りに作業を行ってください。

1. Laravelプロジェクトを作成する

$ laravel new laravel-bbs

今回のプロジェクト名は「laravel-bbs」とします。
プロジェクト名は自分が理解できるものであれば何でも良いです。

2. プロジェクトの初期設定

.envファイルの設定を変更します。
laravel-bbs/.env

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=任意のパスワード

私は.envファイルが見つからなくてめちゃくちゃ悩みました。
.envファイルは先ほど作成したプロジェクトの直下にあります。

ただし、隠しファイルになっているのでMacで隠しファイルが表示されるように設定しましょう。

隠しファイルの表示方法


Macのターミナルを使う場合
$ defaults write com.apple.finder AppleShowAllFiles TRUE

ショートカットキーで実行する方法もありますが、私はターミナルから行いました。
参考記事: 【Mac】隠しファイル・隠しフォルダを表示する方法 @TsukasaHasegawa

日本語環境の設定も行います。
laranel-bbs/config/app.php

    'timezone' => 'Asia/Tokyo',


    'locale' => 'ja',

3. データベースの作成

掲示板のデータを保存・活用するためのマイグレーションファイルの作成を行います。
laravel-bbs/database/migrations/

プロジェクト作成時に2つのマイグレーションファイルが作成されますが、使わないので削除します。
2014_10_12_000000_create_users_table.php
2014_10_12_100000_create_password_resets_table.php

マイグレーションファイルの作成

以下のコマンドでマイグレーションファイルを作成します。

$ php artisan make:migration create_posts_table --create=posts
$ php artisan make:migration create_comments_table --create=comments

新しく作成されたファイル

laravel-bbs/database/migrations/
2019_05_14_181304_create_posts_table.php
2019_05_14_181313_create_comments_table.php
前半は日時なので作成日によって数値が異なります。

ファイルの編集

作成した2つのファイルを編集していきます。
_create_posts_table.phpの編集

<?php

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

class CreatePostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->increments('id');
            $table->string('title', 50);
            $table->text('body');
            $table->timestamps();
        });
    }

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

_create_comments_table.phpの編集

?php

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

class CreateCommentsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('comments', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('post_id');
            $table->text('body');
            $table->timestamps();

            $table->foreign('post_id')->references('id')->on('posts');
        });
    }

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

マイグレーションの実行

編集したマイグレーションファイルを実行します。

$ php artisan migrate

成功した場合は以下のようになります。

Migration table created successfully.
Migrating: 2019_05_14_181304_create_posts_table
Migrated:  2019_05_14_181304_create_posts_table
Migrating: 2019_05_14_181313_create_comments_table
Migrated:  2019_05_14_181313_create_comments_table

このようにすんなりマイグレートできればよかったのですが、僕はここでかなり躓きました。
起きたエラーの内容でググって何とか解決しました。
Mysqlや.envファイルの方が原因なので、その辺りを探ってみてください。
ゼロから始めるPHP ~Laravelでmigrationに失敗する時の解決方法の1例~

4. モデルの作成
テーブルを作成したので、それに対応するモデルを作成します。
laranel-bbs/app/

$ php artisan make:model Post
$ php artisan make:model Comment

laranel-bbs/app/Post.php
laranel-bbs/app/Comment.php
が作成されました。

ファイルの編集

Post.phpの編集

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    protected $fillable = [
        'title',
        'body',
    ];

    public function comments()
    {
        return $this->hasMany('App\Comment');
    }
}

Comment.phpの編集

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
    protected $fillable = [
        'body',
    ];

    public function post()
    {
        return $this->belongsTo('App\Post');
    }
}

4. テストデータの作成

ファクトリを作成します。

$ php artisan make:factory PostFactory --model=Post
$ php artisan make:factory CommentFactory --model=Comment

作成されたファイル

laravel-bbs/database/factories/PostFactory.php
laravel-bbs/database/factories/UserFactory.php
私の環境ではfactoriesというディレクトリの中に各PHPファイルが作成されていました。
ここはチュートリアルとは違う部分でした。

ファイルの編集

PostFactory.phpの編集

<?php

use Faker\Generator as Faker;

$factory->define(App\Post::class, function (Faker $faker) {
    return [
        'title' => '投稿のタイトル',
        'body' => "本文です。テキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト。\nテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト。テキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト。",
    ];
});

UserFactory.phpの編集

<?php

use Faker\Generator as Faker;

$factory->define(App\Comment::class, function (Faker $faker) {
    return [
        'body' => "コメントです。テキストテキストテキストテキストテキストテキスト。\nテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト。",
    ];
});

シーダークラスの作成

$ php artisan make:seeder PostsTableSeeder

作成されたファイル

laravel-bbs/database/seeds/PostsTableSeeder.php

ファイルの編集

PostsTableSeeder.phpの編集

<?php

use Illuminate\Database\Seeder;
use App\Post;
use App\Comment;

class PostsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        factory(Post::class, 50)
            ->create()
            ->each(function ($post) {
                $comments = factory(App\Comment::class, 2)->make();
                $post->comments()->saveMany($comments);
            });
    }
}

50件の投稿を作成して、各投稿に2つのコメントを作成します。

DatabaseSeeder.phpの編集

<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
{
    $this->call(PostsTableSeeder::class);
}

}

テストデータをデータベースに投入

$ composer dump-autoload

実行結果


Generating optimized autoload files> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
Discovered Package: beyondcode/laravel-dump-server
Discovered Package: fideloper/proxy
Discovered Package: laravel/tinker
Discovered Package: nesbot/carbon
Discovered Package: nunomaduro/collision
Package manifest generated successfully.
Generated optimized autoload files containing 3553 classes

Package manifest generated successfully.と表示されれば成功です。

$ php artisan db:seed

実行結果

Seeding: PostsTableSeeder
Database seeding completed successfully.

Database seeding completed successfully.と表示されれば成功です。

5. 投稿の一覧画面の作成

Laravelのローカルサーバーを起動しておきます。

$ php artisan serve

こちらのコマンドは開発中は実行したままにしておきます。
ターミナルは新しい画面を開いておきます。

投稿の一覧画面の作成

ルーティングを設定するためにweb.phpを編集する

laravel-bbs/routs/web.php

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', 'PostsController@index')->name('top');

ファイルの作成

次にコントローラーを作成します。

$ php artisan make:controller PostsController

laravel-bbs/app/Http/Controllers/PostsController.phpが作成されました。

下記のように、indexメソッドを追加します。

ファイルの編集


PostsController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Post;

class PostsController extends Controller
{
    public function index()
    {
        $posts = Post::orderBy('created_at', 'desc')->get();

        return view('posts.index', ['posts' => $posts]);
    }
}

投稿を作成日時の降順で取得して、posts.indexにデータを渡してビューを生成します。

ファイルの作成

レイアウトの共通テンプレートとして、layout.blade.phpを下記のように作成します。

laravel-bbs/resources/views/layout.blade.php

ファイルの編集

layout.blade.php

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Laravel BBS</title>

    <link
        rel="stylesheet"
        href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
        integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
        crossorigin="anonymous"
    >
</head>
<body>
    <header class="navbar navbar-dark bg-dark">
        <div class="container">
            <a class="navbar-brand" href="{{ url('') }}">
                Laravel BBS
            </a>
        </div>
    </header>

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

ファイルの作成

viewsの直下にpostsのディレクトリを作成して、index.blade.phpを作成してください。
laravel-bbs/resources/views/posts/index.blade.php

ファイルの編集

index.blade.php

@extends('layout')

@section('content')
    <div class="container mt-4">
        @foreach ($posts as $post)
            <div class="card mb-4">
                <div class="card-header">
                    {{ $post->title }}
                </div>
                <div class="card-body">
                    <p class="card-text">
                        {!! nl2br(e(str_limit($post->body, 200))) !!}
                    </p>
                </div>
                <div class="card-footer">
                    <span class="mr-2">
                        投稿日時 {{ $post->created_at->format('Y.m.d') }}
                    </span>

                    @if ($post->comments->count())
                        <span class="badge badge-primary">
                            コメント {{ $post->comments->count() }}件
                        </span>
                    @endif
                </div>
            </div>
        @endforeach
    </div>
@endsection

確認

http://localhost:8000/ 
にアクセスして、下記のように表示されればOKです。

6. 投稿の作成

投稿の作成画面の表示・作成処理のためのルーティングを追加する

ファイルの編集


laravel-bbs/routs/web.php
web.phpを編集する
<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', 'PostsController@index')->name('top');
Route::resource('posts', 'PostsController', ['only' => ['create', 'store']]);

ファイルの編集

laravel-bbs/app/Http/Controllers/PostsController.php
PostsControllerに処理を追加します。
投稿を追加した後は、トップページにリダイレクトするようにします。

Public function create()
{
    return view('posts.create');
}

public function store(Request $request)
{
    $params = $request->validate([
        'title' => 'required|max:50',
        'body' => 'required|max:2000',
    ]);

    Post::create($params);

    return redirect()->route('top');
}

ファイルの作成

ビューを作成します。

laravel/bbs/resources/views/posts/create.blade.phpを作成し、以下のように編集します。

ファイルの編集

create.blade.php

extends('layout')
@section('content')
    <div class="container mt-4">
        <div class="border p-4">
            <h1 class="h5 mb-4">
                投稿の新規作成
            </h1>

            <form method="POST" action="{{ route('posts.store') }}">
                @csrf

                <fieldset class="mb-4">
                    <div class="form-group">
                        <label for="title">
                            タイトル
                        </label>
                        <input
                            id="title"
                            name="title"
                            class="form-control {{ $errors->has('title') ? 'is-invalid' : '' }}"
                            value="{{ old('title') }}"
                            type="text"
                        >
                        @if ($errors->has('title'))
                            <div class="invalid-feedback">
                                {{ $errors->first('title') }}
                            </div>
                        @endif
                    </div>

                    <div class="form-group">
                        <label for="body">
                            本文
                        </label>

                        <textarea
                            id="body"
                            name="body"
                            class="form-control {{ $errors->has('body') ? 'is-invalid' : '' }}"
                            rows="4"
                        >{{ old('body') }}</textarea>
                        @if ($errors->has('body'))
                            <div class="invalid-feedback">
                                {{ $errors->first('body') }}
                            </div>
                        @endif
                    </div>

                    <div class="mt-5">
                        <a class="btn btn-secondary" href="{{ route('top') }}">
                            キャンセル
                        </a>

                        <button type="submit" class="btn btn-primary">
                            投稿する
                        </button>
                    </div>
                </fieldset>
            </form>
        </div>
    </div>
@endsection

ファイルの編集

laravel-bbs/resources/views/posts/index.blade.phpを開いて、投稿のリストの上に下記を追加します。

<div class="mb-4">
    <a href="{{ route('posts.create') }}" class="btn btn-primary">
        投稿を新規作成する
    </a>
</div>

追加した結果

extends('layout')

@section('content')

    <div class="container mt-4">
        <div class="mb-4">
            <a href="{{ route('posts.create') }}" class="btn btn-primary">
             投稿を新規作成する
            </a>
        </div>
        @foreach ($posts as $post)
            <div class="card mb-4">
                <div class="card-header">
                    {{ $post->title }}
                </div>
                <div class="card-body">
                    <p class="card-text">
                        {!! nl2br(e(str_limit($post->body, 200))) !!}
                    </p>

投稿を新しく追加できるようになりました。

7. 投稿の詳細・コメントの機能

次に、投稿にコメントを追加する機能を実装します。

投稿の詳細画面を実装

Laravel掲示板 投稿の詳細
投稿の表示のためのルーティングを追加します。

routes/web.phpを開いて、以下のようにshowメソッドにルーティングが通るように修正します。

ファイルの編集

laravel-bbs/routs/web.php

web.phpを編集する

Route::resource('posts', 'PostsController', ['only' => ['create', 'store', 'show']]);

コントローラーに処理を追加します。
PostsController.phpを開いて、showメソッドを追加します。

ファイルの編集


laravel-bbs/app/Http/Controllers/PostsController.php
PostsController.phpを編集する
public function show($post_id)
{
    $post = Post::findOrFail($post_id);

    return view('posts.show', [
        'post' => $post,
    ]);
}

ファイルの作成

次に、ビューを追加します。
laravel-bbs/resources/views/posts/show.blade.php

ファイルの編集

show.blade.php

@extends('layout')

@section('content')
    <div class="container mt-4">
        <div class="border p-4">
            <h1 class="h5 mb-4">
                {{ $post->title }}
            </h1>

            <p class="mb-5">
                {!! nl2br(e($post->body)) !!}
            </p>

            <section>
                <h2 class="h5 mb-4">
                    コメント
                </h2>

                @forelse($post->comments as $comment)
                    <div class="border-top p-4">
                        <time class="text-secondary">
                            {{ $comment->created_at->format('Y.m.d H:i') }}
                        </time>
                        <p class="mt-2">
                            {!! nl2br(e($comment->body)) !!}
                        </p>
                    </div>
                @empty
                    <p>コメントはまだありません。</p>
                @endforelse
            </section>
        </div>
    </div>
@endsection

ファイルの編集

laravel-bbs/resources/views/posts/
index.blade.php

<p class="card-text">
    {!! nl2br(e(str_limit($post->body, 200))) !!}
</p>

<a class="card-link" href="{{ route('posts.show', ['post' => $post]) }}">
    続きを読む
</a>

トップページにアクセスして、投稿の一覧と投稿の詳細画面が表示できればOKです。

8. コメントの追加

投稿にコメントを追加する機能を実装します。
ルーティングを追記します。
laravel-bbs/routs/web.php

Route::resource('comments', 'CommentsController', ['only' => ['store']]);

追記した結果

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', 'PostsController@index')->name('top');
Route::resource('posts', 'PostsController', ['only' => ['create', 'store']]);
Route::resource('posts', 'PostsController', ['only' => ['create', 'store', 'show']]);
Route::resource('comments', 'CommentsController', ['only' => ['store']]);

ファイルの作成

次にコントローラーを作成します。

$ php artisan make:controller CommentsController

laravel-bbs/app/Http/Controllers/CommentsController.php
CommentsController.phpを編集する

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Post;

class CommentsController extends Controller
{
    public function store(Request $request)
    {
        $params = $request->validate([
            'post_id' => 'required|exists:posts,id',
            'body' => 'required|max:2000',
        ]);

        $post = Post::findOrFail($params['post_id']);
        $post->comments()->create($params);

        return redirect()->route('posts.show', ['post' => $post]);
    }
}

投稿の詳細画面に、コメント追加のリクエストを送るためのフォームを追加しましょう。

ファイルの編集


laravel-bbs/resources/views/posts/show.blade.php
show.blade.phpを編集する
<form class="mb-4" method="POST" action="{{ route('comments.store') }}">
    @csrf

    <input
        name="post_id"
        type="hidden"
        value="{{ $post->id }}"
    >

    <div class="form-group">
        <label for="body">
            本文
        </label>

        <textarea
            id="body"
            name="body"
            class="form-control {{ $errors->has('body') ? 'is-invalid' : '' }}"
            rows="4"
        >{{ old('body') }}</textarea>
        @if ($errors->has('body'))
            <div class="invalid-feedback">
                {{ $errors->first('body') }}
            </div>
        @endif
    </div>

    <div class="mt-4">
        <button type="submit" class="btn btn-primary">
            コメントする
        </button>
    </div>
</form>

これで、投稿の詳細画面からコメントを追加できるようになりました。

9. 投稿の編集

次に投稿を編集できるようにしてみます。
ルーティングを開いて、edit, updateを追加します。

ファイルの編集

laravel-bbs/routs/web.php

Route::resource('posts', 'PostsController', ['only' => ['create', 'store', 'show', 'edit', 'update']]);

追記した結果

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', 'PostsController@index')->name('top');
Route::resource('posts', 'PostsController', ['only' => ['create', 'store']]);
Route::resource('posts', 'PostsController', ['only' => ['create', 'store', 'show']]);
Route::resource('comments', 'CommentsController', ['only' => ['store']]);
Route::resource('posts', 'PostsController', ['only' => ['create', 'store', 'show', 'edit', 'update']]);

ファイルの編集

PostsContollerにedit,updateメソッドを追加します。
laravel-bbs/app/Http/Controllers/PostsController.php
PostsController.phpを編集する

public function edit($post_id)
{
    $post = Post::findOrFail($post_id);

    return view('posts.edit', [
        'post' => $post,
    ]);
}

public function update($post_id, Request $request)
{
    $params = $request->validate([
        'title' => 'required|max:50',
        'body' => 'required|max:2000',
    ]);

    $post = Post::findOrFail($post_id);
    $post->fill($params)->save();

    return redirect()->route('posts.show', ['post' => $post]);
}

ファイルの作成

編集画面のビューを作成します。
laravel-bbs/resources/views/posts/edit.blade.php

ファイルの編集

edit.blade.php

@extends('layout')

@section('content')
    <div class="container mt-4">
        <div class="border p-4">
            <h1 class="h5 mb-4">
                投稿の編集
            </h1>

            <form method="POST" action="{{ route('posts.update', ['post' => $post]) }}">
                @csrf
                @method('PUT')

                <fieldset class="mb-4">
                    <div class="form-group">
                        <label for="title">
                            タイトル
                        </label>
                        <input
                            id="title"
                            name="title"
                            class="form-control {{ $errors->has('title') ? 'is-invalid' : '' }}"
                            value="{{ old('title') ?: $post->title }}"
                            type="text"
                        >
                        @if ($errors->has('title'))
                            <div class="invalid-feedback">
                                {{ $errors->first('title') }}
                            </div>
                        @endif
                    </div>

                    <div class="form-group">
                        <label for="body">
                            本文
                        </label>

                        <textarea
                            id="body"
                            name="body"
                            class="form-control {{ $errors->has('body') ? 'is-invalid' : '' }}"
                            rows="4"
                        >{{ old('body') ?: $post->body }}</textarea>
                        @if ($errors->has('body'))
                            <div class="invalid-feedback">
                                {{ $errors->first('body') }}
                            </div>
                        @endif
                    </div>

                    <div class="mt-5">
                        <a class="btn btn-secondary" href="{{ route('posts.show', ['post' => $post]) }}">
                            キャンセル
                        </a>

                        <button type="submit" class="btn btn-primary">
                            更新する
                        </button>
                    </div>
                </fieldset>
            </form>
        </div>
    </div>
@endsection

ファイルの編集

投稿の詳細画面から編集画面へのリンクを追加します。
laravel-bbsresources/views/posts/

show.blade.php

<div class="mb-4 text-right">
    <a class="btn btn-primary" href="{{ route('posts.edit', ['post' => $post]) }}">
        編集する
    </a>
</div>

以上で、投稿を編集できるようになりました。

10. 投稿の削除

次に投稿を削除する機能を実装します。

ルーティングにdestroyメソッドを利用できるように追記します。

ファイルの編集


laravel-bbs/routs/web.php
Route::resource('posts', 'PostsController', ['only' => ['create', 'store', 'show', 'edit', 'update', 'destroy']]);

追記の結果

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', 'PostsController@index')->name('top');
Route::resource('posts', 'PostsController', ['only' => ['create', 'store']]);
Route::resource('posts', 'PostsController', ['only' => ['create', 'store', 'show']]);
Route::resource('comments', 'CommentsController', ['only' => ['store']]);
Route::resource('posts', 'PostsController', ['only' => ['create', 'store', 'show', 'edit', 'update']]);
Route::resource('posts', 'PostsController', ['only' => ['create', 'store', 'show', 'edit', 'update', 'destroy']]);

ファイルの編集

PostsControllerにdestroyメソッドを実装します。

laravel-bbs/app/Http/Controllers/

PostsController.php

public function destroy($post_id)
{
    $post = Post::findOrFail($post_id);

    \DB::transaction(function () use ($post) {
        $post->comments()->delete();
        $post->delete();
    });

    return redirect()->route('top');
}

ファイルの編集

投稿の詳細画面に、削除ボタンを設置します。
編集ボタンの右に並ぶ様に、下記のコードを追記します。
laravel-bbs/resources/views/posts/

show.blade.php

<form
    style="display: inline-block;"
    method="POST"
    action="{{ route('posts.destroy', ['post' => $post]) }}"
>
    @csrf
    @method('DELETE')

    <button class="btn btn-danger">削除する</button>
</form>

追記した結果

@extends('layout')

@section('content')
    <div class="container mt-4">
        <div class="border p-4">

            <div class="mb-4 text-right">
                <a class="btn btn-primary" href="{{ route('posts.edit', ['post' => $post]) }}">編集する</a>
                <form 
                    style="display: inline-block;"
                    method="POST"
                    action="{{ route('posts.destroy', ['post' => $post]) }}">
                @csrf
                @method('DELETE') 
                <button class="btn btn-danger">削除する</button>
            </form>
            </div>

11. ページネーションの実装

PostsControllerのindexメソッド内にて、投稿リストを取得する処理を以下のように変更します。

$posts = Post::orderBy('created_at', 'desc')->paginate(10);

ファイルを編集する

**laravel-bbs/resources/views/posts/index.blade.php
を開いて、投稿リストの下部に追記します。

index.blade.php

<div class="d-flex justify-content-center mb-5">
    {{ $posts->links() }}
</div>

追記した結果

@extends('layout')

@section('content')

    <div class="container mt-4">
        <div class="mb-4">
            <a href="{{ route('posts.create') }}" class="btn btn-primary">
             投稿を新規作成する
            </a>
        </div>
        @foreach ($posts as $post)
            <div class="card mb-4">
                <div class="card-header">
                    {{ $post->title }}
                </div>
                <div class="card-body">
                    <p class="card-text">
                        {!! nl2br(e(str_limit($post->body, 200))) !!}
                    </p>
                    <a class="card-link" href="{{ route('posts.show', ['post' => $post]) }}">続きを読む</a>
                </div>

                <div class="card-footer">
                    <span class="mr-2">
                        投稿日時 {{ $post->created_at->format('Y.m.d') }}
                    </span>

                    @if ($post->comments->count())
                        <span class="badge badge-primary">
                            コメント {{ $post->comments->count() }}件
                        </span>
                    @endif
                </div>
            </div>
        @endforeach
        <div class="d-flex justify-content-center mb-5">
    {{ $posts->links() }}
</div>
    </div>
@endsection

クエリのチューニング

最後に、クエリをチューニングしてみます。

ファイルの編集

Laravelで実行されているクエリがわかるように、
laravel-bbs/app/Providers/AppServiceProvider.phpを開いて、boot関数を以下のように変更します。

AppServiceProvider.php

public function boot()
{
    \DB::listen(function ($query) {
        $sql = $query->sql;
        for ($i = 0; $i < count($query->bindings); $i++) {
            $sql = preg_replace("/\?/", $query->bindings[$i], $sql, 1);
        }
        \Log::info($sql);
    });
}

この状態でトップページにアクセスします。
そして、ログファイルをみると以下のように、クエリを12回実行していることがわかります。

local.INFO: select count(*) as aggregate from `posts`
local.INFO: select * from `posts` order by `created_at` desc limit 10 offset 0
local.INFO: select * from `comments` where `comments`.`post_id` = 53 and `comments`.`post_id` is not null
local.INFO: select * from `comments` where `comments`.`post_id` = 52 and `comments`.`post_id` is not null
local.INFO: select * from `comments` where `comments`.`post_id` = 51 and `comments`.`post_id` is not null
local.INFO: select * from `comments` where `comments`.`post_id` = 1 and `comments`.`post_id` is not null
local.INFO: select * from `comments` where `comments`.`post_id` = 2 and `comments`.`post_id` is not null
local.INFO: select * from `comments` where `comments`.`post_id` = 3 and `comments`.`post_id` is not null
local.INFO: select * from `comments` where `comments`.`post_id` = 4 and `comments`.`post_id` is not null
local.INFO: select * from `comments` where `comments`.`post_id` = 5 and `comments`.`post_id` is not null
local.INFO: select * from `comments` where `comments`.`post_id` = 6 and `comments`.`post_id` is not null
local.INFO: select * from `comments` where `comments`.`post_id` = 7 and `comments`.`post_id` is not null

PostsControllerのindexメソッドで投稿のリストを取得している処理を、以下のように変更します。

$posts = Post::with(['comments'])->orderBy('created_at', 'desc')->paginate(10);

再度トップページにアクセスして、ログファイルを見てみます。

local.INFO: select count(*) as aggregate from `posts`
local.INFO: select * from `posts` order by `created_at` desc limit 10 offset 0
local.INFO: select * from `comments` where `comments`.`post_id` in (1, 2, 3, 4, 5, 6, 7, 51, 52, 53)

クエリの実行回数がが3回だけになったことがわかります。

このような問題をn + 1 問題と言い、Laravelではwithメソッドを利用することで解決することができます。

最後に

以上となります。
参考したページのままやった時に分かり難かったところは自分なりに追記したり、編集したりしています。
1回で完成させるとこは難しいですが、出来るまで何回もトライしてみてください!

26
36
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
36