onetomany - type = "struct" not working

3 views
Skip to first unread message

James Allen

unread,
Jul 31, 2008, 2:58:51 PM7/31/08
to transfer-dev
Hi guys,

Could someone please put me out of my misery here.

I've got two simple objects as shown below:

<!-- User Profile -->
<object name="userProfile" table="tblUserProfile"
decorator="model.users.profile.userProfile">
<id name="ID" type="numeric" />
<property name="NotPublic" type="numeric" column="NotPublic" />

<!-- Link between a user profile and it's settings -->
<onetomany name="Settings">
<link to="users.userProfileSetting" column="lnkIDProfile" />

<collection type="struct">
<key property="Item" />
</collection>
</onetomany>
</object>

<object name="userProfileSetting" table="tblUserProfileSettings"
decorator="model.users.profile.userProfileSetting">
<id name="ID" type="numeric" />
<property name="Item" type="string" column="Item" />
<property name="Value" type="string" column="Value" />
<property name="ValueLong" type="string" column="ValueLong" />
</object>

As you can see, the userProfile object has a onetomany link to
userProfileSetting. Therefore a single profile can have many settings.

All good so far. There is also a user object which has a manytoone to
the userProfile:

<manytoone name="Profile">
<link to="users.userProfile" column="lnkIDProfile"/>
</manytoone>

The problem I'm getting is that when I create a userProfile object and
then create some userProfileSetting objects (adding them with a call
to setParentUserProfile()), I am only seeing one of the
userProfileSetting objects in the Settings collection within the
userProfile object.
Also, the collection is NOT a struct it's an array! I just can't
figure out why it is doing this (although I probably doing something
really stupid).

Here's the test I'm doing and the related code:

<cfset user = us.getCurrentUser()> (just gets a user object)

<cfset userProfile =
variables.instance.transfer.new("users.userProfile") />
<cfset user.setProfile(userProfile) />

<cfset userProfileSetting=
variables.instance.transfer.new("users.userProfileSetting") />
<cfset userProfileSetting.setParentUserProfile(userProfile) />
<cfset userProfileSetting.setItem("Test1") />
<cfset userProfileSetting.setValue("Test Value") />

<cfset userProfileSetting=
variables.instance.transfer.new("users.userProfileSetting") />
<cfset userProfileSetting.setParentUserProfile(userProfile) />
<cfset userProfileSetting.setItem("Test2") />
<cfset userProfileSetting.setValue("Test Value 2") />

For some reason this results in the userProfile consisting of a single
element array with the "Test2" item in it..

Any ideas what I'm doing wrong here - it has to be simple.

Cheers,
James.

Mark Mandel

unread,
Jul 31, 2008, 6:14:58 PM7/31/08
to transf...@googlegroups.com
My question is - what makes you think its an array?

If you set the Item value, which is the key AFTER you add it to the
collection (setParent()), it's going to add it to the struct with ''
as the key... twice, one overwriting the other.

Mark

--
E: mark....@gmail.com
W: www.compoundtheory.com

James Allen

unread,
Jul 31, 2008, 8:11:40 PM7/31/08
to transf...@googlegroups.com
Hi Mark,

When I dump the userProfile bean or the user memento I see the Settings
collection is an array, not a struct. Is it stored like this internally
though?

Ah I see what you mean about the setParent... So the correct way is to
create the userProfileSetting first, then set the item and value and finally
attach it to the parent?

I may have this all confused as I haven't tried a collection with a struct
yet. I wanted to have quick access to all the settings with the item as the
key.

Cheers,
James.

Nando

unread,
Jul 31, 2008, 8:19:21 PM7/31/08
to transf...@googlegroups.com
Would a working example help? A "temperature" can be expressed in different languages in this app, so Temperature has a one2many relationship with the language it is expressed in, and "lan" (en, fr, de, it, etc) is my key in this collection.


<object name="Temperature" table="Temperature" decorator="liantcm.model.TemperatureDecorator">
        <id name="temperatureId" type="numeric" generate="false"/>
        <property name="sortOrder" type="numeric" nullable="false" />
        <onetomany name="TemperatureLan">
            <link to="TemperatureLan" column="temperatureId" />
            <collection type="struct">
                <key property="lan"/>
            </collection>
        </onetomany>
    </object>
   
    <object name="TemperatureLan" table="TemperatureLan" decorator="liantcm.model.TemperatureLanDecorator">
        <id name="temperatureLanId" type="numeric" generate="false"/>
        <property name="lan" type="string" column="lan" nullable="false"/>
        <property name="theTemperature" type="string" column="temperature" nullable="false"/>
    </object>

