【Laravel】選択したカラムだけExcelでダウンロードする

こんにちは❗フリーランス・コンサルタント&エンジニアの 九保すこひ です。

さてさて、以前このブログで「DBデータをCSV・Excelでエクスポートする」という記事を公開しました。

タイトルのとおりデータベースから必要なデータをエクスポートする機能なのですが、今回はこの機能に前からつけてみたかった「便利機能」を開発してみます。

それは・・・・

選択したカラムのデータだけをExcelでダウンロードする

という機能です。

例えばこんなことってないでしょうか。

DMを郵送したいから、会員ユーザーの「名前」と「住所」だけダウンロードしたい📮

もちろん、全データをダウンロードできれば後で編集できますが、必要な部分だけに絞り込むことができればとっても便利です。

そこで!

今回はLaravelを使って選択したカラムだけExcelでダウンロードする機能を作ってみます。

ぜひ楽しみながらやっていきましょう❗

「今調べたら、excelは英語で
『優れている』っていう意味なんですね😊」

開発環境: Laravel 7.x

前提として

今回はusersテーブルのダウンロード機能をつくりますので、以下のようなテストデータが入っていることが前提になります。

もしまだ用意されていない方は、「Laravel6.x以降でログイン機能をインストールする方法」が参考になると思います。

では、実際にやっていきましょう❗

パッケージをインストールする

まずPHPExcelを操作するパッケージをcomposerでインストールします。

composer require phpoffice/phpspreadsheet

モデルにカラムを取得できるメソッドを追加する

今回はカラム(フィールド)はDBテーブルから自動的にとってくるようにしたいので、UserモデルにgetTableColumns()を追加します。

/app/User.php

<?php

// 省略

class User extends Authenticatable
{
    // 省略

    public function getTableColumns() { // 👈 テーブルのカラムを取得

        $table = $this->getTable();
        $columns = $this->getConnection()
            ->getSchemaBuilder()
            ->getColumnListing($table);
        $hidden_columns = $this->getHidden();
        return array_diff($columns, $hidden_columns); // hiddenとされているものを除外

    }

}

なお、この中で重要なのが、$hidden_columnsです。

この中にはpasswordなどモデルとしては表示しないカラムが入っていて、「ダウンロード可能なカラム」からは除外するようにしています。

例えば、カラムが以下3つだとします。

id, name, password

このとき$hidden_columnspasswordが入っているとしたら、最終的に「ダウンロード可能なカラム」

id, name

になります。

※さすがにモデルで隠されているデータをダウンロードできると問題かと思ってつけましたが、お好みで変更してください。

ルートをつくる

では、Laravelにルートをつくりましょう。

Route::get('excel/form', 'ExcelController@form');
Route::post('excel/download', 'ExcelController@download');

1つ目がブラウザから直接アクセスする部分で、2つ目がダウンロードを実行するURLになります。

コントローラーをつくる

では、ルートにセットしたコントローラーをつくっていきます。
以下のコマンドを実行してください。

php artisan make:controller ExcelController

そして、作成されたファイルの中身を次のように変更します。

/app/Http/Controllers/ExcelController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;

class ExcelController extends Controller
{
    public function form() {

        $columns = (new \App\User)->getTableColumns();
        return view('excel.form')->with('columns', $columns); // 👈 ビューへカラムを送る

    }

    public function download(Request $request) {

        $columns = (new \App\User)->getTableColumns();
        $downloading_columns = [];

        foreach($columns as $column) {

            if(in_array($column, $request->columns)) {

                $downloading_columns[] = $column;

            }

        }

        $spreadsheet = new Spreadsheet();
        $sheet = $spreadsheet->getActiveSheet();
        $downloading_data = [];
        $users = \App\User::get();

        foreach($users as $user) {

            $downloading_data[] = $user->only($downloading_columns);

        }

        $sheet->fromArray($downloading_data, null, 'A1');
        $filename = 'users.xlsx';
        $writer = new Xlsx($spreadsheet);
        header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
        header('Content-Disposition: attachment; filename="'. $filename .'"');
        $writer->save('php://output');

    }
}

まずform()の部分ですが、ここはブラウザから直接アクセスする「ダウンロードページ」を作成する部分です。

そのため、ビューを呼び出すことになるのですが、このビューの中で先ほどの「ダウンロード可能なカラム名」が使えるようにしています。

そしてdownload()は、実際にExcelファイルをダウンロードする部分です。

