12/08/2018, 13:29

Single Table Inheritance trong Rails 4 (Phần3)

Trong bài viết trước, chúng tôi đã hướng dẫn việc tạo controller chung và xác định view index. Trong bài này, chúng tôi sẽ bổ sung thêm các view còn thiếu và thấy một số thủ thuật để xử lý đường dẫn cho các model STI. Tạo các đường dẫn động trong file helpers Trước khi thêm các view khác, ta ...

Trong bài viết trước, chúng tôi đã hướng dẫn việc tạo controller chung và xác định view index. Trong bài này, chúng tôi sẽ bổ sung thêm các view còn thiếu và thấy một số thủ thuật để xử lý đường dẫn cho các model STI.

Tạo các đường dẫn động trong file helpers

Trước khi thêm các view khác, ta cần tạo ra các đường dẫn động dựa theo race. Để làm điều đó, chúng ta có thể sử dụng các method sau trong helpers.

# helpers/animals_helper.rb
# Returns a dynamic path based on the provided parameters
def sti_animal_path(race = "animal", animal = nil, action = nil)
  send "#{format_sti(action, race, animal)}_path", animal
end

def format_sti(action, race, animal)
  action || animal ? "#{format_action(action)}#{race.underscore}" : "#{race.underscore.pluralize}"
end

def format_action(action)
  action ? "#{action}_" : ""
end

** Tạo đường dẫn và file view cho action show**

Update index.html với method mới này:

<td><%= link_to 'Show', sti_animal_path(animal.race, animal) %></td>

Và tạo ra file show.html.erb trong views/animals:

<h1>Hi, I'm a <%= @animal.race %>.</h1>
<p>My name is <%= @animal.name %></p>
<%= @animal.talk %>

Bước cuối cùng, chúng ta cần phải xác định @animal trong controller. Để làm điều đó, chúng ta sẽ thêm before_action gọi lại trước mỗi action mà chúng ta cần xác định @animal:

class AnimalsController < ApplicationController
  before_action :set_animal, only: [:show, :edit, :update, :destroy]
  before_action :set_race

  def index
    @animals = race_class.all
  end

  def show
  end

  # Code hidden for brivety

  private

  # Code hidden for brivety

  def set_animal
    @animal = race_class.find(params[:id])
  end

end

Bây giờ refresh lại trang, click vào show! Nó sẽ chuyển đến trang show đúng loại animal tương ứng với đường đẫn (lions/meerkats/wild_boars).

Thêm đường dẫn trong index view

Bổ sung thêm một số đường dẫn đến danh sách từng loại animal vào trang index:

<td><%= link_to "See all #{animal.race.pluralize}", sti_animal_path(animal.race) %></td>

Và đường dẫn để hiển thị danh sách của tất cả các loại animal:

[...]
    </tbody>
</table>

<%= link_to 'See all animals', sti_animal_path %>

Cuối cùng, chúng ta cần một link để tạo animal:

[...]
<%= link_to 'See all animals', sti_animal_path %>
<%= link_to "New #{@race}", sti_animal_path(@race, nil, :new) %>

Hãy test một chút với những gì bạn vừa làm được ^^

Tạo Animal

View cho trang new khá đơn giản, tất cả các logic sẽ được thiết lập trong partial form:

# views/animals/new.html.erb
<h1>New <%=  "#{@race.capitalize}" %></h1>
<%= render 'form' %>
<%= link_to 'Back', sti_animal_path(@race) %>

Ngoài ra, chúng ta cần tạo thêm action new trong animals_controller:

# controllers/animals_controller.rb
def new
  @animal = race_class.new
end

_form.rb :

<%= form_for(@animal) do |f| %>
  <% if @animal.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@animal.errors.count, "error") %> prohibited this animal from being saved:</h2>

      <ul>
      <% @animal.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>

  <div class="field">
      <%= f.label :race %><br>
      <%= f.select :race, Animal.races.map {|r| [r.humanize, r.camelcase]}, {}, disabled: @race != "Animal" %>
  </div>

  <div class="field">
    <%= f.label :age %><br>
    <%= f.text_field :age %>
  </div>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Bây giờ chúng ta có thể tạo được animal. Select_box race chỉ enabled khi bạn tạo Animal, còn nếu bạn tạo một Lion (/lions/new) thì select_box sẽ được thiết lập giá trị mặc định là lion và disabled.

Strong parameters Animal

Bạn có thể thấy trong đoạn code dưới đây, chúng tôi đã thêm một method để xác định các params cho phép tạo cho model Animal. Action create cũng được thafAnimalory đổi:

class AnimalsController < ApplicationController

    # Code hidden for brievety

    def create
        @animal = Animal.new(animal_params)
        if @animal.save
          redirect_to @animal, notice: "#{race} was successfully created."
        else
          render action: 'new'
        end
    end

    # Code hidden for brievety

    private

    # Code hidden for brievety

    def animal_params
        params.require(race.underscore.to_sym).permit(:name, :race, :age)
    end

end

Thêm action update and delete

Đầu tiên, sẽ đặt các đường dẫn để dễ dàng truy cập các action:

<td><%= link_to 'Edit', sti_animal_path(animal.race, animal, :edit) %></td>
  <td><%= link_to 'Destroy', sti_animal_path(animal.race, animal), method: :delete, data: { confirm: 'Are you sure?' } %></td>

Sau đó, chúng tôi xác định các action trong controller:

def update
  if @animal.update(animal_params)
    redirect_to @animal, notice: "#{race} was successfully created."
  else
    render action: 'edit'
  end
end

def destroy
  @animal.destroy
  redirect_to animals_url
end

Cuối cùng là thêm file edit.html.erb :

#views/animals/edit.html.erb
<h1>Editing <%= "#{@race}" %></h1>
<%= render 'form' %>
<%= link_to 'Back', sti_animal_path(@race) %>

Kết:

Thông qua hướng dẫn này, tôi hy vọng bạn hiểu những gì có thể được thực hiện với Single Table Inheritance và làm thế nào nó có thể giúp bạn hạn chế sử dụng các controller tương tự nhau.

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

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

Link dịch phần 2: https://viblo.asia/march_vu/posts/WkwGnWq3v75g

0