Laravel + docker で掲示板を作るチュートリアル

前回でログイン機能と管理画面を導入しました。
今回から実際に簡単なアプリを作っていきます。
今回はよくある掲示板アプリみたいなのを作っていきます。

docker+Laravelをrailsエンジニアが使ってみる
Laravel: ログイン認証と管理画面 【voyager】
Laravel + docker で掲示板を作るチュートリアル←今回はここ
docker + Laravelアプリをherokuにデプロイする

Topicモデルを作成

php artisan make:model Topic -m
-mをつけるとマイグレーションファイルも一緒に作成してくれます。

topicsテーブルを作成

マイグレーションファイルを編集します。
database/migrations/にあります。

_create_topics_table.php

<?php

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

class CreateTopicsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('topics', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('title', 50); //追加
            $table->text('content'); //追加
            $table->timestamps();
        });
    }

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

Topicモデルを編集

app/Topic.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Topic extends Model
{
    protected $fillable = [
        'title',
        'content',
    ];
    public function comments()
    {
        return $this->hasMany('App\Comment');
    }
}

$fillableに入れた値だけ許容してくれるみたいです。
topic has many commentsの関係です。
railsだと

has_many :comments

と書きますね。

一度マイグレートします。
php artisan migrate

Commentモデルを作成

続いてTopicに紐づくCommentモデルを作成します。

php artisan make:model Comment -m

commentsテーブルを作成

_create_comments_table.php

<?php

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

class CreateCommentsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('comments', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->bigInteger('topic_id')->unsigned();
            $table->text('content');
            $table->timestamps();

            $table->foreign('topic_id')->references('id')->on('topics');
        });
    }

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

外部キー制約を行なっています。

Commentモデルを編集

app/Comment.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

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

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

railsだと

belongs_to :topic

みたいな書き方しますね。

マイグレートしておきます。
php artisan migrate

Seederを作成

railsでは、ralis db:seedとやるとダミーデータを作れますが、Laravelはphp artisan db:seedです。

factoryを作成

factoryを使うと簡単にかけるのでfactoryを使います。

php artisan make:factory TopicFactory --model=Topic
php artisan make:factory CommentFactory --model=Comment

factoryを編集

database/factories/TopicFactory.php

<?php

/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Topic;
use Faker\Generator as Faker;

$factory->define(Topic::class, function (Faker $faker) {
    return [
        'title' => $faker->realText(50),
        'content' => $faker->realText(800),
    ];
});

database/factories/CommentFactory.php

<?php

/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Comment;
use Faker\Generator as Faker;

$factory->define(Comment::class, function (Faker $faker) {
    return [
        'content' => $faker->realText(400),
    ];
});

fakerはダミーデータを作ってくれるやつですね。
結構面白いデータとかあるので使ってみると良いと思います。
railsのときは名前にポケモンとか入れてました笑

TopicSeederを作成

TopicSeederを作ります。

php artisan make:seeder TopicSeeder

TopicSeeder.phpを編集します。

database/seeds/TopicSeeder.php

<?php

use Illuminate\Database\Seeder;
use App\Topic; //追加
use App\Comment; //追加

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

TopicとCommentを使うので、上記に記述します。
一つのtopicに二つのコメントをもつデータを作ります。

php artisan db:seedで流したいのでDatabaseSeederを編集します。
特定のseedだけ流したい場合は指定もできます。
例えばこんな感じですね。
php artisan db:seed --class=TopicSeeder

database/seeds/DatabaseSeeder.php

<?php

use Illuminate\Database\Seeder;

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

LaravelのSeederはFactory使うとリレーションしてても簡潔にかける
Eloquent Model Factory を使ってテストデータを整備する
Laravel Factory Relation Fakerの復習

php artisan db:seedを実行
seed実行

データが入りました。

投稿一覧画面

一覧画面から作っていきます。indexアクションからですね。
Topicデータを一覧表示するページを作っていきます。

ルーティングの追加

Laravelでの基本的なアクションはこちらです。
Route::resource('topics', 'TopicsController');で生成されるルート

