Export and Import excel file in Ruby on Rails framework - Phần 2

I. TÓM TẮT PHẦN MỘT

Như phần một đã giới thiệu export, import dữ liệu từ hệ thống lưu trữ dữ liệu ra các định dạng file khác nhau như xls, csv là chức năng cơ bản và cần thiết nhất của mỗi ứng dụng. Trong phần trước chúng ta đã tìm hiểu hai phương pháp export excel file.

  • Export excel file bằng thiết kế XML
  • Export excel file bằng gem "to_xls-Rails"

Mỗi một phương pháp đều có ưu điểm và nhược điểm riêng phù hợp với từng yêu cầu chức năng mà lập trình viên mong muốn. Trong phần này xin tiếp tục giới thiệu một phương pháp export excel file sử dụng gem axlsx có đầy đủ các chức năng từ cơ bản tới phức tạp.

II. EXPORT EXCEL FILE BẰNG GEM AXLSX

Hướng dẫn cài đặt

Việc cài đặt gem axlsx trên framework Ruby on Rails cũng đơn giản và tương tự như các gem khác.

  • Build một project RoR với lệnh rails new project_name
  • Add gem axlsx vào Gemfile
   gem "axlsx"
  • Run bundle install Vậy là chúng ta đã thành công cài đặt gem axlsx và sẵn sàng sử dụng.

Xây dựng chương trình demo

1. Create model

 class CreateStudents < ActiveRecord::Migration
  def change
    create_table :students do |t|
    	t.string :name
    	t.integer :age
    	t.string :phone_number
    	t.string :email
        t.integer :class_room_id
      t.timestamps null: false
    end
  end
end
 class CreateClassRooms < ActiveRecord::Migration
  def change
    create_table :class_rooms do |t|
    	t.string :name
      t.timestamps null: false
    end
  end
end

2. Create controller home để hiển thị dữ liệu

  • Xây dựng app/views/home/index.html
     <div class="row">
      <div class="container">
        <table class="table table-striped">
          <thead>
            <tr>
              <th>ID</th>
              <th>Student Name</th>
              <th>Student Age</th>
              <th>Student Phone Number</th>
              <th>Student Email</th>
              <th></th>
            </tr>
          </thead>
          <tbody>
            <% @students.each do |student| %>
              <tr>
                <td><%= student.id %></td>
                <td><%= student.name %></td>
                <td><%= student.age %></td>
                <td><%= student.phone_number %></td>
                <td><%= student.email %></td>
                <td><%= link_to "Download", axlsx_index_path(id: student.try(:id)) %></td>
              </tr>
            <% end %>
          </tbody>
        </table>
      </div>
      <div class="container">
        <p>
          Download:
          <%= link_to "Excel_By_Xml", xml_index_path(format: "xls") %>
          <br>
          Download:
          <%= link_to "Excel_By_Gem_To_Xls", to_xls_path(format: "xls") %>
          <br>
          Download:
          <%= link_to "Excel_By_Gem_Axlsx", axlsx_index_path %>
        </p>
      </div>
    </div>
  • Cho giao diện như sau.

    Screenshot from 2016-03-26 13:38:31.png

  • Xây dựng controller AxlsxController xử lý xuất dữ liệu của một student khi click vào button download

    class AxlsxController < ApplicationController
      require "axlsx"

      def index
        student = Student.find_by id: params[:id]
        send_xls student
      end

      private
      def send_xls student
        book = Axlsx::Package.new
        workbook = book.workbook
        sheet = workbook.add_worksheet name: "StudentInformation"
        sheet.add_row ["Attributes", "Values"]
        create_data(student).each_with_index do |item|
          sheet.add_row [item.first, item.last]
          sheet.add_data_validation("B2", {type: :list, formula1: "MasterData!$A$3:$A$7"})
        end
        send_excel_file book
      end

      def send_excel_file book
        tmp_file_path = "#{Rails.root}/tmp/#{rand(36**50).to_s(36)}.xlsx"
        book.serialize tmp_file_path
        filename = "Aloha-#{Time.zone.now}.xlsx"
        file_content = File.read(tmp_file_path)
        send_data file_content, filename: filename
        File.delete tmp_file_path
      end

      def create_data student
        [["class_room", student.class_room_name], ["name", student.name],
            ["age", student.age], ["phone_number", student.phone_number], ["email", student.email]]
      end
    end

  • Kết qủa

Screenshot from 2016-03-26 13:46:32.png

  • Giao diện file excel.

Screenshot from 2016-03-26 13:46:50.png

  • Giải thích một số key word trong file app/controllers/axlsx_controller.rb
    • require "axlsx": Thêm thư viện của gem axlsx vào controller để sử dụng.
    • workbook: Object đại diện cho file excel được tạo ra từ class Axlsx::Package của gem axlsx
    • workbook.add_worksheet name: "StudentInformation" tạo ra sheet trong file excel với name là StudentInformation.
    • add_row method thêm một hàng vào trong file excel.
    • book.serialize: Khởi tạo file excel với đường path định sẵn.
    • send_data method để send file excel đã khởi tạo.
    • File.delete xóa file đã được gửi.

Tìm hiểu một số chức năng nâng cao

Về thiết kế chúng ta sẽ thêm một sheet thứ hai chứa data master là dữ liệu được hiển thị trong dropdown class room.

