【Laravel】CSVエクスポートの実装紹介

2025年10月4日Laravel,PHP

紙谷 善照です。
TwitterYoutubeもやってます。

今回はLaravelでCSVのエクスポート機能の紹介です。

インポートの機能はこちらで紹介しています。

コントローラーの作成

UserControllerを作成します。
既に生成済みのコントローラーにエクスポートを追加する場合は、飛ばしてください。

php artisan make:controller UserController

マイグレーション

usersテーブルを作成するマイグレーションファイルを作成します。
こちらもデータベースやモデルが既にある方は飛ばしてください。

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id(); // 自動インクリメントのIDフィールド
            $table->string('first_name');
            $table->string('last_name');
            $table->string('email')->unique();
            $table->string('password');
            $table->string('gender');
            $table->string('account_role');
            $table->timestamp('email_verified_at')->nullable();
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}

コントローラー

コントローラーでは、サービスを実行して、返されたデータを使ってCSVをダウンロードするようにします。

<?php

namespace App\Http\Controllers;

use App\Services\UserExportService;
use Illuminate\Http\Request;

class UserController extends Controller
{
    protected $userExportService;

    // コンストラクタでUserExportServiceを依存注入
    public function __construct(UserExportService $userExportService)
    {
        $this->userExportService = $userExportService;
    }

    // CSVエクスポート機能を提供するメソッド
    public function exportCsv(Request $request)
    {
        // UserExportServiceのexportメソッドを呼び出してCSVデータを生成
        $response = $this->userExportService->export();

        // ストリームダウンロードレスポンスを返す
        return response()->streamDownload(function() use ($response) {
            // CSVの内容を出力
            $handle = fopen('php://output', 'w');
            fwrite($handle, $response['contents']);
            fclose($handle);
        }, 
        // ダウンロードされるファイル名
        $response['filename'], 
        // HTTPレスポンスヘッダー
        $response['headers']);
    }
}

ストリームダウンロードレスポンスを返す

response()->streamDownload

response()->streamDownloadとはストリーミングダウンロードを行うためのメソッドです。
CSVファイルのストリームダウンロードレスポンスを返します。
大きなファイルをサーバーからクライアントにダウンロードする際に役立ちます。
このメソッドを使用すると、サーバー上のメモリ消費を抑えながらファイルをダウンロードすることができます。