<cffunction name="getTemperatureTO" access="public" returnType="void" output="false">
        <cfargument name="event" type="any">
        <cfset var temperatureLanTO = "" />
        <cfset var temperatureLanStr = "" />
        <cfset var qActiveLans = getAdminGateway().findActiveLanguages() />
        <cfset var validationResult = true />
        <cfif StructKeyExists(session,"temperatureTO")>
            <cfset validationResult = session.temperatureTO.getValidationResult() />
        </cfif>
        <!--- Create a Temperature Transfer Object if the form part of the view is displayed
            either by clicking on the + or an existing temperature --->
        <cfif arguments.event.ValueExists("new")>
        <!--- The presence of the value new signifies a new category --->
            <!--- Since it's a new category, we create a new Temperature transfer object ... --->
            <cfset session.temperatureTO = getTransfer().new("Temperature") />
            <!--- ... and then we loop thru the active languages to create and set the child transfer objects in TemperatureTO
                and set some values in each temperatureLanTO --->
            <cfloop query="qActiveLans">
                <cfset temperatureLanTO = getTransfer().new("TemperatureLan") /><!--- create a new TemperatureLan transfer object --->
                <cfset temperatureLanTO.setLan(lan) /><!--- set the lan of the new TO while in the loop --->
                <cfset temperatureLanTO.setParentTemperature(session.temperatureTO) /><!--- set the parent object --->
            </cfloop>
            <!--- Find the maximum sortOrder value and set this sortOrder value +1 --->
            <cfset session.temperatureTO.setSortOrder(getAdminGateway().findNextTemperatureSortOrderValue()) />
            <cfset arguments.event.setValue("temperatureTO", session.temperatureTO) />
        <cfelseif arguments.event.ValueExists("id")><!--- this signifies an existing category being edited --->
            <!--- create the transfer object from the records in the database --->
            <cfset session.temperatureTO = getTransfer().get("Temperature", arguments.event.getValue("id")) />
            <!--- get the structure that contains the TemperatureLan objects composed within Temperature --->
            <cfset temperatureLanStr = session.temperatureTO.getTemperatureLanStruct() />
            <!--- we went to check to make sure that a TemperatureLanTO is present for all active languages,
                because active languages can be added at a later time --->
            <cfloop query="qActiveLans">
                <!--- if the StructKey for a particular language is not present,
                    we create a new temperatureLanTO, set the language and add it as a child of temperatureTO --->
                <cfif NOT StructKeyExists(temperatureLanStr, qActiveLans.lan)>
                    <cfset temperatureLanTO = getTransfer().new("TemperatureLan") /><!--- create a new TemperatureLan transfer object --->
                    <cfset temperatureLanTO.setLan(lan) /><!--- set the lan of the new TO while in the loop --->
                    <cfset temperatureLanTO.setParentTemperature(session.temperatureTO) /><!--- set the parent object --->
                </cfif>
            </cfloop>
            <cfset arguments.event.setValue("temperatureTO", session.temperatureTO) />
        <cfelseif validationResult EQ false>
            <cfset arguments.event.setValue("temperatureTO", session.temperatureTO) />
        </cfif>
    </cffunction>
--

Nando M. Breiter
The CarbonZero Project
CP 234
6934 Bioggio
Switzerland
+41 76 303 4477
na...@carbonzero.ch

Nando

unread,
Jul 31, 2008, 8:23:26 PM7/31/08
to transf...@googlegroups.com
Whoops, my inline comments aren't perfect ... replace "category" with "temperature" as below:

        <!--- The presence of the value new signifies a new temperature --->
            <!--- Since it's a new temperature, we create a new Temperature transfer object ... --->

            <cfset session.temperatureTO = getTransfer().new("Temperature") />
            <!--- ... and then we loop thru the active languages to create and set the child transfer objects in TemperatureTO
                and set some values in each temperatureLanTO --->
            <cfloop query="qActiveLans">
                <cfset temperatureLanTO = getTransfer().new("TemperatureLan") /><!--- create a new TemperatureLan transfer object --->
                <cfset temperatureLanTO.setLan(lan) /><!--- set the lan of the new TO while in the loop --->
                <cfset temperatureLanTO.setParentTemperature(session.temperatureTO) /><!--- set the parent object --->
            </cfloop>
            <!--- Find the maximum sortOrder value and set this sortOrder value +1 --->
            <cfset session.temperatureTO.setSortOrder(getAdminGateway().findNextTemperatureSortOrderValue()) />
            <cfset arguments.event.setValue("temperatureTO", session.temperatureTO) />
        <cfelseif arguments.event.ValueExists("id")><!--- this signifies an existing temperature being edited --->

Mark Mandel

unread,
Jul 31, 2008, 9:51:16 PM7/31/08
to transf...@googlegroups.com
On Fri, Aug 1, 2008 at 10:11 AM, James Allen <sling...@googlemail.com> wrote:
>
> Hi Mark,
>
> When I dump the userProfile bean or the user memento I see the Settings
> collection is an array, not a struct. Is it stored like this internally
> though?

The memento is always a array. Dump the getSettingStruct();

>
> Ah I see what you mean about the setParent... So the correct way is to
> create the userProfileSetting first, then set the item and value and finally
> attach it to the parent?

Yep, if you don't set the key first, how is it meant to know what the
key should be ;o)

>
> I may have this all confused as I haven't tried a collection with a struct
> yet. I wanted to have quick access to all the settings with the item as the
> key.

Mark

James Allen

unread,
Aug 1, 2008, 5:15:38 AM8/1/08
to transf...@googlegroups.com
Hi Mark,

Thanks for that - I knew it had to be a simple answer.. :)

I can see exactly what you mean in terms of the struct key now.. I think the
fact that I link on ID in D/B terms through me abit..

Cheers,
James.

-----Original Message-----
From: transf...@googlegroups.com [mailto:transf...@googlegroups.com]
On Behalf Of Mark Mandel
Sent: 01 August 2008 02:51
To: transf...@googlegroups.com
Subject: [transfer-dev] Re: onetomany - type = "struct" not working

James Allen

unread,
Aug 1, 2008, 5:37:31 AM8/1/08
to transf...@googlegroups.com

Hi Nando,

 

Thanks for the example.. It was the order I was doing things that was the problem – alloctating the parent before setting the ‘item’.. Also I expected the getMemento() to reveal a struct for Settings but as Mark points out it’s not stored that way..

 

Another piece of this excellent framework learnt.. J

Reply all
Reply to author
Forward
0 new messages