1
0

【Rails】rubyXLのエクセル出力で部屋に属する所属毎にシートを分けてusersを書き込んでいく

Last updated at Posted at 2024-01-23

はじめに

開発で部屋に属する所属毎にシートを分けてエクセル出力する必要があり、ある程度実装ができたのでまとめる。
初学者やrubyXLでエクセル出力を実装しようとしている方々に少しでも参考にもなれば嬉しいのですが、
なんちゃってエンジニアによる自分のアウトプット用の投稿なので、よくないコードの書き方や、または分量的に省略している箇所もございますのでご了承をお願いします。
アドバイスがありましたらコメントを頂けますと幸いです。

環境

rubyのバージョン
ruby 2.7.2p137
railsのバージョン
Rails 6.0.3.4
rubyXLのバージョン
rubyXL (3.4.25)

出力フロー

roomの詳細ページから出力し、roomに紐づいているユーザー(users)を所属毎(affiliations)にシートを分けて用意してあるエクセルファイルに書き込んで出力します。
usersは中間テーブルのroom_usersを経由して取得します。

テーブル構図

roomsテーブル(部屋)

rooms
id
name

affiliationsテーブル(所属)

affiliations
id
name

usersテーブル

users
id
name
affiliation_id

rooms_usersテーブル

rooms_users
id
room_id
user_id

どうしたか

group_byを使い、rooms_usersをrooms_user.user.affiliationでグルーピングし、所属毎(affiliation)に処理を行えるようにしました。

@rooms_users.group_by { |rooms_user| rooms_user.user.affiliation }.each do |affiliation, affiliation_rooms_users|
  affiliation_worksheet = workbook.add_worksheet(affiliation.name) # ここで所属毎にシートを作成
## 所属毎の処理を書く
end

@rooms_users.group_by { |rooms_user| rooms_user.user.affiliation }でrooms_usersをrooms_user.user.affiliationによってグループ化しています。
これにより、同じaffiliationに紐づくroom_userたちが同じグループにまとまり、
.each do |affiliation, partner_scenes_users|でgroup_byで生成されたハッシュをループ処理し、affiliation毎にシートを作成できるようになります。
ここら辺の詳しい解説はgroup_byRails ハッシュ ループなどで調べてもらえれば理解が進むと思います。

全体のコード

app/services/excel_generator.rb
class ExcelGenerator
  def initialize(room) # オブジェクトを生成した時に自動で実行されます。引数からroomの変数を作成します。
    @room = room
  end

  def generate
    template_path = Rails.root.join('public/files/template.xlsx') # 配置してあるテンプレートのエクセルファイルのパス
    workbook = RubyXL::Parser.parse(template_path) # テンプレートのエクセルファイルからworkbookを作成する
    worksheet = workbook[0]
    worksheet.add_cell(0,0,@room.name)
    workbook.worksheets.delete_at(0) # 最初のシートを削除 (最初のシートに書き込みがされていないシートが出力されるため。もっと良い解決策はあるかもしれません)
    # roomに関連するユーザー情報を取得
    @rooms_users = @room.rooms_users

    # ここから本題のメイン
    # affiliationごとにシートを分けてデータを書き込む
    @rooms_users.group_by { |rooms_user| rooms_user.user.affiliation }.each do |affiliation, affiliation_rooms_users|
      affiliation_worksheet = workbook.add_worksheet(affiliation.name) # ここで所属毎にシートを作成

      # テンプレートの内容を新しいシートにコピー
      worksheet.each_with_index do |row, row_index|
        row.cells.each_with_index do |cell, col_index|
          affiliation_worksheet.add_cell(row_index, col_index, cell.value) if cell
        end
      end
      # 上記コードでうまくコピーできない場合は以下を試す
      # affiliation_worksheet.sheet_data = worksheet.sheet_data

      affiliation_rooms_users.each_with_index do |rooms_user, index|
        # userを順番に書き込んでいきたいので.each_with_indexでループし処理します
        row = 2 + (index * 3)  # 開始行を調整
        affiliation_worksheet.add_cell(row, 0, room_user.user.name)
      end
    end
    workbook # 呼び出し元にworkbookを返す
  end
end
app/controllers/rooms_controller.rb
require 'rubyXL'
class RoomsController < ApplicationController
  def generate_xlsx
    @room = Room.find(params[:id])
    excel_generator = ExcelGenerator.new(@room) # 一つ前に作成したExcelGeneratorのオブジェクトを作成
    workbook = excel_generator.generate # generateを実行しエクセルファイルを作成

    respond_to do |format|
      format.xlsx do
        send_data workbook.stream.string, filename: 'example.xlsx', type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', disposition: 'attachment' # エクセルファイルを出力
      end
    end
  end
end

最後に

Railsでのエクセル出力やグルーピングなどの操作はこれまであまりやってこなかったので、今回の実装で理解が進みよかったです。
また、個人の感想ですがエクセル出力はcsvやpdfの出力に比べて大変だと感じました。
ここにはあまり記述していませんが、ただ単にエクセルを出力するだけであれば楽ですが、出力する内容を出力したい箇所に当てはめていく作業などを実装していけば地味に大変です。
ただそれでもRailsのおかげで他の言語に比べて実装しやすいのかなと思います。
この投稿はざっくり作成したので自分以外の方々が見るとわかりずらいかと思いますが、修正箇所が見つかれば隙間時間に編集していこうと思います。

1
0
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
1
0