Reputation: 128
Alright, I'm thoroughly stumped. I'm building a light app to help a friend of mine with his business. I won't go into too many details but for some background, he takes components from several brands and builds a widget out of these components.
Since there are several components to each widget, and several of those different component options are made by the same brand, I want to separate the brand from the component so you can easily view products by brand.
I have the following tables
Brands contains "id" and "name" Components contains "id" "model" "brand_id" Widgets contains "id" "compoennt_id"
The displayfield for Brands is "name" The displayfield for Components is "model"
And here is the problem, when adding a new product, cake will only display "model" in the form obviously. I can create a virtual displayfield to display to combine "brand_id" and "model" but that doesn't really solve the problem of users not knowing what brand that model is from.
The seemingly lazy solutions: Remove the brand table all together and store that information in the components table.
Is there some way to do this differently so that in the form when adding a new widget, they can see "Brand Model" in the dropdown box?
Upvotes: 2
Views: 1602
Reputation: 11
I solved a similar problem, but for me the model in question had no useful data and I needed to fetch names from two other associated models. I am very new to CakePHP, so I can't vouch for how much this solution is "proper", how well it scales, etc... but it seems to work. Also I'm using this in CakePHP 1.3, so my solution may not even apply to you.
Anyway, here is what I added to the model:
function __construct($id = false, $table = null, $ds = null) {
parent::__construct($id, $table, $ds);
$list = parent::find('all', array('contain' => array(
'OtherModelA.name', 'OtherModelB.name')));
if( !empty( $list) ) { //just in case there are no results
$this->virtualFields['name'] = sprintf("'%s - %s'",$list[0]['ProductionCompany']['name'],$list[0]['Musical']['name']);
$this->displayField = 'name';
}
}
EDIT: I noticed some errors when the DB was completely empty, so I added a conditional and moved the displayField into the constructor.
Upvotes: 1
Reputation: 517
I spent way too much time looking into the same thing yesterday.
What I think you can do is set up a virtual displayfield in your widget model that does something like is suggested in the limitations virtual fields limitation section in this section of book.cakephp.com:
$this->virtualFields['cool_display_name'] =
sprintf('CONCAT(%s.name, " - ", %s.model)', $this->Brands->alias, $this- >Components->alias);
BUT: this will not work unless your 'find' function is picking up enough data when it does its query. find('list'), which is what you're probably calling for your dropdown, has a default recursion set to -1, so it's not going to traverse down to get it. I ended up overriding what in my case is your Widget model's find(), with something like:
public function find($type = 'first', $query = array()) {
$return = array();
switch ($type) {
case 'list': // having to deal with a virtual field, seems like the least-messy workaround right now.
$query['contain'] = 'User.full_name';
$results = parent::find('all', $query);
foreach($results as $k => $r) {
$return[$r[$this->alias]['id']] = $r['User']['full_name'];
}
break;
default:
$return = parent::find($type, $query);
break;
}
return $return;
}
What I do in there is build by hand the array that listbox for elements expect, which is like:
array( 'id' => 'display text')
You'll also see in there that I used cakephp's Contain feature, so that not all of the data was returned, but just what I needed in order for all of the virtual field data to be supplied.
Upvotes: 1