pono_thea wrote: > Tentang simpan ke database lebih dari 2 tabel. SHORT VERSION
Pasti ada banyak solusi untuk ini, saya hanya akan menunjukkan 1 macam solusi disini, simpan form model awal di session kemudian lanjut ke form model kedua (terakhir) ketika save model terakhir save lah juga model yang ada di session. LONG VERSION Saya punya kata-kata radikal utk kasus pono_thea ini: Inilah kelemahan ORM AR di Rails!!! Masak mau apa-apa cuma 1 form model kenal 1 model??? Coba pakai PEAR+Smarty+PHP dulu itu langsung aja bisa :-P FYI Saya dulu pernah juga bikin Booking Engine untuk Hotel pakai Rails dimana kasusnya sangat sangat mirip dengan yang Anda hadapi. Di codes berikut ini saya akan mensimulasikan apa yang pernah saya bikin itu. id-ruby:~/sukebe/spec arie$ mkdir 2 && cd 2 id-ruby:~/sukebe/spec/2 arie$ rails . --database=sqlite3 exists create app/controllers create app/helpers [...] id-ruby:~/sukebe/spec/2 arie$ [...] coding saya hentikan saat melihat ada yang tidak sinkron antara pertanyaan Anda dengan contoh kasus yang Anda sodorkan. > 1. Validasi (di setiap models) > 2. Jika sukses maka simpan ke databse > saya menggunakan > if @user.save and @hobbies.save and @address.save > pesan sukses simpan data > end > Tetapai tidak sesuai dengan yg saya harapakan, saya juga pernah pake > OR, hasilnya tetap. > Masalahnya : > Jika menggunakan AND, error message akan tampil yang @user.save aja, > sedangkan yang lain engga, kecuali klo @user.save sukse > Klo pake OR dia baca dari belakang, > > Jika saya pake > if @user.save > @hobbies.save > end BTW kasus Anda ini dengan contoh diatas itu sebetulnya nggak sinkron! Kalau cuma, hobbies, sih itu tinggal user has_many hobbies through something aja selesai perkara, gak perlu repot2x kita ngomong akali simpan data lebih dari satu tabel. jadi Logic dari si Felix itu jitu! ;-) mustinya bahkan kalau simple satu aja udah cukup, anyway, kalau tetap mau banyak-banyak hobbynya juga bisa. gini caranya. id-ruby:~/sukebe/spec arie$ rails ponothea --database=mysql && cd ponothea create create app/controllers [...] id-ruby:~/sukebe/spec/ponothea arie$ vi config/database.yml id-ruby:~/sukebe/spec/ponothea arie$ cat config/database.yml config: &config adapter: mysql username: ponothea password: p0N07h34 socket: /tmp/mysql.sock development: database: ponothea_dev <<: *config test: database: ponothea_test <<: *config id-ruby:~/sukebe/spec/ponothea arie$ mysql -uroot -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 1 to server version: 5.0.22-standard Type 'help;' or '\h' for help. Type '\c' to clear the buffer. mysql> create database ponothea_dev; create database ponothea_test; Query OK, 1 row affected (0.16 sec) Query OK, 1 row affected (0.00 sec) mysql> grant all on ponothea_dev.* to [EMAIL PROTECTED]; grant all on ponothea_test.* to [EMAIL PROTECTED]; Query OK, 0 rows affected (0.23 sec) Query OK, 0 rows affected (0.00 sec) mysql> set password for [EMAIL PROTECTED]("p0N07h34"); Query OK, 0 rows affected (0.08 sec) mysql> flush privileges; Query OK, 0 rows affected (0.10 sec) mysql> exit Bye id-ruby:~/sukebe/spec/ponothea arie$ script/about About your application's environment Ruby version 1.8.6 (powerpc-darwin8.9.0) RubyGems version 0.9.4 Rails version 1.2.3 Active Record version 1.15.3 Action Pack version 1.13.3 Action Web Service version 1.2.3 Action Mailer version 1.3.3 Active Support version 1.4.2 Application root /Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea Environment development Database adapter mysql id-ruby:~/sukebe/spec/ponothea arie$ rake stats (in /Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea) +----------------------+-------+-------+---------+---------+-----+-------+ | Name | Lines | LOC | Classes | Methods | M/C | LOC/M | +----------------------+-------+-------+---------+---------+-----+-------+ | Controllers | 7 | 3 | 1 | 0 | 0 | 0 | | Helpers | 3 | 2 | 0 | 0 | 0 | 0 | | Models | 0 | 0 | 0 | 0 | 0 | 0 | | Libraries | 0 | 0 | 0 | 0 | 0 | 0 | | Components | 0 | 0 | 0 | 0 | 0 | 0 | | Integration tests | 0 | 0 | 0 | 0 | 0 | 0 | | Functional tests | 0 | 0 | 0 | 0 | 0 | 0 | | Unit tests | 0 | 0 | 0 | 0 | 0 | 0 | +----------------------+-------+-------+---------+---------+-----+-------+ | Total | 10 | 5 | 1 | 0 | 0 | 0 | +----------------------+-------+-------+---------+---------+-----+-------+ Code LOC: 5 Test LOC: 0 Code to Test Ratio: 1:0.0 id-ruby:~/sukebe/spec/ponothea arie$ script/plugin install svn://rubyforge.org/var/svn/rspec/tags/REL_1_0_8/rspec && script/plugin install svn://rubyforge.org/var/svn/rspec/tags/REL_1_0_8/rspec_on_rails A /Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea/vendor/plugins/rspec A /Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea/vendor/plugins/rspec/plugins A /Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea/vendor/plugins/rspec/plugins/mock_frameworks [...] Exported revision 2483. id-ruby:~/sukebe/spec/ponothea arie$ script/generate rspec create spec create spec/spec_helper.rb create spec/spec.opts create previous_failures.txt create script/spec_server create script/spec id-ruby:~/sukebe/spec/ponothea arie$ script/generate rspec_model User name:string && script/generate rspec_model Hobby title:string && script/generate rspec_model Crazyfication name_id:integer hobby_id:integer since_at:datetime rating:integer exists app/models/ create spec/models/ create spec/fixtures/ create app/models/user.rb create spec/fixtures/users.yml create spec/models/user_spec.rb create db/migrate create db/migrate/001_create_users.rb exists app/models/ exists spec/models/ exists spec/fixtures/ create app/models/hobby.rb create spec/fixtures/hobbies.yml create spec/models/hobby_spec.rb exists db/migrate create db/migrate/002_create_hobbies.rb exists app/models/ exists spec/models/ exists spec/fixtures/ create app/models/crazyfication.rb create spec/fixtures/crazyfications.yml create spec/models/crazyfication_spec.rb exists db/migrate create db/migrate/003_create_crazyfications.rb id-ruby:~/sukebe/spec/ponothea arie$ awk '/t\.column/' db/migrate/00* t.column :name, :string t.column :title, :string t.column :name_id, :integer t.column :hobby_id, :integer t.column :since_at, :datetime t.column :rating, :integer id-ruby:~/sukebe/spec/ponothea arie$ rake spec:models (in /Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea) FFF 1) ActiveRecord::StatementInvalid in 'User should be valid' Mysql::Error: Table 'ponothea_test.users' doesn't exist: SHOW FIELDS FROM users ./spec/models/user_spec.rb:5:in `new' ./spec/models/user_spec.rb:5: 2) ActiveRecord::StatementInvalid in 'Hobby should be valid' Mysql::Error: Table 'ponothea_test.hobbies' doesn't exist: SHOW FIELDS FROM hobbies ./spec/models/hobby_spec.rb:5:in `new' ./spec/models/hobby_spec.rb:5: 3) ActiveRecord::StatementInvalid in 'Crazyfication should be valid' Mysql::Error: Table 'ponothea_test.crazyfications' doesn't exist: SHOW FIELDS FROM crazyfications ./spec/models/crazyfication_spec.rb:5:in `new' ./spec/models/crazyfication_spec.rb:5: Finished in 0.58407 seconds 3 examples, 3 failures rake aborted! Command ruby -I"/Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea/vendor/plugins/rspec/lib" "/Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea/vendor/plugins/rspec/bin/spec" "spec/models/crazyfication_spec.rb" "spec/models/hobby_spec.rb" "spec/models/user_spec.rb" --options "/Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea/config/../spec/spec.opts" failed (See full trace by running task with --trace) # tentu saja kita dapat error, karena belum migrate, ini cuma menunjukkan penekanan praktek XP dengan BDD di Ruby ya. id-ruby:~/sukebe/spec/ponothea arie$ rake db:migrate (in /Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea) == CreateUsers: migrating ===================================================== -- create_table(:users) -> 0.2599s == CreateUsers: migrated (0.2612s) ============================================ == CreateHobbies: migrating =================================================== -- create_table(:hobbies) -> 0.0490s == CreateHobbies: migrated (0.0509s) ========================================== == CreateCrazyfications: migrating ============================================ -- create_table(:crazyfications) -> 0.0087s == CreateCrazyfications: migrated (0.0106s) =================================== # spec'ing again dan BDD sukses 100% id-ruby:~/sukebe/spec/ponothea arie$ rake spec:models (in /Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea) ... Finished in 0.399572 seconds 3 examples, 0 failures id-ruby:~/sukebe/spec/ponothea arie$ cat spec/models/crazyfication_spec.rb require File.dirname(__FILE__) + '/../spec_helper' describe Crazyfication do before(:each) do @crazyfication = Crazyfication.create(:since_at => Time.now, :rating => 5) @user1 = User.create(:name => 'dandy') @hobby1 = Hobby.create(:title => 'racing') @user1.crazyfications << @crazyfication @hobby1.crazyfications << @crazyfication @crazyfication.save @crazyfication2 = Crazyfication.create(:since_at => Time.mktime(2007, 7, 7, 7, 7, 7), :rating => 5) @user2 = User.create(:name => 'arie') @hobby2 = Hobby.create(:title => 'minori aoi') @user2.crazyfications << @crazyfication2 @hobby2.crazyfications << @crazyfication2 @crazyfication2.save @hobby3 = Hobby.create(:title => 'ruby') @crazyfication3 = Crazyfication.create(:since_at => Time.mktime(2004, 1, 1, 1, 1, 1), :rating => 5) @hobby3.crazyfications << @crazyfication3 @user2.crazyfications << @crazyfication3 @crazyfication3.save end it "dandy's hobby should be racing only" do @user1.hobbies.size.should == 1 @user1.hobbies.at(0).title.should == 'racing' end it "arie's hobbies should be both ruby and minori aoi" do @user2.hobbies.size.should == 2 @user2.hobbies.map { |e| e.title }.should == ['minori aoi', 'ruby'] end end # OK, testing duluan sudah ditulis, sekarang kita tes dah id-ruby:~/sukebe/spec/ponothea arie$ spec spec/models/crazyfication_spec.rb FF 1) NoMethodError in 'Crazyfication dandy's hobby should be racing only' undefined method `crazyfications' for #<User:0x31e663c> ./spec/models/crazyfication_spec.rb:8: 2) NoMethodError in 'Crazyfication arie's hobbies should be both ruby and minori aoi' undefined method `crazyfications' for #<User:0x31cbd3c> ./spec/models/crazyfication_spec.rb:8: Finished in 0.342293 seconds 2 examples, 2 failures id-ruby:~/sukebe/spec/ponothea arie$ # nah pasti error kan, baru kita coding sesuai spec yg sudah ditentukan sebelumnya, inilah dia intinya XP, kalo coding tuh harus jelas requirementnya, kalau belum jelas, jangan mau!!! # ini hasil cat setelah saya update semua model pakai textmate id-ruby:~/sukebe/spec/ponothea arie$ cat app/models/* class Crazyfication < ActiveRecord::Base belongs_to :hobby belongs_to :user end class Hobby < ActiveRecord::Base has_many :crazyfications has_many :users, :through => :crazyfications end class User < ActiveRecord::Base has_many :crazyfications has_many :hobbies, :through => :crazyfications end # nah setelah ini kalau kita tes lagi mustinya harus berhasil ;-) id-ruby:~/sukebe/spec/ponothea arie$ spec spec/models/crazyfication_spec.rb FF 1) ActiveRecord::StatementInvalid in 'Crazyfication dandy's hobby should be racing only' Mysql::Error: Unknown column 'crazyfications.user_id' in 'where clause': SELECT * FROM crazyfications WHERE (crazyfications.user_id = 3) ./spec/models/crazyfication_spec.rb:8: 2) ActiveRecord::StatementInvalid in 'Crazyfication arie's hobbies should be both ruby and minori aoi' Mysql::Error: Unknown column 'crazyfications.user_id' in 'where clause': SELECT * FROM crazyfications WHERE (crazyfications.user_id = 4) ./spec/models/crazyfication_spec.rb:8: Finished in 0.621236 seconds 2 examples, 2 failures # oops :-D # coba di cek dan ricek, ini akibat ngantuk atau kurang makan nih... id-ruby:~/sukebe/spec/ponothea arie$ awk '/pass|user/' config/database.yml username: ponothea password: p0N07h34 id-ruby:~/sukebe/spec/ponothea arie$ mysql -uponothea -pp0N07h34 ponothea_dev -A Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 15 to server version: 5.0.22-standard Type 'help;' or '\h' for help. Type '\c' to clear the buffer. mysql> show tables; +------------------------+ | Tables_in_ponothea_dev | +------------------------+ | crazyfications | | hobbies | | schema_info | | users | +------------------------+ 4 rows in set (0.00 sec) mysql> desc crazyfications; +----------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +----------+----------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name_id | int(11) | YES | | NULL | | | hobby_id | int(11) | YES | | NULL | | | since_at | datetime | YES | | NULL | | | rating | int(11) | YES | | NULL | | +----------+----------+------+-----+---------+----------------+ 5 rows in set (0.00 sec) mysql> exit Bye # nah sudah jelas, mustinya user_id, bukan name_id, ayo kita betuli dulu id-ruby:~/sukebe/spec/ponothea arie$ cat db/migrate/003_create_crazyfications.rb class CreateCrazyfications < ActiveRecord::Migration def self.up create_table :crazyfications do |t| t.column :user_id, :integer t.column :hobby_id, :integer t.column :since_at, :datetime t.column :rating, :integer end end def self.down drop_table :crazyfications end end # OK sudah diubah jadi user_id ya. tinggal di migrate. BTW ini utk kasus ringan kita bisa revert, kalau kasus perusahaan besar ya mustinya gak ada salah2x! desain database jangan sampai ngaco tuh, tbl_ tbl_bullshit I'll kill them... id-ruby:~/sukebe/spec/ponothea arie$ rake db:migrate VERSION=0 && rake db:migrate (in /Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea) == CreateCrazyfications: reverting ============================================ -- drop_table(:crazyfications) -> 0.2341s == CreateCrazyfications: reverted (0.2352s) =================================== == CreateHobbies: reverting =================================================== -- drop_table(:hobbies) -> 0.0058s == CreateHobbies: reverted (0.0069s) ========================================== == CreateUsers: reverting ===================================================== -- drop_table(:users) -> 0.0338s == CreateUsers: reverted (0.0349s) ============================================ (in /Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea) == CreateUsers: migrating ===================================================== -- create_table(:users) -> 0.0261s == CreateUsers: migrated (0.0274s) ============================================ == CreateHobbies: migrating =================================================== -- create_table(:hobbies) -> 0.2051s == CreateHobbies: migrated (0.2065s) ========================================== == CreateCrazyfications: migrating ============================================ -- create_table(:crazyfications) -> 0.0079s == CreateCrazyfications: migrated (0.0092s) =================================== id-ruby:~/sukebe/spec/ponothea arie$ spec spec/models/crazyfication_spec.rb FF 1) ActiveRecord::StatementInvalid in 'Crazyfication dandy's hobby should be racing only' Mysql::Error: Unknown column 'crazyfications.user_id' in 'where clause': SELECT * FROM crazyfications WHERE (crazyfications.user_id = 5) ./spec/models/crazyfication_spec.rb:8: 2) ActiveRecord::StatementInvalid in 'Crazyfication arie's hobbies should be both ruby and minori aoi' Mysql::Error: Unknown column 'crazyfications.user_id' in 'where clause': SELECT * FROM crazyfications WHERE (crazyfications.user_id = 6) ./spec/models/crazyfication_spec.rb:8: Finished in 0.555789 seconds 2 examples, 2 failures # boom, kalo masih error .. tenang ;-) pesawat masih dalam kendali kapten star trek mabok id-ruby:~/sukebe/spec/ponothea arie$ rake db:test:purge (in /Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea) id-ruby:~/sukebe/spec/ponothea arie$ rake db:test:clone (in /Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea) # ya, jadi database tesnya kan masih cloning db dev yang sebelumnya yang salah, jadi dihapus dulu baru di create db lagi mengcloning sesuai db dev yg benar saat ini, lalu setelah ini harus betul, kalo gak awas! id-ruby:~/sukebe/spec/ponothea arie$ spec spec/models/crazyfication_spec.rb .. Finished in 1.14454 seconds 2 examples, 0 failures id-ruby:~/sukebe/spec/ponothea arie$ rake spec:models (in /Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea) .... Finished in 0.775302 seconds 4 examples, 0 failures id-ruby:~/sukebe/spec/ponothea arie$ # nah OK, testing sudah betul 100% :-D Loh Anda protes, jadi proses yang lewat formnya mana pak? ya... mustinya dari gambaran testing yang descriptive tadi itu sudah bisa dikerjakan sendiri PR nya. Ayo dicoba dulu mana copy paste codingnya biar dipelototin/dikoreksi lagi sama teman-teman kita bareng2x yang pada beken-beken di id-ruby ini ;-) supaya keliatan progress belajarnya... > Gimana cara rollback nya. > Help! Kalau ada di email saya ini yang setelah Anda coba di komputer Anda malah Anda dapat error, selalu laporkan ke saya beserta keterangan komputer Anda yang jelas, jadi selalu kasih perkembangannya ke kita-kita disini. -- id-ruby:~ arie$ irb-19 irb(main):001:0> {name:'arie', blog:'http://ariekusumaatmaja.wordpress.com'}.is_a?(Hash) => true