簡體   English   中英

嘗試在Ruby on Rails的控制器中鏈接類方法時出錯

[英]Error when trying to chain class method in controller in Ruby on Rails

我試圖從我的用戶模型中鏈接一些類方法以執行多面搜索。 代碼運行時返回以下錯誤

undefined method `has_skill_categories' for #<Array:0x000001026d3de8>

您能告訴我如何將它們鏈接在一起從控制器的模型中調用這些方法嗎?

這是我的代碼:

expert_controller.erb

class ExpertsController < ApplicationController
  layout 'experts'

  def index

    @users = User.text_search(params[:query])
              .has_marketing_assets(params[:marketing_platforms])
              .has_skill_categories(params[:skills])
              .search_for_user_country(params[:user][:country])
  end

  def show
    @user = User.find(params[:id])
  end
end

user.erb

class User < ActiveRecord::Base

  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

  has_many :marketing_assets
  has_many :marketing_platforms, through: :marketing_assets
  has_many :my_skills
  has_many :skills, through: :my_skills
  has_many :past_works
  has_many :past_work_types, through: :past_works

  validates :first_name, :last_name, presence: true

  include PgSearch
  pg_search_scope :search, against: [:first_name, :last_name, :company, :description, :job_title, :website, :email, :country, :city, :state],
                  using: {tsearch: {dictionary: 'english'}},
                  associated_against: {:skills => :name, :past_works => [:description, :title, :url], :marketing_assets => [:platform, :description, :url], :past_work_types => :name,
                                       :marketing_platforms => :name}

  def self.text_search(query)
    if query.present?
      search(query)
    else
      User.all
    end
  end


  def self.has_marketing_assets(platforms)
    if platforms.present?
      @platforms = MarketingPlatform.all
      platforms_count = platforms.count
      where_clause_platforms = 'SELECT *
                                FROM Users
                                WHERE Users.id IN
                                (SELECT Users.id
                                FROM users
                                INNER JOIN marketing_assets ON users.id = marketing_assets.user_id
                                WHERE marketing_assets.marketing_platform_id= '
      n = 0

      if platforms.count > 0

        platforms.each do |platform|
          n += 1
          where_clause_platforms = where_clause_platforms + platform
          if n < platforms_count
            where_clause_platforms = where_clause_platforms + ' OR marketing_assets.marketing_platform_id= '
          end
        end

        where_clause_platforms = where_clause_platforms + " GROUP BY users.id
                                                          HAVING COUNT(DISTINCT marketing_assets.marketing_platform_id) = #{platforms.count})"
        find_by_sql(where_clause_platforms)

      else
        return
      end
    end
  end


  def self.has_skill_categories(skills)
    if skills.present?

      skills_count = skills.count
      where_clause_skills = 'SELECT *
                                      FROM Users
                                      WHERE Users.id IN
                                      (SELECT Users.id
                                      FROM users
                                      INNER JOIN my_skills ON users.id = my_skills.user_id
                                      WHERE my_skills.skill_id= '
      n = 0

      if skills_count > 0

        skills.each do |skill|
          n += 1
          where_clause_skills = where_clause_skills + skill
          if n < skills_count
            where_clause_skills = where_clause_skills + ' OR my_skills.skill_id= '
          end
        end

        where_clause_skills = where_clause_skills + "GROUP BY users.id
                                                        HAVING COUNT(DISTINCT my_skills.skill_id) = #{skills.count})"
        find_by_sql(where_clause_skills)


      else
        return
      end
    end
  end


  def self.search_for_user_country(country)
    if country.present?
      where('country = ?', "#{country}")
    else
      return
    end
  end

end

首先,為了鏈接您的方法,您應該返回一個ActiveRecord查詢對象。 不帶參數調用return將返回nil,這是不可鏈接的。 相反where()您應該返回where() ,它將不做任何修改就返回當前集合。

出現上述錯誤的原因是因為find_by_sql 以數組形式返回結果 ,而不是像where那樣有范圍的查詢。 因此,正如您現在所做的那樣,我認為沒有辦法將它們鏈接起來。 但這可能是一件好事,因為它將迫使您在沒有原始sql語句的情況下重寫查詢和范圍。

我強烈建議您閱讀Active Record Querys上Rails指南 ,如果可能的話,最好避免在Rails項目中編寫原始SQL語句。 這可以大大簡化您的方法。 永遠不應將原始用戶輸入放入SQL查詢中,這看起來就像您在代碼中的多個位置所做的一樣。 Rails提供了一個高級查詢界面來保護您和您的數據,並且您在上面構建的SQL語句極易受到注入攻擊的攻擊。

使用scope和關聯調用的正確組合(可以使用在關聯模型上定義的范圍),您可能可以清理掉很多代碼,並大大提高應用程序的安全性。

更新資料

在我看來,使用范圍和#merge可以大大簡化您的查詢。

def self.has_skill_categories(skill_ids)
  joins(:my_skills).merge Skill.where(id: skill_ids)
end

def self.has_marketing_assets(platform_ids)
  joins(:marketing_assets).merge MarketingAsset.where(marketing_platform_id: platform_ids)
end

這些可能無法完全滿足您的需求,但是據我所知,它應該很接近,並向您展示如何使用內置的ActiveRecord查詢接口來構建復雜的查詢,而無需編寫任何原始SQL。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM