Eloquent: Factories
16466

Ở phần trước các bạn đã được tiếp xúc cơ bản với Eloquent Model and MVC. Trong bài viết này, chúng ta cùng tìm hiểu thêm về Factories nhé!

Factories trong Laravel giúp tạo ra các mô hình (model) và các bản ghi trong cơ sở dữ liệu một cách dễ dàng và tự động, giúp việc kiểm thử và phát triển trở nên thuận tiện hơn.

Tạo Factory

Để tạo một factory, bạn có thể sử dụng lệnh artisan make:factory. Ví dụ, để tạo một factory cho mô hình User, bạn có thể chạy lệnh:

php artisan make:factory UserFactory

Lệnh này sẽ tạo một file factory trong thư mục database/factories.

Định Nghĩa Factory

Trong file factory mới tạo, bạn sẽ định nghĩa các giá trị mặc định cho các thuộc tính của mô hình. Ví dụ:

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

class UserFactory extends Factory
{
    protected $model = User::class;

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

Sử Dụng Factory

Sau khi đã định nghĩa xong factory, bạn có thể sử dụng nó để tạo ra các bản ghi trong cơ sở dữ liệu. Bạn có thể sử dụng phương thức create để lưu bản ghi vào cơ sở dữ liệu hoặc make để chỉ tạo ra đối tượng mà không lưu.

// Tạo một người dùng và lưu vào cơ sở dữ liệu
$user = User::factory()->create();

// Tạo một đối tượng người dùng nhưng không lưu vào cơ sở dữ liệu
$user = User::factory()->make();

Bạn cũng có thể tạo nhiều bản ghi cùng lúc bằng cách truyền vào số lượng cần tạo:

// Tạo 5 người dùng và lưu vào cơ sở dữ liệu
$users = User::factory()->count(5)->create();

Sử Dụng Factory Với Seeder

Factories rất hữu ích khi sử dụng kết hợp với seeders để tạo dữ liệu mẫu cho ứng dụng:

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

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        // Tạo 50 người dùng
        User::factory()->count(50)->create();
    }
}

Chạy lệnh seeder:

php artisan db:seed

# Model and Factory Discovery Conventions

Trong Laravel, các model và factory được kết nối với nhau qua các quy tắc phát hiện (discovery conventions). Khi bạn sử dụng trait Illuminate\Database\Eloquent\Factories\HasFactory trong mô hình của bạn, Laravel sẽ tự động tìm và sử dụng factory phù hợp theo các quy tắc sau:

  1. Tự Động Phát Hiện Factory:

    • Laravel sẽ tìm kiếm một factory trong namespace Database\Factories mà có tên lớp (class name) khớp với tên mô hình (model name) và kết thúc bằng Factory.
    • Ví dụ, nếu bạn có mô hình Flight, Laravel sẽ tìm kiếm một factory có tên FlightFactory trong namespace Database\Factories.
  2. Xác Định Factory Cụ Thể:

    • Nếu bạn muốn xác định factory cho mô hình của bạn theo cách cụ thể hơn hoặc nếu tên và namespace của factory không tuân theo quy tắc tự động, bạn có thể ghi đè phương thức newFactory trên mô hình của mình để trả về instance của factory tương ứng.

Ví Dụ

