web-dev-qa-db-fra.com

concevoir et plusieurs modèles "utilisateur"

J'utilise Rails 3.2 et imagine 2.0 et je suis assez nouveau dans Rails.

Exigences

J'aimerais atteindre les objectifs suivants:

  • avoir 2 modèles "utilisateur" ou plus, par ex. Membre, client, administrateur
  • tous les modèles partagent certains champs obligatoires (par exemple, email et mot de passe)
  • chaque modèle peut avoir des champs uniques (par exemple, société pour le client uniquement)
  • certains champs peuvent être partagés mais n'ont pas la même validation (par exemple, le nom est requis pour le client mais facultatif pour le membre)
  • tous les champs doivent être remplis pendant le processus d'inscription, donc les formulaires sont différents
  • le formulaire de connexion doit être unique

Solutions possibles

J'ai googlé et recherché StackOverflow pendant assez longtemps, mais rien ne me semble juste (je suis un gars Java, désolé :) et maintenant je suis assez confus. Deux solutions sont apparues:

Utilisateur unique de l'appareil

C'est la réponse la plus fréquente. Créez simplement l'utilisateur par défaut et créez des relations entre Membre -> Utilisateur et Client -> Utilisateur. Ma préoccupation ici est de savoir comment puis-je réaliser un processus d'enregistrement personnalisé pour chaque modèle? J'ai essayé différentes choses mais tout s'est terminé comme un gâchis!

Plusieurs utilisateurs d'appareils

Cela résout le processus d'enregistrement personnalisé et me semble juste, mais le formulaire de connexion unique est un bloqueur. J'ai trouvé une réponse sur SO ( Devise - connexion à partir de deux modèles ) qui suggère de remplacer Devise :: Models :: Authenticatable.find_for_authentication (conditions). Cela semble compliqué (?) et comme je suis nouveau sur Rails, j'aimerais savoir si ça pourrait marcher?

Merci pour vos conseils!

73
ddidier

J'ai trouvé un chemin à parcourir et j'en suis assez satisfait jusqu'à présent. Je vais le décrire ici pour les autres.

Je suis allé avec la seule classe "utilisateur". Mon problème était de réaliser un processus d'enregistrement personnalisé pour chaque pseudo modèle.

model/user.rb:

class User < ActiveRecord::Base
  devise :confirmable,
       :database_authenticatable,
       :lockable,
       :recoverable,
       :registerable,
       :rememberable,
       :timeoutable,
       :trackable,
       :validatable

  # Setup accessible (or protected) attributes for your model
  attr_accessible :email, :password, :password_confirmation, :remember_me, :role

  as_enum :role, [:administrator, :client, :member]
  validates_as_enum :role
  ## Rails 4+ for the above two lines
  # enum role: [:administrator, :client, :member]

end

Ensuite, j'ai adapté http://railscasts.com/episodes/217-multistep-forms et http://pastie.org/1084054 pour avoir deux chemins d'enregistrement avec un surchargé manette:

config/routes.rb:

get  'users/sign_up'   => 'users/registrations#new',        :as => 'new_user_registration'

get  'clients/sign_up' => 'users/registrations#new_client', :as => 'new_client_registration'
post 'clients/sign_up' => 'users/registrations#create',     :as => 'client_registration'

get  'members/sign_up' => 'users/registrations#new_member', :as => 'new_member_registration'
post 'members/sign_up' => 'users/registrations#create',     :as => 'member_registration'

contrôleurs/utilisateurs/registrations_controller.rb:

J'ai créé une classe assistant qui connaît les champs à valider à chaque étape

class Users::RegistrationsController < Devise::RegistrationsController

    # GET /resource/sign_up
    def new
        session[:user] ||= { }
        @user = build_resource(session[:user])
        @wizard = ClientRegistrationWizard.new(current_step)

        respond_with @user
    end

    # GET /clients/sign_up
    def new_client
        session[:user] ||= { }
        session[:user]['role'] = :client
        @user = build_resource(session[:user])
        @wizard = ClientRegistrationWizard.new(current_step)

        render 'new_client'
    end

    # GET /members/sign_up
    def new_member
      # same
    end

    # POST /clients/sign_up
    # POST /members/sign_up
    def create
        session[:user].deep_merge!(params[:user]) if params[:user]
        @user = build_resource(session[:user])
        @wizard = ClientRegistrationWizard.new(current_step)

        if params[:previous_button]
            @wizard.previous
        elsif @user.valid?(@wizard)
            if @wizard.last_step?
                @user.save if @user.valid?
            else
                @wizard.next
            end
        end

        session[:registration_current_step] = @wizard.current_step

        if @user.new_record?
            clean_up_passwords @user
            render 'new_client'
        else
            #session[:registration_current_step] = nil
            session[:user_params] = nil

            if @user.active_for_authentication?
                set_flash_message :notice, :signed_up if is_navigational_format?
                sign_in(:user, @user)
                respond_with @user, :location => after_sign_up_path_for(@user)
            else
                set_flash_message :notice, :"signed_up_but_#{@user.inactive_message}" if is_navigational_format?
                expire_session_data_after_sign_in!
                respond_with @user, :location => after_inactive_sign_up_path_for(@user)
            end
        end

    end

    private

    def current_step
        if params[:wizard] && params[:wizard][:current_step]
            return params[:wizard][:current_step]
        end
        return session[:registration_current_step]
    end

