我正在尝试从我的User模型中链接一些类方法来执行分面搜索。代码运行时,它返回以下错误
undefined method `has_skill_categories' for #<Array:0x000001026d3de8>
您能告诉我如何通过将它们链接在一起从控制器中的模型调用这些方法吗?
这是我的代码:
experts_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
答案 0 :(得分:0)
首先,为了链接你的方法,你应该返回一个ActiveRecord查询对象。在没有参数的情况下调用return
将返回nil,这是不可链接的。您应该返回where()
,这将返回当前集合而不进行任何修改。
您收到上述错误的原因是find_by_sql
returns results as an array,而不是像where
这样的范围查询。所以,正如你现在所做的那样,我认为没有办法将它们联系起来。但这可能是件好事,因为它会强制你在没有原始sql语句的情况下重写你的查询和范围。
我强烈建议您查看Rails Guides on Active Record Querying,并尽可能避免在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。