I am am developing a recruitment application for storing records of people who come for an interview. So I have two models - Applicant & Employee which seem like one in OO sense. i.e. Employee was earlier an applicant. Therefore I am planning to create a Single table inheritance like:
Applicant < Person
Employee < Person
All the fields of Applicant are there in Employee. Employee has several other fields that are not in Applicant.
Am I correct with this plan. I have another similar scenario to deal with - prospects & clients.
Update: As I am working more and more with STI, I am starting to dislike it.
As per my personal opinion , in OO point of view you are correct.
Applicant < Person
Employee < Person
But when it comes to database/ tables i will go for a one table called 'persons' (Person) that has all the columns for both 'Applicant' and 'Employee' and have a column with a flag indicating whether the Person is an Applicant or Employee
NOTE :: but this is only true if you have ONLY 'several other fields' not many
HTH
sameera
Related
I'm trying to think in a business model very similar to the one described here, using STI.
class Person < ActiveRecord::Base
# identified by email
end
class Owner < Person
end
class Customer < Person
end
class Employee < Person
end
class Store < ActiveRecord::Base
belongs_to :owner
has_many :customers
has_many :employees
end
The classes above describe what I intend to do. The problem here is that a Employee can never act as a Customer, and hire the services provided by the store he works, or even another store, unless a new record is created to represent the same person acting as the a different role in a different context. That is not very DRY, but I don't know if there is a better solution.
Is there? Anyone has any suggestion on how I could resolve this issue?
Thank you very much.
Being an owner (note that there may be several for a given store and one person may own several stores) is not part of a person's identity, it is a relationship between a person and store so subclassing isn't really appropriate here. Similarly for being a customer or employee.
This leaves us with five components:
People.
Stores.
The "person owns a store" relationship.
The "person is a customer of a store" relationship.
The "person is an employee of a store" relationship.
All three relationships are, realistically, many-to-many. Also note that there's STI anywhere in sight; this is a good thing, STI is almost always (IMO) a mistake so you should start questioning your data model and your judgement as soon as it shows up. STI does have its place of course but you should think hard to justify it whenever it comes up.
This leaves us with two fairly simple models (Person and Store) and three many-to-many relationships between people and stores. The standard ways of modelling many-to-many relationships with ActiveRecord are has_many ... :through and has_and_belongs_to_many. If you need to work with one of the person-store relationships as a separate entity (such as an employee with an employee number, hourly rate, tax records, ...) then you'd probably want has_many :through; if you only need the association then has_and_belongs_to_many would probably work.
Some references:
The has_many :through Association
The has_and_belongs_to_many Association
Choosing Between has_many :through and has_and_belongs_to_many
Actually, it is DRY from a code perspective. I actually work on a very similar project using STI where we have users, managers, and administrators, and there must be three records for each in the database. This is DRY from a Rails perspective because each of those records has their own unique attributes, methods in their own classes, etc. but share common code from a similar model to what you call Person. I actually think this is a good way to do it if you're using STI.
An alternative would be to have common code in a module which you could include in each of Customer, Employee, and Owner.
Another alternative (most likely what I would do if starting from scratch) would be to have a single Person table and use roles, using cancan and maybe even rolify. This way you have one class you're dealing with called Person where an instance of Person can have one or many roles, such as customer, employee, or owner.
Consider this simple application in RUby on Rails:
Student & Teacher extends Person.
Person has 2 properties, name & age.
Student has 1 extra property, grade.
Teacher has 1 extra property, salary.
I want to store the student & teacher information in separate db tables. I want to take advantage of RoR's utility class ActiveRecord:Base to retrive data for these models. Because Person is not expected to be instantiated, I can mix it with Student & Teacher as follows:
class Student < ActiveRecord:Base
include Person
end
module Person
end
Question:
Is this the correct way of implementation?
How to implement this without creating a databse table called persons? I only want to generate 2 tables, namely students and teachers, and both should have the 3 properties mentioned above.
Person does not have to be a model - it can be a library/module (which you then mixin as per your example). You'd keep that in RAILS_ROOT/lib.
Your Student and Teacher classes can be models (which use AR::Base), and the person can just be a bunch of common methods used by both.
Alternatively, if your child class is far more complicated - look into "Single table inheritance" - the description of which is beyond the scope of this answer... but you can google for it.
In the Rails ActiveRecord Associations guide, I'm confused over why the tables for has_one and has_many are identical:
Example tables for has_many:
customers(id,name)
orders(id,customer_id,order_date)
Example tables for has_one:
these tables will, at the database level, also allow a supplier to have many accounts, but we just want one account per supplier
suppliers(id,name)
accounts(id,supplier_id,account_number) #Foreign Key to supplier here??
Shouldn't the tables for has_one be like this instead:
suppliers(id,name,account_id) #Foreign Key to account here
accounts(id,account_number)
Now because the account_id is in the suppliers table, a supplier can never have more than one account.
Is the example in the Rails Guide incorrect?
Or, does Rails use the has_many kind of approach but restricts the many part from happening?
If you think about this way -- they are all the same:
1 customer can have many orders, so each order record points back to customer.
1 supplier can have one account, and it is a special case of "has many", so it equally works with account pointing back to supplier.
and it is the same case with many-to-many, with junction table pointing back to the individual records... (if a student can take many classes, and one class can have many students, then the enrollment table points back to the student and class records).
as to why account points back to supplier vs account points to supplier, that one I am not entirely sure whether we can have it either way, or one form is better than the other.
I believe it has to do with the constraints. With has_one rails will try to enforce that there is only one account per supplier. However, with a has_many, there will be no constraint enforced, so a supplier with has_many would be allowed to exist with multiple accounts.
It does take some getting used to when thinking about the relationships and their creation in rails. If you want to enforce foreign keys on the database side (since rails doesn't do this outside of the application layer), take a look at Mathew Higgins' foreigner
If I understand your question correctly, you believe there is a 1:1 relationship bi-directionally in a has_one/belongs_to relationship. That's not exactly true. You could have:
Class Account
belongs_to :supplier
belongs_to :wholesaler
belongs_to :shipper
# ...
end
account = supplier.account # Get supplier's account
wholesaler = Wholesaler.new
wholesaler.accounts << account # Tell wholesaler this is one of their suppliers
wholesaler.save
I'm not saying your app actually behaves this way, but you can see how a table -- no, let's say a model -- that "belongs to" another model is not precluded from belonging to any number of models. Right? So the relationship is really infinity:1.
I should add that has_one is really a degenerate case of has_many and just adds syntactic sugar of singularizing the association and a few other nits. Otherwise, it's pretty much the same thing and it's pretty much why they look alike.
I have two simple models each with acts_as_tree, say Departments and Employees.
My goal is to create a treeview combining both models in to one overall tree, like so:
Department 1
SubDepartment 1.1
Employee A
Employee B
SubDepartment 1.2
Department 2
Subdepartment 2.1
Employee C
Department 3
SubDepartment 3.1
Employee D
Employee E
Subdepartment 3.2
etc
I found this already: Acts as Tree with Multiple Models but I'm afraid I could use a little more pointers in the right direction.
Thanks!
So your schema is like this?
Department
acts_as_tree #requires departments.parent_id field
has_many :employees
Employee
belongs_to :department #requires employees.department_id field
I would just stick with this rather than trying to make the tree 'know' about employees. The only things that have the tree relationship are the departments. The employees belong to a department but they're not part of the tree structure.
As far as editing goes, then, when you change a department you set parent_id to be the id of its parent in the tree, and when you move an employee you set department_id to be the id of its 'parent'.
What's your actual problem? I mean what are you trying to do?
I have multiple user account types, each with different information. For example, business contacts link to businesses, school administrators and students link to schools. Students have physical addresses, but business contacts and school admins use the organizations address. There's other information unique to each type as well.
I'm leaning toward separate tables for students, school admins and business contacts, but using Authlogic, I have a Users table with authentication information (all need to log in).
The question is really how best to link this single authentication table with the individual profiles. It seems like a one-to-one relationship requires a single table (e.g. Users <-> Students or Users <-> Business_contacts). I want to have something of a "one-to-one-of-the-following" relationship (Users <-> Students or Business_contacts). Is there a good way to do this using a join table or other construct?
Alternatively, I could unify the common information in the Users table and come up with a "profile" column in XML to support the unique information. My thought was that keeping everything in clean DB columns would simplify selects/inserts.
Thoughts, ideas?
business contacts link to businesses, school administrators and students link to schools.
This means you have different roles of users rather than different users.
Students have physical addresses, but business contacts and school admins use the organizations address
There's other information unique to each type as well.
Which means different roles have different data and behavior, leading to different OO classes.
The question is really how best to link this single authentication table with the individual profiles.
I want to have something of a "one-to-one-of-the-following" relationship (Users <-> Students or Business_contacts). Is there a good way to do this using a join table or other construct?
The simplest model I can think of if the following:
User (Username, Email, Password, Names and other common data for every user)
Participant, abstract. (has relation to one user - defines the role of the participant, behavior and its common data). Should provide the interface for participants.
Student (inherits from Participant, adds its own behavior and data)
SchoolAdministrator (inherits from participant)
And so on
In the Object Oriented World (non RDBMS) this has a simple advantage: polymorphism. Having a user you don't need to know exactly who he is. You just do things like this:
user.participant.can_manage_stuff?
user.participant.order_book(harry_potter)
And the appropriate actions will take place which will be implemented in Student, SchoolAdministrator or other class inherited from Participant.
Another things is that it is very easy to add new roles for the system. Just inherit from Participant and implement its interface.
Now, when Object Oriented Design is done, let's have a look at the data storage. I assume you use RDBMS.
So now you have 2 types of links:
One-to-one association between User and Participant.
Generalization (inheritance) between Participant and Student, SchoolAdministrator.
Implementation of 1st is as simple as having has_one and/or belongs_to association for the following table (via participant_id column):
users:
id | username | email | password | etc | participant_id (non-null) FK_TO_participants table |
So you can easily implement that.
Now, the 2nd link can be implemented in RDBMS in number of different wasy.
But fortunately or unfortunately ActiveRecord supports only one option to do that. And it is hierarchy per table mapping strategy that uses discriminator column to distinguish the type (stored in type column by convention).
So you will have the 2nd table that looks like this:
perticipants:
id | type | student_card_number (null) | administrator_number (null) | etc
This table will have all the columns for all possible participants. This is the simplest and easiest implementation of hierarchy in DB. But not the most optimal from some points of view. As I said with ActiveRecords it is only one option available anyway.
So as a result of this design you will end up with 2 database tables (users, participants) and at least 3 classes (User, Participant, Student).
And, of course, you can vary from here a lot but that should deliver my point.
And yes, please no XML in database, don't waste your nerves and valuable personal time.
Cheers.
If you really need seperate models for each role you use, then I would recomend to use polymorphic association (read more here). So:
# User model
belongs_to :owner, :polymorphic => true
# Student model
has_one :user, :as => :owner
# Any other model
has_one :user, :as => :owner
And you need to add to users table:
t.integer :owner_id
t.string :owner_type