end

et mes vues sont:

  • new.rb
  • new_client.rb y compris un partiel selon l'étape de l'assistant:
    • _new_client_1.rb
    • _new_client_2.rb
  • new_member.rb y compris un partiel selon l'étape de l'assistant:
    • _new_member_1.rb
    • _new_member_2.rb
18
ddidier

Bienvenue à bord Java guy =), j'espère que vous apprécierez le monde Rails. Simplement, pour résoudre votre problème, vous avez 2 solutions:

  1. Pour chaque utilisateur, créez une table dans la base de données et le modèle correspondant.
  2. Créez une table unique dans la base de données et pour chaque type d'utilisateur créez un modèle. C'est ce qu'on appelle l'héritage de table unique (STI).

Lequel choisir? Cela dépend des attributs communs des rôles. S'ils sont presque courants (par exemple tous ont un nom, un email, un mobile, ...) et que quelques attributs sont différents, je recommande fortement la solution STI.

Comment faire l'IST? 1. Créez simplement le modèle utilisateur et la table du modèle à l'aide de la commande Rails generate devise User 2. Ajoutez une colonne nommée type avec le type de données chaîne à la table utilisateur de la base de données à l'aide d'une migration. 3. Pour chaque type d'utilisateur, créez un modèle (par exemple Rails g model admin) 4. Faites en sorte que la classe Admin hérite du modèle d'utilisateur

class Admin < User
end

Voilà c'est fait =) ... Yupeee

Pour créer un administrateur, exécutez la commande Admin.create(...) où les points correspondent aux attributs administrateur, par exemple l'e-mail, le nom, ...

Je pense que cela question pourrait aussi vous aider

66
mohamagdy

Je suis dans la même situation que vous, après avoir essayé toutes sortes d'approches, je suis allé avec un seul modèle utilisateur, qui appartiendrait à des rôles polymorphes. Cela semble être le moyen le plus simple de réaliser une connexion unique.

Le modèle utilisateur contiendrait les informations spécifiques à la connexion uniquement.

Le modèle de rôle stockerait des champs spécifiques à chaque rôle, ainsi que d'autres associations spécifiques au rôle.

Les nouvelles inscriptions seraient personnalisées pour chaque type d'utilisateur (rôles) via des contrôleurs individuels, puis créer des attributs imbriqués pour l'utilisateur.

class User < ActiveRecord::Base
    #... devise code ...
    belongs_to :role, :polymorphic => true
end

class Member < ActiveRecord::Base
    attr_accessible :name, :tel, :city  #etc etc....
    attr_accessible :user_attributes #this is needed for nested attributes assignment

    #model specific associations like  
    has_many :resumes

    has_one :user, :as => :role, dependent: :destroy
    accepts_nested_attributes_for :user
end 

Routes - juste des trucs réguliers pour le modèle Member.

resources :members
#maybe make a new path for New signups, but for now its new_member_path

Controller - vous devez build_user pour les attributs imbriqués

#controllers/members_controller.rb
def new
    @member = Member.new
    @member.build_user
end

def create
    #... standard controller stuff
end

vues/membres/new.html.erb

<h2>Sign up for new members!</h2>
<%= simple_form_for @member do |f| %>

    # user fields
    <%= f.fields_for :user do |u| %>
      <%= u.input :email, :required => true, :autofocus => true %>
      <%= u.input :password, :required => true %>
      <%= u.input :password_confirmation, :required => true %>
    <% end %>

    # member fields
    <%= f.input :name %>
    <%= f.input :tel %>
    <%= f.input :city %>

    <%= f.button :submit, "Sign up" %>
<% end %>

Je voudrais souligner qu'il n'y a AUCUN BESOIN à atteindre pour la gemme nested_form; car l'exigence est que l'utilisateur ne puisse appartenir qu'à un seul type de rôle.

26
tw airball

Alors, qu'est-ce qui ne va pas? Exécutez simplement Rails g devise:views [model_name], personnalisez chaque formulaire d'inscription et dans config/initializer/devise.rb il suffit de mettre config.scoped_views = true.

6
Hauleth