Seeder and Model Factory in Laravel 5.8

min read

Day 12 #100DaysOfCode
Tiếp tục với cuộc hành trình 100DaysOfCode của tôi, này tôi tiếp tục tìm hiểu về Seeder và Model Factory trong Laravel 5.8. Bài viết vừa qua mình đã dùng Migrations trong Laravel 5.8, bạn có thể xem lại bài viết ở đây Migrations in Laravel 5.8 

* Seeder in Laravel

Okay giả sử mình tạo một table "Products" dùng Migrations

php artisan make:migration create_products_table --create=products 

Seeder and Model Factory in Laravel 5.8

Chạy lệnh migration trên ta sẽ có một file vừa tạo trong thư mục App\database\migrations 
+ 2021_01_11_080651_create_products_table.php : cấu hình thuộc tính cột table "products"

public function up()
    {
        Schema::create('products', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->string('image');
            $table->string('address');
            $table->string('phone');
            $table->timestamps();
        });
    }

Chạy lệnh migration sau để tạo table "products"

php artisan migrate

Seeder and Model Factory in Laravel 5.8

Bạn sẽ được table "products" trong database như sau:

Seeder and Model Factory in Laravel 5.8

Okay giờ là lúc ta chèn dữ liệu vào table "products" bằng Seeder trong Laravel 5.8
Trong laravel, thì Seeder được sử dụng rất nhiều, giúp ít cho việc tạo dữ liệu mẫu, để giúp ta dễ test ứng dụng của ta, bạn sẽ thấy thư mục Seeder trong laravel như App\database\seeds , các file tạo seeds được extends từ class Seeder, qua thư viện  Illuminate\Database\Seeder;
Các cách để có thể chạy một Seeders:

php artisan migrate --seed
php artisan migrate:refresh --seed

chạy thực thi Seeder

php artisan db:seed
php artisan db:seed --class=ProductsTableSeeder

Một là ta chạy hàm run() mặc định trong class DatabaseSeeder mà laravel đã thêm sẵn, Hai là ta có thể chỉ rõ class seeder mà ta cần chạy thông qua --class

Ok giờ ta tạo một Seeder : 

php artisan make:seeder ProductsTableSeeder

Seeder and Model Factory in Laravel 5.8

Bây giờ bạn sẽ nhìn thấy một class ProductsTableSeeder trong thư mục App\database\seeds, giờ chúng ta sẽ sửa nó, sau đó thêm nó vào class DatabaseSeeder, rồi chạy lệnh thực thì
Mở file sau App/database/seeds/DatabaseSeeder.php 

public function run()
{
    $this->call(ProductsTableSeeder::class);
}

Bây giờ chúng ta sửa ProductsTableSeeder class, để thêm dữ liệu record,

<?php

use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
class ProductsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        DB::table('products')->insert([
            'name'=>'Thanh Hòa',
            'email'=>'xyz123abcz@gmail.com',
            'image'=>'https://hoanguyenit.com/public/images/avatar.png',
            'address'=>'Việt Nam',
            'phone'=>'0123456789'
        ]);
    }
}

Okay, giờ ta chạy câu lệnh thực thi seeder như sau:

php artisan db:seed 
//or
php artisan db:seed --class=ProductsTableSeeder

Seeder and Model Factory in Laravel 5.8

Bạn chạy xong, kiểm tra thử trong database mình có dữ liệu không nhé

Seeder and Model Factory in Laravel 5.8

* Model Factory in Laravel

Giờ ta tìm hiểu tiếp về Model Factory trong Laravel, ta cũng có thể nhập dữ liệu mẫu cho table, cũng giống như Seeder vậy, khác một chút về phần cấu hình của nó, còn gọi chạy lệnh thực thi thì cũng y chang bên trên

Tạo một modle factory 

php artisan make:factory ProductsFactory

Seeder and Model Factory in Laravel 5.8

Sau khi chạy lệnh trên bạn sẽ được một class ProductsFactory trong thư mục App/database/factories sẽ có cấu trúc file như thế này

<?php

/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Model;
use Faker\Generator as Faker;

$factory->define(Model::class, function (Faker $faker) {
    return [
        //
    ];
});

Giờ ta cần thêm dữ liệu cho table "products" , ta cần thực hiện như sau, ta cần tạo class model Product trong thư mục App

php artisan make:model Product 

Khi có class model Product trong App, ta chỉnh sửa file ProductsFactory.php trong App/database/factories 

<?php

/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Product;
use Faker\Generator as Faker;

$factory->define(Product::class, function (Faker $faker) {
    return [
        'name' =>$faker->name,
        'email'=>$faker->unique()->safeEmail,
        'image'=>$faker->image('public/storage/images',640,480,true,false),
        'address'=>$faker->address,
        'phone'=>$faker->tollFreePhoneNumber,
        'created_at'=>\Carbon\Carbon::now(),
        'updated_at'=>\Carbon\Carbon::now(),
    ];
});

Đoạn code trên mình có cấu hình tạo hình ảnh ngẫu nhiên bằng faker và lưu trong thư mục public/storage/images, đầu tiên để làm được việc đó bạn chạy link sau

php artisan storage:link

Okay, tiếp trong App\public\storage tạo thư mục "images" nửa là được! 
Sửa đổi $baseUrl của class Image trong vendor\fzaninotto\faker\src\Faker\Provider\Image.php

<?php

namespace Faker\Provider;

/**
 * Depends on image generation from http://lorempixel.com/
 */
class Image extends Base
{
    protected static $categories = array(
        'abstract', 'animals', 'business', 'cats', 'city', 'food', 'nightlife',
        'fashion', 'people', 'nature', 'sports', 'technics', 'transport'
    );

