如何在控制器中建立.group_by关联的模型

问题描述 投票:0回答:1

这里有新开发者。我有一个customer_controller.rb,它会收集所有已完成的订单并按电子邮件地址将其分组以获取“客户”列表。

customer_controller:

class CustomerController < ApplicationController

  def index
    orders = Spree::Order.where(completed_at: from...to)
    @customers = orders.group_by(&:email)
  end

end

我有这样的视图模板:

<% @customers.each_pair do |email, orders| %>
<% total_orders = Spree::Order.where(state: "complete", email: email) %>
<% arr = orders  %>
<% amount = arr.inject(0) {|sum, hash| sum + hash[:payment_total]} %>
<tbody id="tbodyid">
  <tr>
    <td><%= email %></td>
    <th><%= orders.count %></td>
    <td><%= total_orders.count %></td>
    <td><%= total_orders.first.completed_at.strftime("%D") if total_orders.first.completed_at.present? %></td>
    <td>$<%= amount %></td>
  </tr>
</tbody>

<% end %>

为此处理模型的最佳方法是什么?将这种逻辑置于视图中是服务器上的噩梦。我想有一个基于@customers实例的模型,但我不知道该怎么做。

我是否完全遵循了这个方向并修改了order_decorator.rb模型以获得客户群体?

编辑:为回答以下问题,这是一个Spree :: Order记录。所有订单都有电子邮件,并非所有订单都有user_id。

#<Spree::Order id: 334, number: "R826173067", item_total: #<BigDecimal:7f9ee91516b8,'0.2198E2',18(18)>, total: #<BigDecimal:7f9ee91515f0,'0.1999E2',18(18)>, state: "complete", adjustment_total: #<BigDecimal:7f9ee91514b0,'-0.199E1',18(18)>, user_id: 45, completed_at: "2020-06-10 15:43:35", bill_address_id: 243, ship_address_id: 244, payment_total: #<BigDecimal:7f9ee9150e20,'0.1999E2',18(18)>, shipping_method_id: nil, shipment_state: "partial", payment_state: "paid", email: "[email protected]", special_instructions: nil, created_at: "2020-06-10 15:42:28", updated_at: "2020-06-10 15:43:35", currency: "USD", last_ip_address: "127.0.0.1", created_by_id: 45, shipment_total: #<BigDecimal:7f9ee91500d8,'0.0',9(18)>, additional_tax_total: #<BigDecimal:7f9ee9150010,'0.0',9(18)>, promo_total: #<BigDecimal:7f9edd683ef8,'-0.199E1',18(18)>, channel: "spree", included_tax_total: #<BigDecimal:7f9edd683db8,'0.0',9(18)>, item_count: 2, approver_id: nil, approved_at: nil, confirmation_delivered: true, considered_risky: false, express: false, source: nil, purchased_via_subscription: false> 
ruby-on-rails postgresql spree
1个回答
0
投票

由于不确定您的某些字段是用来表示这一点的,因此在某些地方有些猜测。我可以使用上面要求的信息进行清理。如果您的数据库是Postgres,则可以执行以下操作:

@customers = Spree::Order.group(:email).select('count(id) AS total_orders',
 'sum(payment_total) AS amount', 'array_agg(completed_at) AS completion_dates'
).where(state: "complete")

=> [#<Order:0x00007fbc8f0bd2d8 id: nil, email: '[email protected]'>,
   #<Order:0x00007fbc8f0bd148 id: nil, email: '[email protected]'>,
   #<Order:0x00007fbc8f0bcec8 id: nil, email: '[email protected]'>,
   #<Order:0x00007fbc8f0bcd88 id: nil, email: '[email protected]'>...]

注意,这些对象没有:id,因为它们是“虚拟”对象,因为每个对象实际上都是与该电子邮件地址匹配的一组订单。但是它们都有以下方法:

total_orders => the count of completed orders for that email address
amount => sum of all payment_total for the orders belonging to that email
completion_dates => array of completed_on dates for that email's orders

您已经在数据库中完成了所有选择和计算,因此将尽快完成,并且可以显示为:

<% @customers.each do |customer| %>
<tbody id="tbodyid">
  <tr>
    <td><%= customer.email %></td>
    <td><%= customer.total_orders %></td>
    <td><%= customer.completion_dates.sort.last %></td>
    <td>$<%= customer.amount %></td>
  </tr>
</tbody>
<% end %>

customer.completion_dates.sort从最早到最新排序。如果要最新的订单,请使用.last,对于最旧的订单,请使用.first

您可以添加所需的任何字段,只要使用count(zzz) AS xxxsum(zzz) AS xxxmax(...min(...等功能对它们进行汇总即可。或者,如果您希望与该电子邮件,您必须使用array_agg(:some_column) as some_name将其聚合到一个数组中。如果在查询中多次使用任何聚合函数,则必须使用AS xxxx格式,否则将无法访问其他计数,等等。

@some_data = SomeClass.group(:email).select('sum(cost)', 
'count(id)')

@some_data.first.sum
#=> 23.45
@some_data.first.count
#=> 4

@some_data = SomeClass.group(:email).select('sum(cost) AS total_cost', 
'sum(tax) AS total_tax')

@some_data.first.total_cost
#=> 23.45
@some_data.first.total_tax
#=> 3.46
© www.soinside.com 2019 - 2024. All rights reserved.