Полиморфные ассоциации Rails плюс маршруты

У меня есть модель Report, которая является полиморфной. Так много itens на моем сайте может иметь много из этого.

И я хотел бы иметь общий контроллер для его публикации. Это очень простая модель, имеет только текстовое сообщение и ассоциацию.

на моих маршрутах я делаю что-то вроде

map.resources :users, :has_many => [ :reports ]
map.resources :posts, :has_many => [ :reports ]

но в моем report_controller я хотел бы получить связь с тем, откуда он исходит.

как:

before_filter :get_reportable

def get_reportable
   reportable = *reportable_class*.find params[:reportable_id]
end

Это возможно?

как я могу получить reportable_class и reportable_id?

Я могу получить params[:user_id], если он поступает из пользовательского контроллера, или params[:post_id], когда он поступает из сообщений. Я мог бы сделать случай со всеми отношениями, но это совсем не кажется чистым решением...

было бы лучше иметь полиморфную ассоциацию, есть ли как?


person Tiago    schedule 15.01.2010    source источник


Ответы (2)


Если у вас есть один контроллер, который обрабатывает запросы по двум разным путям, вам необходимо сообщить ему контексты, в которых он будет вызываться. Вы часто видите много кода, который выглядит примерно так:

before_filter :load_reportable

def load_reportable
  if (params[:user_id])
    @user = User.find(params[:user_id])
    @reportable = @user
  elsif (params[:post_id])
    @post = Post.find(params[:post_id])
    @reportable = @post
  end
rescue ActiveRecord::RecordNotFound
  render(:partial => 'not_found', :status => :not_found)
  return false
end

Поскольку вы используете полиморфную ассоциацию, вместо этого вы можете сделать что-то вроде этого:

before_filter :load_reportable

def load_reportable
  unless (@reportable = @report.reportable)
    # No parent record found
    render(:partial => 'not_found', :status => :not_found)
    return false
  end

  # Verify that the reportable relationship is expressed properly
  # in the path.

  if (params[:user_id])
    unless (@reportable.to_param == params[:user_id])
      render(:partial => 'user_not_found', :status => :not_found)
      return false
    end
  elsif (params[:post_id])
    unless (@reportable.to_param == params[:post_id])
      render(:partial => 'post_not_found', :status => :not_found)
      return false
    end
  end
end

Проблема с этим подходом, когда у вас есть один контроллер, который обслуживает два совершенно разных маршрута, заключается в том, что он генерирует сообщения об ошибках, такие как «пользователь не найден» по сравнению с «сообщение не найдено». Это может быть сложно сделать правильно, если вы, например, не наследуете от Users::BaseController.

Во многих случаях проще создать два независимых контроллера «отчетов», таких как пользователи/отчеты и сообщения/отчеты, где любая общая функциональность импортируется из модуля. Эти контроллеры обычно наследуются от базового контроллера, который выполняет загрузку и обработку ошибок. Базовый контроллер также может устанавливать макет, заголовок страницы и т. д. без необходимости повторной реализации этой функции для каждого контроллера подресурсов.

Альтернативой является разъединение отчетов и запуск их в качестве собственного контроллера, где связь с «отчетной» записью в основном не имеет значения.

person tadman    schedule 15.01.2010

Или попробуйте так:

before_filter :get_reportable

def get_reportable
  params.each do |name, value|
    if name =~ /(.+)_id$/
      @reportable = $1.classify.constantize.find(value)
    end
  end
end

Он просматривает все параметры и пытается найти один из них, оканчивающийся на _id, затем берет предыдущую часть и находит соответствующую запись.

person misza222    schedule 09.01.2012