Рельсы Полиморфные Ассоциации плюс Маршруты - PullRequest
3 голосов
/ 15 января 2010

У меня есть модель, отчет, который является полиморфным. У многих сайтов на моем сайте их может быть много.

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

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

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], когда он приходит из сообщений. Я мог бы заняться делом со всеми отношениями, но это совсем не кажется чистым решением ...

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

Ответы [ 2 ]

4 голосов
/ 15 января 2010

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

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.

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

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

0 голосов
/ 09 января 2012

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

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, затем захватывает его перед частью и находит соответствующую запись.

...