Single Table Inheritance trong Rails 4 (Phần 2)

Trong bài viết này, chúng ta sẽ đi tìm hiểu xem làm thế nào để tạo ra một controller chung cho các model (Animal, Lion, ...). Chúng ta cần phải tạo routes tương ứng và thêm một số helper để tạo ra đường dẫn đúng. Nếu bạn không có sẵn code từ phần 1, hãy xem lại để có thể bắt đầu ở đây.

Controller chung

Đầu tiên, chúng ta tạo controller chung cho các model là AnimalsController tương ứng với model cha.


rails g controller AnimalsController index show new edit create update destroy

Thiết lập routes

Di chuyển đến file routes.rb và xác định các đường dẫn:


# Remove these lines from config/routes.rb
get "animals/index"
# Code hidden for brevity
get "animals/destroy"

Xác định root của app và routes cho Animals:


# config/routes.rb
Sti::Application.routes.draw do
 resources :animals
 root 'animals#index'
end

Thiết lập hành động index và view tương ứng

Chạy server và truy cập localhost: 3000 và bạn sẽ thấy, không có gì 😄. Chúng ta cần thêm nội dung cho view index, có thể là danh sách của tất cả các animal trong database. Để cung cấp danh sách này, ta cần thêm vào controller dòng sau:


# app/controllers/animals_controller.rb
def index
  @animals = Animal.all
end

Và copy/paste đoạn code sau vào animals/index.html.erb:

# app/views/animals/index.html.erb
<h1>The Lion Tribe</h1>
<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Race</th>
      <th>Age</th>
      <th></th>
      <th></th>
      <th></th>
    </tr>
</thead>
  <tbody>
    <% @animals.each do |animal| %>
      <tr>
        <td><%= animal.name %></td>
        <td><%= animal.race %></td>
        <td><%= animal.age %></td>
        <td><%= link_to 'Show', "" %></td>
        <td><%= link_to 'Edit', "" %></td>
        <td><%= link_to 'Destroy', "", method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

Bây giờ chúng ta có một cách cơ bản để hiển thị dữ liệu là bắt đầu với Single Table Inheritance. Trong phần 1, ta đã tạo ra một bảng animals và một model Animal. Ta cũng tạo ra 3 model khác (Lion, Meerkat, WildBoars) là các model kế thừa từ model Animal và tất cả dùng chung 1 bảng. Nhờ đó, chúng ta tránh lặp lại code. Ta có thể tuân theo các quy tắc DRY trong controller bằng cách sử dụng một controller duy nhất cho tất cả các mô hình STI.

Routes và STI

Hành động index của controller Animals sẽ hiển thị tất cả các animal hoặc 1 loại animal. Với Rails, ta có thể xác định bằng cách thêm một param mặc định vào các đường dẫn:


# config/routes.rb
resources :animals
resources :lions, controller: 'animals', type: 'Lion'
resources :meerkats, controller: 'animals', type: 'Meerkat'
resources :wild_boars, controller: 'animals', type: 'WildBoar'

root 'animals#index'

Là một tip nhỏ khi sử dụng type. Mỗi khi chúng ta truy cập /lions/*, Rails sẽ thêm một param type vào. Vì vậy, đối với mỗi model kế thừa ta thêm type tương ứng vào đường dẫn.

Bây giờ chúng ta có thể sử dụng các giá trị type trong controller:


class AnimalsController < ApplicationController
    before_action :set_race

    def index
       @animals = Animal.all
    end

    # Code hidden for brivety

    private

    def set_race
      @race = race
    end

    def race
       Animal.races.include?(params[:type]) ? params[:type] : "Animal"
    end

    def race_class
       race.constantize
    end

end

Về cơ bản, trước bất kỳ một hành động nào đó chúng ta xác định giá trị race bằng params[:type], hoặc bằng "Animal" nếu không có params[:type], đặt thêm biến @race để có thể dùng trong view. Bây giờ chúng ta có thể thay đổi Animal.all bằng race_class.all.


def index
  @animals = race_class.all
end

Nếu bạn truy cập vào http://localhost:3000, bạn sẽ có một danh sách của tất cả các animal. Nhưng nếu bạn truy cập vào http://localhost:3000/lions hoặc http://localhost:3000/meerkats, bạn chỉ nhận được danh sách các animal với type tương ứng.

Đây là tất cả nội dung của bài viết này. Trong phần tiếp theo chúng ta sẽ thêm đường dẫn động và các view còn thiếu!

Tài liệu dịch: https://samurails.com/tutorial/single-table-inheritance-with-rails-4-part-2/

Link dịch phần 1: https://viblo.asia/march_vu/posts/pVYRPJZkG4ng


All Rights Reserved