LaravelでスクレイピングしたデータをSeederに逆生成してみた話

# やりたい事
- 某サイトからスクレイピングしてきたデータをDBに保存。   
※某サイトからは許可を得ている。
- ローカルの開発環境でもある程度、テストデータとして、Seederは用意しておきたい。
- PHPUnitを実行する時にテストデータを作っておきたい。

# 導入方法
<img width="830" alt="スクリーンショット 2019-12-24 0.19.32.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85181/12058e02-2af4-859e-36c3-90b82be5a9bc.png">

## 1.fabpot/goutteをインストール
```
$ composer require fabpot/goutte
```
## 2.スクレイピングするバッチを作成
```php
<?php

namespace App\Console\Commands;

use App\Entity\Article;
use Goutte\Client;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;

/**
 * Class ScrapingCommand
 * @package App\Console\Commands
 */
class ScrapingCommand extends Command
{

    /**
     * スクレイピング先のURL
     */
    const SCRAPING_URL = 'http://example.com';

    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'command:scraping_command';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {

        //インスタンス生成
        $client = new Client();

        //取得とDOM構築
        $crawler = $client->request('GET', self::SCRAPING_URL);

        //要素の取得
        $tr = $crawler->filter('table tr')->each(function($element){
            echo $element->text()."\n";
        });

        // 以下略

        // 取得したデータをArticleエンティティに設定 ※例なので、ざっくり書いてます。
        $article = new Article();
        // 本当は良い感じに取得したデータをエンティティに詰める
        $article->title = $tr;
        // 以下略

        DB::beginTransaction();
        try {
            $article->save();
            DB::commit();

        } catch (\Exception $exception) {
            Log::error('記事の更新に失敗しました', [
                'exception' => $exception->getMessage(),
                'file' => __FILE__,
                'method' => __FUNCTION__,
                'line' => __LINE__
            ]);
            DB::rollBack();
        }

        Log::notice('スクレイピングバッチの実行が成功しました。');
    }
}

```
## 3.orangehill/iseedをインストール
https://github.com/orangehill/iseed

```
$ composer require --dev "orangehill/iseed"
```
を実行。   
config/app.phpにProviderの設定を追加

```php
'providers' => [

        /*
         * データベースからLaravelのSeederを逆生成する
         */
        Orangehill\Iseed\IseedServiceProvider::class
    ],
```


## 4.下記のコマンドを実行すればテーブルの内容に応じたSeederクラスが生成される。

```
$ php artisan iseed {table_name} 
```
を実行。

## 5.Seederクラス生成後のイメージ

```php
<?php

use Illuminate\Database\Seeder;

class ArticlesTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        \DB::table('articles')->delete();

        \DB::table('articles')->insert([
                0 => [
                    'id' => 1,
                    'title' => 'タイトル',
                    'descrition' => '記事の説明文',
                    'category' => 'Tech',
                ],
                1 => [
                    'id' => 2,
                    'title' => 'タイトル',
                    'descrition' => '記事の説明文',
                    'category' => 'Tech',
                ],
                2 => [
                    'id' => 3,
                    'title' => 'タイトル',
                    'descrition' => '記事の説明文',
                    'category' => 'Tech',
                ]
            ]
        );
    }
}

```   

# 使ってみた感想
- けっこうコマンドの実行時間も短いし、良かった
- データ量が多いとファイルサイズが大きくなってしまうので、上手くfor文とかで重複データはコードで簡潔にまとめてもらえたら尚嬉しい！
- お客さんがマスタデータを提供していない or APIが存在せず、自分でデータを取得しなければいけない案件には向いてそう！

# 参考記事
- https://speakerdeck.com/bumptakayuki/tetahesufalsetetawolaravelfalseseedernini-sheng-cheng  
自分の発表資料も参考により具体的に書いてみました。
- https://qiita.com/imunew/items/3973658bdcae9ab77b8a