Tengo los siguientes modelos. Los usuarios tienen UserActions, y una posible UserAction puede ser una ContactAction (UserAction es un polimorfismo). Hay otras acciones como LoginAction etc. AsíRuby on Rails 3: combina los resultados de múltiples asociaciones de has_many o has_many_through
class User < AR::Base has_many :contact_requests, :class_name => "ContactAction" has_many :user_actions has_many_polymorphs :user_actionables, :from => [:contact_actions, ...], :through => :user_actions end class UserAction < AR::Base belongs_to :user belongs_to :user_actionable, :polymorphic => true end class ContactAction < AR::Base belongs_to :user named_scope :pending, ... named_scope :active, ... end
La idea es que un ContactAction se une a dos usuarios (con otras consecuencias dentro de la aplicación) y siempre tiene una recepción y un extremo emisor. Al mismo tiempo, una ContactAction puede tener diferentes estados, p. caducado, pendiente, etc.
Puedo decir @user.contact_actions.pending
o @user.contact_requests.expired
para enumerar todas las solicitudes pendientes/caducadas que un usuario ha enviado o recibido. Esto funciona bien
Lo que ahora me gustaría es una forma de join ambos tipos de ContactAction. Es decir. @user.contact_actions_or_requests
. Probé el siguiente:
class User def contact_actions_or_requests self.contact_actions + self.contact_requests end # or has_many :contact_actions_or_requests, :finder_sql => ..., :counter_sql => ... end
pero todos ellos tienen el problema de que no es posible utilizar buscadores o named_scopes adicionales en la parte superior de la asociación, por ejemplo, @user.contact_actions_or_requests.find(...)
o @user.contact_actions_or_requests.expired
.
Básicamente, necesito una forma de expresar una asociación 1: n que tiene dos caminos diferentes. Uno es User -> ContactAction.user_id
, el otro es User -> UserAction.user_id -> UserAction.user_actionable_id -> ContactAction.id
. Y luego unir los resultados (ContactActions) en una sola lista para su posterior procesamiento con named_scopes y/o buscadores.
Como necesito esta asociación en literalmente docenas de lugares, sería una gran molestia escribir (y mantener) SQL personalizados para cada caso.
Preferiría resolver esto en Rails, pero también estoy abierto a otras sugerencias (por ejemplo, un procedimiento de PostgreSQL 8.3 o algo similar). Lo importante es que, al final, puedo usar las funciones de conveniencia de Rails como cualquier otra asociación y, lo que es más importante, también anidarlas.
Cualquier idea sería muy apreciada.
¡Gracias!
Para proporcionar una especie de respuesta a mi propia pregunta:
probablemente voy a resolver este usando una vista de base de datos y añadir asociaciones apropiadas según sea necesario. Por lo anterior, puedo
- utilizar el SQL en finder_sql para crear la vista,
- el nombre de "contact_actions_or_requests",
- modificar la cláusula SELECT para agregar una columna user_id,
- agregar una aplicación /models/ContactActionsOrRequests.rb,
- y luego agregue "has_many: contact_actions_or_requests" a user.rb.
No sé cómo voy a manejar la actualización de los registros aún - esto parece que no es posible con una vista - pero tal vez este es un primer comienzo.
Actualmente me tener un caos de llamadas find_by_sql esparcidas por la aplicación. Todavía tengo que actualizar esto para usar ARel y especialmente el método #arel_table (vea el comentario a continuación). Cuando haga esto, publicaré mis resultados aquí. – Jens
¿Cómo te enfrentaste a esto al final? –
Todavía uso finder_sql y counter_sql, pero extraje las cadenas de SQL en un método de clase que luego incluyo a través de un proceso. Esto es mucho más limpio que antes, aunque tengo que volver a pensar de nuevo ahora que nos estamos moviendo a Rails 4 y finder_sql está en desuso. – Jens