return response()->streamDownload(function() use ($response) {
       $handle = fopen('php://output', 'w');
       fwrite($handle, $response['contents']);
       fclose($handle);
}, 

クロージャ

response()->streamDownload引数には、ストリームとして出力したいデータを生成するクロージャ(無名関数)を渡します。
このクロージャ内で、データを逐次出力するコードを書きます。

php://outputストリーム

php://outputは、直接PHPの出力バッファに書き込むための特殊なストリームラッパーです。
これを使うことで、データを直接クライアントに送信することができます。

fwritefclose

  • fwrite関数を使って、CSVデータをphp://outputストリームに書き込みます。
  • fclose関数でストリームを閉じます。

ファイル名とヘッダー

第二引数にはダウンロードされるファイル名を指定し、
第三引数にはHTTPレスポンスヘッダーを指定します。

これにより、ブラウザがファイルを正しくダウンロードできるようになります。

fopenとは

fopenとは、ファイルを読み取り、書き込み、または新しいファイルを作成することができます。

$handle = fopen('ファイル名', 'モード');
  • 'r': 読み取り専用 ファイルが存在しなければエラーを出します。
  • 'w': 書き込み専用 ファイルが存在しなければ新規作成し、存在する場合は内容を削除します。
  • 'a': 追記専用。ファイルが存在しなければ新規作成し、存在する場合はファイルの末尾に追記します。
  • 'x': 書き込み専用。ファイルが存在する場合はエラーを出します。
$handle = fopen('php://output', 'w');

fopen関数を使って、PHPの出力バッファ(php://output)を開きます。
php://outputは、直接PHPの出力バッファに書き込むための特殊なストリームです。

CSVの内容を出力

fwriteを使用して、php://outputストリームにCSVデータを書き込みます。
fcloseでストリームを閉じます。

fwriteとは

fwrite関数は、ファイルやストリームにデータを出力できます。

fwrite ( resource $handle , string $string [, int $length ] )
  • handle: 書き込み先のファイルポインタリソース。fopen関数で取得します。
  • $string: 書き込む文字列データ。
  • $length: オプションで、書き込むバイト数を指定します。指定しない場合は、文字列全体が書き込まれます。戻り値: 書き込まれたバイト数を返します
fwrite(
  $handle,               // 書き込み対象のファイルポインタ(出力バッファを指す 'php://output')
  $response['contents']. // 書き込むデータ(生成されたCSVの内容)
);

fwrite関数を使って、生成されたCSVデータ($response['contents'])を出力バッファに書き込みます。

fcloseとは

fclose関数を使って、出力バッファを閉じます。
これにより、書き込み操作が完了し、データがクライアントに送信されます。

fclose($handle); // ファイルを閉じる

モデル

ユーザーモデルを作成します。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use HasFactory, Notifiable;

    protected $fillable = [
        'first_name',
        'last_name',
        'email',
        'password',
        'gender',
        'account_role',
    ];

    protected $hidden = [
        'password',
        'remember_token',
    ];

    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

ルーティングの設定

use App\Http\Controllers\UserController;

Route::get('export-users', [UserController::class, 'exportCsv']);

サービス

<?php

namespace App\Services;

use App\Models\User;

class UserExportService
{
    // エクスポート処理を実行するメソッド
    public function export()
    {
        // 全ユーザーを取得
        $users = User::all();
        
        // CSVデータを生成
        $csvData = $this->generateCsv($users);

        // ファイル名を生成
        $fileName = $this->generateFileName();
        
        // ヘッダーを生成
        $headers = $this->generateHeaders($fileName);

        // エクスポートデータを返す
        return [
            'contents' => $csvData, // CSVの内容
            'headers' => $headers,  // HTTPレスポンスヘッダー
            'filename' => $fileName, // ダウンロードされるファイル名
        ];
    }

    // ユーザーリストからCSVデータを生成するメソッド
    protected function generateCsv($users)
    {
        // CSVのヘッダー行
        $csvHeaders = ['ID', 'FirstName', 'LastName', 'Email', 'Gender', 'Account Role'];
        $csv = implode(',', $csvHeaders) . "\n";

        // 各ユーザー情報をCSV形式で追加
        foreach ($users as $user) {
            $csv .= implode(',', [
                $user->id,           // ユーザーID
                $user->first_name,   // First Name
                $user->last_name,    // Last Name
                $user->email,        // Email
                $user->gender,       // Gender
                $user->account_role, // Account Role
            ]) . "\n";
        }

        return $csv; // 生成されたCSVデータを返す
    }

    // ファイル名を生成するメソッド
    protected function generateFileName()
    {
        // 現在の日付と時刻を含むファイル名を生成
        return 'users_' . date('Ymd_His') . '.csv';
    }

    // HTTPレスポンスヘッダーを生成するメソッド
    protected function generateHeaders($fileName)
    {
        return [
            'Content-Type' => 'text/csv',                          // コンテンツタイプ
            'Content-Disposition' => "attachment; filename=\"$fileName\"", // ファイルのダウンロード用ヘッダー
        ];
    }
}

CSVのヘッダー行を作成

implode関数を使ってCSVのヘッダー行のデータ行を作成します。

// CSVのヘッダー行
$csvHeaders = ['ID', 'FirstName', 'LastName', 'Email', 'Gender', 'Account Role'];
$csv = implode(',', $csvHeaders) . "\n";

implodeとは

  • $glue: 配列の各要素を結合するために使う区切り文字。
  • $pieces: 結合する配列。
implode(string $glue , array $pieces)
// CSVのヘッダー行
$csvHeaders = ['ID', 'FirstName', 'LastName', 'Email', 'Gender', 'Account Role'];
$csv = implode(',', $csvHeaders) . "\n";

$csvHeadersの各要素(’ID’, 'FirstName’, 'LastName’, 'Email’, 'Gender’, 'Account Role’)をカンマで結合し、文字列に変換します。

結合された文字列の末尾に改行文字(\n)を追加します。
これにより、CSVのヘッダー行が完成します。

CSVのデータを作成

implode関数を使ってCSVのデータ行を作成します。

// 各ユーザー情報をCSV形式で追加
foreach ($users as $user) {
        $csv .= implode(',', [
            $user->id,           // ユーザーID
            $user->first_name,   // First Name
            $user->last_name,    // Last Name
            $user->email,        // Email
            $user->gender,       // Gender
            $user->account_role, // Account Role
        ]) . "\n";
}

ファイル名を作成

現在の日付と時刻を含むファイル名を生成します。
これにより、エクスポートされたファイルがユニークな名前を持つようになります。

return 'users_' . date('Ymd_His') . '.csv';

HTTPレスポンスヘッダーを生成

Content-TypeContent-Dispositionヘッダーを設定して、ブラウザがファイルを正しくダウンロードできるようにします。

// HTTPレスポンスヘッダーを生成するメソッド
protected function generateHeaders($fileName)
{
    return [
        'Content-Type' => 'text/csv',                          // コンテンツタイプ
        'Content-Disposition' => "attachment; filename=\"$fileName\"", // ファイルのダウンロード用ヘッダー
    ];
}

Content-TypeContent-Dispositionヘッダーを設定して、ブラウザがファイルを正しくダウンロードできるようにします。

Laravel,PHPLaravel

Posted by kami