[分享] 更方便在 Rails 內寫 OR 語句的 gem


#1

gem 連結:rails_or

有關注 Rails 的人可能知道,Rails 5 終於在 Active Record 內加上了 or 函式
讓你可以寫以下的語句:

Person.where(name: 'Pearl').or(Person.where(age: 24))

但是呢,當你需要 join 其它 table 的資料時,你 or 的對象也都必須加上 join,一切都變得很繁瑣:

User.joins(:posts).where(id: 2)
                  .or(User.joins(:posts).where('posts.title = ?',"title"))
                  .or(User.joins(:posts).where('posts.created_at > ?', 1.day.ago))

這個 gem 支援了許多更方便寫 OR 語句的語法糖(更多例子可以去 github頁 看)


而且很重要的是同時也支援 Rails 3Rails 4
不用再苦苦等待升版到 Rails 5 的那天才能寫 OR 語句!!

範例:

Person.where(name: 'Pearl').or(age: 24)
User.joins(:posts).where(id: 2)
                  .or('posts.title': "title")
                  .or('posts.created_at > ?', 1.day.ago)

可以不用重複的一直寫 Model.joins(XXX).where(...),語法簡潔很多~~


#2

嘛 … 我都寫純 SQL 的說 … 可以很明確的定義之類的,不用怕 Rails 把 SQL 順序弄亂或是語意不明

當然安全性也要自己顧就是了


#3

其實我覺得如果寫成

User.joins(:posts).where(id: 2)
                  .or('posts.title': "title")
                  .or('posts.created_at > ?', 1.day.ago)

反而才會容易造成混淆,尤其是如果 #where 前跟 #where 後又有一些條件的增減會更不容易閱讀

單純針對你說要重複寫 #joins 很麻煩的解法,其實很簡單:

user_with_posts = User.joins(:posts)

user_with_posts.where(id: 2)
                  .or(user_with_posts.where('posts.title = ?',"title"))
                  .or(user_with_posts.where('posts.created_at > ?', 1.day.ago))

#4

可能我例子舉的不太好,不過當要寫一個簡單的 OR 語句時
例如想要支援可以用 emailaccount 登入

純SQL:

User.where('email = :email OR account = :email', email: email)

Rails 5 #or

User.where(email: email).or(User.where(account: email))

rails_or:

User.where(email: email).or(account: email)

就是一個語法糖。個人覺得在這個情況下看起來會簡潔很多^^


#5

我的意思是,當你寫了下面兩句的時候,你分得出差別嗎?

User.joins(:posts).where(email: email).or(name: email)

User.where(email: email).or(name: email).joins(:posts)

#6

我覺得二句是等價的耶,有什麼不同嗎@@?


#7

哦……我看懂你的 gem 在寫什麼了

那應該不會不一樣,你是把左右兩句的條件抽出來並在一起作 Arel Or

所以這個在 Rails 3 4 的情況下不能用像 Rails 5 的方法對不對……


#8

我自己是有寫測試,在Rails 3Rails 4用起來應該行為要跟 Rails 5 一樣

只是Rails 5內有特別驗語句要一模一樣,不能省略 joins
假如專案短期內有想要升級到Rails 5的打算,就乖乖寫 joins 不要省略


簡單來說 Rails 5 能用的在 Rails 3Rails 4應該都要能用
而某些特定的語法糖(例如省略 groupjoins),在 Rails 5內就不能用

不知道你問的是這個嗎?


#9

在 Rails 5 的情況下跟 Rails 4 的情況下,下面的語句是一樣的嗎?

User.joins(:posts).where(id: 2).or(User.joins(:records).where('records.title = ?', title))

#10

剛才測了一下,joins 的東西要完全一樣才能,不然 Rails 5 會直接噴錯

ArgumentError: Relation passed to #or must be structurally compatible. Incompatible values: [:joins]

假如一樣的話,Rails 3, 4, 5的行為都一樣~~