Reputation: 8344
First, the relevant bits of the table structure:
contact
-contactID
-email
data
-value
-contactID
-definitionID
definition
-definitionID
-name
Each record in the contact
table represents a single contact along with their e-mail address.
Each record in the definition
table represents the definition of a custom data field. For example, there might be five records in definition
- Organization, Zip Code, Comments, Address, Phone. In addition to the names of the fields, it also defines relevant meta-data about the fields.
Each record in the data
table contains the value of the custom data field as it relates to the contact. Its definition is taken from the definition
table.
To further clarify, if I wanted to generate a table of contacts along with their custom field data, it might look like this:
| E-Mail | Organization | Zip Code | Comments | Address | Phone |
-------------------------------------------------------------------------------------------------------------
| [email protected] | ACME | 12345 | Cool guy! | 123 Test St | 555-5555 |
| [email protected] | SomeCo. | 54321 | Doesn't know anything about ORM! | 321 Test Blvd | 444-4444 |
The benefit of this system is that it can scale out as much as I need it to and is easily customizable. The detriment is that I have no idea how to define the relationships :)
I've attempted defining contact:data
as 1:M
and definition:data
as 1:M
, but the results seem a little odd: With two contacts and a single definition, adding a row to data for each contact and then calling entityLoad( 'Contact' )
results in an interesting relationship. It appears like this(just going to use some pseudo-struct notation because it's easy enough to type and, I hope, read):
{
contact: {
email: '[email protected]',
data: {
value: 'ACME',
definition: {
name: 'Organization',
data: {
value: 'SomeCo.',
contact: {
email: '[email protected]'
}
}
}
}
}
It looks like it is creating an indirect relationship between contact
and definition
based on the data
table's relation to both tables. As you can imagine, upping the amount of contacts and custom fields just makes the problem exponentially worse.
Is this type of relationship possible using CF9's ORM? How can I accomplish it?
Thanks in advance!
EDIT: Forgot to specify - I am using MySQL, if it matters.
EDIT 2: CFC definitions follow:
Contact.cfc
/**
* @persistent true
*/
component name='Contact' {
/**
* @type numeric
* @sqltype int(11)
* @generator increment
* @fieldtype id
*/
property contactID;
/**
* @type string
* @sqltype varchar(50)
*/
property email;
/**
* @type array
* @fieldtype one-to-many
* @cfc Data
* @fkcolumn dataID
*/
property data;
}
Definition.cfc
/**
* @persistent true
*/
component name='Definition' {
/**
* @type numeric
* @sqltype int(11)
* @generator increment
* @fieldtype id
*/
property definitionID;
/**
* @type string
* @sqltype varchar(50)
*/
property name;
/**
* @type array
* @fieldtype one-to-many
* @cfc Data
* @fkcolumn dataID
*/
property data;
}
Data.cfc
/**
* @persistent true
*/
component {
/**
* @type numeric
* @sqltype int(11)
* @generator increment
* @fieldtype id
*/
property dataID;
/**
* @type string
* @sqltype varchar(50)
*/
property value;
/**
* @fieldtype many-to-one
* @fkcolumn contactID
* @cfc Contact
* @inverse true
*/
property contact;
/**
* @fieldtype many-to-one
* @fkcolumn definitionID
* @cfc Definition
* @inverse true
*/
property definition;
}
Upvotes: 2
Views: 392
Reputation: 2178
I believe you want the relationships to be handled by Data.cfc, having
Data --m2o--> Contact
Data --m2o--> Definition
and for convenience
Contact --o2m--> Data (inverse=true)
CFC's
// Contact.cfc
component persistent="true" {
property name="contactID" fieldtype="id" generator="native";
property name="email";
property name="data" cfc="Data" fieldtype="one-to-many" inverse="true" lazy="true";
}
// Definition.cfc
component persistent="true" {
property name="definitionID" fieldtype="id" generator="native";
property name="name";
}
// Data.cfc
component persistent='true'{
property name="dataID" fieldtype="id" generator="native";
property name="value";
property name="contact" cfc="Contact" fieldtype="many-to-one" fkcolumn="contactID";
property name="definition" cfc="Definition" fieldtype="many-to-one" fkcolumn="definitionID";
}
index.cfm
<cfscript>
ormReload();
transaction {
con = new Contact();
con.setEmail("[email protected]");
entitySave(con);
def1 = new Definition();
def1.setName("twitter");
entitySave(def1);
def2 = new Definition();
def2.setName("interests");
entitySave(def2);
data1 = new Data();
data1.setValue("d1rtym0nk3y");
data1.setDefinition(def1);
data1.setContact(con);
entitySave(data1);
data2 = new Data();
data2.setValue("ColdFusion");
data2.setDefinition(def2);
data2.setContact(con);
entitySave(data2);
// this is important, you must set both sides of the relationship or "bad things" happen
// i'd recommend overriding contact.addData()/data.setContact to ensure both sides get set
con.addData(data1);
con.addData(data2);
}
writeDump(con);
</cfscript>
To retrieve the Data attributes for a Contact in a sensible fashion you can do
myData = ormExecuteQuery("
select new map(
data.value as value,
def.name as name
)
from Data data
join data.definition def
where data.contact.contactID = :id
", {id=con.getContactID()});
writeDump(myData);
Upvotes: 1