LaravelでDBのデータをCSV形式でダウンロードする機能を実装したので、まとめておきます。
splfileObjectというphpのクラスを使って実装します。
環境
Laravel: 6.8
MySQL: 8.0
ディレクトリ構成
一応app配下のディレクトリ構成を載せます。
app
├── Http
│ └── Controllers
│ └── DummyController.php
├── Models
│ ├── Dummy.php
├── Providers
│ ├── AppServiceProvider.php
├── Services
│ └── DummyService.php
└── User.php
最近、自分が扱えるようになった、サービスクラスを使い、モデル・サービス・コントローラーに処理を分けて実装します。
まず、DBにデータを登録します。
public function up()
{
Schema::create('dummies', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name', 50);
$table->string('user_name', 50);
$table->string('sex', 1);
$table->string('post_code', 7);
$table->string('address');
$table->string('email');
$table->string('tel');
$table->string('password');
$table->string('text', 100);
$table->integer('number');
$table->timestamps();
});
}
use App\Models\Dummy;
use Faker\Generator as Faker;
$factory->define(Dummy::class, function (Faker $faker) {
return [
// 名前 姓名
'name' => $faker->name(),
// ユーザー名
'user_name' => $faker->unique()->userName(),
// 配列内の文字をランダムに出力 今回は男or女
'sex' => $faker->randomElement($array=['男', '女']),
// 正規表現で郵便番号7桁を出力
'post_code' => $faker->regexify('[1-9]'),
// 住所 郵便番号も
'address' => $faker->address(),
// メールアドレス
'email' => $faker->email(),
// 電話番号
'tel' => $faker->phoneNumber(),
// パスワード
'password' => $faker->password(),
// ランダムに日本語文を20文字で出力
'text' => $faker->realText(20),
// 10~100の数字をランダムに出力
'number' => $faker->numberBetween(10, 100)
];
});
use Illuminate\Database\Seeder;
use App\Models\Dummy;
class DummyTableSeeder extends Seeder
{
public function run()
{
factory(Dummy::class, 50)->create();
}
}
最後にseederを登録して、php artisan migrate:fresh --seed
を実行します。
これで100件のダミーデータが登録されました。
ここからが本題です。
DB から全件取得します。
use Illuminate\Database\Eloquent\Model;
class Dummy extends Model
{
public static function getDummyAll()
{
$allData = self::all();
return $allData;
}
}
サービスクラスにCSV処理を記述します。
use App\Models\Dummy;
use Illuminate\Support\Arr;
class DummyService
{
public function getDummyCsvAll()
{
$allData = Dummy::getDummyAll();
/**
* CSVフォーマット
* 文字コード:SJIS
* 改行コード:CRLF
* 囲い文字:ダブルクォート
* 区切り文字:カンマ
*/
$stream = fopen('php://temp', 'w');
// ヘッダータイトル
$columnHeader = implode(",", [
'"ID"',
'"氏名"',
'"ユーザー名"',
'"性別"',
'"郵便番号"',
'"住所"',
'"メールアドレス"',
'"電話番号"',
'"パスワード"',
'"テキスト"',
'"数字"',
'"作成日時"',
'"更新日時"',
]);
foreach ($allData as $data) {
// 囲み文字の""付きの文字列を1行ずつ生成
$outputText = implode(",", [
'"' . $data->id . '"',
'"' . $data->name . '"',
'"' . $data->user_name . '"',
'"' . $data->sex . '"',
'"' . $data->post_code . '"',
'"' . $data->address . '"',
'"' . $data->email . '"',
'"' . $data->tel . '"',
'"' . $data->password . '"',
'"' . $data->text . '"',
'"' . $data->number . '"',
'"' . $data->created_at . '"',
'"' . $data->updated_at . '"',
]);
// csv形式にフォーマット
fputcsv($stream, [
$outputText
], ",", "\n", "");
}
// ファイルポインタの位置を先頭に戻す
rewind($stream);
// CRLF変換
$csv = str_replace(PHP_EOL, "\r\n", stream_get_contents($stream));
// 不要な改行コードを削除
$csv = str_replace("\r\n\r\n", '', $csv);
// CSVのヘッダータイトルを追加
$csv = $columnHeader . $csv;
// SJIS変換
$csv = mb_convert_encoding($csv, 'SJIS');
return $csv;
}
}
作成したサービスクラスをプロバイダに登録します。
これでコントローラから呼べるようになります。
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
// サービスクラスの登録
$this->app->bind('App\Services\DummyService');
}
}
コントローラー内でサービスクラスを呼び、その結果をViewに返しています。
use Illuminate\Http\Request;
use App\Services\DummyService;
class DummyController extends Controller
{
protected $dummyService;
public function __construct(DummyService $dummyService)
{
$this->dummyService = $dummyService;
}
public function csv(Request $request)
{
$csvData = $this->dummyService->getDummyCsvAll();
// ヘッダー設定
$headers = array(
'Content-Type' => 'text/csv',
'Content-Disposition' => 'attachment; filename="テスト.csv"',
);
return response($csvData, 200, $headers);
}
}
あとは、Viewにダウンロード用ボタンを設置して、ルーティング(web.php)で CSV処理用のコントローラに飛ばすだけで使えるようになります。