Rails 3 - Лучший способ обработки вложенных запросов ресурсов в ваших контроллерах?

StackOverflow https://stackoverflow.com/questions/7342470

Вопрос

Если есть что -то, что я узнал о 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/местоположения. Таким образом, приземляя меня в локациях#Индекс.

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

Так что я заканчиваю чем -то подобным ...

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

Это было полезно?

Решение

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

Рассмотрим альтернативу: обратите внимание, как ваш if/elsif/else Conditional, по сути, просто находит возможность поиска для отправки 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

Унаследованные_resources это отличный жемчужина для сценариев с чистой обработкой. Написано Хосе Валимом (из рельсов). я полагать Это должно работать с Habtm, но, честно говоря, я не позитивен, если я когда -либо попробовал это.

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

class LocationController < InheritedResources::Base
  belongs_to :contact, :account, :polymorphic => true, :optional => true
end

Другие советы

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

Ваш файл маршрутов должен выглядеть так:

resources :accounts
resources :contacts
resources :locations

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

resources :locations do
    resources :accounts
    resources :contacts
end

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top