Reputation: 163
I have the following table of Sales from some Articles
id | user_id | article_id | status
1 | 1 | 1 | 0
2 | 1 | 2 | 0
3 | 2 | 2 | 0
4 | 1 | 1 | 1
5 | 2 | 2 | 1
Where status = 0
is unconfirmed and status = 1
is confirmed
The logic follows: The user clicks on a button to buy an Article Then, a record is saved on the table with the unconfirmed status. After the payment is confirmed, another record is save on the table with the status confirmed. Both records are saved on the table to keep a timeline of the whole transaction. The id
field determines the oldest record from a given transaction.
In my application, I wanted to get a list of 5 Most Sold Articles, here's how I've done it so far:
# In articles_helper.rb
def get_articles_sold
# Fetch all Articles that are present in the Sales table
sales = Article.where(id: Sale.select(:article_id).map(&:article_id))
sold = []
sales.each do |s|
s.status.group_by(&:status).each do |status, sale|
if status.to_sym == :confirmed
sold << {article: s, sold: sale.count}
end
end
end
# Returns the sold array
sold
end
# In the Article model there's this Virtual Attribute 'status'
# It returns me the last status from a given transaction
def status
sales = Sale.where(article_id: self.id).order(id: :desc)
statuses = []
sales.group_by(&:user_id).each do |user, sale|
statuses << sale.first
end
statuses
end
#In my application
@most_sold_articles = get_articles_sold.sort_by{|v| v[:sold]}.reverse
It returns me something like:
@most_sold_articles = [
{article: #<some_article_entity>, sold: 4},
{article: #<some_article_entity>, sold: 2},
{article: #<some_article_entity>, sold: 1},
]
This code actually works and give me what I want, but I think it's a lot of code for a Ruby application. Is there any ellegant or easier way of getting this same result?
Upvotes: 1
Views: 154
Reputation: 23671
I think this should do it
@articles = Article
.joins(:sales)
.select("articles.*, count(sales.article_id) as sold")
.group('articles.id').order('sold DESC').limit(5)
If you want the same format as in your answer
@most_sold_articles = @articles.map{|a| {article: a, sold: a.sold}}
#=> [
#=> {article: #<some_article_entity>, sold: 100},
#=> {article: #<some_article_entity>, sold: 80},
#=> {article: #<some_article_entity>, sold: 75},
#=> {article: #<some_article_entity>, sold: 66},
#=> {article: #<some_article_entity>, sold: 30}
#=> ]
Upvotes: 3