ここではひと手間加えてセキュリティ上のリスクを排除しています。
手順としては次のとおりです。

  1. まず(元々の)「ダウンロード可能なカラム」を取得
  2. 実際に送信されてきた選択カラムから1に含まれているカラムだけ抽出
  3. ダウンロードに適用

つまり、悪意のある送信があったとしても「ダウンロード可能なカラム」に入っていない場合はダウンロードできないなっています。(バリデーションのin条件を使ってもいいかもしれません)

※つまり、直接送信されてきたカラムを直接使ってダウンロードさせるのはセキュリティ上あまりいいとはいえません。

あとは、DBからデータを取得してExcelファイルとして保存するようにしているだけです。

ビューをつくる

では、最後に「ダウンロードページ」のためのビューを作成しましょう。
(今回は通常の送信フォームを使うので、VueAjaxは使わずネイティブなJavaScriptだけで作ってみました)

/resources/views/excel/form.blade.php

<html>
<head>
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="p-3">
        <h3 class="mb-4">選択したカラムのデータだけExcelでダウンロード</h3>
        <form id="form" method="post" action="/excel/download">
            @csrf
            <div class="row">
                <div class="col-md-6">
                    <div class="card">
                        <div class="card-body">
                            <!-- ダウンロード可能なカラムのチェックボックスをつくる ・・・ ① -->
                            @foreach($columns as $column)
                                <label>
                                    <input class="columns" name="columns[]" type="checkbox" value="{{ $column }}"> {{ $column }}
                                </label>
                                <br>
                            @endforeach
                        </div>
                        <div class="card-footer">
                            <button type="submit" class="btn btn-primary float-right">ダウンロードする</button>
                            <label>
                                <input id="all_check" type="checkbox"> 全て選択/解除
                            </label>
                        </div>
                    </div>
                </div>
            </div>
        </form>
    </div>
    <script>

        document.querySelector('#form') // フォームが送信されたとき実行 ・・・ ②
            .addEventListener('submit', e => {

                const columns = document.querySelectorAll('.columns:checked');

                if(columns.length === 0) {

                    e.preventDefault();
                    alert('最低でもひとつはカラムを選択してください。');

                }

            });
        document.querySelector('#all_check')    // 全て選択/解除を実行 ・・・ ③
            .addEventListener('click', e => {

                const allChecked = e.target.checked;
                const columns = document.querySelectorAll('.columns');

                for(let column of columns) {

                    column.checked = allChecked;

                }

            });

    </script>
</body>
</html>

ではこの中でやっていることを見ていきましょう。

①ダウンロード可能なカラムのチェックボックスをつくる

コントローラーでセットした「ダウンロード可能なカラム」をここでループさせてチェックボックを作っています。

②フォーム送信されたとき実行

ダウンロードする」ボタンがクリックされたときに実行される部分です。

ここでは、チェックが入ったカラムの数を取得し、もしゼロの場合は「最低でもひとつはカラムを選択してください。」と表示するようにしています。

またその場合、e.preventDefault()で送信をキャンセルしています。

③全て選択/解除を実行

まず現在の「全て選択/解除」のチェックがtrue or falseかを取得し、ダウンロードしたいカラムの全チェックボックスをその状態に合わせるようにしています。

テストしてみる

では実際にテストしてみましょう❗

このチェックボックスの中から「id」「name」「email」だけにチェックを入れてダウンロードボタンをクリックしてみます。

するとダウンロードが実行されるので、そのファイルを開くと・・・・・

はい、このとおり選択した3つのカラムだけがダウンロードできました❗
成功です😊✨

教材ソースコードをダウンロードする

以下からは今回実際に開発した教材ソースコード一式をダウンロードすることができます。

【Laravel】選択したカラムだけExcelでダウンロードする

おわりに

ということで、今回は選択されたカラムだけをExcelでダウンロードする機能をつくってみました。

こういった機能があると、よりユーザビリティが向上すると思うので、きっと使う側からは感謝されるんじゃないでしょうか(というか、そうあってほしいです😂)

ではでは〜❗

「コレ便利ですね〜、
と言われるとテンション上がりますよね👍✨」

開発のご依頼お待ちしております 😊✨
開発のご依頼はこちらから: お問い合わせ
どうぞよろしくお願いいたします!
このエントリーをはてなブックマークに追加       follow us in feedly  

開発効率を上げるための機材・まとめ