12/08/2018, 13:02

ActiveRecord serialize trong Rails

Khi lập trình rails,đã bao giờ bạn muốn lưu trữ và truy xuất 1 object vào cơ sở dữ liệu mà không biết phải làm cách nào. ActiveRecord#Serialize chính là giải pháp đơn giản nhất để giải quyết vấn đề đó. Và hôm nay tôi muốn hướng dẫn cho bạn những điều cơ bản nhất về kỹ thuật này. 1 Khai báo Bạn ...

Khi lập trình rails,đã bao giờ bạn muốn lưu trữ và truy xuất 1 object vào cơ sở dữ liệu mà không biết phải làm cách nào. ActiveRecord#Serialize chính là giải pháp đơn giản nhất để giải quyết vấn đề đó. Và hôm nay tôi muốn hướng dẫn cho bạn những điều cơ bản nhất về kỹ thuật này.

1 Khai báo

Bạn có trước hết bạn có 1 model như sau.

class User < ActiveRecord::Base
  serialize :properties
end

Rails cung cấp một nền tảng cơ bản trong việc lưu trữ dữ liệu là 1 object trong ActiveRecord với phương thức serialize. Chúng ta cần chuẩn bị một trường text trong cơ sở dữ liệu để lưu trữ cũng như truy xuất object mà ta khai báo trong model User, trên đây là trường properties

$ rails generate migration AddPropertiesToUser properties:text
$ rake db:migrate

Nào giờ bạn vào console và kiểm tra lại xem chúng ta đã có trường properties chưa

irb(main):001:0> user = User.create
irb(main):002:0> user.properties #=> nil

2 Lưu Hash vào cơ sở dữ liệu

Giờ khi đã khai bảo với hướng dẫn ở trên bạn hoàn toàn có thể lưu 1 object vào trường properties. Và chúng ta sẽ thử với Hash object :

irb(main):003:0> user.properties = {twitter_handle: "@BrianVanLoo", location: "CA"}
irb(main):004:0> user.save

Giờ bạn đã lưu thành công và hãy kiểm tra thử xem cái gì đã được lưu trữ ở trường properties trong cơ sở dữ liệu.

---
:twitter_handle: '@BrianVanLoo'
:location: CA

Vậy đây có phải là string? Nó là YAML đại diện cho hash trước đó ta lưu vào. YAML là ngôn ngữ mặc định cho serialized atrribute nó cho phép ta lưu trữ 1 object và lấy nó ra với chính xác kiểu của nó.

irb(main):005:0> user = User.last
irb(main):006:0> user.properties #=> {:twitter_handle=>"@BrianVanLoo", :location=>"CA"}

Ngoài YAML bạn cũng có thể sử dụng JSON để mã hóa bằng cách khai báo như sau:

class User < ActiveRecord::Base
  serialize :properties, JSON
end

Giờ hãy xem object của chúng ta được lưu trong cơ sở dữ liệu như thế nào:

{"twitter_handle":"@BrianVanLoo","location":"CA"}

Bạn cũng hết sức lưu rằng khi dữ liệu được đưa vào cơ sở dữ liệu với các key là các symbol khi lấy ra với cách mã hóa bằng định dang JSON các key này sẽ chuyển hết thành string

irb(main):007:0> user = User.last
irb(main):008:0> user.properties #=> {"twitter_handle"=>"@BrianVanLoo", "location"=>"CA"}

3 Lưu các object phức tạp

Ở trên trên bạn đã có thể lưu 1 object đơn giản như Hash vào cơ sở dữ liệu. Vậy với những object có cấu truc phức tập hơn thì sao? Ta có một ví dụ như sau :

<%= form_for @user.properties do |f| %>
  #...
<% end %>

Vấn đề đầu tiên gặp phải trong ví dụ này đó là properties của @user (được khai báo trong controller @user = User.new) sẽ nhận thông báo lỗi do hiện tại nó nhận giá trị là nil

ActionView::Template::Error (undefined method `model_name' for NilClass:Class):
    1: <%= form_for @user.properties do |f| %>
    2:   #...
    3: <% end %>

Bạn có thể giải quyết bằng cách khai báo properties như là 1 hash rông trong controller:

@user.properties ||= {}

Giờ bạn lại tiếp tục nhận được một thông báo lỗi khác:

ActionView::Template::Error (undefined method `model_name' for Hash:Class):
    1: <%= form_for @user.properties do |f| %>
    2:   #...
    3: <% end %>

Trường hợp này form_for muốn object phải dưới dạng ActiveRecord. Chúng ta phải sửa lại model Properties như sau:

class User
  class Properties
    include ActiveModel::Conversion
    extend ActiveModel::Naming

    attr_accessor :twitter_handle, :location

    def persisted?; true end

    def id; 1 end
  end
end

Quay trở lại với User model chúng ta đổi lại định dạng lưu trữ JSON thành object Properties chúng ta mới tạo:

class User < ActiveRecord::Base
  serialize :properties, Properties
end

KHi chúng ta tạo mới 1 User tức là đồng thời chúng ta cũng tạo mới 1 Properties:

irb(main):007:0> user = User.new
irb(main):008:0> user.properties #=> #<User::Properties:0x9c579e4>

Giờ bạn đã có thể xóa dòng khai báo

@user.properties ||= {}

mà ta viết trong controller trước đó.

Object Properties giờ đây sẽ được mã hóa bằng YAML vào sẽ có dạng trong cơ sở dữ liệu như sau:

--- !ruby/object:User::Properties {}

Chúc các bạn thành công

0