12/08/2018, 16:08

Locate cho ActiveRecord attributes

1. Giới thiệu Nếu bạn muốn project của mình đa ngôn ngữ, bạn chắc chắn sẽ phải dịch các bản ghi. Có rất nhiều cách có thể thực hiện yêu cầu trên. Và một trong những cách được biết đến nhiều nhất là sử dụng các thư viện bên ngoài ví dụ như Globalize. Vấn đề ở đây là những thư viện đó thường thêm ...

1. Giới thiệu

Nếu bạn muốn project của mình đa ngôn ngữ, bạn chắc chắn sẽ phải dịch các bản ghi. Có rất nhiều cách có thể thực hiện yêu cầu trên. Và một trong những cách được biết đến nhiều nhất là sử dụng các thư viện bên ngoài ví dụ như Globalize. Vấn đề ở đây là những thư viện đó thường thêm những đoạn code phức tạp mà có khi dự án của bạn không cần dùng đến, thậm chí dẫn đến conflict code. Lý do bởi các thư viện bên người đôi khi có thêm rất nhiều callback, virtual attribute, scope… vào model của bạn. Thậm chí đôi khi còn sửa đổi hành vi của ActiveRecord.

Giờ đây, với bản update cung cấp các trường native JSON của PostgreSQL, MySQL… Bạn có thể thấy ví dụ mà tôi sẽ nói đến dưới đây, native JSON cung cấp các attribute cần được dịch một cách dễ dàng mà không cần sử dụng bất kỳ một thư viện bên ngoài nào. Ngoài ra với native JSON bạn cũng dễ dàng query các bản ghi bằng cách sử dụng mệnh đề WHERE mà không cần join với các bản khác.

2. Ví dụ

Ví dụ bạn có model Product cần phải dịch trường name và description. Để làm điều này chúng ta chỉ cần tạo hai trường JSON có tên lần lượt là name_translation và description_translation. Việc thêm hậu tố _translation chỉ là một quy ước mang tính cá nhân chứ không bắt buộc. Bạn có thể đặt tên theo sở thích cá nhân.

class CreateProducts < ActiveRecord::Migration[5.1]
  def change
    create_table :products do |t|
      t.string :sku

      t.json :name_translations, default: {}
      t.json :description_translations, default: {}

      t.timestamps
    end
  end
end

OK. Giờ việc tiếp theo là tạo ra một hash nơi chứa các bản dịch và các key

p = Product.new
p.name_translations = {"en" => "My great product", "fr" => "Mon super produit"}
p.name_translations["en"]
#=> "My great product"

Vì ở trên chúng ta đã set giá trị default của cột là 1 hash rỗng nên chúng ta không cần phải kiểm tra trước mà có thể truy xuất trược tiếp product.name_translations [locale].

Nào giờ chúng ta chuyển đến xây dựng form để người dùng có thể nhập các ngôn ngữ khác nhau cho các trường ở trên

<%= fields_for "product[name_translations]", OpenStruct.new(product.name_translations) do |translation_fields| %>
  <% I18n.available_locales.each do |locale| %>
    <div class="field">
      <%= translation_fields.label locale, "Name #{locale}" %>
      <%= translation_fields.text_field locale %>
    </div>
  <% end %>
<% end %>

Như bạn thấy ở ví dụ trên thủ thuật chỉ đơn giản là đóng gói hash và trong một OpenStruct. Bằng cách này Rails hepler có thể đọc được giá trị của hash.

Mọi việc gần như đã hoàn thiện. Giờ chúng ta chỉ cần thêm strong param vào controller như sau

def product_params
  params.require(:product).permit(:sku, name_translations: I18n.available_locales)
end

Ngoài ra như đã nói ở trên chúng ta còn có thể sử dụng native JSON để query

pry(main)> Product.where("name_translations->>'en' = ?", "My great product").first
=> #<Product id: 1, sku: "SKU", name_translations: ...

3. Tổng kết

Như vậy ở trên tôi đã giới thiệu cho các bạn một cách hết sức đơn giản để xây dựng một project lưu trữ các bản dịch của record mà không cần sử dụng bất kỳ thư viện bên ngoài nào.f

Hãy đọc tài liệu về JSON của PostgreSQL và MySQL để có thêm nhiều thông tin về truy vấn này.

0