アクションURLname
index/topicstopics.index
create/topics/createtopics.create
store/topics/topics.store
show/topics/{id}topics.show
edit/topics/{id}/edittopics.edit
update/topics/{id}topics.update
destroy/topics/{id}topics.destroy

基本的にはrailsともあまり変わりません。
違うのはnewアクションがcreateアクション、createアクションがstoreアクションになっているところぐらいですかね。
混同しないようにしたいです。

routes/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('/', function () {
    return view('welcome');
});

Auth::routes();

Route::get('/home', 'HomeController@index')->name('home');

//ここを追加
Route::group(['middleware' => 'auth'], function() {
    Route::resource('topics', 'TopicsController', ['only' => ['index']]);
});

Route::group(['prefix' => 'admin'], function () {
    Voyager::routes();
});

ログインしないと入れないようにルーティングを設定します。

このauthというミドルウェアは、ユーザがログインしているかどうかを確認できるミドルウェアです。
ログインしている場合はルーティングで指定したコントローラー(今回だとPostController)を実行し、ログインしていない場合はログインページ(/login)へリダイレクトされるようになります。
Laravel 5.7 でログインユーザのみ投稿ページにアクセスできるようにする方法

コントローラの作成

php artisan make:controller TopicsControllerを実行します。

app/Http/Controllers/TopicsController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Topic; // 追加

