Package home | Report new bug | New search | Development Roadmap Status: Open | Feedback | All | Closed Since Version 1.0.2

Bug #3469 enum fields don't show proper value
Submitted: 2005-02-14 18:19 UTC
From: andrew dot clark at ucsb dot edu Assigned: justinpatrin
Status: Closed Package: DB_DataObject_FormBuilder
PHP Version: 4.3.10 OS: FreeBSD 4.10
Roadmaps: (Not assigned)    
Subscription  


 [2005-02-14 18:19 UTC] andrew dot clark at ucsb dot edu
Description: ------------ Refer to bug #3124 Enum fields, whether frozen or not, do not display their proper value from the database. The sample code in bug #3124 should reproduce the problem.

Comments

 [2005-02-15 03:05 UTC] justinpatrin
Ok, I *know* that enums are displaying the right value in my code. Please try a very simple example and let me know if it still has the problem. If it does, please post some simple code.
 [2005-02-15 03:08 UTC] justinpatrin
Also, please let me know some more details. What is your enum field like? What options are you using? Is it showing the same thing every time or is it showing, say the one below the one it should or the one above the one it should?
 [2005-02-15 20:04 UTC] andrew dot clark at ucsb dot edu
Sure thing. Here's the DB table: CREATE TABLE net_contact ( net_contact_id int(16) unsigned NOT NULL auto_increment, net_id int(16) unsigned NOT NULL default '0', contact_id int(16) unsigned NOT NULL default '0', role enum('primary','secondary','security','financial') NOT NULL default 'secondary', last_modified timestamp(14) NOT NULL, last_verified datetime default NULL, PRIMARY KEY (net_contact_id), UNIQUE KEY net_id (net_id,contact_id,role) ) TYPE=MyISAM; Here's the DO code: <?php /** * Table Definition for net_contact */ require_once 'DB/DataObject.php'; class DataObjects_Net_contact extends DB_DataObject { ###START_AUTOCODE /* the code below is auto generated do not remove the above tag */ var $__table = 'net_contact'; // table name var $net_contact_id; // int(16) not_null primary_key unsigned var $net_id; // int(16) not_null unsigned var $contact_id; // int(16) not_null unsigned var $role; // string(9) enum var $last_modified; // timestamp(14) not_null unsigned zerofill timestamp var $last_verified; // timestamp(14) not_null unsigned zerofill /* ZE2 compatibility trick*/ function __clone() { return $this;} /* Static get */ function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('DataObjects_Net_contact',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE var $fb_enumFields = array('role'); var $fb_enumOptions = array('role' => array('primary', 'secondary', 'security', 'financial') ); // var $fb_selectAddEmpty = array('role'); /* * workaround for improper enum option handling * in DataObject FormBuilder */ /* function preGenerateForm(&$fb) { $role_options = array( null => null, 'primary' => 'primary', 'secondary' => 'secondary', 'security' => 'security' , 'financial' => 'financial' ); $this->fb_preDefElements['role'] = HTML_QuickForm::createElement( 'select', $fb->elementNamePrefix .'role' .$fb->elementNamePostfix, 'Role', $role_options ); } */ } ?> here's the implementing code: <?php require_once 'include/config.php'; require_once 'DB/DataObject/FormBuilder.php'; DB_DataObject::debugLevel(0); $self =& DB_DataObject::factory('net_contact'); if (PEAR::isError($self)) { die ( $self->getMessage() ); } $self->get(7); $builder =& DB_DataObject_FormBuilder::create($self); $form =& $builder->getForm(); $form->freeze(); echo $form->toHtml(); ?> If the form is not frozen, the enum field has nothing selected (though it does show the options). If it is frozen, it always shows the first enum option (primary). I notice if I remove my fb_enumOptions code, it seems to work... Here's the relevant row from the db: +----------------+--------+------------+-----------+----------------+---------------+ | net_contact_id | net_id | contact_id | role | last_modified | last_verified | +----------------+--------+------------+-----------+----------------+---------------+ | 7 | 205 | 214 | secondary | 20050126165246 | NULL | +----------------+--------+------------+-----------+----------------+---------------+
 [2005-02-15 21:06 UTC] justinpatrin
This bug has been fixed in CVS. In case this was a documentation problem, the fix will show up at the end of next Sunday (CET) on pear.php.net. In case this was a pear.php.net website problem, the change will show up on the website in short time. Thank you for the report, and for helping us make PEAR better. I see, so it was an issue with the enumOptions option.
 [2005-02-16 00:34 UTC] andrew dot clark at ucsb dot edu
Fix verified. Thanks! Is setting enumOptions now optional, that is, is DB_DataObject_FormBuilder now able to figure out the enum values on its own?
 [2005-02-16 00:41 UTC] justinpatrin
The enum feature has always been able to automatically get the enum values, but only for mysql. At least, mysql is the only one I've tested it with. It parses out the values from an SQL call.
 [2005-04-17 00:49 UTC] vtamara at pasosdejesus dot org
Good evening in the Lord In my humble opinion, this fix reduces functionality. In my code I was relying on the difference between keys and values in fb_enumOptions, for example with a field tipo_ubicaicion of type CHAR(1) I had: var $fb_enumOptions = array('tipo_ubicacion' => array('S' => 'Sin Información', 'R' => 'Rural', 'U' => 'Urbano', 'A' => 'Ambos' ), ... To show human readable messages in the interface while maintaining just one letter in the database (I used this in several tables). Now I have to implement a callback for each table where I want this feature: class DataObjects_Caso extends DB_DataObject { ... var $fb_enumOptions = array('tipo_ubicacion' => array('S' => 'Sin Información', 'R' => 'Rural', 'U' => 'Urbano', 'A' => 'Ambos' ), ... function enumCallback($table, $key) { return $this->es_enumOptions[$key]; } ... function preGenerateForm( &$formbuild ) { $formbuild->enumOptionsCallback=array($this, "enumCallback"); ... Wasn't it better to fix the documentation? I think that the problem in the bug report could be solved also by replacing: var $fb_enumOptions = array('role' => array('primary', 'secondary', 'security', 'financial') ); with var $fb_enumOptions = array('role' => array('primary'=>'primary', 'secondary'=>'secondary', 'security'=>'security', 'financial'=>'financial') ); Best regards. God illuminate us. Vladimir Támara
 [2005-04-17 03:13 UTC] justinpatrin
Oops, I missed that one. The enumOptionsCallback should *also* be run through this fix which makes the keys the same as the values. My reasoning is thus: The ENUM type in DBs handles them by assigning a number to the ENUM option and storing the number. The length of the string has no bearing on the storage size. Therefore the values of the enums should be the full string and not a placeholder, such as a single char. Since you're using a single char, you're really not using the enum as it's meant to be used. You're using it as a shortcut to a foreign key. I suggest you instead switch to using a seperate table for the values of the field and use a link (foreign key) as that's a better solution. I am very likely to change the way that FormBuilder handles enumOptionsCallback, making only its values important, unless I get a very compelling reason to make it otherwise.
 [2005-04-19 07:25 UTC] michael at baselinesols dot com
I tend to agree with vtamara on this one - while for the bulk of cases, you will have a 'value' => 'value' mapping for enums, there are certainly cases where you'd want the description to be more verbose (and indeed descriptive) than the value. It would also be useful for supporting databases that don't have a native ENUM type, where you must emulate it. A simple way to keep the current functionality, but still allow for more complex mappings would be to check if the keys of the $options array are sequential and start at 0, ie something like this: if (array_keys($options) == range(0, count($options)-1)) ... If true, then assume it's an enum options array of the type array('option1', 'option2', 'option3') - if not, then assume it's of type array('AM' => 'Morning', 'PM' => 'Afternoon) and use the keys accordingly. The only time this won't work is when you *need* consecutive numeric indices starting at 0, ie: array('0' => 'Low Priority', '1' => 'Medium Priority') However, if the order isn't *too* important, then this could be re-expressed as: array('1' => 'Medium Priority', '0' => 'Low Priority') Arguably the best way would be to have a config variable to specify whether the options array should keep its keys or not, but this would require a little more effort (though not much).
 [2005-04-19 07:32 UTC] michael at baselinesols dot com
Oh yeah - forgot to add, there's something that kinda smells bad around lines 1279-1281 of version 1.151 of FormBuilder.php. Currently it looks like this: foreach ($options as $option) { $element =& $this->_createRadioButtons($key, $options); } It works... but the foreach loop does nothing except waste cycles AFAICS. I think it should just be: $element =& $this->_createRadioButtons($key, $options);
 [2005-04-28 17:06 UTC] justinpatrin
All right, all right. I'm putting it all back so that the keys mean something. However I have put in a check to see if the first key of the array is an integer. If it is then the array is recreated. If you don't want this to happen make your keys strings. i.e. array('0' => 'option 0', '1' => 'option 1'); NOT array(0 => 'option 0', 1 => 'option 1'); This will be updated in the docs once I get them back up (hopefully today).
 [2005-04-28 23:29 UTC] michael at baselinesols dot com
Er... but PHP converts integer strings into integers when used as array keys, ie array('0'=>'a') === array(0=>'a') (see http://www.php.net/manual/en/language.types.array.php), so this approach won't work. That's why I had proposed this method: if (array_keys($options) == range(0, count($options)-1)) Then, if you jumble up the keys, even though they're ints, it will still differentiate. Or you can use something like array('00'=>'a', '01'=>'b'), as this representation will not be converted (but you may need to convert them to ints explicitly when processing the form).
 [2005-04-29 00:09 UTC] justinpatrin
Damn it! I knew that PHP converted strings to ints and vice-versa, but I didn't realize it did that with array keys like that. That is so unhelpful....argh! I was trying to make this so that you didn't have to change the order of the options to make this all work. Also, note that if you change the order of the options the order of the options in the select will be different. FormBuilder doesn't do any sorting on enum options. Ok, then. BTW, your solution won't quite work as array(0, 1) == array(1 => 1, 0 => 0) is true, even though the order is different. However, array(0, 1) === array(1 => 1, 0 => 0) is false. However, because of the sorting issue I mention above I don't really like this solution. I suppose now I'll just have to force the user to put the keys in the array.
 [2005-05-18 01:35 UTC] michael at baselinesols dot com
Perfect - your fix in 0.15.0 (which I think reverts to the old behaviour) is the right one IMHO :-) Even though it may require a little more coding for some people, I feel it's the best way to get customised behaviour. In PHP 5, you can use $enums = array_combine($enums, $enums) to create an array where the keys are the same as the values - for those that can't be bothered listing the keys with the values.
 [2005-05-18 04:07 UTC] justinpatrin
Yes, since PHP helpfully converts keys like '0' to integers, I have no easy way of allowing people to do both. Oh well. The docs should also be updated.