Rails ActiveRecord joins with multiple tables - ruby-on-rails

I have a Category Model which belongs to Offer Model through a third Model OfferCategory. I also have a similar association for PlumCake Model which is associated to Category model through PlumCakeCategory Model.
Category:
has_many :offer_categories, dependent: :destroy, inverse_of: :category
has_many :offers, through: :offer_categories, source: :offer
has_many :plum_cake_categories, dependent: :destroy, inverse_of: :category
has_many :plum_cakes, through: :plum_cake_categories, source: :plum_cake
Offer:
has_many :offer_categories, dependent: :destroy, inverse_of: :offer
has_many :categories, through: :offer_categories, source: :category, dependent: :destroy
OfferCategory:
belongs_to :offer
belongs_to :category
PlumCake:
has_many :plum_cake_categories, dependent: :destroy, inverse_of: :plum_cake
has_many :categories, through: :plum_cake_categories, source: :category, dependent: :destroy
PlumCakeCategory:
belongs_to :plum_cake
belongs_to :category
and a similar association for category/plumcakes as well.
Now I want to get all categoires that the selected offers and plumcakes has. The following query gives me the list of categories that the eligible_offer_ids offers has.
Category.joins(:offer_categories).where(offer_categories: { offer_id: eligible_offer_ids })
I can fire a similar query for plum_cake and get uniq categoires of these two queries.
cat1 = Category.joins(:offer_categories).where(offer_categories: { offer_id: eligible_offer_ids })
cat2 = Category.joins(:plum_cake_categories).where(plum_cake_categories: { plum_cake_id: eligible_plum_cake_ids })
(cat1 + cat2).uniq
But Is there a way I get the same result((cat1 + cat2).uniq) in a single query?

if you don't to want change your structure:
Category.left_outer_joins(:offer_categories, :plum_cake_categories).where(offer_categories: { offer_id: eligible_offer_ids }).or(Category.left_outer_joins(:offer_categories, :plum_cake_categories).where(plum_cake_categories: { plum_cake_id: eligible_plum_cake_ids })).uniq

I think if you use rails STI(single table inheritance) you can easily implement your functionality.
if your structure be somthing like following:
class Category
has_many :offer_categories, dependent: :destroy, inverse_of: :category
has_many :offers, through: :offer_categories, source: :offer
has_many :plum_cake_categories, dependent: :destroy, inverse_of: :category
has_many :plum_cakes, through: :plum_cake_categories, source: :plum_cake
has_many :sub_categories
end
class SubCategory
belongs_to :offer
belongs_to :plum_cake
belongs_to :category
end
class OfferCategory < SubCategory
validate_presence_of :offer_id
end
class PlumCakeCategory < SubCategory
validate_presence_of :plum_cake_id
end
your query will be:
Category.joins(:sub_categories).where(sub_categories: { offer_id: eligible_offer_ids }).or(Category.joins(:sub_categories).where(sub_categories: { plum_cake_id: eligible_plum_cake_ids }))
STI documentation

Related

How to do a query for multiple categories

I have 3 simple models
class Tag < ApplicationRecord
belongs_to :category
belongs_to :course
end
class Course < ApplicationRecord
has_many :tags, dependent: :destroy
has_many :categories, through: :tags
end
class Category < ApplicationRecord
has_many :tags, dependent: :destroy
has_many :courses, through: :tags
end
Given a category as a string (for example 'Japanese') it's trivial to find courses by Category.find_by_title('Japanese').courses
I'm having a hard time doing a query if categories are in an array, for example ['Japanese', 'Language'] which needs to return courses tagged as 'Japanese' and 'Language'
You can query by using joins:
Course.joins(categories: :tags).where(categories: {title: ['array condition']}, tags: {tag: ['array condition']})

Rails has_many through with where clause

