Delving Deeper into the Active Record with Ruby-on-Rails - Relationship Mapping (Page 2 of 4 )
In real world applications, tables never exist in isolation. They are related at the database level using relationships based on the foreign key concept. One table can be related to another table through one-to-one, one-to-many or many-to-one relationships. Active Record bases its object-to-object relationship on how the corresponding tables are related at the database level. On the basis of types of database level relationships, there are three types of basic mapping in Active Record:
Each of these are mapped using two-way mapping. That means the classes on both sides of relationships know how they are related to the other class.
In one-to-one mapping, a foreign key in one row of a table references at most a single row of another table. For example, let's take two tables - INVOICES and ORDERS. The following diagram shows the relation between the tables:
To map tables with a one-to-one relationship, Active Record provides two declarations that have to be added to the corresponding classes. They are belongs_to and has_one. The belongs_to declaration appears in the class/model for the table that contains the foreign key, whereas has_one is declared in the class which is on the other end i.e. the one whose primary key is acting as the foreign key. To map the classes according to the relationship between INVOICES and ORDERS, the addition to the classes would be:
class Order < ActiveRecord::Base
. . .
class Invoice < ActiveRecord::Base
Since for each Order there is at most one Invoice, the Order class contains the has_one declaration connecting it to the object of the Invoice class. Similarly, the Invoice class contains the belongs_to declaration to map it with the Order class, more specifically the object of Order class.
In a One-to-many relationship, a primary key in one row of a table references or is associated with an arbitrary number of rows in another table. For example, the relationship between ORDERS and LINE_ITEMS is that of one-to-many. The DDL of the tables will make it more clear:
An order can be associated with any number of line-items. To map such a relationship, one must use the declarations has_many and belongs_to. The declaration has_many must be declared in the class representing the table at the "one" end of a one-to-many relationship, whereas belongs_to is declared in the class representing the table at the "many" end of the relationship. To illustrate further, below are the classes corresponding to the ORDERS and LINE_ITEMS tables with the mapping declaration embedded:
class Order < ActiveRecord::Base
. . .
class LineItem < ActiveRecord::Base
Using the has_many declaration, the Order class is associated with the line_items class and tells the framework that one instance/object of the Order class is associated with many instances of the Line_Items class. The belongs_to declaration works in the same way as it works in one-to-one relationship mapping.
When many records of one table are associated with many records of another table, then the tables have a many-to-many relationship. To implement such a relationship, a link/join table needs to be used. The join table contains a foreign key for each of the tables its linking, so each row in the join table represents a linkage between the two tables. For example, PRODUCT and CATEGORIES have a many-to-many relationship. To implement the relationship, a link table has to be introduced: CATEGORIES_PRODUCT. It contains foreign key references to both tables. Here are the DDL of all three tables:
The conventional way of mapping a many-to-many relationship would contain a declaration for the join/link table too. But that is not done with Active Record. Only the main tables are required to be mapped. It assumes that the join table is named after the two tables it joins (with the names in alphabetical order). To map the main tables, the classes corresponding to them must have the following declaration: has_and_belongs_to_many. This declaration links them both through the join/link table which is taken care by the Active Record.
class Product < ActiveRecord::Base
. . .
class Category < ActiveRecord::Base
In the above example there is a mention of the link/join table. The reason is that once Active Record sees the declaration has_and_belongs_to_many in both tables, it checks for the link table categories_products automatically. Thus "Convention-over-Configuration" works here too.