+1

Export file CSV theo mô hình MVC

Như tiêu đề, hôm nay mình xin giới thiệu cách export file CSV theo chuẩn MVC cho những bạn chưa biết 😄. Trước hết xin nói về cách mà trước đây đa số mọi người vẫn đùng như sau:

users_controller.rb

class UsersController < ApplicationController
  def index
    @users = User.all

    respond_to do |format|
      format.html
      format.csv { send_data @users.to_csv, filename: "users-#{Date.today}.csv" }
    end
  end

user.rb

class User < ActiveRecord::Base
  def self.to_csv
    attributes = %w{id email name}

    CSV.generate(headers: true) do |csv|
      csv << attributes

      all.each do |user|
        csv << attributes.map{ |attr| user.send(attr) }
      end
    end
  end

  def name
    "#{first_name} #{last_name}"
  end
end

Và link gọi action sẽ có dạng như sau:

link_to("Export user to csv", users_path(format: :csv))

Với cách trên ta có thể thấy sự thiếu thống nhất trong chuẩn MVCresful. Action index trong resful được hiểu là sẽ hiển thị toàn bộ users trong view. Và theo MVC thì controller có nhiệm vụ lấy dữ liệu từ database chuyển cho view hiển thị. View hiển thị như thế nào thì sẽ được thể hiện trong file view tương ứng. Trong cách làm trên thì function to_csv thể hiện việc hiển thị data lại được quy định trong model và hoàn toàn không có file view tương ứng.

Vậy để khắc phục những điều trên mình xin giới thiệu cách làm sau rõ ràng hơn.

book.rb

require 'csv'

class Book < ActiveRecord::Base
end

** books_controller.rb**

def index
    respond_to do |format|
      format.html do
        @books = Book.all
      end

      format.csv do
        filename = "Book_#{Time.now.to_i}.csv"
        headers['Content-Disposition'] = "attachment; filename=#{filename}"
        headers['Content-Type'] ||= 'text/csv'

        @books = Book.all
      end
    end
  end

Trong controller trên ta khai báo thông tin của file csv filename, headers['Content-Disposition'], headers['Content-Type'] và gửi data sang view bằng biến @books

Tiếp tục ta tạo 1 file index.csv tương ứng để sắp xếp dữ liêu trong file csv nội dung như sau:

index.csv.slim

ruby:
  attributes = ["id", "title", "author", "price"]

= CSV.generate_line attributes

- @books.each do |book|
  - record = attributes.map { |attribute| book.send(attribute) }
  = CSV.generate_line record

Đặt link export vào trang index tương tự như phần trên:

index.html.slim

= link_to 'export to csv', books_path(format: :csv)

Khi bấm vào link này ta sẽ thu được file csv như sau: 7803e8b684513aa874e74356b1bba133.png

Như vậy ta có thể dễ dàng control việc sắp xếp dữ liệu và luồng xử lý với action tuơng ứng.

Toàn bộ code các bạn có thể xem tại đây

HAPPY CODING 😃)


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.