I need help to achieve something.
Is it possible that my Analysis has_many :klasses, through: :subjects, but filtered using attributes from the join table AnalysisSubject? Or my models should be different?
class Analysis
has_many :analysis_subjects, dependent: :destroy
has_many :subjects, through: :analysis_subjects
has_many :klasses, -> { where(year: ??????, semester: ??????), through: :subjects
end
class AnalysisSubject
belongs_to :analysis
belongs_to :subject
# There are year:integer and semester:integer attributes
# I want to use those attributes in my where clause for analysis.klasses
end
class Subject
has_many :klasses
has_many :analysis_subjects
has_many :analyses, through: :analysis_subjects
end
class Klass
belongs_to :subject
end
I'm using Rails 5 if it is important. Thank you for your help.
Yes, but you should afford it in a different way: You must declare the where in a has_many :filtered_analysis_subjects.
class Analysis
has_many :filtered_analisys_subjects, -> { where(year: x, semester: y }, foreign_key: "analysis_id", class_name: "AnalysisSubject", dependent: :destroy
has_many :subjects, through: :filtered_analysis_subjects
has_many :klasses, through: :subjects

Remove association from Rails 5 self join table without deleting

I have a self join table on my product model using a model called matches as the join table. What I would like to do is when deleting a product to have the associated product removed but not deleted. Right now I am trying dependent: :destroy which doesn't work, but I know its not what I want because I don't want to delete the self associated product.
product.rb
class Product < ApplicationRecord
...
has_many :variations, -> { order(:order) }, dependent: :destroy
has_and_belongs_to_many :categories
has_and_belongs_to_many :tags
has_many :matches
has_many :matched_products, through: :matches, dependent: :destroy
...
end
match.rb
class Match < ActiveRecord::Base
belongs_to :product
belongs_to :matched_product, class_name: 'Product', dependent: :destroy
has_many :variations, :through => :matched_product
end
I suggest you update your models as follows:
product.rb
class Product < ApplicationRecord
...
has_many :variations, -> { order(:order) }, dependent: :destroy
has_and_belongs_to_many :categories
has_and_belongs_to_many :tags
has_many :matches, dependent: :destroy
has_many :product_matches, class_name: 'Match', foreign_key: :matched_product_id, dependent: :destroy
has_many :matched_products, through: :matches
...
end
This will ensure that all matches records are deleted when deleting a product whether the product is a product or matched_product in the match record. Removing dependent: :destroy from has_many :matched_products will prevent deletion of the, well, matched_products.
match.rb
class Match < ActiveRecord::Base
belongs_to :product
belongs_to :matched_product, class_name: 'Product'
has_many :variations, :through => :matched_product
end
Similar to above, removing dependent: :destroy from belongs_to :matched_product, class_name: 'Product' will prevent deletion of the matched_product.

Has_many: :through for LineItems - Rails

I have four models:
User
Listing
Order
OrderGroup
User:
has_many :listings
has_many :orders
Listing:
belongs_to :seller, class_name: "User", foreign_key: :user_id
has_many :order_groups, through: :orders
has_many :orders
Order:
has_one :seller, through: :listing
belongs_to :listing
belongs_to :order_group
OrderGroup:
has_many :listings, through: :orders
has_many :orders
has_many :sellers, through: :orders
When I try to pull Order.where(seller: User.find(3)), I get an empty collection. However, when I do Order.last.seller, I get the seller's user_id.
How can I pull Order.where(seller: User.find(3))' ?
You can write query as
Order.joins(:listing).where('listings.user_id = ?', 3)

Cannot have a has_one :through association ' where the :through association is a collection

I have the following associations. PropertyOwner is a join model which belongs to a property and polymorphically belongs to an owner, which in the below example is a ForeclosureDefense. Everything works well, until I had the has_one :main_property. The idea is the ForeclosureDefense model can have many properties, but the last property is the main property:
class ForeclosureDefense < ActiveRecord::Base
has_many :property_owners, as: :owner
has_many :properties, through: :property_owners
has_one :main_property, through: :property_owners, source: :property, order: 'created_at desc'
end
class PropertyOwner < ActiveRecord::Base
belongs_to :property
belongs_to :owner, polymorphic: :true
end
class Property < ActiveRecord::Base
has_many :property_owners
has_many :owners, through: :property_owners
has_many :foreclosure_owners, through: :property_owners, source: :owner, source_type: "ForeclosureDefense"
has_many :folder_owners, through: :property_owners, source: :owner, source_type: "Folder"
end
Unfortunately, when I try to use that has_one :main_property association, I get the following error:
ActiveRecord::HasOneThroughCantAssociateThroughCollection: Cannot have a has_one :through association 'ForeclosureDefense#main_property' where the :through association 'ForeclosureDefense#property_owners' is a collection.
What am I doing wrong?
My solution was just to add it as a class-level macro:
def main_property
properties.order('created_at desc').first
end

Resources