แสดงผลใน textbox

  • warning: realpath() [function.realpath]: SAFE MODE Restriction in effect. The script whose uid is 1005 is not allowed to access /tmp owned by uid 0 in /var/www/sites/sugree/codenone.com/subdomains/www/html/includes/file.inc on line 190.
  • warning: realpath() [function.realpath]: SAFE MODE Restriction in effect. The script whose uid is 1005 is not allowed to access /tmp owned by uid 0 in /var/www/sites/sugree/codenone.com/subdomains/www/html/includes/file.inc on line 190.

ใน rails ปกติแล้วมันมีวิธีเอาข้อมูลใน hash ไปแสดงใน textbox ง่ายๆแบบนี้ใช่มั้ยครับ

<%= form_tag :action => "update" , :id=>@product%>
	name : <%= text_field	"product", "name"  %>
<%= end_form_tag%>

ทีนี้ ปัญหาคือ ถ้าเกิด model ของผมเป็นแบบนี้

class Product < ActiveRecord::Base
	belongs_to	:supplier
end

แล้วใน supplier ที่ atrributes ชื่อ name

ผมจะเอาไปเติมใน textbox ยังไงเหรอครับ แล้วในกรณีเดียวกัน ถ้ามันซ้อนกันเยอะๆเนี่ยจะทำยังไงดีครับ เพราะว่ามันเกิดปัญหาว่าถ้าข้อมูลมันเกิดเป็น nil ขึ้นมา (เช่น supplier เป็น nil) มันจะ error ก็เลยต้องนั่งตรวจเองหมดเลย ลำบากกว่าเขียน php ธรรมดาอีก

พี่ว่ามันแปลกๆนะ ในกรณีที่เอา entity ฝั่ง many-to-one มาเป็น text-field
ซึ่งหมายความว่ามันถูก update ได้ด้วยไช่ไหม
อย่างไรก็ตาม
ลองแบบนี้ดูก่อน
ถ้ากรณี read only

class Product < ActiveRecord::Base
  belongs_to :supplier
 
  def supplier_name
    supplier ? supplier.name : '--'
  end
end
<p><label for="supplier_name">Supplier Name</label><br/>
<%= text_field 'product', 'supplier_name' %></p>

ข้างบนมันมีปัญหาว่า ถ้าใน method update|save ของเราใช้คำสั่ง
@product.update_attributes(params[:product])
มันก็จะ error เพราะมันพยายาม save ค่า supplier_name ด้วย
ซึ่งแก้ได้โดย
1.disable text_field เพื่อที่ตอน submit จะได้ไม่ send ค่ากลับมา
2.เปลี่ยนวิธีการ render ให้เป็นแบบ text ธรรมดา
แต่ถ้ายังอยากใช้ text_field เหมือนเดิม เราก็ต้องทำแบบนี้

  def supplier_name=(value)
  end

ข้างในไม่ทำอะไร ถ้าเป็นกรณี read-only


แต่ถ้า design ไว้ว่า อยากให้ update ได้ด้วย
ก็เขียนเป็นแบบนี้แทน

class Product < ActiveRecord::Base
  belongs_to :supplier
 
  def supplier_name
    supplier ? supplier.name : '--'
  end
 
  def supplier_name=(value)
    @supplier_change = true
    self.supplier = Supplier.new unless self.supplier
    supplier.name = value
  end
 
  before_save :save_supplier
 
  protected
  def save_supplier
    if @supplier_change
      supplier.save
    end
  end
end



Note: ถ้ามีกรณีอย่างนี้ เยอะๆหลายๆ field แล้วเราขี้เกียจทำด้วยมือซ้ำๆ
ให้สังเกตุว่ามันมี pattern ของการเขียนอยู่
ซึ่งเราสามารถดึงออกมาเป็น meta-programming ได้เช่น
ดึงออกมาเป็น

class ActiveRecord::Base
  def self.ognl(beanName, propName, accessName)
    class_eval %Q{
      def #{accessName}
        #{beanName} ? #{beanName}.#{propName} : '--'
      end
 
      def #{accessName}=(value)
        @#{beanName}_change = true
        self.#{beanName} = #{beanName.to_s.camelcase}.new unless self.#{beanName}
        #{beanName}.#{propName} = value
      end
    }
  end
