Rails 3 - Лучший способ обработки вложенных запросов ресурсов в ваших контроллерах?
-
27-10-2019 - |
Вопрос
Если есть что -то, что я узнал о 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
Таким образом, ваш код становится многоразовым, масштабируемым, обслуживаемым, и все остальные хорошие вещи, которые нам сказали, отлично.
Надеюсь, поможет!