class TopicsController extends Controller
{
    public function index()
    {
        $topics = topic::latest()->get();

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

latestで最新順に取ってきます。

railsだと下記がデフォルトですね。

def index
 @topics = Topic.all
end

Laravel・データベースからデータ取得する全実例
Laravel5.7: usersのCRUD機能を実装する
LaravelでRoute::resourceを使うときに気をつけること

viewを作成

Laravelはviewは手動で作らないといけません。これ結構面倒ですけど、不要なファイルが生まれないからいいのかな?
views配下にtopicsディレクトリを作り、index.blade.phpを作りましょう。

resources/views/topics/index.blade.php

@extends('layouts.app')

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

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

Laravel 6.0 基本のタスクリスト

localhost:8080/topicsにアクセスします
一覧

一覧表示できました!

ログイン後の遷移先を変更

現状ログインするとhomeへ飛ばされます。
ユーザビリティが悪いので、投稿一覧ページに飛ばしてあげましょう。
投稿一覧ページは/topicsなのでそれに変更します。

app/Http/Controllers/Auth/LoginController.php

        <中略>
    use AuthenticatesUsers;

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = '/topics'; // /homeを/topicに
        <中略>

ついでに登録後の遷移先も変更しましょう。

app/Http/Controllers/Auth/RegisterController.php

        <中略>
    use RegistersUsers;

    /**
     * Where to redirect users after registration.
     *
     * @var string
     */
    protected $redirectTo = '/topics';
        <中略>

ログイン画面と同様ですね。
次は投稿画面を作成していきます。

投稿画面の作成

次は投稿画面を作成していきます。

アクションURLname
create/topics/createtopics.create
store/topics/topics.store

createアクションの設定

createアクションから作っていきましょう。
railsだとnewアクションです。

ルーティングの追加

routes/web.php

<中略>
Route::group(['middleware' => 'auth'], function() {
    Route::resource('topics', 'TopicsController', ['only' => ['index', 'create']]);
});
<中略>

ルーティングを新しく追加します。

アクション追加

app/Http/Controllers/TopicsController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Topic;

class TopicsController extends Controller
{
    public function index()
    {
        $topics = topic::latest()->get();

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

    public function create()
    {
        return view('topics.create');
    }
}

ただ投稿ページを返すだけです。
railsだとインスタンス生成したりしますけど、シンプルでわかりやすいです。
railsもシンプルですけど笑
最後に投稿ページを作ります。

viewの作成

resources/views/topics/create.blade.php

@extends('layouts.app')

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

            <form method="POST" action="{{ route('topics.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="content">
                            本文
                        </label>

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

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

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

現状、このページにアクセスすると<form method="POST" action="{{ route('topics.store') }}">の部分でエラーが出ると思います。まだtopics.storeを定義してないため、そんなのないよと怒られるためです。なのでstoreアクションを作っていきましょう。

Laravel使うならrouteには名前をつけたほうが良い
Laravelリダイレクト実例・全5パターン

storeアクションの追加

ルーティングを追加します

routes/web.php

<中略>
Route::group(['middleware' => 'auth'], function() {
    Route::resource('topics', 'TopicsController', ['only' => ['index', 'create', 'store']]);
});
<中略>
アクションを追加します

app/Http/Controllers/TopicsController.php

<中略>
    public function store(Request $request)
    {
        $params = $request->validate([
            'title' => 'required|max:50',
            'content' => 'required|max: 800',
        ]);

        Topic::create($params);

        return redirect()->route('topics.index');
    }
<中略>

バリデーションをかけます。
簡単にかけますね。
requiredは必須、max:は文字数ですね。
すごくわかりやすくていいですね。

リンクを追加する

resources/views/topics/index.blade.php

<中略>
@extends('layouts.app')

@section('content')
    <div class="container mt-4">
        <div class="mb-4">
            <a href="{{ route('topics.create') }}" class="btn btn-primary">
                投稿を新規作成する
            </a>
        </div>
        @foreach ($topics as $topic)
            <div class="card mb-4">
                <div class="card-header">
                    {{ $topic->title }}
                </div>
<中略>

できました。
実際に投稿してみましょう!

投稿画面
投稿画面
投稿一覧
投稿一覧画面

投稿詳細画面

showアクションですね。

ルーティングを追加

routes/web.php

<中略>
Route::group(['middleware' => 'auth'], function() {
    Route::resource('topics', 'TopicsController', ['only' => ['index', 'create', 'store', 'show']]);
});
<中略>

アクションの追加

app/Http/Controllers/TopicsController.php

<中略>
    public function show($topic_id)
    {
        $topic = Topic::findOrFail($topic_id);

        return view('topics.show',[
            'topic' => $topic,
        ]);
    }
<中略>

パラメータからtopic_idを取ってきて、それを元にTopicを取り出します。

viewの追加

show.blade.phpを作って下記を記述

resources/views/topics/show.blade.php

@extends('layouts.app')

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

            <p class="mb-5">
                {!! nl2br(e($topic->body)) !!}
            </p>
        </div>
    </div>
@endsection

リンクを追加

resources/views/topics/index.blade.php

<中略>
<div class="card-body">
        <p class="card-text">
                {!! nl2br(e(str_limit($topic->body, 200))) !!}
        </p>
        <a class="card-link" href="{{ route('topics.show', ['topic' => $topic]) }}">
                続きを読む
        </a>
</div>
<中略>

投稿編集画面

投稿を編集できるようにしていきましょう。

アクションURLname
edit/topics/{id}/edittopics.edit
update/topics/{id}topics.update

ルーティングを追加

routes/web.php

<中略>
Route::group(['middleware' => 'auth'], function() {
    Route::resource('topics', 'TopicsController', ['only' => ['index', 'create', 'store', 'show', 'edit', 'update']]);
});
<中略>

アクションの追加

app/Http/Controllers/TopicsController.php

<中略>
    public function edit($topic_id)
    {
        $topic = Topic::findOrFail($topic_id);

        return view('topics.edit',[
            'topic' => $topic,
        ]);
    }

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

        $topic = Topic::findOrFail($topic_id);
        $topic->fill($params)->save();

        return redirect()->route('topics.show', ['topic' => $topic]);
    }
<中略>

viewを追加

edit.blade.phpを作って下記を記述

resources/views/topics/edit.blade.php

@extends('layouts.app')

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

            <form method="POST" action="{{ route('topics.update', ['topic' => $topic]) }}">
                @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') ?: $topic->title }}" type="text">
                        @if ($errors->has('title'))
                            <div class="invalid-feedback">
                                {{ $errors->first('title') }}
                            </div>
                        @endif
                    </div>

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

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

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

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

編集画面

リンクを追加します

resources/views/topics/show.blade.php

<中略>
@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('topics.edit', ['topic' => $topic]) }}">
                    編集する
                </a>
            </div>
            <h1 class="h5 mb-4">
                {{ $topic->title }}
            </h1>
