In my Invoicing app invoices
can be sent to either a company
or a person
. As I understand it, this is a good use case for Rails' single table inheritance (STI). Since both types share a number of attributes and functions, I figured that a super-class Recipient
might be a good way to go:
class Recipient < ActiveRecord::Base
end
class Company < Recipient
has_many :people
end
class Person < Recipient
belongs_to :company
end
I also understand that I need an attribute type
in the Recipient
model.
The only thing that bothers me is the fact that a person
may (or may not) belong to a company
. How can this be modelled in Rails? Normally, I'd simply add another database field company_id
to the people
table. But there's only one table (recipients
) here. So how can this be done?
Thanks for any help.
A structure could look like this:
class Recipient < ActiveRecord::Base
has_many :invoices
end
class Company < Recipient
has_many :people
end
class Person < Recipient
belongs_to :company
end
class Invoice < ActiveRecord::Base
belongs_to :recipients
end
# Schema
create_table "invoices", force: :cascade do |t|
t.integer "recipient_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["recipient_id"], name: "index_invoices_on_recipient_id"
end
create_table "recipients", force: :cascade do |t|
t.integer "company_id"
t.string "type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
I've just tried in the console:
> Recipient.all
=> [#<Company:0x007fd55d797220
id: 1,
company_id: nil,
type: "Company",
created_at: Fri, 04 Aug 2017 10:57:41 UTC +00:00,
updated_at: Fri, 04 Aug 2017 10:57:41 UTC +00:00>,
#<Person:0x007fd55d796730
id: 2,
company_id: 1,
type: "Person",
created_at: Fri, 04 Aug 2017 10:57:41 UTC +00:00,
updated_at: Fri, 04 Aug 2017 10:57:41 UTC +00:00>,
#<Person:0x007fd55d796208
id: 3,
company_id: nil,
type: "Person",
created_at: Fri, 04 Aug 2017 10:57:41 UTC +00:00,
updated_at: Fri, 04 Aug 2017 10:57:41 UTC +00:00>]
> Person.last.company
Person Load (0.2ms) SELECT "recipients".* FROM "recipients" WHERE "recipients"."type" IN ('Person') ORDER BY "recipients"."id" DESC LIMIT ? [["LIMIT", 1]]
=> nil
> Person.first.company
Person Load (0.2ms) SELECT "recipients".* FROM "recipients" WHERE "recipients"."type" IN ('Person') ORDER BY "recipients"."id" ASC LIMIT ? [["LIMIT", 1]]
Company Load (0.2ms) SELECT "recipients".* FROM "recipients" WHERE "recipients"."type" IN ('Company') AND "recipients"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> #<Company id: 1, company_id: nil, type: "Company", created_at: "2017-08-04 10:57:41", updated_at: "2017-08-04 10:57:41">