5 votes

Commandes de produits entre 2 utilisateurs

J'ai trois modèles : Utilisateur, Produit, Offre et un problème de relation entre ces modèles.

Scénario :

L'utilisateur 1 publie un produit

L'utilisateur 2 peut envoyer à l'utilisateur 1 une offre avec un prix, par exemple 10 $.

L'utilisateur 1 peut accepter ou refuser l'offre

Mes questions sont maintenant :

Quelle est la bonne relation entre l'utilisateur, le produit et l'offre ?

Comment puis-je gérer ces actions "accepter ou rejeter" ?

N'y a-t-il pas une meilleure solution ?

Modèle d'utilisateur :

class User < ActiveRecord::Base
    attr_accessible :name, :email, :password, :password_confirmation, :remember_me, :avatar, :screen_name
    has_many :products
    has_many :offers,:through => :products
end

Modèle de produit :

class Product < ActiveRecord::Base
    attr_accessible :content, :price, :title, :tag_list, :productimage, :user_id
    belongs_to :user
    has_many :offers, :through => :users
end

Modèle d'offre :

class Offer < ActiveRecord::Base
    attr_accessible :offer_price, :status, :user_id, :product_id
    has_many :products
    has_many :users, through: :products
end

Merci d'avance :)

EDIT :

J'utilise Rails 3.2.8.

7voto

Andrew Haines Points 6404

Attention : voici un petit roman.

Partie 1 : mise en place des associations

Je vous recommande de lire le Guide Rails sur les associations Lisez-le attentivement, mettez-le dans vos favoris et relisez-le encore, car il est essentiel de bien le comprendre et cela peut s'avérer un peu délicat - il existe de nombreuses options dès que l'on dépasse les associations de base.

Une chose à noter à propos de votre application est que vos utilisateurs ont deux rôles, les acheteurs et les vendeurs. Vous allez devoir faire attention aux noms de vos associations - Est-ce que @user.offers retourner les offres que l'utilisateur a sur ou les offres que l'utilisateur a recibido ? Vous pourriez vouloir être en mesure de mettre des listes de ces deux choses dans le profil de l'utilisateur.

Les relations de base que vous décrivez sont assez simples :

  • Un utilisateur peut vendre plusieurs produits, donc User has_many :products y Product belongs_to :user

  • Un utilisateur peut faire plusieurs offres, donc User has_many :offers y Offer belongs_to :user

  • Un produit peut recevoir de nombreuses offres de sorte que Product has_many :offers y Offer belongs_to :product

Tout cela est très bien, et vous pourriez certainement vous en sortir en ne faisant que cela - dans ce cas, vous pouvez passer directement à la partie 2 :)

Cependant, dès que l'on commence à essayer d'ajouter la through relations les eaux vont devenir boueuses. Après tout,

  • Offer belongs_to :user (l'acheteur), mais il a aussi un utilisateur par le biais du produit (le vendeur)

  • User has_many :products (qu'ils vendent), mais ils ont également de nombreux produits à travers des offres (qu'ils achètent - enfin, qu'ils essaient d'acheter).

Aargh, déroutant !

C'est le moment où vous avez besoin de la :class_name qui vous permet de nommer une association différemment de la classe à laquelle elle se réfère, et l'option :source qui vous permet de nommer les associations sur le modèle "from" différemment du modèle "through".

Vous pourriez donc former vos associations comme ceci :

# User
has_many :products_selling, class_name: 'Product'
has_many :offers_received, class_name: 'Offer',
         through: :products_selling, source: :offers

has_many :offers_made, class_name: 'Offer'
has_many :products_buying, class_name: 'Product',
         through: :offers_made, source: :product

# Product
belongs_to :seller, class_name: 'User', foreign_key: :user_id
has_many :offers
has_many :buyers, class_name: 'User', through: :offers

# Offer
belongs_to :product
belongs_to :buyer, class_name: 'User', foreign_key: :user_id
has_one :seller, class_name: 'User', through: :product

Bien que si vous avez renommé votre user_id colonnes pour seller_id dans le products table, et buyer_id dans le offers table, vous n'auriez pas besoin de ces :foreign_key options.

Partie 2 : accepter/rejeter les offres

Il y a plusieurs façons de s'attaquer à ce problème. Je mettrais un champ booléen accepted en Offer et alors vous pourriez avoir quelque chose comme

# Offer
def accept
  self.accepted = true
  save
end

def reject
  self.accepted = false
  save
end

et vous pourriez trouver les offres exceptionnelles (où accepted est nulle)

scope :outstanding, where(accepted: nil)

Pour que la logique d'acceptation/de rejet se produise dans le contrôleur, vous pouvez envisager de ajout de nouvelles actions RESTful (le guide en lien est un autre guide qui mérite d'être lu attentivement). Vous devriez trouver une ligne comme