Giả sử bạn có mô hình Flight và factory tương ứng FlightFactory:

  1. Tạo Mô Hình và Factory:

    • Mô hình Flight:
      namespace App\Models;
      
      use Illuminate\Database\Eloquent\Factories\HasFactory;
      use Illuminate\Database\Eloquent\Model;
      
      class Flight extends Model
      {
          use HasFactory;
      
          // Bạn có thể tùy chỉnh phương thức newFactory nếu cần
          protected static function newFactory()
          {
              return \Database\Factories\FlightFactory::new();
          }
      }
      
    • Factory FlightFactory:
      namespace Database\Factories;
      
      use App\Models\Flight;
      use Illuminate\Database\Eloquent\Factories\Factory;
      
      class FlightFactory extends Factory
      {
          /**
           * Tên của mô hình mà factory tương ứng.
           *
           * @var class-string<\Illuminate\Database\Eloquent\Model>
           */
          protected $model = Flight::class;
      
          /**
           * Định nghĩa các thuộc tính mẫu cho mô hình.
           *
           * @return array
           */
          public function definition()
          {
              return [
                  'flight_number' => $this->faker->unique()->word,
                  'departure' => $this->faker->dateTime,
                  'arrival' => $this->faker->dateTime,
              ];
          }
      }
      
  2. Sử Dụng Factory Trong Seeder:
    Khi bạn sử dụng factory trong seeder, bạn có thể dễ dàng tạo dữ liệu mẫu:
    use Illuminate\Database\Seeder;
    use App\Models\Flight;
    
    class DatabaseSeeder extends Seeder
    {
        public function run()
        {
            // Tạo 10 bản ghi flight
            Flight::factory()->count(10)->create();
        }
    }
    
  3. Chạy Seeder:
    php artisan db:seed
    

# Factory States

Factory states trong Laravel cho phép bạn định nghĩa các trạng thái (states) khác nhau để áp dụng các thay đổi cụ thể cho các mô hình khi tạo dữ liệu mẫu. Điều này rất hữu ích khi bạn muốn có các biến thể của mô hình với các thuộc tính khác nhau mà không cần phải viết nhiều factory khác nhau.

Cách Định Nghĩa và Sử Dụng Các Trạng Thái Trong Factory

  1. Định Nghĩa Trạng Thái:

    Bạn có thể định nghĩa trạng thái trong factory bằng cách sử dụng phương thức state. Phương thức này nhận một closure mà sẽ nhận các thuộc tính gốc của factory và trả về các thuộc tính đã được thay đổi.

    Ví dụ:

    Giả sử bạn có một factory cho mô hình User và bạn muốn định nghĩa một trạng thái suspended để thay đổi thuộc tính account_status thành suspended:

    namespace Database\Factories;
    
    use App\Models\User;
    use Illuminate\Database\Eloquent\Factories\Factory;
    
    class UserFactory extends Factory
    {
        /**
         * Tên của mô hình mà factory tương ứng.
         *
         * @var class-string<\Illuminate\Database\Eloquent\Model>
         */
        protected $model = User::class;
    
        /**
         * Định nghĩa các thuộc tính mẫu cho mô hình.
         *
         * @return array
         */
        public function definition()
        {
            return [
                'name' => $this->faker->name,
                'email' => $this->faker->unique()->safeEmail,
                'password' => bcrypt('password'),
                'account_status' => 'active',  // Giá trị mặc định
            ];
        }
    
        /**
         * Chỉ định rằng người dùng bị tạm ngưng.
         *
         * @return \Illuminate\Database\Eloquent\Factories\Factory
         */
        public function suspended(): Factory
        {
            return $this->state(function (array $attributes) {
                return [
                    'account_status' => 'suspended',
                ];
            });
        }
    }
    
  2. Sử Dụng Các Trạng Thái:

    Khi bạn muốn sử dụng một trạng thái cụ thể khi tạo dữ liệu mẫu, bạn có thể gọi phương thức trạng thái đó trước khi gọi create() hoặc make().

    Ví dụ:

    // Tạo một người dùng với trạng thái bị tạm ngưng
    $suspendedUser = User::factory()->suspended()->create();
    
    // Tạo một người dùng với trạng thái mặc định
    $activeUser = User::factory()->create();
    
  3. Trạng Thái "Trashed":

    Nếu mô hình Eloquent của bạn hỗ trợ xóa mềm (soft deletes), bạn có thể sử dụng trạng thái trashed được tích hợp sẵn để chỉ định rằng mô hình đã bị "xóa mềm".

    Ví dụ:

    use App\Models\User;
    
    // Tạo một người dùng đã bị xóa mềm
    $trashedUser = User::factory()->trashed()->create();
    
    Trạng thái trashed tự động có sẵn cho tất cả các factory mà mô hình hỗ trợ xóa mềm. Bạn không cần phải định nghĩa trạng thái này trong factory của mình.

