我會從最簡單的解決方案開始。取決於您檢查此頁面的頻率以及您有多少產品和交易可能已經足夠好。 那麼最簡單的解決方案是什麼?我將邏輯提取到一個 Dashboard
(或任何你想稱之爲)對象。這樣你不代碼雜亂款車型具體到儀表板:
class Dashboard
def initialize(range_start, range_end)
@range_start = range_start
@range_end = range_end
end
attr_reader :range_start, :range_end
def range
(range_start..range_end)
end
def products
Product.joins(:transactions).where('date(transactions.recorded_at) >= :start AND date(transactions.recorded_at) <= :end', start: range_start, end: range_end).select("products.*").distinct
end
def transactions_on(product, date)
product.transactions.where('date(recorded_at) = ?', date)
end
def transactions_in_range(product)
product.transactions.where('date(transactions.recorded_at) >= :start AND date(transactions.recorded_at) <= :end', start: range_start, end: range_end)
end
end
然後你就可以使用這個儀表板類在視圖中建表:
Displaying <%= @dashboard.range_start %> - <%= @dashboard.range_end %>
<table border="1">
<thead>
<tr>
<th>Product</th>
<% @dashboard.range.each do |date| %>
<td><%= date.strftime('%^a') %></td>
<% end %>
<th>TOTAL</th>
</tr>
</thead>
<tbody>
<% @dashboard.products.each do |product| %>
<tr>
<td><%= product.name %></td>
<% @dashboard.range.each do |date| %>
<td><%= @dashboard.transactions_on(product, date).count %></td>
<% end %>
<td><%= @dashboard.transactions_in_range(product).count %></td>
</tr>
<% end %>
</tbody>
</table>
這是非常動態的,你可以改變範圍,桌子將適應。您可以添加分頁以顯示上一週/下週或更大的範圍。
在儀表盤控制器:
def show
range_start = Time.zone.today.beginning_of_week - 1.week # would probably get it from params
range_end = range_start.end_of_week
@dashboard = Dashboard.new(range_start, range_end)
end
這將觸發相當長的一段數查詢(1個+ range.size * products.size)。但解決方案很容易理解和維護。如果這樣做有效,那麼我不會花太多時間來實現過於聰明的事情。
聰明的一種方法是使用AR會自動添加屬性的事實。您可以使用select
添加計算字段併爲其添加別名。之後,您可以使用普通的紅寶石來訪問這些值。所以,如果你想要檢索的單個SQL語句中的所有值:
def table
scope = Product.all.select('products.*')
range.each_with_index do |date, index|
scope = scope.select("(SELECT count(*) from transactions t WHERE t.product_id = products.id AND date(t.recorded_at) = date('#{date.to_s(:db)}')) as d_#{index}")
end
scope = scope.select("(SELECT count(*) from transactions t WHERE t.product_id = products.id AND date(t.recorded_at) >= date('#{range_start.to_s(:db)}') AND date(recorded_at) <= date('#{range_end.to_s(:db)}')) as d_total")
scope
end
,然後在視圖中使用這樣的:
<table border="1">
<thead>
<tr>
<th>Product</th>
<% @dashboard.range.each do |date| %>
<td><%= date.strftime('%^a') %></td>
<% end %>
<th>TOTAL</th>
</tr>
</thead>
<tbody>
<%- @dashboard.table.each do |product| %>
<tr>
<td><%= product.name %></td>
<% @dashboard.range.each_with_index do |date, index| %>
<td><%= product.public_send("d_#{index}") %></td>
<% end %>
<td><%= product.public_send('d_total') %></td>
</tr>
<% end %>
</tbody>
</table>
需要一些清理。但它的工作。請注意,第二個解決方案當前顯示所有產品。如果你想限制那些有相應交易的人,那麼你需要根據你的需求來調整它。
您可能可以使用數據庫特定的日期函數按星期幾分組。你使用哪個數據庫(Postgres,MySQL等)? – ajporterfield
@ajporterfield感謝問 - 我正在使用Postgres –