Active Record: Jantung Model dalam Rails – Panduan Lengkap
Active Record adalah jantung dari lapisan model dalam framework Ruby on Rails. Ini adalah Object-Relational Mapping (ORM) yang menyediakan antarmuka antara basis data dan aplikasi Anda, memungkinkan Anda berinteraksi dengan data basis data menggunakan sintaks Ruby yang familiar dan intuitif. Dalam panduan lengkap ini, kita akan menjelajahi secara mendalam Active Record, mulai dari dasar-dasarnya hingga fitur-fitur canggihnya, dan bagaimana cara memanfaatkannya secara efektif dalam aplikasi Rails Anda.
Daftar Isi
- Pendahuluan Active Record
- Apa itu Active Record?
- Mengapa Menggunakan Active Record?
- Prinsip Dasar Active Record
- Konfigurasi dan Koneksi Basis Data
- Konfigurasi database.yml
- Membuat Migrasi
- Menjalankan Migrasi
- Membuat dan Menggunakan Model
- Mendefinisikan Model
- Atribut Model
- Konvensi Penamaan
- CRUD (Create, Read, Update, Delete) dengan Active Record
- Membuat Data (Create)
- Membaca Data (Read)
- Menemukan Satu Record
- Menemukan Banyak Record
- Kondisi WHERE
- Operator Perbandingan
- Operator Logika
- Urutan (Order)
- Limit dan Offset
- Memperbarui Data (Update)
- Menghapus Data (Delete)
- Asosiasi (Associations)
- Jenis-jenis Asosiasi
belongs_to
has_one
has_many
has_many :through
has_one :through
has_and_belongs_to_many
- Menggunakan Asosiasi
- Eager Loading
- Jenis-jenis Asosiasi
- Validasi
- Jenis-jenis Validasi
- Validasi Kustom
- Menangani Kesalahan Validasi
- Callback
- Jenis-jenis Callback
- Menggunakan Callback
- Scope
- Mendefinisikan Scope
- Menggunakan Scope
- Query Lanjutan dengan Active Record
- Menggunakan
joins
- Menggunakan
group
- Menggunakan
select
- Menggunakan
pluck
- Menggunakan
find_by_sql
(Hati-hati!)
- Menggunakan
- Serialisasi
- Menggunakan Serialisasi
- Polimorfisme
- Apa itu Asosiasi Polimorfik?
- Contoh Kasus
- Implementasi di Rails
- STI (Single Table Inheritance)
- Konsep STI
- Implementasi STI
- Tips dan Praktik Terbaik
- Optimasi Kinerja Query
- Menghindari N+1 Query
- Menggunakan Index
- Kesimpulan
1. Pendahuluan Active Record
Apa itu Active Record?
Active Record adalah lapisan ORM yang disediakan oleh Rails. Ini memfasilitasi interaksi dengan basis data dengan menyediakan representasi objek dari tabel basis data. Setiap model Active Record sesuai dengan tabel basis data, dan instance dari model tersebut mewakili baris dalam tabel tersebut. Active Record memungkinkan Anda untuk melakukan operasi CRUD (Create, Read, Update, Delete) pada data basis data menggunakan sintaks Ruby yang sederhana dan ekspresif.
Mengapa Menggunakan Active Record?
Berikut beberapa alasan mengapa menggunakan Active Record sangat bermanfaat:
- Abstraksi Basis Data: Active Record mengabstraksi detail kompleks dari interaksi basis data, memungkinkan Anda fokus pada logika aplikasi Anda.
- Sintaks Sederhana: Active Record menyediakan sintaks yang mudah dibaca dan dipahami untuk berinteraksi dengan basis data.
- Portabilitas: Active Record mendukung berbagai macam sistem basis data, sehingga memudahkan untuk mengganti basis data jika diperlukan.
- Keamanan: Active Record membantu mencegah serangan injeksi SQL dengan secara otomatis membersihkan input pengguna.
- Konvensi over Konfigurasi: Rails, dan khususnya Active Record, menganut prinsip “konvensi over konfigurasi,” yang berarti bahwa banyak keputusan konfigurasi dibuat berdasarkan konvensi, sehingga mengurangi jumlah kode boilerplate yang perlu Anda tulis.
Prinsip Dasar Active Record
Active Record didasarkan pada beberapa prinsip dasar:
- Model sesuai dengan tabel basis data.
- Instance model sesuai dengan baris dalam tabel.
- Atribut model sesuai dengan kolom dalam tabel.
- Active Record menyediakan metode untuk melakukan operasi CRUD pada data basis data.
2. Konfigurasi dan Koneksi Basis Data
Konfigurasi database.yml
File config/database.yml
adalah tempat Anda mengkonfigurasi koneksi basis data untuk aplikasi Rails Anda. File ini berisi pengaturan koneksi untuk berbagai lingkungan, seperti pengembangan, pengujian, dan produksi.
Contoh database.yml
:
default: &default
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: your_username
password: your_password
development:
<<: *default
database: your_app_development
test:
<<: *default
database: your_app_test
production:
<<: *default
database: your_app_production
username: <%= ENV['DATABASE_USERNAME'] %>
password: <%= ENV['DATABASE_PASSWORD'] %>
Pastikan untuk mengganti your_username
, your_password
, your_app_development
, your_app_test
, dan your_app_production
dengan nilai yang sesuai untuk lingkungan Anda.
Membuat Migrasi
Migrasi adalah cara untuk mengubah skema basis data Anda dari waktu ke waktu secara terstruktur dan konsisten. Anda menggunakan migrasi untuk membuat tabel, menambahkan kolom, menghapus kolom, dan melakukan perubahan lain pada struktur basis data Anda.
Untuk membuat migrasi, gunakan perintah rails generate migration
:
rails generate migration CreateUsers
Perintah ini akan membuat file migrasi baru di direktori db/migrate
. Anda kemudian dapat mengedit file migrasi untuk menentukan perubahan yang ingin Anda lakukan pada skema basis data Anda.
Contoh migrasi:
class CreateUsers < ActiveRecord::Migration[7.0]
def change
create_table :users do |t|
t.string :name
t.string :email
t.timestamps
end
end
end
Menjalankan Migrasi
Untuk menjalankan migrasi, gunakan perintah rails db:migrate
:
rails db:migrate
Perintah ini akan menjalankan semua migrasi yang tertunda dalam urutan kronologis.
Anda juga dapat membatalkan migrasi menggunakan perintah rails db:rollback
:
rails db:rollback
Perintah ini akan membatalkan migrasi terakhir yang dijalankan.
3. Membuat dan Menggunakan Model
Mendefinisikan Model
Untuk membuat model Active Record, gunakan perintah rails generate model
:
rails generate model User name:string email:string
Perintah ini akan membuat file model baru di direktori app/models
. Perintah ini juga akan membuat file migrasi untuk membuat tabel yang sesuai di basis data. Anda dapat menentukan tipe data untuk setiap atribut langsung di command line.
Contoh model:
class User < ApplicationRecord
end
Atribut Model
Atribut model sesuai dengan kolom dalam tabel basis data. Anda dapat mengakses atribut model menggunakan sintaks titik:
user = User.new(name: "John Doe", email: "john.doe@example.com")
puts user.name # Output: John Doe
puts user.email # Output: john.doe@example.com
Konvensi Penamaan
Active Record menggunakan konvensi penamaan untuk menghubungkan model ke tabel basis data yang sesuai:
- Nama model harus menggunakan singular, CamelCase (misalnya,
User
). - Nama tabel basis data harus menggunakan plural, snake_case (misalnya,
users
).
4. CRUD (Create, Read, Update, Delete) dengan Active Record
Membuat Data (Create)
Ada beberapa cara untuk membuat data menggunakan Active Record:
- Menggunakan
new
dansave
:
user = User.new(name: "Jane Doe", email: "jane.doe@example.com")
user.save
- Menggunakan
create
:
user = User.create(name: "Peter Pan", email: "peter.pan@neverland.com")
- Menggunakan
create!
:
user = User.create!(name: "Alice", email: "alice@wonderland.com") # Akan raise error jika validasi gagal
Metode create!
akan melempar pengecualian jika validasi gagal.
Membaca Data (Read)
Ada beberapa cara untuk membaca data menggunakan Active Record:
Menemukan Satu Record
- Menggunakan
find
:
user = User.find(1) # Menemukan user dengan ID 1
Metode find
akan melempar pengecualian ActiveRecord::RecordNotFound
jika record tidak ditemukan.
- Menggunakan
find_by
:
user = User.find_by(email: "john.doe@example.com") # Menemukan user dengan email john.doe@example.com
Metode find_by
akan mengembalikan nil
jika record tidak ditemukan.
- Menggunakan
find_by!
:
user = User.find_by!(email: "john.doe@example.com") # Menemukan user dengan email john.doe@example.com, akan raise error jika tidak ditemukan
Menemukan Banyak Record
- Menggunakan
all
:
users = User.all # Mengembalikan semua user
- Menggunakan
where
:
users = User.where(name: "John Doe") # Mengembalikan semua user dengan nama John Doe
Kondisi WHERE
Anda dapat menggunakan kondisi WHERE
yang kompleks untuk memfilter data:
users = User.where("name LIKE ?", "%John%") # Mengembalikan semua user yang namanya mengandung "John"
Operator Perbandingan
Active Record mendukung berbagai operator perbandingan:
=
(sama dengan)!=
(tidak sama dengan)>
(lebih besar dari)<
(lebih kecil dari)>=
(lebih besar dari atau sama dengan)<=
(lebih kecil dari atau sama dengan)LIKE
(pencocokan pola)IN
(dalam daftar)NOT IN
(tidak dalam daftar)IS NULL
(null)IS NOT NULL
(tidak null)
Operator Logika
Anda dapat menggunakan operator logika untuk menggabungkan kondisi WHERE
:
AND
OR
NOT
users = User.where(name: "John Doe").or(User.where(email: "john.doe@example.com")) # Mengembalikan user dengan nama John Doe ATAU email john.doe@example.com
Urutan (Order)
Anda dapat mengurutkan hasil query menggunakan metode order
:
users = User.order(:name) # Mengurutkan user berdasarkan nama secara ascending
users = User.order(name: :desc) # Mengurutkan user berdasarkan nama secara descending
Limit dan Offset
Anda dapat membatasi jumlah record yang dikembalikan menggunakan metode limit
dan offset
:
users = User.limit(10) # Mengembalikan hanya 10 user pertama
users = User.offset(10).limit(10) # Mengembalikan 10 user berikutnya setelah 10 user pertama
Memperbarui Data (Update)
Ada beberapa cara untuk memperbarui data menggunakan Active Record:
- Menggunakan
update
pada instance model:
user = User.find(1)
user.update(name: "Johnny Doe")
- Menggunakan
update!
pada instance model:
user = User.find(1)
user.update!(name: "Jonathan Doe") # Akan raise error jika validasi gagal
- Menggunakan
update_all
:
User.where(email: "john.doe@example.com").update_all(name: "Jonathan Doe") # Memperbarui semua user dengan email john.doe@example.com
Menghapus Data (Delete)
Ada beberapa cara untuk menghapus data menggunakan Active Record:
- Menggunakan
destroy
pada instance model:
user = User.find(1)
user.destroy
- Menggunakan
delete
pada instance model:
user = User.find(1)
user.delete # Sama seperti destroy, tapi skip callback
- Menggunakan
delete_all
:
User.where(email: "john.doe@example.com").delete_all # Menghapus semua user dengan email john.doe@example.com
- Menggunakan
destroy_all
:
User.where(email: "john.doe@example.com").destroy_all # Menghapus semua user dengan email john.doe@example.com dan menjalankan callback
Perbedaan penting antara delete
dan destroy
adalah bahwa destroy
memicu callback model, sedangkan delete
tidak.
5. Asosiasi (Associations)
Asosiasi adalah cara untuk menghubungkan model satu sama lain. Misalnya, seorang penulis mungkin memiliki banyak buku, dan setiap buku dimiliki oleh seorang penulis. Active Record menyediakan beberapa jenis asosiasi:
Jenis-jenis Asosiasi
belongs_to
has_one
has_many
has_many :through
has_one :through
has_and_belongs_to_many
belongs_to
Asosiasi belongs_to
menunjukkan bahwa sebuah model dimiliki oleh model lain. Misalnya, model Book
mungkin belongs_to
model Author
:
class Book < ApplicationRecord
belongs_to :author
end
Dalam hal ini, tabel books
harus memiliki kolom author_id
, yang merupakan foreign key ke tabel authors
.
has_one
Asosiasi has_one
menunjukkan bahwa sebuah model memiliki satu instance model lain. Misalnya, model Author
mungkin has_one
model Profile
:
class Author < ApplicationRecord
has_one :profile
end
Dalam hal ini, tabel profiles
harus memiliki kolom author_id
, yang merupakan foreign key ke tabel authors
.
has_many
Asosiasi has_many
menunjukkan bahwa sebuah model memiliki banyak instance model lain. Misalnya, model Author
mungkin has_many
model Book
:
class Author < ApplicationRecord
has_many :books
end
Dalam hal ini, tabel books
harus memiliki kolom author_id
, yang merupakan foreign key ke tabel authors
.
has_many :through
Asosiasi has_many :through
menunjukkan bahwa sebuah model memiliki banyak instance model lain melalui model perantara. Misalnya, seorang dokter mungkin memiliki banyak pasien melalui janji temu:
class Doctor < ApplicationRecord
has_many :appointments
has_many :patients, through: :appointments
end
class Patient < ApplicationRecord
has_many :appointments
has_many :doctors, through: :appointments
end
class Appointment < ApplicationRecord
belongs_to :doctor
belongs_to :patient
end
has_one :through
Asosiasi has_one :through
mirip dengan has_many :through
, tetapi hanya mengembalikan satu instance model lain. Misalnya, seorang pemasok mungkin memiliki satu akun melalui riwayat akun:
class Supplier < ApplicationRecord
has_one :account_history
has_one :account, through: :account_history
end
class Account < ApplicationRecord
end
class AccountHistory < ApplicationRecord
belongs_to :supplier
belongs_to :account
end
has_and_belongs_to_many
Asosiasi has_and_belongs_to_many
menunjukkan hubungan banyak-ke-banyak antara dua model. Misalnya, sebuah buku mungkin memiliki banyak penulis, dan seorang penulis mungkin memiliki banyak buku:
class Book < ApplicationRecord
has_and_belongs_to_many :authors
end
class Author < ApplicationRecord
has_and_belongs_to_many :books
end
Dalam hal ini, Anda perlu membuat tabel join bernama authors_books
dengan kolom author_id
dan book_id
.
Menggunakan Asosiasi
Setelah Anda mendefinisikan asosiasi, Anda dapat menggunakannya untuk mengakses data terkait:
author = Author.find(1)
books = author.books # Mengembalikan semua buku yang ditulis oleh author dengan ID 1
book = Book.find(1)
author = book.author # Mengembalikan author yang menulis buku dengan ID 1
Eager Loading
Eager loading adalah teknik untuk memuat asosiasi secara efisien. Secara default, Active Record memuat asosiasi secara lambat, yang berarti bahwa asosiasi dimuat hanya ketika diakses. Ini dapat menyebabkan masalah kinerja, terutama jika Anda mengakses banyak asosiasi dalam satu tampilan. Eager loading memungkinkan Anda untuk memuat semua asosiasi yang diperlukan dalam satu query, mengurangi jumlah query basis data yang diperlukan.
Anda dapat menggunakan eager loading dengan metode includes
:
authors = Author.includes(:books) # Memuat semua authors dan buku mereka dalam satu query
authors.each do |author|
puts author.name
author.books.each do |book|
puts book.title
end
end
6. Validasi
Validasi digunakan untuk memastikan bahwa data yang disimpan ke basis data valid. Active Record menyediakan berbagai jenis validasi yang dapat Anda gunakan untuk memvalidasi data Anda.
Jenis-jenis Validasi
acceptance
confirmation
exclusion
inclusion
length
numericality
presence
absence
uniqueness
format
Contoh validasi:
class User < ApplicationRecord
validates :name, presence: true, length: { maximum: 50 }
validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP }
end
Validasi Kustom
Anda dapat membuat validasi kustom dengan mendefinisikan metode validasi di model Anda:
class User < ApplicationRecord
validate :password_complexity
def password_complexity
if password.present? && !password.match(/\A(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*]).*\z/)
errors.add :password, "must include at least one lowercase letter, one uppercase letter, one digit, and one special character"
end
end
end
Menangani Kesalahan Validasi
Ketika validasi gagal, Active Record akan menambahkan pesan kesalahan ke objek errors
. Anda dapat mengakses pesan kesalahan ini dalam tampilan Anda:
<% if @user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% @user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
7. Callback
Callback adalah metode yang dipanggil pada titik-titik tertentu dalam siklus hidup objek Active Record. Anda dapat menggunakan callback untuk melakukan tindakan seperti mengirim email, membuat log, atau memperbarui data terkait.
Jenis-jenis Callback
before_validation
after_validation
before_save
around_save
before_create
around_create
after_create
before_update
around_update
after_update
before_destroy
around_destroy
after_destroy
after_save
after_commit
(setelah transaksi berhasil di-commit)after_rollback
(setelah transaksi di-rollback)
Menggunakan Callback
Contoh penggunaan callback:
class User < ApplicationRecord
before_save :normalize_email
private
def normalize_email
self.email = email.downcase.strip
end
end
8. Scope
Scope adalah cara untuk merangkum query basis data yang umum digunakan. Scope memungkinkan Anda untuk membuat kode yang lebih ringkas dan dapat dibaca.
Mendefinisikan Scope
Anda dapat mendefinisikan scope menggunakan metode scope
:
class User < ApplicationRecord
scope :active, -> { where(active: true) }
scope :recent, -> { order(created_at: :desc).limit(10) }
end
Menggunakan Scope
Anda dapat menggunakan scope dengan memanggilnya pada model:
active_users = User.active # Mengembalikan semua user aktif
recent_users = User.recent # Mengembalikan 10 user terbaru
9. Query Lanjutan dengan Active Record
Active Record menyediakan berbagai metode untuk melakukan query lanjutan pada basis data Anda.
Menggunakan joins
Metode joins
memungkinkan Anda untuk menggabungkan tabel basis data:
users = User.joins(:posts) # Mengembalikan semua user yang memiliki post
users = User.joins("LEFT JOIN posts ON users.id = posts.user_id") # Mengembalikan semua user dan post mereka (jika ada)
Menggunakan group
Metode group
memungkinkan Anda untuk mengelompokkan hasil query:
user_counts = User.group(:city).count # Mengembalikan jumlah user per kota
Menggunakan select
Metode select
memungkinkan Anda untuk memilih kolom tertentu yang akan dikembalikan:
users = User.select(:id, :name) # Hanya mengembalikan kolom id dan nama
Menggunakan pluck
Metode pluck
memungkinkan Anda untuk mengekstrak nilai dari satu kolom:
user_names = User.pluck(:name) # Mengembalikan array nama user
Menggunakan find_by_sql
(Hati-hati!)
Metode find_by_sql
memungkinkan Anda untuk menjalankan query SQL mentah. Namun, Anda harus berhati-hati saat menggunakan metode ini, karena dapat rentan terhadap serangan injeksi SQL.
users = User.find_by_sql("SELECT * FROM users WHERE name LIKE '%John%'") # Mengembalikan semua user yang namanya mengandung "John"
Sebaiknya hindari menggunakan find_by_sql
jika memungkinkan dan gunakan metode Active Record yang lebih aman.
10. Serialisasi
Serialisasi adalah proses mengubah objek Ruby menjadi format yang dapat disimpan ke basis data atau dikirim melalui jaringan. Active Record menyediakan cara untuk melakukan serialisasi secara otomatis untuk kolom tertentu.
Menggunakan Serialisasi
class User < ApplicationRecord
serialize :preferences, Hash
end
Dalam contoh ini, kolom preferences
akan disimpan sebagai hash berseri. Ketika Anda membaca data dari basis data, kolom preferences
akan secara otomatis di-deserialisasi menjadi hash Ruby.
11. Polimorfisme
Apa itu Asosiasi Polimorfik?
Asosiasi polimorfik memungkinkan sebuah model untuk berasosiasi dengan beberapa model lain pada satu asosiasi. Ini sangat berguna ketika Anda memiliki fitur yang dapat di-share oleh beberapa model berbeda, seperti komentar, likes, atau attachments.
Contoh Kasus
Bayangkan Anda memiliki model `Article` dan `Photo`, dan Anda ingin mengizinkan pengguna untuk memberikan komentar pada keduanya. Daripada membuat tabel `comments` terpisah untuk setiap model, Anda dapat menggunakan asosiasi polimorfik.
Implementasi di Rails
- Membuat Migrasi:
rails generate migration CreateComments commentable:references{polymorphic: true} body:text
Migrasi ini akan membuat tabel `comments` dengan kolom `commentable_id` dan `commentable_type`. `commentable_type` menyimpan nama model (misalnya, “Article” atau “Photo”), dan `commentable_id` menyimpan ID record terkait.
- Mendefinisikan Asosiasi di Model:
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true
end
class Article < ApplicationRecord
has_many :comments, as: :commentable
end
class Photo < ApplicationRecord
has_many :comments, as: :commentable
end
Sekarang Anda dapat membuat komentar untuk artikel atau foto:
article = Article.find(1)
article.comments.create(body: "Great article!")
photo = Photo.find(1)
photo.comments.create(body: "Beautiful photo!")
12. STI (Single Table Inheritance)
Konsep STI
Single Table Inheritance (STI) adalah pola desain di mana hierarki kelas disimpan dalam satu tabel basis data. Kolom “type” digunakan untuk membedakan jenis record yang berbeda.
Implementasi STI
- Membuat Migrasi:
rails generate model Employee type:string name:string salary:integer
- Mendefinisikan Model:
class Employee < ApplicationRecord
end
class Manager < Employee
end
class Programmer < Employee
end
Sekarang Anda dapat membuat manager dan programmer:
manager = Manager.create(name: "John Doe", salary: 100000)
programmer = Programmer.create(name: "Jane Doe", salary: 80000)
Kolom `type` akan secara otomatis diisi dengan “Manager” atau “Programmer”.
13. Tips dan Praktik Terbaik
Optimasi Kinerja Query
- Gunakan Index: Pastikan Anda memiliki index yang sesuai pada kolom yang sering digunakan dalam query
WHERE
. - Batasi Kolom yang Dipilih: Hanya pilih kolom yang Anda butuhkan untuk mengurangi jumlah data yang diambil dari basis data.
- Gunakan Eager Loading: Muat asosiasi secara eager untuk menghindari N+1 query.
- Hindari Loop di Basis Data: Lakukan operasi massal di basis data daripada melakukan banyak query kecil dalam loop.
Menghindari N+1 Query
N+1 query adalah masalah kinerja yang umum di aplikasi Rails. Ini terjadi ketika Anda menjalankan query untuk mendapatkan data, dan kemudian menjalankan N query tambahan untuk mendapatkan data terkait.
Untuk menghindari N+1 query, gunakan eager loading (includes
).
Menggunakan Index
Index adalah struktur data yang meningkatkan kecepatan operasi pencarian data pada tabel basis data. Pastikan Anda memiliki index pada kolom yang sering digunakan dalam kondisi WHERE
.
14. Kesimpulan
Active Record adalah komponen penting dari Ruby on Rails yang menyederhanakan interaksi dengan basis data. Dengan memahami konsep dasar, jenis asosiasi, validasi, callback, dan tips optimasi kinerja, Anda dapat membangun aplikasi Rails yang efisien dan mudah dipelihara. Panduan ini mencakup berbagai aspek Active Record, dari konfigurasi dasar hingga teknik query lanjutan, dan diharapkan dapat membantu Anda memanfaatkan kekuatan Active Record secara maksimal dalam proyek Rails Anda.
“`