YuHo のブログ

Laravel, PHP, JavaScript

Laravel 5.6 でブログサイトを制作する2 #3 Postモデルの追加

概要

今回は Post モデルを追加します。

Post Model を追加する

artisan コマンドで Post モデルと migration ファイルを作成し、migrate します。これで Post.php と posts テーブルが出来上がります。

php artisan make:model Post -m

Route を作成する

Route を作成します。リソースコントローラーの全てのメソッドを使います。

[blog/routes/web.php]

<?php

Route::resource('posts', 'PostController');

Authorization を準備する

Authorization 用のファイルを作っていきます。Authorization は特定のユーザーが特定の行動の許可を持つか確かめることです。

Gate を作成する

AuthServiceProvider に Gate を登録します。この開発では Post モデルの Authorization は Policy を使って行うので、これはなくてもいいようです。

[blog/app/Providers/AuthServiceProvider.php]

<?php

public function boot()
{
    $this->registerPolicies();

    Gate::resource('posts', 'PostPolicy');
}

PostPolicy を作成する

artisan コマンドで PostPolicy を作成します。

php artisan make:policy PostPolicy --model=Post

PostPolicy を Post Model に紐付ける

作成した PostPolicy を Post モデルに紐づけます。これで認証済みユーザーのインスタンスから can() メソッドで PostPolicy のメソッドを使用することができるようになります。その際、can() メソッドの第2引数には Post モデルのインスタンスを渡す必要があります。

[blog/app/Providers/AuthServiceProvider.php]

<?php

protected $policies = [
    'App\Model' => 'App\Policies\ModelPolicy',
    'App\User' => 'App\Policies\UserPolicy',
    'App\Post' => 'App\Policies\PostPolicy',
];

PostPolicy を編集する

PostPolicy の各メソッドを記述します。

[blog/app/Policies/PostPolicy.php]

<?php

class PostPolicy
{
    use HandlesAuthorization;

    /**
     * Determine whether the user can view the post.
     *
     * @param  \App\User  $user
     * @param  \App\Post  $post
     * @return mixed
     */
    public function view(User $user, Post $post)
    {
        //
    }

    /**
     * Determine whether the user can create posts.
     *
     * @param  \App\User  $user
     * @return mixed
     */
    public function create(User $user)
    {
        return $user->id;
    }

    /**
     * Determine whether the user can update the post.
     *
     * @param  \App\User  $user
     * @param  \App\Post  $post
     * @return mixed
     */
    public function update(User $user, Post $post)
    {
        return $user->id === $post->user_id;
    }

    /**
     * Determine whether the user can delete the post.
     *
     * @param  \App\User  $user
     * @param  \App\Post  $post
     * @return mixed
     */
    public function delete(User $user, Post $post)
    {
        return $user->id === $post->user_id;
    }
}

PostController の作成

PostController を作成します。

作成

artisan コマンドで PostController を作成します。

php artisan make:controller PostController --resource --model=Post

編集

PostController の各メソッドを記述します。

[app/Http/Controllers/PostController.php]

<?php

class PostController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $posts = Post::paginate(10);

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

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        return view('posts.create');
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $post = new App\Post;
        
        $post->title = $request->input('title');
        $post->content = $request->input('content');
        $post->user_id = $request->user()->id;
        
        $post->save();
    }

    /**
     * Display the specified resource.
     *
     * @param  \App\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function show(Post $post)
    {
        return view('posts.show', ['post' => $post]);
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  \App\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function edit(Post $post)
    {
        return view('posts.edit', ['post' => $post]);
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \App\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, Post $post)
    {
        if ($request->user()->can('update', $post)) {
            $post->title = $request->input('title');
            $post->content = $request->input('content');
            $post->save();
        }

        return redirect('/');
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  \App\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function destroy(Request $request, Post $post)
    {
        if ($request->user()->can('delete', $post)) {
        }

        return redirect('/');
    }
}

Blade を作成する

Blade Template を作成します。

[blog/resources/views/posts/index.blade.php]

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Post Index</div>

                <div class="card-body">
                    @foreach($posts as $post)
                    <div><a href="{{ route('posts.show', ['post' => $post->id])}}">{{ $post->title }}</a></div>
                    @endforeach
                </div>

            </div>
        </div>
    </div>
</div>
@endsection
[blog/resources/views/posts/show.blade.php]

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Post Show</div>

                <div class="card-body">
                    <div>{{ $post->title }}</div>
                    <div>{{ $post->content }}</div>
                </div>

            </div>
        </div>
    </div>
</div>
@endsection
[blog/resources/views/posts/create.blade.php]

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Post Create</div>

                <div class="card-body">
                    <form action="{{ route('posts.store', ['post' => $post->id]) }}" method="POST">
                        @csrf
                        <input name="title">
                        <textarea name="content"></textarea>
                        <button>Store</button>
                    </form>
                </div>

            </div>
        </div>
    </div>
</div>
@endsection
[blog/resources/views/posts/edit.blade.php]

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Post Edit</div>

                <div class="card-body">
                    <form action="{{ route('posts.update', ['post' => $post->id]) }}" method="POST">
                        @csrf
                        @method('PUT')
                        <input value="{{ $post->title }}" name="title">
                        <textarea name="content">{{ $post->content }}</textarea>
                        <button>Update</button>
                    </form>
                </div>

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

Seeder を作成する

Seeder を作成します。

作成

artisan コマンドで Seeder ファイルを作成します。

php artisan make:seeder PostsTableSeeder

DatabaseSeeder.php の編集

DatabaseSeeder.php を編集して、そこから作成した Seeder ファイルを呼び出すようにします。

[blog/database/seeds/DatabaseSeeder.php]

<?php

public function run()
{
    $this->call([
        UsersTableSeeder::class,
        PostsTableSeeder::class,
    ]);
}

PostFactory を作成する

PostFactory を作成します。これによって、試験的な posts テーブルの行データが作成できるようになります。Post モデルの試験的なインスタンスと言う方がいいかもしれません。

[blog/database/factories/PostFactory.php]

<?php

use Faker\Generator as Faker;

/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| This directory should contain each of the model factory definitions for
| your application. Factories provide a convenient way to generate new
| model instances for testing / seeding your application's database.
|
*/

