J'ai un projet Rails 3. Avec Rails 3 est venu Arel et la possibilité de réutiliser une portée pour en construire une autre. Je me demande s'il existe un moyen de utilisez des étendues lors de la définition d'une relation (par exemple, "has_many").
J'ai des enregistrements qui ont des colonnes d'autorisation. Je voudrais construire un default_scope qui prend en compte mes colonnes d'autorisation afin que les enregistrements (même ceux accessibles via une relation) soient filtrés.
Actuellement, dans Rails 3, default_scope (y compris les correctifs que j'ai trouvés) ne fournit pas de moyen pratique de passer un proc (dont j'ai besoin pour la liaison de variable tardive). Est-il possible de définir un has_many dans lequel une portée nommée peut être passée?
L'idée de réutiliser une portée nommée ressemblerait à:
Orders.scope :my_orders, lambda{where(:user_id => User.current_user.id)}
has_many :orders, :scope => Orders.my_orders
Ou coder implicitement cette étendue nommée dans la relation ressemblerait à:
has_many :orders, :scope => lambda{where(:user_id => User.current_user.id)}
J'essaie simplement d'appliquer default_scope avec une liaison tardive. Je préférerais utiliser une approche Arel (s'il y en a une), mais utiliserais n'importe quelle option réalisable.
Puisque je fais référence à l'utilisateur actuel, je ne peux pas compter sur des conditions qui ne sont pas évaluées au dernier moment possible, telles que:
has_many :orders, :conditions => ["user_id = ?", User.current_user.id]
Je vous suggère de jeter un œil à "Les étendues nommées sont mortes"
L'auteur y explique à quel point Arel est puissant :)
J'espère que ça vous aidera.
Comme certains commentaires l'indiquent, la différence est désormais une question de goût personnel.
Cependant, je recommande toujours personnellement d'éviter d'exposer la portée d'Arel à une couche supérieure (être un contrôleur ou tout autre élément qui accède directement aux modèles), et cela nécessiterait:
Et les extensions d'association?
class Item < ActiveRecord::Base
has_many :orders do
def for_user(user_id)
where(user_id: user_id)
end
end
end
Item.first.orders.for_user(current_user)
MISE À JOUR: Je voudrais souligner l'avantage des extensions d'association par rapport aux méthodes de classe ou aux étendues, c'est que vous avez accès aux internes du proxy d'association:
proxy_association.owner renvoie l'objet dont l'association fait partie. proxy_association.reflection renvoie l'objet de réflexion qui décrit l'association. proxy_association.target renvoie l'objet associé pour appartient_à ou has_one, ou la collection d'objets associés pour has_many ou has_and_belongs_to_many.
Plus de détails ici: http://guides.rubyonrails.org/association_basics.html#association-extensions
Au lieu d'étendues, je viens de définir des méthodes de classe, ce qui fonctionne très bien
def self.age0 do
where("blah")
end
J'utilise quelque chose comme:
class Invoice < ActiveRecord::Base
scope :aged_0, lambda{ where("created_at IS NULL OR created_at < ?", Date.today + 30.days).joins(:owner) }
end
Vous pouvez utiliser la méthode merge afin de fusionner les portées de différents modèles. Pour plus de détails, recherchez la fusion dans ce railscast
Si vous essayez simplement d'obtenir les commandes de l'utilisateur, pourquoi n'utilisez-vous pas simplement la relation?
En supposant que l'utilisateur actuel est accessible à partir de la méthode current_user dans votre contrôleur:
@my_orders = current_user.orders
Cela garantit que seules les commandes spécifiques d'un utilisateur seront affichées. Vous pouvez également effectuer des jointures imbriquées arbitrairement pour obtenir des ressources plus approfondies en utilisant joins
current_user.orders.joins(:level1 => { :level2 => :level3 }).where('level3s.id' => X)