# Tạo Mô Hình Sử Dụng Factory

Khi bạn đã định nghĩa các factory, bạn có thể sử dụng phương thức factory tĩnh được cung cấp bởi trait Illuminate\Database\Eloquent\Factories\HasFactory trong các mô hình của bạn để khởi tạo các phiên bản factory cho mô hình đó. Dưới đây là hướng dẫn chi tiết về cách tạo mô hình sử dụng factory.

Khởi Tạo Mô Hình

  1. Tạo Mô Hình Mà Không Lưu Vào Cơ Sở Dữ Liệu:

    Để tạo các mô hình mà không lưu vào cơ sở dữ liệu, bạn có thể sử dụng phương thức make:

    use App\Models\User;
    
    $user = User::factory()->make();
    

    Phương thức này tạo ra một đối tượng User mới nhưng không lưu vào cơ sở dữ liệu.

  2. Tạo Một Bộ Sưu Tập Các Mô Hình:

    Bạn có thể tạo một số lượng mô hình nhất định bằng cách sử dụng phương thức count:

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

    Lệnh trên tạo ra ba đối tượng User mà không lưu chúng vào cơ sở dữ liệu.

Áp Dụng Trạng Thái

Bạn có thể áp dụng các trạng thái khác nhau cho các mô hình. Nếu bạn muốn áp dụng nhiều trạng thái cho các mô hình, bạn chỉ cần gọi các phương thức trạng thái trực tiếp:

$users = User::factory()->count(5)->suspended()->make();

Trong ví dụ này, năm đối tượng User được tạo ra với trạng thái bị tạm ngưng.

Ghi Đè Thuộc Tính

Nếu bạn muốn ghi đè một số giá trị mặc định của mô hình, bạn có thể truyền một mảng các giá trị vào phương thức make. Chỉ những thuộc tính được chỉ định mới bị thay đổi, trong khi các thuộc tính khác vẫn giữ giá trị mặc định:

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

Ngoài ra, bạn có thể sử dụng phương thức state để thực hiện việc ghi đè thuộc tính trực tiếp:

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

Lưu ý rằng việc tạo mô hình bằng factory tự động tắt bảo vệ gán hàng loạt (mass assignment).

Lưu Mô Hình Vào Cơ Sở Dữ Liệu

Phương thức create không chỉ khởi tạo mô hình mà còn lưu nó vào cơ sở dữ liệu:

use App\Models\User;

// Tạo một đối tượng User và lưu vào cơ sở dữ liệu...
$user = User::factory()->create();

// Tạo ba đối tượng User và lưu vào cơ sở dữ liệu...
$users = User::factory()->count(3)->create();

Bạn có thể ghi đè các thuộc tính mặc định của factory bằng cách truyền một mảng các thuộc tính vào phương thức create:

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

Sequences

Đôi khi bạn muốn luân phiên giá trị của một thuộc tính mô hình cho mỗi mô hình được tạo. Bạn có thể thực hiện điều này bằng cách định nghĩa một chuỗi (sequence) trong factory. Ví dụ, bạn có thể muốn luân phiên giá trị của thuộc tính admin giữa Y và N:

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

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

Trong ví dụ này, năm đối tượng User sẽ có giá trị admin là Y và năm đối tượng sẽ có giá trị là N.

Nếu cần, bạn có thể sử dụng một closure làm giá trị chuỗi. Closure sẽ được gọi mỗi khi chuỗi cần một giá trị mới:

use Illuminate\Database\Eloquent\Factories\Sequence;

$users = User::factory()
                ->count(10)
                ->state(new Sequence(
                    fn (Sequence $sequence) => ['role' => UserRoles::all()->random()],
                ))
                ->create();