<中略>

削除機能を追加

最後に削除できるようにしていきましょう。

アクションURLname
destroy/topics/{id}topics.destroy

ルーティングを追加

routes/web.php

<中略>
Route::group(['middleware' => 'auth'], function() {
    Route::resource('topics', 'TopicsController');
});
<中略>

全てアクションが揃ったので、onlyを取り除きます。

アクションの追加

app/Http/Controllers/TopicsController.php

<中略>
public function destroy($topic_id)
{
        $topic = Topic::findOrFail($topic_id);

        $topic->delete();

        return redirect()->route('topics.index');
}
<中略>

ボタンを追加

resources/views/topics/show.blade.php

@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('topics.edit', ['topic' => $topic]) }}">
                    編集する
                </a>
                <form style="display: inline-block;" method="POST" action="{{ route('topics.destroy', ['topic' => $topic]) }}">
                    @csrf
                    @method('DELETE')
                    <button class="btn btn-danger">削除する</button>
                </form>
            </div>

削除ボタン
ボタンができました!

コメント機能を追加

ルーティングを追加

routes/web.php

<中略>
Route::group(['middleware' => 'auth'], function() {
    Route::resource('topics', 'TopicsController');
    Route::resource('comments', 'CommentsController', ['only' => ['store']]);
});
<中略>

コントローラを作成

php artisan make:controller CommentsController
上記のコマンドでコントローラを作成します

app/Http/Controller/CommentsController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Topic;

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

        $topic = Topic::findOrFail($params['topic_id']);
        $topic->comments()->create($params);

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

viewの作成

resources/views/topics/show.blade.php

@extends('layouts.app')

@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('topics.edit', ['topic' => $topic]) }}">
                    編集する
                </a>
                <form style="display: inline-block;" method="POST" action="{{ route('topics.destroy', ['topic' => $topic]) }}">
                    @csrf
                    @method('DELETE')
                    <button class="btn btn-danger">削除する</button>
                </form>
            </div>
            <h1 class="h5 mb-4">
                {{ $topic->title }}
            </h1>

            <p class="mb-5">
                {!! nl2br(e($topic->content)) !!}
            </p>
            <section>
                <h2 class="h5 mb-4">
                    コメント
                </h2>

                @forelse($topic->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>
        <form class="mb-4" method="POST" action="{{ route('comments.store') }}">
            @csrf

            <input name="topic_id" type="hidden" value="{{ $topic->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>
        </div>
    </div>
@endsection

コメント
コメント一覧が出ました。
コメントの投稿もできるようになっています。

投稿に紐づくコメントも削除

コメントを追加したので、削除機能で作ったやつだけじゃ、削除できないようになっています。
なので、投稿が削除された時は紐づいているコメントも一緒に削除しちゃいましょう。

app/Topic.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Topic extends Model
{
    protected $fillable = [
        'title',
        'content',
    ];
    public function comments()
    {
        return $this->hasMany('App\Comment');
    }

    public static function boot()
    {
        parent::boot();

        static::deleting(function($topic) {
            $topic->comments()->delete();
        });
    }
}



ページネーションを追加する

投稿が増えるとページが長くなるので、投稿数を限定してページネーションを追加しましょう。
app/Http/Controller/TopicsController.php

<中略>
    public function index()
    {
        $topics = topic::latest()->paginate(10);

        return view('topics.index', ['topics' => $topics]);
    }
<中略>

resources/views/topics/index.blade.php

<中略>
                <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

ページネーション

n+1を解消

app/Http/Controllers/TopicsController.php

    public function index()
    {
        $topics = Topic::with(['comments'])->latest()->paginate(10);

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

自分の投稿のみ削除できるようにする

現状だと他の人の投稿も削除できてしまいます。
なので、自分の投稿のみ削除できるようにしましょう。

topicにuser_idを持たせる

php artisan make:migration add_user_id_to_to_topics_table --table=topicsでマイグレーションファイルを作ります。

database/migrations/2020_01_19_013132_add_user_id_to_to_topics_table.php

<?php

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

class AddUserIdToToTopicsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('topics', function (Blueprint $table) {
            $table->bigInteger('user_id')->unsigned();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('topics', function (Blueprint $table) {
            $table->dropColumn('user_id');
        });
    }
}

php artisan migrateを実行して、カラムを追加します。

投稿時にuser_idを追加する

app/Http/Controller/TopicsController.php

<中略>
public function store(Request $request)
{
        $topic = new Topic();
        $topic->title = $request->title;
        $topic->content = $request->content;
        $topic->user_id = \Auth::user()->id;

        $topic->save();

        return redirect()->route('topics.index');
}
<中略>


バリデーションを追加

php artisan make:request CreateTopic
上記コマンドを実行します。

app/Http/Requests/CreateTopic.phpができるので編集します。

app/Http/Requests/CreateTopic.php

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class CreateTopic extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'title' => 'required|max:50',
            'content' => 'required|max: 800',
        ];
    }
}


