Wednesday, July 7, 2010

ColdMVC: Parse checkboxes or radios generically

I ran into an interesting issue awhile, back. I wanted to edit a product and click on checkboxes for one to many relationships to colors, categories, and sizes. When I post the form to the ProductController save() I wanted a generic way to convert checkbox values (which are ids) to actually objects. Below are the steps I took followed by the code.

First, I call private functions to parse the specific checkbox ids ( Ex. parseCategories()), but they all just call parseResource().

Next, while in parseResource() I look into the variables scope for the model (Ex. _Size) to dynamically get the object by using findByID().

Lastly, I append the object to an array and populate the Product object and save it.

ProductController.cfc

/**
* @accessors true
* @action list
* @extends coldmvc.Controller
*/
component {
property _Size;
property _Color;
property _Category;

function save() {

var product = _Product.new();

params.product.categories = parseCategories(params.product.categories);
params.product.sizes = parseSizes(params.product.sizes);
params.product.colors = parseColors(params.product.colors);

product.populate(params.product);

product.save();

redirect({controller="product",action="setup"},"productID=#product.id()#");
}

private array function parseCategories(string categoryIDs) {

return parseResource("Category",arguments.categoryIDs);

}

private array function parseSizes(string sizeIDs) {

return parseResource("Size",arguments.sizeIDs);

}

private array function parseColors(string colorIDs) {

return parseResource("Color",arguments.colorIDs);

}

private array function parseResource(string resource, string resourceIDs){

var resources = $.string.toArray(arguments.resourceIDs);

var result = [];
var i = "";

for (i=1; i <= arrayLen(resources); i++) {

arrayAppend(result, variables["_#arguments.resource#"].findByID(resources[i]));

}

return result;

}
}


I wanted to share this just in case someone else is running into the issue.

5 comments:

  1. Instead of using Model.findByID(), which requires ColdMVC to parse your method name to figure out what you want to do, you could use Model.get(), which does a lookup using the PK.

    Good use of checking the variables scope for the injected Model. I was actually thinking of adding a ModelFactory to ColdMVC that might help simplify scenarios like this, but it looks like you found a decent workaround. In case you're curious, the ModelFactory would look like such:

    arrayAppend(result, modelFactory.get(resource).get(resources[i]));

    Maybe instead of modelFactory.get(model) it should be modelFactory.getModel(model). Thoughts? Should I add it?

    ReplyDelete
  2. Update: I forgot ColdMVC also supports Modal.getAll(), so instead of looping over the list of checkboxes, your parseResource function could be a lot smaller:

    function parseResource(string resource, string resourceIDs) {
    return variables["_#resource#"].getAll(resourceIDs);
    }

    ReplyDelete
  3. @Tony

    Yeah I like modelFactory.get(resource).get(resources[i]). That would come in handy. It took me awhile to figure out where the _Model was being stored at.

    ReplyDelete
  4. FYI,

    I added the modelFactory like we had talked about. I also updated the populate method to automatically populate relationships using IDs. So in your case, you could update your save method to simply be:

    function save() {

    var product = _Product.new(params.product);

    product.save();

    redirect({controller="product", action="setup", id=product});

    }

    Can't get much simpler than that. :)

    ReplyDelete