Trong closure của chuỗi, bạn có thể truy cập các thuộc tính $index hoặc $count trên đối tượng chuỗi được chèn vào closure. Thuộc tính $index chứa số lần lặp qua chuỗi cho đến thời điểm đó, trong khi thuộc tính $count chứa tổng số lần chuỗi sẽ được gọi:

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

Để tiện lợi, bạn cũng có thể áp dụng chuỗi bằng phương thức sequence, phương thức này thực hiện gọi phương thức state nội bộ. Phương thức sequence chấp nhận một closure hoặc các mảng thuộc tính được chuỗi hóa:

$users = User::factory()
                ->count(2)
                ->sequence(
                    ['name' => 'First User'],
                    ['name' => 'Second User'],
                )
                ->create();

# Factory Relationships

Trong Laravel, bạn có thể xây dựng các mối quan hệ giữa các mô hình bằng cách sử dụng các phương thức factory. Dưới đây là cách bạn có thể sử dụng factories để tạo các mối quan hệ hasManybelongsTo giữa các mô hình.

Quan Hệ "Has Many"

Khi một mô hình có nhiều mối quan hệ với một mô hình khác, ví dụ như một User có nhiều Post, bạn có thể sử dụng phương thức has của factory để tạo các mô hình liên quan.

  1. Tạo Mô Hình Có Quan Hệ Has Many

    Giả sử bạn có mô hình UserPost, và mô hình User định nghĩa một mối quan hệ hasMany với Post. Để tạo một người dùng với ba bài viết, bạn có thể sử dụng phương thức has như sau:

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

    Laravel sẽ tự động giả định rằng mô hình User có một phương thức posts để định nghĩa mối quan hệ hasMany. Nếu cần, bạn có thể chỉ định tên của mối quan hệ:

    $user = User::factory()
                ->has(Post::factory()->count(3), 'posts')
                ->create();
    
  2. Sử Dụng Các Phương Thức "Ma Thuật"

    Để tiện lợi, bạn có thể sử dụng các phương thức mối quan hệ "ma thuật" của factory. Ví dụ, phương thức sau sẽ tự động xác định rằng các mô hình liên quan nên được tạo thông qua phương thức posts trên mô hình User:

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

    Bạn cũng có thể ghi đè thuộc tính cho các mô hình liên quan:

    $user = User::factory()
                ->hasPosts(3, [
                    'published' => false,
                ])
                ->create();
    

    Và bạn có thể sử dụng closure nếu trạng thái cần truy cập vào mô hình cha:

    $user = User::factory()
                ->hasPosts(3, function (array $attributes, User $user) {
                    return ['user_type' => $user->type];
                })
                ->create();
    

Quan Hệ "Belongs To"

Bây giờ, hãy khám phá cách xây dựng các mối quan hệ ngược lại, tức là khi một mô hình thuộc về một mô hình khác. Bạn có thể sử dụng phương thức for để định nghĩa mô hình cha mà các mô hình do factory tạo ra thuộc về.

  1. Tạo Các Mô Hình Thuộc Về Một Mô Hình Cha

    Để tạo ba bài viết thuộc về một người dùng duy nhất, bạn có thể làm như sau:

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

    Nếu bạn đã có một đối tượng mô hình cha và muốn liên kết với các mô hình bạn đang tạo, bạn có thể truyền đối tượng mô hình vào phương thức for:

    $user = User::factory()->create();
    
    $posts = Post::factory()
                ->count(3)
                ->for($user)
                ->create();
    
  2. Sử Dụng Các Phương Thức "Ma Thuật"

    Để tiện lợi, bạn cũng có thể sử dụng các phương thức mối quan hệ "ma thuật" của factory để định nghĩa quan hệ belongsTo. Ví dụ, phương thức sau sẽ tự động xác định rằng các bài viết nên thuộc về mối quan hệ user trên mô hình Post:

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