自分の投稿だけ編集・削除リンクを表示

resources/views/topics/show.blade.php

@extends('layouts.app')

@section('content')
    <div class="container mt-4">
        <div class="border p-4">
            @if ($topic->user_id === Auth::id())
                <div class="mb-4 text-right">
                    <a class="btn btn-primary" href="{{ route('topics.edit', ['topic' => $topic]) }}">
                        編集する
                    </a>
                    <form style="display: inline-block;" method="POST" action="{{ route('topics.destroy', ['topic' => $topic]) }}">
                        @csrf
                        @method('DELETE')
                        <button class="btn btn-danger">削除する</button>
                    </form>
                </div>
            @endif
<中略>

seederの作成

php artisan make:seeder UserSeederを実行する

database/seeds/UserSeeder.php

<?php

use Illuminate\Database\Seeder;

use App\User;

class UserSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        factory(User::class, 10)->create();
    }
}

database/seeds/DatabaseSeeder.php

<?php

use Illuminate\Database\Seeder;

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

database/factories/TopicFactory.php

<?php

/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Topic;
use Faker\Generator as Faker;

$factory->define(Topic::class, function (Faker $faker) {
    return [
        'title' => $faker->realText(50),
        'content' => $faker->realText(800),
        'user_id' => $faker->randomDigit(),
    ];
});
Qiita
 
[PHP] Fakerでランダムなフェイクデータを作成する - Qiita
https://qiita.com/Sa2Knight/items/fb82be7551cc84764267
# 概要PHPでランダムなフェイクデータ(テストデータ/ダミーデータ)を作成するためのライブラリである(https://github.com/fzaninotto/Faker)のTips# 前提以下環境で動作...

php artisan migrate:refresh --seed
php artisan voyager:install --with-dummy
php artisan voyager:admin admin@example.com --create
上記を実行します。

php artisan db:seed --class=VoyagerDatabaseSeederを実行しないと、admin画面にログインしたあとに、voyagerの管理画面に遷移できないことがあるみたいです。

自分の投稿かどうかわかるようにする

resources/views/topics/index.blade.php

@extends('layouts.app')

@section('content')
    <div class="container mt-4">
        <div class="mb-4">
            <a href="{{ route('topics.create') }}" class="btn btn-primary">
                投稿を新規作成する
            </a>
        </div>
        @foreach ($topics as $topic)
            <div class="card mb-4">
                <div class="card-header">
                    {{ $topic->title }}
                    @if ($topic->user_id === Auth::id())
                        <span style="float: right;">Your</span>
                    @endif
                </div>

投稿一覧

これで自分の投稿がわかりましたね!

次はherokuにこのアプリをデプロイしていきます!

コメント

  1. bahis より:

    The data mentioned in the write-up are a number of the ideal obtainable. Anton Penez