    /**
     * Generate the URL that will return a random image
     *
     * Set randomize to false to remove the random GET parameter at the end of the url.
     *
     * @example 'http://lorempixel.com/640/480/?12345'
     *
     * @param integer $width
     * @param integer $height
     * @param string|null $category
     * @param bool $randomize
     * @param string|null $word
     * @param bool $gray
     *
     * @return string
     */
    public static function imageUrl($width = 640, $height = 480, $category = null, $randomize = true, $word = null, $gray = false)
    {
       /*  $baseUrl = "https://lorempixel.com/"; */
       $baseUrl = "https://via.placeholder.com/";

        $url = "{$width}/{$height}/";

        if ($gray) {
            $url = "gray/" . $url;
        }

        if ($category) {
            if (!in_array($category, static::$categories)) {
                throw new \InvalidArgumentException(sprintf('Unknown image category "%s"', $category));
            }
            $url .= "{$category}/";
            if ($word) {
                $url .= "{$word}/";
            }
        }

        if ($randomize) {
            $url .= '?' . static::randomNumber(5, true);
        }

        return $baseUrl . $url;
    }

    /**
     * Download a remote random image to disk and return its location
     *
     * Requires curl, or allow_url_fopen to be on in php.ini.
     *
     * @example '/path/to/dir/13b73edae8443990be1aa8f1a483bc27.jpg'
     */
    public static function image($dir = null, $width = 640, $height = 480, $category = null, $fullPath = true, $randomize = true, $word = null, $gray = false)
    {
        $dir = is_null($dir) ? sys_get_temp_dir() : $dir; // GNU/Linux / OS X / Windows compatible
        // Validate directory path
        if (!is_dir($dir) || !is_writable($dir)) {
            throw new \InvalidArgumentException(sprintf('Cannot write to directory "%s"', $dir));
        }

        // Generate a random filename. Use the server address so that a file
        // generated at the same time on a different server won't have a collision.
        $name = md5(uniqid(empty($_SERVER['SERVER_ADDR']) ? '' : $_SERVER['SERVER_ADDR'], true));
        $filename = $name .'.jpg';
        $filepath = $dir . DIRECTORY_SEPARATOR . $filename;

        $url = static::imageUrl($width, $height, $category, $randomize, $word, $gray);

        // save file
        if (function_exists('curl_exec')) {
            // use cURL
            $fp = fopen($filepath, 'w');
            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_FILE, $fp);
            $success = curl_exec($ch) && curl_getinfo($ch, CURLINFO_HTTP_CODE) === 200;
            fclose($fp);
            curl_close($ch);

            if (!$success) {
                unlink($filepath);

                // could not contact the distant URL or HTTP error - fail silently.
                return false;
            }
        } elseif (ini_get('allow_url_fopen')) {
            // use remote fopen() via copy()
            $success = copy($url, $filepath);
        } else {
            return new \RuntimeException('The image formatter downloads an image from a remote HTTP server. Therefore, it requires that PHP can request remote hosts, either via cURL or fopen()');
        }

        return $fullPath ? $filepath : $filename;
    }
}

Vì sao mà mình nên chỉnh lại, bởi vì hiện tại https://lorempixel.com/ chưa được fix , nên ta cần chỉnh sửa lại xíu, để có thể tạo những tấm hình ngẫu nhiên
Bạn có thể thấy tại đây: https://github.com/fzaninotto/Faker/issues/1884

Okay,Giờ là lúc mình cấu hình insert dữ liệu vào table "products", để chạy được ta cần gọi nó trong class DatabaseSeeder trong thư mục App\database\seeds

<?php

use Illuminate\Database\Seeder;
use App\Product;
class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        // $this->call(ProductsTableSeeder::class);
        
        //create one
        $product = factory(Product::class)->create();

        
    }
}

Chạy lệnh artisan dưới đây, và bạn xem lại database của bạn đã có dữ liệu mới không!

php artisan db:seed

Bạn có thể tạo nhiều record bằng cách chỉnh sửa lại như sau:

//create many
factory(Product::class,5)->create();

Seeder and Model Factory in Laravel 5.8

Một số faker thường dùng:

'name' => $faker->name,
'name' => $faker->word,
'first_name'  => $this->faker->firstName,
'last_name'   => $this->faker->lastName,
'email'       => $this->faker->unique()->safeEmail,
'email' => $faker->email,
'email' => $faker->unique()->email;
'short_description' => $faker->sentence,
'description' => $faker->paragraph,
'category_id' => function () {
            return factory(App\Category::class)->create()->id;
}, 
'amount' => $faker->randomFloat(2, 0, 10000),
'image' => $faker->image('public/storage/images',640,480, null, false),
'phone' => $this->faker->phoneNumber,
'address'     => $this->faker->streetAddress,
'city'        => $this->faker->city,
'state'       => $this->faker->stateAbbr,
'zip'         => $this->faker->postcode,
'country'     => $this->faker->country,
'company_id' => function () {
    return factory(App\Company::class)->create()->id;
},
'company_size' => function ($contact) {
    // Uses the "company_id" property generated above
    return App\Company::find($contact['company_id'])->size;
},
'description' => $this->faker->paragraph

Bạn có thể tìm hiểu thêm tại đây: 
https://laravel.com/docs/5.8/database-testing#factory-states
https://github.com/fzaninotto/Faker

 

x

Ủng hộ tôi bằng cách click vào quảng cáo. Để tôi có kinh phí tiếp tục phát triển Website!(Support me by clicking on the ad. Let me have the money to continue developing the Website!)