Sunday, February 05, 2006

Multi parameter กับ update_attributes ใน ActiveRecord

กำลังทำเรื่อง widget วันที่ ใน rails อยู่
ก็เลยพึ่งเห็นวิธีที่ rails ใช้จัดการกับวันที่

ใน rhtml template เราสามารถใช้ date_select
ช่วยการ render date widget ได้
หน้าตาที่ได้ ก็จะเป็น dropdown box แบบนี้



เมื่อเกิด form submit
ข้อมูลที่ post กลับมาจะอยู่ในรูปแบบนี้

{
"start_date(1i)"=>"2006",
"start_date(2i)"=>"1",
"start_date(3i)"=>"1"
}

สังเกตว่า มีวงเล็บ แปลกๆอยู่ด้วย

เมื่อ controller ได้รับข้อมูลกลับมา
ก็จะทำการ assign ให้กับ model
ซึ่งเราสามารถใช้คำสั่งแบบนี้ได้

mymodel.attributes = params[:mydate]
mymodel.save

หรือจะใช้

mymodel.update_attributes(params[:mydate])

ทั้งสองวิธีทำงานเหมือนกัน (วิธีที่ 2 code ข้างใน ก็ไปเรียกแบบที่ 1)

ประโยค
mymodel.attributes = ...

ไม่ได้เป็นการ assigment แบบธรรมดา
ภายใน ActiveRecord มีการ declare method นี้ไว้ดังนี้

def attributes=(attributes)
return if attributes.nil?
attributes.stringify_keys!

multi_parameter_attributes = []
remove_attributes_protected_from_mass_assignment(attributes).each do |k, v|
k.include?("(") ? multi_parameter_attributes << [ k, v ] : send(k + "=", v)
end
assign_multiparameter_attributes(multi_parameter_attributes)
end

การทำงานก็คือ แต่ละ attribute ที่ pass เข้ามา
กรณีที่เป็น attribute แบบไม่มีเครื่องหมาย (
ก็จะถูกเรียกใช้แบบนี้ send(k+"=", v)

สมมติ attribute เป็น "name" => "pphetra"
ประโยค send(k+"=", v) ก็คือ send("name=", "pphetra)
ซึ่งเทียบได้กับ name = "pphetra"

แต่กรณีที่มันพบ attribute ที่มีเครืื่องหมาย "("
กรณีนี้จะถูก handle แบบพิเศษโดยใช้ assign_multiparameter_attributes
ซึ่งถ้าดูใน document ของ method นี้ เขาอธิบายการทำงานดังนี้

# Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
# by calling new on the column type or aggregation type (through composed_of) object with these parameters.
# So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
# written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
# parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum, f for Float,
# s for String, and a for Array. If all the values for a given attribute is empty, the attribute will be set to nil.

Related link from Roti

No comments: