Search code examples
formscoldfusionstructcfwheelsfw1

ColdFusion Converting Form Values into a Struct


I am building a form for my ColdFusion application using the naming format report[{field-name}] which when using RoR or CFWheels would give me a struct on the backend called report containing all of my field names. I am using FW/1 so all of my form fields get put into the RC scope rather than remaining in the Form scope. I know that it is possible to convert my form fields into a ColdFusion struct, because, as I said, CFWheels does it. I just have no idea how to make my application do it.

Here is part of the form that I am talking about

<dl class="oneColumn">
    <dt class="first"><label for="report[name]">Name</label></dt>
    <dd><input type="text" name="report[name]" class="text" /></dd>
    <dt><label for="report[description]">Description</label></dt>
    <dd><textarea name="report[description]" class="textarea"></textarea></dd>
</dl>

Solution

  • Adam had the right context, but his code snippet was wrong.

    A function that will work is this:

    <cffunction name="$createNestedParamStruct" returntype="struct" access="public" output="false">
        <cfargument name="params" type="struct" required="true" />
        <cfscript>
            var loc = {};
            for(loc.key in arguments.params)
            {
                if (Find("[", loc.key) && Right(loc.key, 1) == "]")
                {
                    // object form field
                    loc.name = SpanExcluding(loc.key, "[");
    
                    // we split the key into an array so the developer can have unlimited levels of params passed in
                    loc.nested = ListToArray(ReplaceList(loc.key, loc.name & "[,]", ""), "[", true);
                    if (!StructKeyExists(arguments.params, loc.name))
                    arguments.params[loc.name] = {};
    
                    loc.struct = arguments.params[loc.name]; // we need a reference to the struct so we can nest other structs if needed
                    loc.iEnd = ArrayLen(loc.nested);
                    for(loc.i = 1; loc.i lte loc.iEnd; loc.i++) // looping over the array allows for infinite nesting
                    {
                        loc.item = loc.nested[loc.i];
                        if (!StructKeyExists(loc.struct, loc.item))
                            loc.struct[loc.item] = {};
                        if (loc.i != loc.iEnd)
                            loc.struct = loc.struct[loc.item]; // pass the new reference (structs pass a reference instead of a copy) to the next iteration
                        else
                            loc.struct[loc.item] = arguments.params[loc.key];
                    }
                    // delete the original key so it doesn't show up in the params
                    StructDelete(arguments.params, loc.key, false);
                }
            }
        </cfscript>
        <cfreturn arguments.params />
    </cffunction>
    

    I tested it in my application (outside of CFWheels) and it worked perfectly. All you do is pass in a struct (in my case the Rc struct from FW/1) containing what should be structures, but displaying as strings and you will be returns a structure with nested structures.

    Example:

    <cfscript>
        Struct['hello[world]'] = 1;
        Struct['hello[earth]'] = 2;
        myhello = $createNestedParamStruct(Struct);
        /* Now myhello equals this:
            myhello.hello.world = 1;
            myhello.hello.eath = 2;
        */
    </cfscript>