Skip to content

Eloquent: Factories

Giới thiệu (Introduction)

Khi testing hoặc seeding database, bạn có thể cần thêm bản ghi. Thay vì chỉ định từng cột thủ công, Laravel cho phép định nghĩa một bộ thuộc tính mặc định cho mỗi Eloquent model bằng model factories.

Xem file database/factories/UserFactory.php:

php
namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
 */
class UserFactory extends Factory
{
    /**
     * Password hiện tại đang sử dụng.
     */
    protected static ?string $password;

    /**
     * Định nghĩa trạng thái mặc định của model.
     *
     * @return array<string, mixed>
     */
    public function definition(): array
    {
        return [
            'name' => fake()->name(),
            'email' => fake()->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => static::$password ??= Hash::make('password'),
            'remember_token' => Str::random(10),
        ];
    }

    /**
     * Đánh dấu email chưa xác minh.
     */
    public function unverified(): static
    {
        return $this->state(fn (array $attributes) => [
            'email_verified_at' => null,
        ]);
    }
}

Định nghĩa Model Factories

Tạo Factory (Generating Factories)

Sử dụng lệnh Artisan:

bash
php artisan make:factory PostFactory

Factory class mới sẽ nằm trong thư mục database/factories.

Quy ước phát hiện Model và Factory

Sử dụng method factory static (từ trait HasFactory) để tạo factory instance:

php
use App\Models\User;

$user = User::factory()->make();

Laravel tự tìm factory trong namespace Database\Factories với tên class khớp model + hậu tố Factory. Nếu không khớp, dùng attribute UseFactory:

php
use Illuminate\Database\Eloquent\Attributes\UseFactory;
use Database\Factories\Administration\FlightFactory;

#[UseFactory(FlightFactory::class)]
class Flight extends Model
{
    // ...
}

Hoặc dùng attribute UseModel trên factory:

php
use App\Administration\Flight;
use Illuminate\Database\Eloquent\Factories\Attributes\UseModel;
use Illuminate\Database\Eloquent\Factories\Factory;

#[UseModel(Flight::class)]
class FlightFactory extends Factory
{
    // ...
}

Factory States

State cho phép định nghĩa các biến thể cho factory:

php
/**
 * Chuyến bay bị tạm ngưng.
 */
public function suspended(): Factory
{
    return $this->state(function (array $attributes) {
        return [
            'status' => 'suspended',
        ];
    });
}

Sử dụng:

php
use App\Models\Flight;

$flight = Flight::factory()->suspended()->make();

Factory Callbacks

Đăng ký callback sau khi tạo model:

php
use App\Models\User;

$user = User::factory()
    ->afterMaking(function (User $user) {
        // ...
    })
    ->afterCreating(function (User $user) {
        // ...
    })
    ->create();

Hoặc định nghĩa trong factory class:

php
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;

class UserFactory extends Factory
{
    public function configure(): static
    {
        return $this->afterMaking(function (User $user) {
            // ...
        })->afterCreating(function (User $user) {
            // ...
        });
    }
}

Tạo Model bằng Factories

Tạo instance (Instantiating Models)

Method make tạo model instance mà không lưu vào database:

php
use App\Models\User;

$user = User::factory()->make();

Tạo collection:

php
$users = User::factory()->count(3)->make();

Ghi đè thuộc tính:

php
$user = User::factory()->make([
    'name' => 'Abigail Otwell',
]);

Lưu vào Database (Persisting Models)

Method create tạo model instance và lưu vào database bằng save:

php
use App\Models\User;

// Tạo một user
$user = User::factory()->create();

// Tạo 3 users
$users = User::factory()->count(3)->create();

// Ghi đè thuộc tính
$user = User::factory()->create([
    'name' => 'Abigail',
]);

Sequences

Thay đổi thuộc tính cho mỗi model được tạo:

php
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Sequence;

$users = User::factory()
    ->count(10)
    ->state(new Sequence(
        ['admin' => 'Y'],
        ['admin' => 'N'],
    ))
    ->create();

Sử dụng closure cho logic phức tạp:

php
$users = User::factory()
    ->count(10)
    ->state(new Sequence(
        fn (Sequence $sequence) => ['name' => 'Name ' . $sequence->index],
    ))
    ->create();

Factory Relationships

Has Many Relationships

Tạo user với 3 posts:

php
use App\Models\Post;
use App\Models\User;

$user = User::factory()
    ->has(Post::factory()->count(3))
    ->create();

Cú pháp shorthand (magic method):

php
$user = User::factory()
    ->hasPosts(3)
    ->create();

Ghi đè thuộc tính:

php
$user = User::factory()
    ->has(Post::factory()->count(3), 'posts')
    ->create();

// Với state
$user = User::factory()
    ->has(
        Post::factory()
            ->count(3)
            ->state(function (array $attributes, User $user) {
                return ['user_type' => $user->type];
            })
    )
    ->create();

Belongs To Relationships

Tạo post thuộc về user:

php
use App\Models\Post;
use App\Models\User;

$posts = Post::factory()
    ->count(3)
    ->for(User::factory()->state([
        'name' => 'Jessica Archer',
    ]))
    ->create();

Magic method:

php
$posts = Post::factory()
    ->count(3)
    ->forUser([
        'name' => 'Jessica Archer',
    ])
    ->create();

Many to Many Relationships

Tạo user với 3 roles:

php
use App\Models\Role;
use App\Models\User;

$user = User::factory()
    ->has(Role::factory()->count(3))
    ->create();

// Với pivot attributes
$user = User::factory()
    ->hasAttached(
        Role::factory()->count(3),
        ['active' => true]
    )
    ->create();

Polymorphic Relationships

Tương tự relationship thường:

php
use App\Models\Post;

$post = Post::factory()
    ->has(Comment::factory()->count(3), 'comments')
    ->create();

Định nghĩa Relationships trong Factories

php
use App\Models\User;

public function definition(): array
{
    return [
        'user_id' => User::factory(),
        'title' => fake()->title(),
        'content' => fake()->paragraph(),
    ];
}

Tái sử dụng Model cho Relationships (Recycling)

Sử dụng recycle để tái sử dụng model thay vì tạo mới:

php
$airline = Airline::factory()->create();

$tickets = Ticket::factory()
    ->count(5)
    ->recycle($airline)
    ->create();

Truyền collection để chọn ngẫu nhiên:

php
$airlines = Airline::factory()->count(3)->create();

$tickets = Ticket::factory()
    ->count(5)
    ->recycle($airlines)
    ->create();