Sean Walsh
Sean Walsh

Reputation: 8344

Is this relationship possible in CF9's ORM? How?

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

Answers (1)

Chris Blackwell
Chris Blackwell

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

Related Questions