この記事はOPENLOGI Advent Calendar 2021の16日目の記事です
はじめに
LaravelではFakerというテストデータをランダムに生成するライブラリーがあります
Fakerを使えば、郵便番号や都道府県、市区町村などの住所はランダムに生成できますが、あくまでもランダムなので、実在する住所ではありません
この記事では、Fakerをカスタマイズして、実在する郵便番号と住所のランダムな値を取得するようにしてみます
使用するデータは日本郵便番号株式会社で公開しているものを利用します
環境
$ php -v
PHP 7.4.21 (cli) (built: Jul 1 2021 23:31:08) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
with Zend OPcache v7.4.21, Copyright (c), by Zend Technologies
$ php artisan -V
Laravel Framework 8.75.0
$ composer -V
Composer version 2.1.3 2021-06-09 16:31:20
Fakerの住所関係のメソッドを実行してみる
実装する前にまず、tinkerコマンドでFakerのメソッドを実行してみて、得られる値を確認してみます
$ php artisan tinker
Psy Shell v0.10.12 (PHP 7.4.21 — cli) by Justin Hileman
>>> use Faker;
>>> $faker = Faker\Factory::create('ja_JP');
=> Faker\Generator {#3556}
>>> $faker->addProvider(new Faker\Provider\ja_JP\Address($faker));
>>>
>>> $faker->postcode();
=> "6828679"
>>>
>>> $faker->prefecture();
=> "兵庫県"
>>>
>>> $faker->city();
=> "井高市"
>>>
>>> $faker->streetAddress();
=> "佐藤町山口2-1-7"
>>>
>>> $faker->streetName();
=> "桐山町"
>>>
>>> $faker->address();
=> "2161479 長野県近藤市南区青田町野村7-6-6"
>>>
こんな感じでFakerを使えば、ランダムな住所を生成できます
prefecture以外は、ランダムなので、ググっても、実在しない住所です
以下より、実際に Fakerをカスタマイズしていきたいと思います
手順
以下のステップで行います
- Laravelの環境構築
- 日本郵便の住所データを登録するテーブルを作成し、データを入れておく
- モデルを作成する
- 日本郵便の住所を返すFakerクラスを作成する
- 4で作成したクラスをServiceProviderに登録する
- Fakerを使用してデータを投入する
1. Laravelの環境構築
Laravelのプロジェクトを作成します
composer create-project --prefer-dist laravel/laravel random-address
データベースの設定を行います。データベースには sqlite を使用します
touch database/database.sqlite
.envのデータベース接続設定をsqlite用に書き換えます
DB_CONNECTION=sqlite
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=laravel
# DB_USERNAME=root
# DB_PASSWORD=
app.phpを以下のように書き換えます
- 'timezone' => 'UTC',
+ 'timezone' => 'Asia/Tokyo',
- 'locale' => 'en',
+ 'locale' => 'ja',
- 'faker_locale' => 'en_US',
+ 'faker_locale' => 'ja_JP',
2. 日本郵便の住所データを登録するテーブルを作成し、データを入れておく
郵便番号データを格納するテーブルを作成します
php artisan make:migration create_japan_postcodes_table
作成されたマイグレーションにテーブルの定義を追加します
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateJapanPostcodesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('japan_postcodes', function (Blueprint $table) {
$table->string('postcode');
$table->string('prefecture');
$table->string('city');
$table->string('street');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('japan_postcodes');
}
}
テーブルに初期データを追加するSeedを作成します
php artisan make:seeder JapanPostCodesTableSeeder
作成されたSeederファイルを編集します
登録する元データの郵便番号ファイルは storage
直下に格納しておきます
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class JapanPostCodesTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// storage直下に日本郵便のサイトからダウンロードしたCSVファイルを解凍して配置
// ref: https://www.post.japanpost.jp/zipcode/dl/oogaki-zip.html
$file = new \SplFileObject(storage_path().'/13TOKYO.CSV');
$japanPostcodes = collect($file)->map(function ($line) {
$encLine = mb_convert_encoding($line, 'UTF-8', 'SJIS');
$csvLine = str_getcsv($encLine);
if (isset($csvLine[2]) && isset($csvLine[6]) && isset($csvLine[7]) && isset($csvLine[8])) {
// 郵便番号, 都道府県, 市区町村, 町域すべてあるデータしか登録しない
return [
'postcode' => $csvLine[2],
'prefecture' => $csvLine[6],
'city' => $csvLine[7],
'street' => $csvLine[8],
];
}
return null;
})->reject(function ($value) {
return is_null($value);
})->toArray();
DB::table('japan_postcodes')->insert($japanPostcodes);
}
}
Seederを実行するための設定を行います
- // \App\Models\User::factory(10)->create();
+ $this->call([
+ JapanPostCodesTableSeeder::class,
+ ]);
以下のコマンドを実行し、テーブル作成 & 初期データを登録します
php artisan migrate:fresh --seed
3. モデルを作成する
php artisan make:model JapanPostcode
作成されたモデルを編集します
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
* @property string postcode
* @property string prefecture
* @property string city
* @property string street
*/
class JapanPostcode extends Model
{
use HasFactory;
}
4. 日本郵便の住所を返すFakerクラスを作成する
<?php
namespace App\Faker;
use Faker\Provider\Base;
use App\Models\JapanPostcode;
class JpAddress extends Base
{
/**
* addressを配列で返す
*
* @return array
*/
public static function jpAddressArray(): array
{
$japanPostcode = self::getRandomJapanPostcode();
return [
'postcode' => $japanPostcode->postcode,
'prefecture' => $japanPostcode->prefecture,
'city' => $japanPostcode->city,
'streetAddress' => "{$japanPostcode->street}1-2-3",
];
}
/**
* ランダムにJapanPostcodeを1件返す
*
* @return JapanPostcode
*/
private static function getRandomJapanPostcode(): JapanPostcode
{
return JapanPostcode::query()
->orderByRaw('RANDOM()')
->first();
}
}
5. 4で作成したクラスをServiceProviderに登録する
php artisan make:provider FakerServiceProvider
作成したFakerServiceProviderを編集します
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Faker\Factory;
use Faker\Generator;
use App\Faker\JpAddress;
class FakerServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register(): void
{
$this->app->singleton(Generator::class, function ($app) {
$faker = Factory::create($app['config']->get('app.faker_locale', 'en_US'));
$faker->addProvider(new JpAddress($faker));
return $faker;
});
}
}
FakerServiceProviderを登録します
'providers' => [
...
App\Providers\FakerServiceProvider::class,
],
6. Fakerを使用してデータを投入する
Fakerで生成するランダムな住所を登録するテーブルを作成します
php artisan make:migration create_address_table
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateAddressTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('addresses', function (Blueprint $table) {
$table->id();
$table->string('postcode');
$table->string('prefecture');
$table->string('city');
$table->string('street');
$table->string('name');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('addresses');
}
}
php artisan migrate
モデルを作成します
php artisan make:model Address
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
* @property string postcode
* @property string prefecture
* @property string city
* @property string street
* @property string name
*/
class Address extends Model
{
use HasFactory;
}
ファクトリーを作成します
php artisan make:factory AddressFactory
<?php
namespace Database\Factories;
use App\Models\Address;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\Factory;
class AddressFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
$address = $this->faker->jpAddressArray();
return [
'postcode' => $address['postcode'],
'prefecture' => $address['prefecture'],
'city' => $address['city'],
'street' => $address['streetAddress'],
'name' => $this->faker->name(),
];
}
}
シーダーを作成してデータを投入します
php artisan make:seeder AddressesTableSeeder
<?php
namespace Database\Seeders;
use App\Models\Address;
use Illuminate\Database\Seeder;
class AddressesTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
Address::factory()->count(10)->create();
}
}
$this->call([
JapanPostCodesTableSeeder::class,
+ AddressesTableSeeder::class,
]);
$ php artisan db:seed --class=AddressesTableSeeder
最後に
シーダーの実行結果を確認すると、以下のようにランダムに住所情報を登録できていることが確認できます