$factory->define(App\Post::class, function (Faker $faker) {
    return [
    ];
});

PostsTableSeeder.php の編集

Seeder ファイルを記述します。

[blog/database/seeds/PostsTableSeeder.php]

<?php

public function run()
{
    factory(App\Post::class, 30)->create();
}

Seeder を実行する

Seeder を実行します。

composer dump-autoload
php artisan migrate:refresh --seed

補足

app.blade.php を編集する

app.blade.php に posts.index 用のリンクを追加します。

[blog/resources/views/layouts/app.blade.php]

<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Scripts -->
    <script src="{{ asset('js/app.js') }}" defer></script>

    <!-- Fonts -->
    <link rel="dns-prefetch" href="https://fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css?family=Raleway:300,400,600" rel="stylesheet" type="text/css">

    <!-- Styles -->
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
    <div id="app">
        <nav class="navbar navbar-expand-md navbar-light navbar-laravel">
            <div class="container">
                <a class="navbar-brand" href="{{ url('/') }}">
                    {{ config('app.name', 'Laravel') }}
                </a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>

                <div class="collapse navbar-collapse" id="navbarSupportedContent">
                    <!-- Left Side Of Navbar -->
                    <ul class="navbar-nav mr-auto">
                        <li><a class="nav-link" href="{{ route('users.index') }}">Users</a></li>
                        <li><a class="nav-link" href="{{ route('posts.index') }}">Posts</a></li>

                    </ul>

                    <!-- Right Side Of Navbar -->
                    <ul class="navbar-nav ml-auto">
                        <!-- Authentication Links -->
                        @guest
                            <li><a class="nav-link" href="{{ route('enter') }}">Enter</a></li>
                            <li><a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a></li>
                            <li><a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a></li>
                        @else
                            <li class="nav-item dropdown">
                                <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
                                    {{ Auth::user()->name }} <span class="caret"></span>
                                </a>

                                <div class="dropdown-menu" aria-labelledby="navbarDropdown">
                                    <a class="dropdown-item" href="{{ route('logout') }}"
                                       onclick="event.preventDefault();
                                                     document.getElementById('logout-form').submit();">
                                        {{ __('Logout') }}
                                    </a>

                                    <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
                                        @csrf
                                    </form>
                                </div>
                            </li>
                        @endguest
                    </ul>
                </div>
            </div>
        </nav>

        <main class="py-4">
            @yield('content')
        </main>
    </div>
</body>
</html>

posts テーブル用の migration ファイルを作成する

posts テーブルに title と content カラムを追加するための migration ファイルを作成します。

php artisan make:migration add_posts_table --table=posts

posts 用の migration ファイルを編集する

migration ファイルを記述します。

[blog/database/migrations/????_??_??_?????_add_posts_table.php]

<?php

class AddPostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('posts', function (Blueprint $table) {
            $table->string('title');
            $table->string('content');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('posts', function (Blueprint $table) {
            $table->dropColumn(['title', 'content']);
        });
    }
}

PostsFactory.php を編集する

[blog/database/factories/PostsFactory.php]

<?php

$factory->define(App\Post::class, function (Faker $faker) {
    return [
        'title' => $faker->name,
        'content' => $faker->name,
    ];
});

migration を実行する

php artisan migrate:refresh --seed