Ruby on Rails 網站程式設計基礎班
第九課:多形關聯

今天上課的程式碼...
Movie Time!
Flash Message
# 在 application.html.erb 裡加入:
<% flash.each do |key, value| %>
<div class="alert alert-<%= key %>"><%= value %></div>
<% end %>
# 接下來就可在後端 Controller 把要顯示的字串放入 flash 這個 hash,就會顯示在前端
def create
@order = Order.new(order_params)
if @order.save # true, false
flash[:success] = "已成功下訂!"
redirect_to orders_path
else
flash[:danger] = "漏填資料囉,啾咪!"
render :new
end
end
來幫訂便當加上留言的功能吧...
class Comment < ApplicationRecord
belongs_to :user
belongs_to :order
end
class Order < ApplicationRecord
# 在 rails 的層面做資料驗證
# 一筆訂單的訂購者應該要填名字,不然不給建立一筆訂單
validates :name, presence: true
# 訂購者的名字不應該只有一個字元
validates :name, length: { minimum: 2 }
validates :name, length: { maximum: 10 }
# 訂購者的 email 是必填,不然無法聯絡
validates :email, presence: true
has_many :comments
end
class User < ApplicationRecord
# rails 內建的使用者認證方法,需要搭配 bcrypt gem 使用
has_secure_password validation: false
has_many :comments
end
rails g model comment
執行:
來幫訂便當加上留言的功能吧...

來幫訂便當加上留言的功能吧...
class OrdersController < ApplicationController
# GET /orders/1
def show
# 對映到顯示單筆資料的頁面
# show 頁面的留言表單需要一個空物件
@comment = Comment.new
end
end
來幫訂便當加上留言的功能吧...
class CommentsController < ApplicationController
def create
# 找相關連的訂單
@order = Order.find(params[:order_id])
# build 與 new 的意思一樣
@comment = @order.comments.build(params.require(:comment).permit(:content))
# 綁定現在登入的使用者
@comment.user = current_user
if @comment.save
redirect_to order_path(@order)
else
render 'orders/show'
end
end
end
rails g controller comments --no-assets --no-test-framework
產生 Comments Controller 的指令:
嵌套/巢狀路由
resources :orders do
# 嵌套/巢狀路由,這樣可以產生出 orders/1/comments/ 的 url
resources :comments, only: [:create]
end
來幫訂單列表加一個投票的功能吧...

來幫留言加一個投票的功能吧...

資料庫該如何設計...
欄位名稱: | vote |
order_id | user_id |
---|---|---|---|
資料型別: | boolean | integer | integer |
class OrderVote < ApplicationRecord
belongs_to :order
end
class Order < ApplicationRecord
has_many :order_votes
end
但是...
欄位名稱: | vote |
comment_id | user_id |
---|---|---|---|
資料型別: | boolean | integer | integer |
class OrderVote < ApplicationRecord
belongs_to :order
end
class Post < ApplicationRecord
has_many :order_votes
end
class CommentVote < ApplicationRecord
belongs_to :comment
end
class Comment < ApplicationRecord
has_many :comment_votes
end
今天我們若要也要幫 comments 加上投票功能...
order_votes 和 comment_votes兩個資料表的 schema 幾乎一樣,若每多一個 model 需要 voting 就增加一個資料表紀錄 votes,只會造成資料庫本身變得越來越複雜...
多型關連(Polymorphic Associations)
欄位名稱: | vote |
voteable_type | voteable_id | user_id |
---|---|---|---|---|
資料型別: | boolean | string | integer | integer |
多型關連(Polymorphic Associations)可以讓一個 Model 不一定關連到某一個特定的 Model,秘訣在於除了整數的 _id 外部鍵之外,再加一個字串的 _type 欄位說明是哪一種Model
多型關連(Polymorphic Associations)
class CreateVotes < ActiveRecord::Migration[5.0]
def change
create_table :votes do |t|
t.boolean :vote
t.integer :user_id
t.string :voteable_type
t.integer :voteable_id
t.timestamps
end
end
end
rails g migration create_votes
先來寫一個 migration 檔...
多型關連(Polymorphic Associations)
class Vote < ApplicationRecord
# 關聯的設定可以給別名,foreign key 和相關連的 model 需要自己設定
belongs_to :creator, foreign_key: :user_id, class_name: "User"
belongs_to :voteable, polymorphic: true
validates_uniqueness_of :creator, scope: [:voteable_type, :voteable_id]
end
class Order < ApplicationRecord
has_many :votes, as: :voteable
end
class Comment < ApplicationRecord
has_many :votes, as: :voteable
end
多型關連(Polymorphic Associations)
class Order < ApplicationRecord
has_many :comments
has_many :votes, as: :voteable
def up_votes
self.votes.where(vote: true).length
end
def down_votes
self.votes.where(vote: false).length
end
def total_votes
up_votes - down_votes
end
end
加上一些投票相關的功能...
多型關連(Polymorphic Associations)
# 改寫 routes.rb...
resources :orders do
member do # 客製化連結
post :vote
# 這樣會產出 orders/1/vote
end
# 嵌套/巢狀路由,這樣可以產生出 orders/1/comments/ 的 url
resources :comments, only: [:create] do
member do
post :vote
# 產出 orders/1/comments/1/vote
end
end
end
多型關連(Polymorphic Associations)
class PostsController < ApplicationController
def vote
@order = Order.find(params[:id])
@vote = Vote.create(voteable: @order, creator: current_user, vote: params[:vote])
redirect_to root_path
end
end
多型關連(Polymorphic Associations)
<table class="table table-hover table-striped">
<thead>
<tr>
<th>票數</th>
<th>讚</th>
<th>噁</th>
<th>姓名</th>
<th>Email</th>
<th>訂單內容</th>
<th>年齡</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @orders.each do |order| %>
<tr>
<td><%= order.total_votes %></td>
<td>
<%= link_to vote_order_path(order, vote: true), class: "btn btn-primary", method: 'post' do %>
讚
<% end %>
</td>
<td>
<%= link_to vote_order_path(order, vote: false), class: "btn btn-danger", method: 'post' do %>
噁
<% end %>
</td>
<td><%= order.name %></td>
<td><%= order.email %></td>
<td><%= order.description %></td>
<td><%= order.age %></td>
<td><%= link_to '檢視訂單', order_path(order), class: 'btn btn-info' %></td>
<% if logged_in? %>
<td><%= link_to '編輯訂單', edit_order_path(order), class: 'btn btn-warning' %></td>
<td><%= link_to '刪除訂單', order_path(order), method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger' %></td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
deck
By Eugene Chang
deck
- 899