Laravelで作られた掲示板に機能を追加する①

<b>Laravelの勉強の備忘録です</b>

Laravelの勉強のために[このサイト](https://nodoame.net/archives/category/laravel)の**【Laravel】掲示板を作成する（１）～（７）**をなぞりながら掲示板を作りました。
記事を書かれた方にこの場を借りてお礼申し上げます。

そしてLaravel技術力アップのため、この掲示板に機能を追加していきます（本記事の内容）

## 環境

前提条件：【Laravel】掲示板を作成する（１）～（７）をLaravel 8で作り終えていること。


Windows 10
Laravel 8（※掲示板を作成された方の環境はLaravel 5.8のようですが、8で作っています）
Docker

<br>

## 目標

- Userデータを作成し、UserとPostやCommentのリレーションを設定する
 - Fakerを使いUserのダミーデータを作成
 - PostとCommentテーブルのnameカラム名削除をし、user_idカラムを作成
 - Userとのリレーションの設定を行う
 - viewの変更
 - 検索の修正


<br>
## Fakerを使いUserのダミーデータを作成

Userのダミーデータを作成します。
UserモデルとUserFactoryは仕様で作成されているので、Seederを作成します。

```
$ php artisan make:seeder UsersTableSeeder
```

ダミーデータをとりあえず5つ作成

```database\seeds\UsersTableSeeder.php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use App\Models\User;

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

パスワードの記述の変更

```database\factories\UserFactory.php
    public function definition()
    {
        return [
            'name' => $this->faker->name(),
            'email' => $this->faker->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
            'remember_token' => Str::random(10),
        ];
    }

　　↓　変更　↓　

    public function definition()
    {
        return [
            'name' => $this->faker->name(),
            'email' => $this->faker->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => bcrypt('password'),
            'remember_token' => Str::random(10),
        ];
    }
```

ダミーユーザーを5つ作ったので、PostsとCommentsテーブルのuser_id(後ほど作成)に1~5の数字をランダムに流し込むために内容を変更します。

```database\factories\PostFactory.php　と　database\factories\CommentFactory.php（２つとも記述方法が同じ）
    public function definition()
    {
        return [
　　　　　　 （略）
            // 'name' => $this->faker->name,    // 氏名
             ↓↓　変更
            'user_id' => $this->faker->numberBetween(1,5),
　　　　　　 （略）
        ];
    }
```

seederを流し込むための記述を追記します。

```database\seeders\DatabaseSeeder.php
    public function run()
    {
        $this->call(UsersTableSeeder::class);　// ←　リレーションの関係でPostsTableSeederより上に追記
        $this->call(PostsTableSeeder::class);
    }
```

<br>

## PostとCommentテーブルの内容を変更

nameカラムを削除し、user_idを追加します。

Laravelはデフォルトではカラム名の変更はできないらしいので、Doctrine DBALをインストールします。
（カラム名を変更していないので、Doctrine DBALはインストールしなくてもいいかもしれません）

```
$ composer require doctrine/dbal
```

postsとcommentsテーブルのnameカラムを削除し、user_idカラムを作成するためのマイグレーションファイルの作成を行います。

（１）nameカラム削除用のmigrationファイルの作成
とりあえずわかりやすいかなと思う名前で作成。

```
$ php artisan make:migration drop_name_column_from_posts_table --table=posts
$ php artisan make:migration drop_name_column_from_comments_table --table=comments
```

最初はdrop_column_name_columnにしようとしました。
しかし、posts作成後にcomments分を作成しようとしたところ、既にファイルが存在しているとエラーがでたため、上記のような書き方にしました。

（２）nameカラム削除用のmigrationファイルへの記述



```drop_name_column_from_posts_table.phpとdrop_name_column_from_comments_table.php（２つとも記述方法が同じ）
    public function up()
    {
        Schema::table('comments', function (Blueprint $table) {
            $table->dropColumn('name');  //カラムの削除
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('comments', function (Blueprint $table) {
            $table->string('name');  //カラム追加
        });
    }
```

（３）nameカラム削除用のmigrationファイルの反映

```
$ php artisan migrate
```

down処理の検証をしたい場合は、 php artisan migrate:rollback --step=2　とやって、カラムが巻き戻っているかを確認すればいいと思います。というか、した方がいいです。

（４）user_idカラム追加用のmigrationファイルの作成

```
$ php artisan make:migration add_user_id_to_posts_table --table=posts
$ php artisan make:migration add_user_id_to_comments_table --table=comments
```

（５）user_idカラム追加用のmigrationファイルへの記述

Usersテーブルのidを確認したところ、タイプがbigint、属性がunsignedだったのでunsignedBingIntegerで作成します。

```add_user_id_to_posts_table.php
    public function up()
    {
        Schema::table('posts', function (Blueprint $table) {
            $table->unsignedBigInteger('user_id');  //カラム追加
            $table->foreign('user_id')->references('id')->on('users');
        });
    }

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

```add_user_id_to_comments_table.php
    public function up()
    {
        Schema::table('comments', function (Blueprint $table) {
            $table->unsignedBigInteger('user_id');  //カラム追加
            $table->foreign('user_id')->references('id')->on('users');
        });
    }

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

リレーション設定のために内容に変更や追記をします。

```app\Models\User.php
    public function posts()
    {
        return $this->hasMany('App\Models\Post');
    }
    
    public function comments()
    {
        return $this->hasMany('App\Models\Comment');
    }
```

```app\Models\Comment.php

    protected $fillable = [
        'post_id',
        'user_id',　// 'name'から変更
        'comment', 
    ];

    public function user()　// 追記
    {
        return $this->belongsTo('App\Models\User');
    }
```

```app\Models\Post.php

    protected $fillable = [
        'user_id',　// 'name'から変更
        'subject',
        'message', 
        'category_id'
    ];

    public function user()　// 追記
    {
        return $this->belongsTo('App\Models\User');
    }
```

これでseederとリレーションが出来たので、ダミーデータを流し込みます。

```
$ php artisan migrate:refresh --seed
```

<br>

## viewの編集

今まではpostsテーブルのnameカラムを参照していました。
しかし、nameカラムを削除し、user_idを参照するようになったので、内容を変更します。

```index.blade.php
                <td>{{ $post->name }}</td>
　　　　　　　　　↓　変更　↓
                <td>{{ $post->user->name }}</td>

```

```edit.blade.php
                        value="{{ old('name') ?: $post->name }}"
　　　　　　　　　　　　　↓　変更　↓
                        value="{{ old('name') ?: $post->user->name }}"
```

上記の記述にするとちゃんと名前が表示されるようになりました。

<br>

## 検索の修正

テーブルの構造を変えたので、検索をしても名前がひっかからなくなりました。
ですので、検索対象のカラムを変更します。

postsテーブルのmessegeカラム
postsテーブルのnameカラム
↓
postsテーブルのmessegeカラム
usersテーブルのnameカラム

```app\Models\Post.php

    public function scopeFuzzyNameMessage($query, $searchword)
    {
　　　（略）
        return $query->where(function ($query) use($searchword) {
            $query->orWhere('name', 'like', "%{$searchword}%")
                ->orWhere('message', 'like', "%{$searchword}%");
        });
    }

　　↓　変更　↓

    public function scopeFuzzyNameMessage($query, $searchword)
    {
　　　（略）
        $user_ids = User::where('name', 'like', "%{$searchword}%")->pluck("id");
        $query->whereIn('user_id', $user_ids)
              ->orWhere('message', 'like', "%{$searchword}%");
        return $query;
    }
```

まずuser_idsに検索ワードと一致したusersテーブルのnameカラムと一致するidをぶち込みます。
whereInを使っている理由は、user_idsはpluckメソッドを使っていて配列が入っています。そのためwhereIn()を使うのが適しているらしいからです。

この修正を行った後に検索をすると、名前とメッセージに一致している記事を拾ってきてくれるようになります。

<br>

---
続きは、[Laravelで作られた掲示板に機能を追加する②](https://qiita.com/YKhojo/items/fd86d0c3d1b2273f310a)にて！