Rails 3 - лучший способ обработки вложенных запросов ресурсов в ваших контроллерах? - PullRequest
4 голосов
/ 08 сентября 2011

Если я кое-что узнал о Rails 3, то, что если мне трудно что-то делать, я, вероятно, делаю это неправильно.Поэтому я ищу помощи.

У меня есть несколько моделей, которые связаны между собой многими.

Я могу без проблем создавать ассоциации в моделях.Моя проблема заключается в том, как построить контроллеры для работы с этими отношениями.Я попытаюсь привести пример, если вы не видите, куда я иду с этим.

Например ...

class Account < ActiveRecord::Base
    has_many :locations
end

class Contact < ActiveRecord::Base
    has_many :locations
end

class Location < ActiveRecord::Base
    has_and_belongs_to_many :accounts
    has_and_belongs_to_many :contacts
end

Допустим, у меня есть вышеуказанные модели.Это были бы мои ресурсы ...

resources :accounts do
    resources :locations
end

resources :contacts do
    resources :locations
end

resources :locations do
    resources :accounts
    resources :contacts
end

Так что просто для краткости, скажем, я хочу список всех местоположений для учетной записи.Вышеуказанные маршруты будут предположительно иметь учетную запись / 1 / location.Таким образом, посадка меня в местах # index.

Надеюсь, я не облажался с моим примером на данный момент, но каков наилучший способ построить это действие, так как оно действительно имеет несколько заданий ... как минимум в местахдля учетной записи, контакта и всех мест.

Итак, я получаю что-то вроде этого ...

class LocationController < ApplicationController
    def index
        if params[:account_id]
            @locations = Location.find_all_by_account_id(params[:account_id])
        elsif params[:contact_id]
            @locations = Location.find_all_by_contact_id(params[:account_id])
        else
            @locations = Location.all
        end

        respond_with @locations
    end
end

Обновление № 1: Чтобы уточнить, как я получаю некоторые ответы, которыепредложить мне изменить мои отношения модели.Я работаю с устаревшей системой, в которой я НЕ могу изменить отношения на данном этапе.В конечном счете, моя цель - очистить базу данных и отношения, но пока я не могу.Поэтому мне нужно найти решение, которое работает с этой конфигурацией.

Ответы [ 3 ]

6 голосов
/ 08 сентября 2011

Ваш текущий подход не является СУХИМЫМ, и у вас возникнет головная боль, если, например, вы захотите наложить дополнительные области на индекс;например, разбиение на страницы, упорядочивание или поиск по полю.

Рассмотрим альтернативу: Обратите внимание, что ваше условное условие if / elsif / else по сути просто находит область поиска для отправки find?Почему бы не перенести эту ответственность на метод, который делает именно это?Таким образом, упрощая ваши действия и удаляя лишний код.

def index
  respond_with collection
end

def show
  respond_with resource
end

protected

# the collection, note you could apply other scopes here easily and in one place,
# like pagination, search, order, and so on.
def collection
  @locations ||= association.all
  #@locations ||= association.where(:foo => 'bar').paginate(:page => params[:page])
end

# note that show/edit/update would use the same association to find the resource
# rather than the collection
def resource
  @location ||= association.find(params[:id])
end

# if a parent exists grab it's locations association, else simply Location
def association
  parent ? parent.locations : Location
end

# Find and cache the parent based on the id in params. (This could stand a refactor)
#
# Note the use of find versue find_by_id.  This is to ensure a record_not_found
# exception in the case of a bogus id passed, which you would handle by rescuing
# with 404, or whatever.
def parent
  @parent ||= begin
    if id = params[:account_id]
      Account.find(id)
    elsif id = params[:contact_id]
      Contact.find(id)
    end
  end
end

наследованный_ресурс является прекрасным украшением для чистой обработки таких сценариев, как этот.Автор Хосе Валим (из Rails).Я верю , что это должно работать с HABTM, но, честно говоря, я не уверен, что когда-либо пробовал.

Вышеприведенный пример, по сути, показывает, как работает Legited_resources, но в основном это работает волшебноза кулисами, и вы перезаписываете методы только в случае необходимости.Если это работает с HABTM (я думаю, что должно), вы могли бы написать свой текущий контроллер примерно так:

class LocationController < InheritedResources::Base
  belongs_to :contact, :account, :polymorphic => true, :optional => true
end
4 голосов
/ 08 сентября 2011

Вы не должны предоставлять несколько способов достижения одного и того же ресурса.Предполагается, что сопоставление ресурсов с ассоциациями не должно быть однозначным.

Ваш файл маршрутов должен выглядеть следующим образом:

resources :accounts
resources :contacts
resources :locations

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

resources :locations do
    resources :accounts
    resources :contacts
end

Но вам абсолютно не следует указывать как вложенные учетные записи / местоположения, так и маршруты расположения / учетных записей.

0 голосов
/ 08 сентября 2011

На мой взгляд, поскольку учетные записи и контакты, похоже, ведут себя одинаково, имеет смысл использовать наследование одной таблицы (STL) и иметь другой ресурс, например User.

Таким образом, вы можете сделать это ...

class User < ActiveRecord::Base
    has_many :locations
end
class Account < User
end

class Contact < User
end

class Location < ActiveRecord::Base
    has_and_belongs_to_many :user
end

Ресурсы остаются прежними ...

resources :accounts do
    resources :locations
end

resources :contacts do
    resources :locations
end

resources :locations do
    resources :accounts
    resources :contacts
end

Тогда у вас есть один и тот же способ доступа к местоположениям, независимо от типа работы.

class LocationController < ApplicationController
    def index
        if params[:user_id]
            @locations = Location.find_all_by_account_id(params[:user_id])
        else
            @locations = Location.find_all_by_id(params[:account_id])
        end

        respond_with @locations
    end
end

Таким образом, ваш код становится многократно используемым, масштабируемым, обслуживаемым, и все остальное, что нам было сказано, замечательно.

Надеюсь, это поможет!

...