How to model with inheritance a structure of People?

Asked

Viewed 1,520 times

1

Hi, I’m having a hard time doing my Pessoa modeling. Theoretically it seems a very simple thing, but I can’t find a way to follow, say, "good programming practices". My problem is this:

In my system I have a structure of several people, for example: User, Client, Supplier, Employee...(among others). On top of all these classes I intend to have a Person class where all my "actors" will inherit. A Client actor can also be a User and/or Supplier and/or any other actor (After all, this is what happens in real life).

I intend to have only ONE table for Person, so that when I associate a Client with a sale(Sale), I associate the Person ID.

inserir a descrição da imagem aqui

I would like to have, for example, the following possibilities with my objects:

-> person = Person.new(name: "Jonn")
-> person.name #> "Jonn"
-> person.user #> nil

-> user = User.new(name: "Joe", login: "joe", password: "123456")
-> user.name #> "Joe"
-> user.login #> "joe"
-> user.person.user.login #> "joe"

Someone would give me a hint of how I could do the modeling for the database tables and the coding for the Models?

Thank you so much.

2 answers

1


Well, I’ll post right here what was the solution I found and meets all my needs.

First I created Gem delegate_associations and added to my project.

The next step was to adjust my migrations:

class CreatePeople < ActiveRecord::Migration
  def change
    create_table :people do |t|
      t.string :name
      t.string :cpf_cnpj    
      t.timestamps
    end
  end
end

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users, id: false do |t|
      t.primary_key :person_id
      t.string :login
      t.string :password
      t.timestamps
    end
  end
end

class CreateSellers < ActiveRecord::Migration
  def change
    create_table :sellers, id: false do |t|
      t.primary_key :person_id
      t.decimal :comission, default: 0.0
      t.timestamps
    end
  end
end

Notice that I removed the column id of User and Seller and added as primary key to column person_id. I did this so that when I associate for example a Seller for a sale, I will always have the id of the Person which is the same as Seller.

After that I created 2 Modules:

# app/models/concerns/person_helper.rb
module PersonHelper
    extend ActiveSupport::Concern
    included do
        validates :name, presence: true
    end
end


# app/models/concerns/is_a_person.rb
module IsAPerson
    extend ActiveSupport::Concern
    included do
        include PersonHelper
        belongs_to :person, autosave: true
        delegate_associations to: :person
        delegate_attributes to: :person
        def self.find_by_id(value)
            find_by_person_id(value)
        end
        def person
            super || build_person
        end
    end    
end

My models were as follows:

# app/models/person.rb
class Person < ActiveRecord::Base
    include PersonHelper

    has_one :user
    accepts_nested_attributes_for :user
    has_one :seller
    accepts_nested_attributes_for :seller
end

# app/models/user.rb
class User < ActiveRecord::Base
    include IsAPerson
    validates :login, :password, presence: true
end

# app/models/seller.rb
class Seller < ActiveRecord::Base
    include IsAPerson
end

Now all the validations and methods that a person has I will not put directly into the model of Person and yes in the module PersonHelper so that my User and my Seller may also have the same things.

With this I can have for example these possibilities:

> user = User.new(name: 'Nome da pessoa', login: 'admin', password: '123456')
> user.save # true
> user.person.persisted? # true
> user.seller #nil
> user.build_seller # Irá instanciar um Seller e associar com a Person
> user.seller.comission # 0.0

> user.name = 'Joe'
> user.person.name # Joe

Notice it’s like the User had the attribute name, and every time I get a new name for the user he automatically arrow in person. This happens by the use of the Gem I made where I use the artifices of delegate.

Perhaps this is not the most appropriate option, but it has met all my needs and works perfectly.

1

Uses normal relationships 1-1 (has_one/belongs_to) or 1-n (has_many/belongs_to) as appropriate.

class Person < ActiveRecord::Base
  has_one :user
  has_one :client
  has_one :emplyee
end

class User < ActiveRecord::Base
  belongs_to :person
end

class Client < ActiveRecord::Base
  belongs_to :person
  has_many :sales
end

class Employee < ActiveRecord::Base
  belongs_to :person
end

class Sale < ActiveRecord::Base
  belongs_to :client
end

And then create a new user that way:

person = Person.new(name: "Joe")
person.user = User.new(login: "joe", password: "123456")

If you really need to create the two records at once, you can use accepts_nested_attributes_for in the Person class:

class Person < ActiveRecord::Base
  has_one :user
  accepts_nested_attributes_for :user
end

person = Person.new(name: 'Joe', user_attributes: {login: "joe", password: "123456"})
  • this way that you posted will also work, even I was doing it that way. I posted what was the solution I found to meet my need. Thank you for responding.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.