重构!如何在数据库查询中重新使用变量来检索 Rails 控制器中的数据

Refactoring! How to Re-Use a Variable in Database Query to retrieve data in Rails controller

我正在尝试重构我的railsAPI中的代码,它发送图表的分析数据(它目前正在工作)。因为我在 1. 书签,2. 期刊,3. 目标上执行了相同的代码,所以它不是很枯燥。

我正在尝试创建 1 个可以接收类型(书签、目标、日志)作为参数的数据库查询(使用 PostgreSQL)...我尝试使用 [:type] 和硬编码“类型”,其中书签曾经去例如...

我要重构的现有(工作)代码:

class AnalyticsController < ApplicationController
  before_action :authenticate_user

  def entries_by_date
    bookmarks = current_user.bookmarks.group_by_day(
      :created_at,
      format: '%Y-%m-%d',
      range: 4.weeks.ago.midnight..Time.zone.now
    ).count
    journals = current_user.journals.group_by_day(
      :created_at,
      format: '%Y-%m-%d',
      range: 4.weeks.ago.midnight..Time.zone.now
    ).count
    goals = current_user.goals.group_by_day(
      :created_at,
      format: '%Y-%m-%d',
      range: 4.weeks.ago.midnight..Time.zone.now
    ).count

    entries_array = []
    entries_array.push(@bookmarks)
    entries_array.push(goals)
    entries_array.push(journals)

    total_entries_by_date = entries_array.inject { |memo, el| memo.merge(el) { |_k, old_v, new_v| old_v + new_v } }

    render json: {
      total_entries_by_date: total_entries_by_date,
      bookmarks: bookmarks,
      goals: goals,
      journals: journals
    }
  end
end

我试过这个:

.... 

  def entries_by_date
    def fetch_activity(type)
      current_user[:type].group_by_day(
        :created_at,
        format: '%Y-%m-%d',
        range: 4.weeks.ago.midnight..Time.zone.now
      ).count
    end

    @bookmarks = fetch_activity(bookmarks)
    @goals = fetch_activity(goals)
    @journals = fetch_activity(journals) 


... (cont'd)

我试过current_user.type... >> 未定义的局部变量或方法`bookmarks

我试过 current_user.[:type]... >> "语法错误,未知 ["

我试过current_user[:type]... >> NameError(未定义的局部变量或方法`bookmarks'

我试过current_user.%{type}... & current_user.#{type}... & current_user.${type}

任何指导将不胜感激 :) 我是 Ruby on Rails 世界的新手,期待在我更好地理解重新使用传递给数据库查询的变量

您已经非常接近解决方案了。只需使用 public_send 并将要调用的方法的名称作为字符串或符号传递。

def entries_by_date
  @bookmarks = fetch_activity(:bookmarks)
  @goals = fetch_activity(:goals)
  @journals = fetch_activity(:journals) 

  # ...
end

private

def fetch_activity(type)
  current_user.public_send(type).group_by_day(
    :created_at,
    format: '%Y-%m-%d',
    range: 4.weeks.ago.midnight..Time.zone.now
  ).count
end

@bookmarks = fetch_activity(bookmarks)
@goals = fetch_activity(goals)
@journals = fetch_activity(journals) 

为了改进 Spickerman 的回答 - 您应该考虑从控制器中提取业务逻辑并将其放置在它所属的模型中:

class User < ApplicationRecord
  # Gets a single activity 
  def fetch_activity(type)
    public_send(type).group_by_day(
      :created_at,
      format: '%Y-%m-%d',
      range: 4.weeks.ago.midnight..Time.zone.now
    ).count
  end

  # Gets a hash containing the counts of the users activities and total 
  def fetch_activities(*types)
    types.each_with_object({}) do |key, hash|
      hash[key] = fetch_activity(key)
    end.then do |hash|
      hash.merge(total_entries_by_date: hash.values.sum)
    end
  end
  # ... 
end

模型比控制器更容易测试,因为您只需设置它并直接在对象上调用方法,而不是通过 HTTP 并解析响应 - 这是您的控制器应该如此瘦的关键原因尽可能:

class AnalyticsController < ApplicationController
  before_action :authenticate_user

  def entries_by_date
    render json: current_user.fetch_activities(:bookmarks, :goals, :journals)
  end
end