Tóm Tắt

  • Quan Hệ "Has Many": Sử dụng phương thức has để tạo các mô hình liên quan với mối quan hệ hasMany. Bạn có thể áp dụng trạng thái và sử dụng các phương thức "ma thuật" để dễ dàng định nghĩa mối quan hệ.

  • Quan Hệ "Belongs To": Sử dụng phương thức for để xác định mô hình cha mà các mô hình do factory tạo ra thuộc về. Bạn cũng có thể sử dụng các phương thức "ma thuật" để đơn giản hóa việc định nghĩa quan hệ.

Các tính năng này giúp bạn dễ dàng tạo dữ liệu mẫu cho việc kiểm thử và phát triển ứng dụng với các mối quan hệ giữa các mô hình.

Ở bài tiếp theo, chúng ta sẽ cùng khám phá các Relationships và các vấn đề liên quan đến Factories nhé

Danh mục


  1. Khác
  2. ThreeJS
  3. Ubuntu/Linux
  4. HTML/CSS
  5. Git
  6. Amazon Web Services
  7. Javascript
  8. Docker
  9. Laravel

Bài viết liên quan


Quản Lý Thời Gian Tự Động Trong Eloquent

Quản Lý Thời Gian Tự Động Trong Eloquent

01.08.2024
Author: ADMIN
Khám phá cách quản lý timestamps trong Eloquent Laravel: tắt tự động cập nhật, tùy chỉnh định dạng, đổi tên cột và cập nhật dữ liệu mà không ảnh hưởng updated_at.
Các Mối Quan Hệ Trong Eloquent

Các Mối Quan Hệ Trong Eloquent

01.08.2024
Author: ADMIN
Tìm hiểu cách thiết lập và sử dụng các mối quan hệ trong Eloquent Laravel, giúp quản lý dữ liệu dễ dàng và linh hoạt hơn.
Basic Eloquent Model and MVC

Basic Eloquent Model and MVC

01.08.2024
Author: ADMIN
Tìm hiểu cách Laravel triển khai kiến trúc MVC với Model, View, Controller. Hướng dẫn chi tiết về Eloquent, xử lý logic trong Controller và hiển thị dữ liệu với Blade.
Eager Loading

Eager Loading

01.08.2024
Author: ADMIN
Eager Loading trong Eloquent giúp tối ưu hiệu suất bằng cách giảm số lượng truy vấn không cần thiết. Tìm hiểu cách sử dụng with, withCount và nested eager loading cho các mối quan hệ phức tạp!

Bài viết khác

Routing

Routing

01.08.2024
Author: ADMIN
Hướng dẫn chi tiết về Basic Routing trong Laravel, từ cách định nghĩa route, sử dụng middleware, route caching đến route naming giúp tối ưu hóa ứng dụng.
Blade Basics

Blade Basics

01.08.2024
Author: ADMIN
Khám phá Blade trong Laravel: từ if-else, loops, kế thừa layout đến include sub-views. Giúp code gọn gàng, dễ quản lý và bảo trì hơn!
9 Mẹo Hữu Ích Khi Sử Dụng Blade Trong Laravel

9 Mẹo Hữu Ích Khi Sử Dụng Blade Trong Laravel

01.08.2024
Author: ADMIN
Khám phá 9 mẹo Blade giúp bạn viết code Laravel sạch, tối ưu và chuyên nghiệp hơn. Từ @forelse, @auth, @guest, đến format ngày, tối ưu SEO – tất cả trong một bài viết súc tích, dễ áp dụng!
Hiển thị giá trị trong Blade

Hiển thị giá trị trong Blade

01.08.2024
Author: ADMIN
Hướng dẫn hiển thị biến trong Laravel Blade: escape HTML tự động, hiển thị dữ liệu thô, giá trị mặc định và cách truy xuất mảng, đối tượng. Giúp bạn tối ưu hiển thị dữ liệu một cách an toàn!