3

У меня есть приложение Rails с рядом Продуктов, некоторые из которых связаны с моделью проблемы. Некоторые из этих продуктов имеют issue_id (так что проблема has_many продуктов), а некоторые нет. Продукты без идентификатор проблемы - новое дополнение, над которым я работаю.Создание области ActiveRecord с несколькими условными выражениями

Я ранее имел именованный масштаб, так что я могу перечислить продукты, используя Product.published, который выглядит следующим образом:

scope :published, -> { 
    joins(:issue).reorder('products.created_at ASC, products.number ASC') 
    .where('products.status = ?', Product.statuses[:available]) 
    .where('issues.status = ?', Issue.statuses[:published]) 
} 

Результат этого является то, что я могу найти только продукты, которые связаны с опубликованным выпуском (думаю журнал выпуск).

Теперь я добавляю продукты, которые не будут связаны с какой-либо конкретной проблемой, но все равно будут иметь проект/доступное состояние. Вышеуказанная область не находит эти продукты, поскольку она ищет not_ty_id, которая не существует.

Я думал, что я мог бы изменить сферу, как это, добавив OR issue_id IS NULL часть в последней строке:

scope :published, -> { 
    joins(:issue).reorder('products.created_at ASC, products.number ASC') 
    .where('products.status = ?', Product.statuses[:available]) 
    .where('issues.status = ? OR issue_id IS NULL', Issue.statuses[:published]) 
} 

Но это не работает. Я все еще получаю только «доступные» продукты, связанные с «опубликованной» проблемой. Продукты без issue_id не включены в возвращенную коллекцию.

(Существует окно, в котором продукт будет установлен в имеющейся до его связанного вопроса публикуется, поэтому для таких ситуаций я должен проверить состояние обеих записей.)

Вот SQL порожденная выше (оболочкой для удобства чтения):

pry(main)> Product.published.to_sql 
=> "SELECT `products`.* FROM `products` INNER JOIN `issues` ON `issues`.`id` = 
`products`.`issue_id` WHERE (products.status = 1) AND (issues.status = 1 OR 
issue_id IS NULL) ORDER BY products.created_at ASC, products.number ASC" 

Я уже создал метод класса продукта, который принимает аргумент в качестве альтернативного подхода, но не работает во всех случаях, потому что я часто смотрю вверх продукт, основанный на идентификаторе, не зная заранее, существует ли ассоциация проблем o r нет (например, для продукта show вид).

Кроме того, Product.published является приятным и лаконичным, и альтернативой является загрузка всех опубликованных продуктов (например, Product.where(:status => :published)), а затем итерация, чтобы удалить те, которые связаны с еще не опубликованной проблемой во второй операции.

Я чувствую, что есть кое-что, что я не совсем понимаю, делая более сложные запросы в пределах области. Мой идеальный результат - это измененная область действия, которая может возвращать доступные продукты, как с проблемой, так и без нее, и без аргумента.

Возможно ли это, или я должен смириться с поиском альтернативного подхода, когда я добавляю эти несвязанные продукты?

+0

Посмотрите на sql, сгенерированный этим запросом, используя, например, 'Product.published.to_sql' (отредактируйте свой вопрос и добавьте туда результат, чтобы мы все его могли видеть) :) Часто вы можете понять, почему SQL ошибается, а затем выполнить резервное копирование исправления, необходимого для области рубина. –

+1

Хорошая идея @TarynEast, спасибо. Я добавил сгенерированный SQL, который я делал ранее, но я действительно не обмотал голову вокруг соединений, так что это не помогло мне увидеть проблему. – Kenn

ответ

4

Проблема в том, что вы используете joins(:issue). Этот метод делает INNER JOIN между products и issues таблицами и отбрасывает все продукты, у которых нет проблемы. Возможно, вы могли бы использовать LEFT JOIN, чтобы вы могли хранить все продукты независимо от того, у них есть проблема.

scope :published, -> { 
    joins('LEFT JOIN issues ON issues.id = products.issue_id') 
    .select('products.*') 
    .reorder('products.created_at ASC, products.number ASC') 
    .where('products.status = ?', Product.statuses[:available]) 
    .where('issues.status = ? OR products.issue_id IS NULL', Issue.statuses[:published]) 
} 
+0

Это сделало это, спасибо! Мне явно нужно узнать более реальный SQL. – Kenn

+0

Удивительный! Я рад, что вы смогли заставить его работать! –

Смежные вопросы