1. Thêm dropdown để chọn dữ liệu

  • Thêm method make_pulldown trong method send_xls
  private
  def send_xls student
    book = Axlsx::Package.new
    workbook = book.workbook
    sheet = workbook.add_worksheet name: "StudentInformation"
    info_sheet = book.workbook.add_worksheet name: "MasterData"
    make_pulldown sheet, info_sheet, student
    send_excel_file book
  end
  • Nội dung private method make_pulldown
    def make_pulldown sheet, info_sheet, student
    info = {class_room: ClassRoom.pluck(:name)}

    base = [*1..info.values.to_a.map(&:size).max]
    info_sheet_data = base.each_with_index.inject([]) do |info_sheet_data, (item, index)|
      info_sheet_data << info.values.map{|item| item[index]}.unshift(item)
    end
    info_sheet.add_row info.keys.map{|item| "List All Class Room"}
    info_sheet.add_row [""]
    info_sheet_data.each do |data|
      info_sheet.add_row data[1..-1], types: Array.new(data.length, nil)
    end

    sheet.add_row ["Attributes", "Values"]
    create_data(student).each_with_index do |item|
      sheet.add_row [item.first, item.last]
      sheet.add_data_validation("B2", {type: :list, formula1: "MasterData!$A$3:$A$7"})
    end
  end

Trong đó:

  • info_sheet: Thêm một sheet khác trong file excel để chứa dữ liệu master.
  • info_sheet_data: Dữ liệu master data có dạng một mảng.
  • Thêm dữ liệu master dưới dạng mảng.
  info_sheet_data.each do |data|
    info_sheet.add_row data[1..-1], types: Array.new(data.length, nil)
  end
  • Thêm thông tin của student và định nghĩa data trong pull down được lấy từ sheet thứ hai.
  sheet.add_row ["Attributes", "Values"]
    create_data(student).each_with_index do |item|
      sheet.add_row [item.first, item.last]
      sheet.add_data_validation("B2", {type: :list, formula1: "MasterData!$A$3:$A$7"})
    end

Kết qủa:

Screenshot from 2016-03-26 14:38:03.png

Dữ liệu master trong sheet thứ hai:

Screenshot from 2016-03-26 14:38:57.png

2. Custom Styles của file excel

Những style như font chữ, màu sắc, độ rộng cột ... đều được cung cấp và dễ dàng sử dụng.

  • Customer method make_pulldown
    def make_pulldown workbook, student
  	sheet = workbook.add_worksheet name: "StudentInformation"
  	info_sheet = workbook.add_worksheet name: "MasterData"
    info = {class_room: ClassRoom.pluck(:name)}

    base = [*1..info.values.to_a.map(&:size).max]
    info_sheet_data = base.each_with_index.inject([]) do |info_sheet_data, (item, index)|
      info_sheet_data << info.values.map{|item| item[index]}.unshift(item)
    end
    info_sheet.add_row info.keys.map{|item| "List All Class Room"}
    info_sheet.add_row [""]
    info_sheet_data.each do |data|
      info_sheet.add_row data[1..-1], types: Array.new(data.length, nil)
    end

    style = workbook.styles
  	black_cell = style.add_style bg_color: "00", fg_color: "FF", sz: 14, alignment: {horizontal: :center}
  	blue_cell =  style.add_style bg_color: "0000FF", fg_color: "FF", sz: 20, alignment: {horizontal: :center}

  	sheet.column_widths 30, 50
    sheet.add_row ["Attributes", "Values"], :style => [black_cell, blue_cell]
    create_data(student).each_with_index do |item|
      sheet.add_row [item.first, item.last]
      sheet.add_data_validation("B2", {type: :list, formula1: "MasterData!$A$3:$A$7"})
    end
  end
  • Trong đó:
    • style = workbook.styles: Khởi tạo object để chứa các cài đặt style.
    • add_style: Method dùng để thêm style đã được khởi tạo vào trong file excel theo từng dòng.
    • sheet.column_widths 30, 50: Cài đặt độ rộng của từng cột tính theo px.
    • Các method như bg_color, fg_color, sz, ... đều được cung cấp đầy đủ dùng để định nghĩa các cài đặt style tới mức nhỏ nhất.

Kết qủa:

Screenshot from 2016-03-26 15:52:07.png

3. Một số chức năng nổi bật

  • Thêm hình ảnh vào trong file.
  • Thêm biểu đồ vào trong file.
  • Thêm công thức vào trong từng ô của file.
  • Customer kiểu dữ liệu trong từng ô của file.đều
  • .....v v

Tất cả những cài đặt, cách sử dụng những chức năng trên đều được cũng cấp và hướng dẫn cụ thể trên trang chủ của gem axlsx tại đây

4. Một số chức năng nổi bật

Có rất nhiều cách để import dữ liệu từ file excel tuy nhiên xin giới thiệu một cách đơn giản sử dụng gem roo được hướng dẫn cụ thể tại đây.

IV. KẾT LUẬN

Có rất nhiều phương pháp để xây dựng chức năng này, mỗi một phương pháp đều có ưu điểm và nhược điểm riêng tùy theo yêu cầu mà chọn lựa cách hợp lý nhất. Bài viết chỉ đề xuất một vài phương án trong đó còn nhiều thiếu xót mong nhận được góp ý từ bạn đọc.

Thanks you for listening!!!