13
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

OPENLOGIAdvent Calendar 2021

Day 16

LaravelのFakerをカスタマイズして、存在する郵便番号と住所のテストデータをランダムに生成する

Last updated at Posted at 2021-12-16

この記事は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をカスタマイズしていきたいと思います

手順

以下のステップで行います

  1. Laravelの環境構築
  2. 日本郵便の住所データを登録するテーブルを作成し、データを入れておく
  3. モデルを作成する
  4. 日本郵便の住所を返すFakerクラスを作成する
  5. 4で作成したクラスをServiceProviderに登録する
  6. Fakerを使用してデータを投入する

1. Laravelの環境構築

Laravelのプロジェクトを作成します

composer create-project --prefer-dist laravel/laravel random-address

データベースの設定を行います。データベースには sqlite を使用します

touch database/database.sqlite

.envのデータベース接続設定をsqlite用に書き換えます

.env
DB_CONNECTION=sqlite
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=laravel
# DB_USERNAME=root
# DB_PASSWORD=

app.phpを以下のように書き換えます

config/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

作成されたマイグレーションにテーブルの定義を追加します

database/migrations/2021_12_11_131423_create_japan_postcodes_table.php
<?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 直下に格納しておきます

database/seeders/JapanPostCodesTableSeeder.php
<?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を実行するための設定を行います

database/seeders/DatabaseSeeder.php

-        // \App\Models\User::factory(10)->create();
+        $this->call([
+            JapanPostCodesTableSeeder::class,
+        ]);

以下のコマンドを実行し、テーブル作成 & 初期データを登録します

php artisan migrate:fresh --seed

3. モデルを作成する

php artisan make:model JapanPostcode

作成されたモデルを編集します

app/Models/JapanPostcode.php
<?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クラスを作成する

app/Faker/JpAddress.php
<?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を編集します

app/Providers/FakerServiceProvider.php
<?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を登録します

config/app.php

    'providers' => [
        ...


        App\Providers\FakerServiceProvider::class,
    ],

6. Fakerを使用してデータを投入する

Fakerで生成するランダムな住所を登録するテーブルを作成します

php artisan make:migration create_address_table
database/migrations/2021_12_14_181551_create_address_table.php
<?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
app/Models/Address.php
<?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
database/factories/AddressFactory.php
<?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
database/seeders/AddressesTableSeeder.php
<?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();
    }
}
database/seeders/DatabaseSeeder.php

        $this->call([
            JapanPostCodesTableSeeder::class,
+           AddressesTableSeeder::class,
        ]);

$ php artisan db:seed --class=AddressesTableSeeder

最後に

シーダーの実行結果を確認すると、以下のようにランダムに住所情報を登録できていることが確認できます
スクリーンショット 2021-12-14 19.29.51.png

参考

13
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?