Let's pretend for a moment that we really are building some sort of eCommerce site. (Perhaps we sell silly lights that look like they're wearing hats!)
To do this, we would need to have a table of products which would hold the details of the lamps being sold. This is easy.
But, what if we wanted to associate the products a Customer bought back to them somehow? Or, what if we wanted to be able to pull a report on all the invoices for the day? Or, what if we wanted to find all the people who purchased a particularly strange lamp so that we could report them to the authorities?
It sounds to me like we're going to need to have a table of invoices. An Invoice would represent all the items a customer purchased in one transaction. We could have multiple invoices per customer.
To relate products to invoices we'll have to create a table that relates to the two. Reactor thinks of these as "Linking" tables. Linking tables are tables that relate two tables to each other.
Let's do some quick database designing:
Product Table
| Name | Type | Nullable | Default | Other |
|---|---|---|---|---|
|
productId |
Int |
No |
|
PK / Identity |
|
name |
Varchar(50) |
No |
|
|
|
description |
Varchar(2000) |
No |
|
|
|
price |
Money |
No |
|
|
Invoice Table
| Name | Type | Nullable | Default | Other |
|---|---|---|---|---|
|
invoiceId |
Int | No | PK / Identity | |
|
customerId |
Int | No | FK to Customer.customerId |
InvoiceProduct Table
| Name | Type | Nullable | Default | Other |
|---|---|---|---|---|
|
productInvoiceId |
Int |
No |
|
PK / Identity |
|
invoiceId |
Int |
No |
|
FK to Invoice.invoiceId |
|
productId |
Int |
No |
|
FK to Customer.customerId |
Note: These are example tables. You probably wouldn't do anything this simple in a real application. Also, I used varchar(2000) for the product description so that the "like" operator would work in a future example.
By now you should know that by simply creating the tables in your database you can now generate and instantiate Record objects which represent the tables. So, we're going to skip over that part.
Think about what you know right now and how you'd configure these tables in the database. You would have a hasMany relationship from both Invoice and Product to the InvoiceProduct table. The InvoiceProduct table would have two hasOne relationships back to Invoice and Product.
Given that, what would your code look like if you wanted to get all the products on a particular invoice? Maybe like this:
<!--- create the reactorFactory --->
<cfset Reactor = CreateObject("Component", "reactor.reactorFactory").init(expandPath("reactor.xml"))
/>
<!--- create an invoice record --->
<cfset InvoiceRecord = Reactor.createRecord("Invoice").load(invoiceId=1)
/>
<!--- output all the products on this invoice --->
<!--- get all of the Invoice Product Records --->
<cfset InvoiceProducts = InvoiceRecord.getInvoiceProductIterator().getArray()
/>
<!--- loop over the invoice products and get the product record --->
<cfloop from="1" to="#ArrayLen(InvoiceProducts)#"
index="x">
<cfset
ProductRecord = InvoiceProducts[x].getProduct()/>
<cfoutput>
#ProductRecord.getName()#<br>
</cfoutput>
</cfloop>
Wouldn't it be cool if we could somehow tell the Invoice and Product tables that they were linked? And maybe then we'd be able to use the same Iterator interface we did for other hasMany relationships? Not to worry, you can!
To support this, you use the "link" tag inside a hasMany tag. For example:
<object name="Product">
<hasMany
name="Invoice">
<link
name="InvoiceProduct" />
</hasMany>
</object>
<object name="Invoice">
<hasMany
name="Product">
<link
name="InvoiceProduct" />
</hasMany>
</object>
<object name="InvoiceProduct">
<hasOne
name="Product">
<relate
from="productId" to="productId" />
</hasOne>
<hasOne
name="Invoice">
<relate
from="invoiceId" to="invoiceId" />
</hasOne>
</object>
Note: Adding a link through another table implicitly adds a hasOne relationship to the object with the link. So, the link from Product to Invoice says to Reactor that the Product hasOne InvoiceProduct.
<!--- create the reactorFactory --->
<cfset Reactor = CreateObject("Component", "reactor.reactorFactory").init(expandPath("reactor.xml"))
/>
<!--- create an invoice record --->
<cfset InvoiceRecord = Reactor.createRecord("Invoice").load(invoiceId=1)
/>
<!--- dump all of the Products on the invoice --->
<cfdump var="#InvoiceRecord.getProductIterator().getQuery()#"
/>
Wasn't that easier? Reactor intelligently knows how to join the Invoice table to the InvoiceProduct table to the Product table. Furthermore, it returns only the records from the product table.
This leads me to the last few points in this crash course, how to customize Reactor generated objects.