end

เวลาใช้ก็

require 'ognl'
class Product < ActiveRecord::Base
  belongs_to :supplier
  ognl :supplier, :name, :supplier_name
 
  before_save :save_supplier
 
  protected
  def save_supplier
    if @supplier_change
      supplier.save
    end
  end
end

Note1: ognl คือชื่อ file ที่เรา open class ActiveRecord::Base ไว้

ขอบคุณครับ จริงๆแล้วผมต้องการจะดึงข้อมูลเคยกรอกลงในแบบฟอร์มมาช่วยในการกรอกข้อมูลครั้งนี้ครับ แต่เนื่องจากว่าข้อมูลนี้มันอาจะไม่เหมือนครั้งก่อน ก็เลยจะให้เค้า update ในครั้งเดียวไปเลยน่ะครับ

เวลาไปแก้ ActiveRecord::Base นี่ แก้ไฟล์ไหนเหรอครับ ? ในโปรเจกต์เราเอง หรือว่าที่ตัว Rails เลย ?

ไว้ได้หลายที่ครับ
ง่ายสุดก็ไว้ที่ $RAILS_ROOT/lib
ตั้งชื่ออะไรก็ได้ สมมติชื่อ ognl

ที่นี้อยากให้ rails load ก็สั่งให้มัน require file นี้
require 'ognl'
ส่วนที่ require ก็ไว้ได้หลายที่
เช่นเอาไว้ใน environment.rb, ไว้ใน model class

แล้วแบบนี้ มันจะทำ validation ให้กับ supplier_name มั๊ยครับ ?

อันนี้ยังไม่ทดลองนะ
แต่สมมติเรามี validation ในส่วนของ Supplier Model
ที่ตรวจสอบว่า name ต้องไม่เป็น blank


ในกรณีที่เรา set product.supplier_name = ""
แล้วสั่ง product.save
ไอ้เจ้า block before_save มันไปทำ supplier.save
มันก็น่าจะเด้ง error ขึ้นมาจากจุดนั้น

แล้วถ้าเป็น many-to-many นี่ทำไงอ่ะครับ

สมมติผมมี message หลายๆ อัน

แล้วก็ แต่ละ message สามารถโยงไปหาอีก message ได้

m1 -> m2
m1 -> m3
m2 -> m1
m3 -> m2
m3 -> m5

อะไรทำนองนี้

ใน Rails จะเขียนยังไงครับ ?

class Message < ActiveRecord::Base
  has_and_belongs_to_many :fromMessages,
                          :class_name => "Message",
                          :join_table => "messages_messages",
                          :foreign_key => "from_id",
                          :association_foreign_key => "to_id"
  has_and_belongs_to_many :toMessages,
                          :class_name => "Message",
                          :join_table => "messages_messages",
                          :foreign_key => "to_id",
                          :association_foreign_key => "from_id"
end

table หน้้าตาแบบนี้

mysql> SELECT * FROM messages_messages;
+----+---------+-------+
| id | from_id | to_id |
+----+---------+-------+
|  2 |       2 |     1 | 
+----+---------+-------+
1 row IN SET (0.01 sec)

Note: column id จะไม่มีก็ได้, ของผมที่มีเพราะผมใช้ migration generate

ทดสอบใน console

m1 = Message.new
m1.name='x'
m1.save
 
m2 = Message.new
m2.name='y'
m2.save
 
m1.toMessages << m2
 
m2.reload
m2.fromMessages[0].name # => x

์Note: เลือก assoication ให้เหลือแต่ toMessages อันเดียวก็ได้
ที่ทำสองอัน ก็เพื่อแสดงให้ดูว่าทำ navigation 2 ทางได้

ย้าย Codenone

ประกาศย้าย Codenone ไปใช้ Forum ของ Blognone แทนครับ ตามไปตั้งกระทู้ต่อได้ที่ Codenone Forum (รายละเอียดอ่านจากกระทู้ ย้าย Codenone ไปรวมกับ Blognone)

กระทู้เก่าๆ จะย้ายตามไปในภายหลัง ตอนนี้ปิดการโพสต์กระทู้ไว้ เหลือไว้เฉพาะอ้างอิงเท่านั้น