resources :offers

dans config/routes.rb, qui fournit les actions standards index , show , edit etc. Vous pouvez le changer en

resources :offers do
  member do
    post :accept
    post :reject
  end
end

et mettez quelque chose comme ceci dans votre OffersController

def accept
  offer = current_user.offers_received.find(params[:id])
  offer.accept
end

# similarly for reject

Ensuite, vous pouvez envoyer une requête POST à offers/3/accept et il fera en sorte que l'offre avec l'id 3 soit acceptée. Quelque chose comme ça dans une vue devrait faire l'affaire :

link_to "Accept this offer", accept_offer_path(@offer), method: :post 

Notez que je n'ai pas seulement écrit Offer.find(params[:id]) car un utilisateur astucieux pourrait alors accepter des offres au nom du vendeur. Voir Meilleures pratiques Rails .

4voto

Deepak Prasanna Points 545

Vos modèles sont assez bons, sauf pour les relations. La confusion commence lorsque vous essayez de différencier les produits possédés des produits intéressés (offerts) et le propriétaire du produit des utilisateurs intéressés (utilisateurs qui ont placé l'offre). Si vous pouvez trouver une meilleure convention de dénomination, vous pouvez facilement résoudre ce problème.

1. De meilleures relations

class User < ActiveRecord::Base
  attr_accessible :name, :email, :password, :password_confirmation, :remember_me, :avatar, :screen_name
  has_many :owned_products, :class_name => "Product"
  has_many :offers 
  has_many :interested_products, :through => :offers
end

class Offer < ActiveRecord::Base
  attr_accessible :offer_price, :status, :user_id, :product_id
  belongs_to :interested_user, :class_name => "User", :foreign_key => :user_id
  belongs_to :interested_product, :class_name => "Product", :foreign_key => :product_id
end

class Product < ActiveRecord::Base
  attr_accessible :content, :price, :title, :tag_list, :productimage, :user_id
  belongs_to :owner, :foreign_key => :user_id, :class_name => "User"
  has_many :offers 
  has_many :interested_users, :through => :offers
end

Avec ces relations, je pense que vous pouvez obtenir toutes les informations de base qui vous intéressent. Par exemple,

@product = Product.find(1)
@product.owner # would give you the user who created the product
@product.interested_users # would give you users who placed an offer for this product

@user = User.find(1)
@user.owned_products # would give you the products created by this user
@user.interested_products # would give you the products where the user placed an offer

2. Gérer les actions d'acceptation et de rejet.

D'après votre description, je vois qu'il peut y avoir 2 changements d'état possibles pour une offre, "créé" -> "accepté" ou "créé" -> "rejeté". Je vous suggère de consulter machine d'état . La machine d'état ajoutera une saveur agréable à votre modèle avec ses méthodes d'aide, ce qui, je pense, sera très utile dans votre cas. Ainsi, votre Offer ressemblera à quelque chose comme ceci,

class Offer < ActiveRecord::Base
  # attr_accessible :title, :body
  attr_accessible :offer_price, :status, :user_id, :product_id
  belongs_to :interested_user, :class_name => "User", :foreign_key => :user_id
  belongs_to :interested_product, :class_name => "Product", :foreign_key => :product_id

  state_machine :status, :initial => :created do
    event :accept do
      transition :created => :accepted
    end
    event :reject do
      transition :created => :reject
    end
  end
end

#cool helper methods
@offer = Offer.new
@offer.accepted? #returns false
@offer.reject #rejects the offer
@offer.rejected? #returns true

J'espère que cela vous donne une meilleure idée.

2voto

claasz Points 537

Et si

class User < ActiveRecord::Base
  has_many :products  # All products posted by this user
  has_many :offers    # All offers created by this user
end

class Product < ActiveRecord::Base
  belongs_to :user   # This is the user who posts the product (User 1)
  has_many :offers
end

class Offer < ActiveRecord::Base
  belongs_to :product
  belongs_to :user   # This is the user who creates the offer (User 2)

  # Use a 'state' field with values 'nil', 'accepted', 'rejected' 
end 

Pour votre scénario :

# User 1 posts a product
product = user1.products.create

# User 2 can send User 1 an offer with an price e.g $ 10
offer = user2.offers.create(:product => product)

# User 1 can accept or reject the offer
offer.state = 'rejected'

Vous pouvez l'affiner en fonction de vos besoins - par exemple, si le même produit peut être affiché par différents utilisateurs.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X