見出し画像

[laravel8] Model Factory

Laravelで開発を行っていて、新たにテーブルにカラムを追加することになりマイグレーションを編集し

php artisan migrate:fresh

上のコマンドを実行するということはよくあることです。

しかしこのコマンドを実行すると今までデータベースに入っていたテスト用のデータはすべて消えてしまい、もう一度データベースにデータを挿入しなくてはなりません。

そんなときにLaravelではテスト用のデータを生成してくれるModel Factoryという機能があります。

UserFactory

LaravelではデフォルトでUserFactoryが存在しているので、Model Factoryを理解するためにもUserFactoryを見ていきましょう。

database/factories/UserFactory.php

<?php

namespace Database\Factories;

use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;

class UserFactory extends Factory
{
   /**
    * The name of the factory's corresponding model.
    *
    * @var string
    */
   protected $model = User::class;

   /**
    * Define the model's default state.
    *
    * @return array
    */
   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),
       ];
   }

   /**
    * Indicate that the model's email address should be unverified.
    *
    * @return \Illuminate\Database\Eloquent\Factories\Factory
    */
   public function unverified()
   {
       return $this->state(function (array $attributes) {
           return [
               'email_verified_at' => null,
           ];
       });
   }
}

このクラスで注目する点はdefinitionメソッドです。

   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),
       ];
   }

このFactoryはmodelプロパティからもわかるようにUserクラスのデータを生成するFactoryで、

protected $model = User::class;

そのUserモデルの生成したいデータをこのdefinitionメソッドの中で生成するデータを定義しています。

カラムによってはデフォルトの値が設定されていることもあるので、全てのカラムを定義する必要はありません。

この場合はname,email,email_verified_at,password,remember_tokenを生成するようになっています。

またfakerとありますが、これは文字列を生成してくれるライブラリです。

$this->faker->name()

は名前を自動で生成してくれます。他にも色々ありますが、使いながら紹介していきます。

UserModel

では、Factoryを設定するModel側はどのような設定が必要でしょうか?

app/Models/User.php

<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
   use HasFactory, Notifiable;
   
   ・・・・・・

ModelFactoryを使用するにはモデルでHasFactory traitをuseする必要があります。

use Illuminate\Database\Eloquent\Factories\HasFactory;
use HasFactory

HasFactory traitについて詳しく知る必要はありませんが、これをuseすることでModelのfactoryメソッドを使用することができるようになります。

factoryメソッド


$php artisan db:seed

コマンドを使ってデータベースにデータを挿入するときに実行される、(デフォルトで存在している)DatabaseSeeder.phpを見てると、

database/seeders/DatabaseSeeder.php

\App\Models\User::factory(10)->create();

ここで確認できるようにfactoryメソッドを使いデータを生成し、そのデータをデータベースへ挿入できます。

つまりfactoryメソッドが使えれば、テストデータを生成してそのデータをデータベースに挿入することができるようになるということです。

Userデータを作成

tinkerを使ってデフォルトのUserFactoryを使ってみます。

$php artisan tinker
Psy Shell v0.10.8 (PHP 8.0.3 — cli) by Justin Hileman
>>> User::factory()->create();
[!] Aliasing 'User' to 'App\Models\User' for this Tinker session.
=> App\Models\User {#3421
    name: "Rowena Cummerata",
    email: "stanford63@example.org",
    email_verified_at: "2021-06-01 13:11:42",
    #password: "$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi",
    #remember_token: "RCFziLrvcC",
    updated_at: "2021-06-01 13:11:42",
    created_at: "2021-06-01 13:11:42",
    id: 1,
  }
User::factory()->create();

を実行してみると、自動生成されたデータが返ってきます。

もちろんデータベースにも入ってきています。

スクリーンショット 2021-06-01 22.14.17

User::factory(10)->create();

と10ユーザーと指定してみるとさらに10ユーザーが挿入されます。

スクリーンショット 2021-06-01 22.17.20

PostモデルのFactoryを作ってみる

Postモデルはコマンドで作ったのでHasFactoryはuseされています。

app/Models/Post.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
   use HasFactory;

   protected $guarded = [];

   public function category()
   {
       return $this->belongsTo(Category::class);
   }
}

この状態ではfactoryメソッドは使えるようになりますが、LaravelはPostFactoryに定義があるのかを探しにいってしまうので、エラーになってしまいます。

なので、PostFactoryを作成します。

$php artisan make:factory PostFactory

database/factories/PostFactory.php に雛形のファイルが生成されるのでdefinitionメソッドに定義をしていきます。

    public function definition()
   {
       return [
           'user_id' => User::factory,
           'category_id' => Category::factory,
           'title' => $this->faker->sentence,
           'slug' => $this->faker->slug,
           'excerpt' => $this->faker->sentence,
           'body' => $this->faker->paragraph 
       ];
   }

比較的短いtitleとexcerptは $this->faker->sentence 、slugも用意されているのでslugには $this->faker->slug 、またブログ記事の内容になるbodyには$this->faker->paragraphでデータを生成するように定義します。

user_idとcategory_idに関してはそれぞれのモデルのfactoryを使い、それぞれ生成されたデータを代入するように設定しています。

CategoryFactoryはまだ作っていないので、ここで一緒に作っておきます。

$php artisan make:factory CategoryFactory

database/factories/CategoryFactory.php

nameとslugを生成するように設定します。

<?php

namespace Database\Factories;

use App\Models\Category;
use Illuminate\Database\Eloquent\Factories\Factory;

class CategoryFactory extends Factory
{
   /**
    * The name of the factory's corresponding model.
    *
    * @var string
    */
   protected $model = Category::class;

   /**
    * Define the model's default state.
    *
    * @return array
    */
   public function definition()
   {
       return [
           'name' => $this->faker->word,
           'slug' => $this->faker->slug
       ];
   }
}

TinkerでPostモデルのFactoryが動くか見てみます。

$php artisan tinker
Psy Shell v0.10.8 (PHP 8.0.3 — cli) by Justin Hileman
>>> App\Models\Post::factory()->create();
=> App\Models\Post {#3447
    user_id: 1,
    category_id: 1,
    title: "Consequatur quam id id nesciunt.",
    slug: "ducimus-dolor-quas-et-nihil-aut-adipisci-error",
    excerpt: "Ducimus dolorem nihil maiores eaque.",
    body: "Consequuntur amet aliquam ducimus et quam sit hic. Aspernatur et et accusantium dolorem aspernatur natus ut. Rerum laudantium ducimus maxime sit dicta quos.",
    updated_at: "2021-06-01 13:42:01",
    created_at: "2021-06-01 13:42:01",
    id: 1,
  }

問題なく新しい記事が生成されているようです。

データベースを見ても

Postsテーブル

スクリーンショット 2021-06-02 19.23.26

Usersテーブル

スクリーンショット 2021-06-02 19.23.33

Categoriesテーブル

スクリーンショット 2021-06-02 19.23.39

すべて生成されています。

Tinkerで

App\Models\Post::factory(3)->create();

などと生成したいデータの数も指定できるので、適当に生成したあとにトップページを確認してみると

スクリーンショット 2021-06-02 19.33.06

生成されたデータでブログができていますね。

最後に

Laravelのmodelのfactoryを使うことで、データベースをリフレッシュした後に毎回テストデータを挿入する必要がなくなりました。

次の記事ではこのFactoryを使用するSeederと呼ばれるコマンドを説明していきます。

今回のソースコードは


この記事が気に入ったらサポートをしてみませんか?