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
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
Bạn sẽ được table "products" trong database như sau:
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
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
Bạn chạy xong, kiểm tra thử trong database mình có dữ liệu không nhé
* 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
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();
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