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

Bug #3877 extra fields in crosslink table lost on update to parent table
Submitted: 2005-03-18 18:35 UTC Modified: 2006-12-16 02:21 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-03-18 18:35 UTC] andrew dot clark at ucsb dot edu
Description: ------------ Given a table and a cross-linking table (I'll call them Net and Net_contact), where crossLinks are in use and on the form (as selects) update some attribute of a Net which has linked Net_contacts. The update to the Net table causes the Net_contacts to be deleted and reinserted, which causes any attributes of that Net_contact other than ids to be lost. For clarity, I'll say Net_contact looks like so: net_contact_id int(16) primary_key, contact_id int(16) foreign key, net_id int(16) foreign key, last_modified timestamp, role enum(primary, secondary, security, ...) According to similar bugs, $fb_crossLinkExtraFields is supposed to fix this. Defining that in the crossLink table results in the code dying: [Fri Mar 18 15:27:02 2005] [error] PHP Fatal error: Call to undefined function: processform() in /usr/local/share/pear/DB/DataObject/FormBuilder.php on line 2 384 and deletes the linked Net_contacts from the table (except for those with enum value 1, apparently). Reproduce code: --------------- <?php require_once 'include/config.php'; require_once 'DB/DataObject/FormBuilder.php'; function pe($err) { die($err->getMessage().' '.$err->getUserInfo()); } PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'pe'); DB_DataObject::debugLevel(0); $self =& DB_DataObject::factory('net'); if (PEAR::isError($self)) { die ( $self->getMessage() ); } $self->get(283); $fb_options = array( 'elementNamePrefix' => $self->ident() ); $builder =& DB_DataObject_FormBuilder::create($self,$fb_options); $form =& $builder->getForm(); $form->process(array(&$builder,'processForm'), false); echo $form->toHtml(); Expected result: ---------------- Changes to a Net shouldn't affect the Net_contact attributes. Actual result: -------------- DataObjects debug output: dataobjects_net_contact: QUERY: DELETE FROM net_contact WHERE net_contact.net_contact_id = 1055 dataobjects_net_contact: query: QUERY DONE IN 0.0011179447174072 seconds dataobjects_net_contact: 1: Clearing Cache for dataobjects_net_contact dataobjects_net_contact: FETCH: a:3:{s:10:"contact_id";s:3:"150";s:6:"net_id";s:3:"283";s:14:"net_contact_id";s:4:"1054";} dataobjects_net_contact: QUERY: DELETE FROM net_contact WHERE net_contact.net_contact_id = 1054 dataobjects_net_contact: query: QUERY DONE IN 0.0010049343109131 seconds dataobjects_net_contact: 1: Clearing Cache for dataobjects_net_contact dataobjects_net_contact: FETCH: N; dataobjects_net_contact: FETCH: Last Data Fetch'ed after 0.00086498260498047 seconds dataobjects_net_contact: QUERY: INSERT INTO net_contact (net_id , contact_id ) VALUES ( 283 , 150 ) dataobjects_net_contact: query: QUERY DONE IN 0.00096011161804199 seconds dataobjects_net_contact: QUERY: INSERT INTO net_contact (net_id , contact_id ) VALUES ( 283 , 111 ) dataobjects_net_contact: query: QUERY DONE IN 0.00095391273498535 seconds

Comments

 [2005-03-18 18:52 UTC] justinpatrin
What version of FormBuilder are you using? The code was changed a while ago not to delete all of the crossLinks and re-insert them.
 [2005-03-18 19:16 UTC] andrew dot clark at ucsb dot edu
It claims to be 0.12.1.
 [2005-03-18 19:52 UTC] justinpatrin
And you're absolutely sure that the version your script is including is this one? The only time FormBuilder will delete a record is after it has successfully fetched a crossLink or tripleLink record and the value is not set upon submission. It won't delete a crossLink unless you uncheck the chekcbox. Otherwise crossLinkExtraFields would be useless. crossLinkExtraFields doesn't "fix" this, it allows editing of these extra fields next to their crossLink entries. Now that I've said all of that, it looks to me from your debug info that all is working as expected. Try clicking a crossLink checkbox and hitting submit. Then go an edit that crossLink record (change one of its "extra" fields). Then come back to the record where you checked the crossLink entry, change something (do not uncheck the checkbox) and click submit. Then go back to the crossLink record and see if your extra field is still filled out. It should be. The errors about processForm are from errors when creating FormBuilder objects for the crossLinkExtraFields. I ought to add some more error checking to that so that we can see where these are failing...
 [2005-03-18 20:04 UTC] andrew dot clark at ucsb dot edu
Actually, I'm using select menus, rather than checkboxes. It seems that crossLinkExtraFields doesn't display in that case, but I'll try checkboxes. I'm pretty darn sure I'm using the current version, as linkNewValue is present.
 [2005-03-18 20:13 UTC] justinpatrin
I see. Yes, crossLinkExtraFields won't work when the crossLinks are displaying as select boxes. I should perhaps put a check in the code to disallow them when selects are used. Or perhaps use some Javascript to display them...no, that would be too hard. Maybe I should force the checkbox kind when there are extra fields... Anyway, let me know how it works when you use checkboxes instead. And let me know if you're *really* losing the extra data. You will, of course, lose it if you deselect the crossLink (as the record is deleted) but you shouldn't lose it if you leave the value selected.
 [2005-03-18 20:22 UTC] andrew dot clark at ucsb dot edu
Checkboxes and my formElementPrefix might be unhappy together, I'll have to do more debug on that one. As far as the test you proposed: "Now that I've said all of that, it looks to me from your debug info that all is working as expected..." I took a net, added myself as a net contact (default role is secondary (enum 2), then changed the role of that net_contact to financial (enum 4), went back to the net, changed it, and I'm not deleted, but I am set back to the default role (secondary). DataObject debug shows that row being deleted and reinserted. I am losing Net_contact crosslinks (strangely, for some Nets, just those that aren't role 1 (primary) but I need to muck with that some more) if crossLinkExtraFields is set in Net_contact. In that case, the code exits with the error I mentioned. Reloading the page that displays the Net object shows that all Net_contacts (except for the primary contact as in cases above) have been deleted.
 [2005-03-18 20:31 UTC] justinpatrin
formElementPrefix? Do you mean elementNamePrefix? What prefix are you using? I know for absolutely sure that FormBuilder (in my testing environment) does not delete crossLink records which have not been unchecked when processing them. I just tested without extraFields and they stayed intact.
 [2005-03-18 20:32 UTC] justinpatrin
Also, try: PEAR::setErrorHandling(PEAR_ERROR_DIE); and turning on crossLinkExtraFields to see what error is causing it to fail.
 [2005-03-28 15:15 UTC] andrew dot clark at ucsb dot edu
Was out on vacation for a week. I'll try this with the new release of FB and update the bug if it still exists.
 [2005-03-28 16:04 UTC] andrew dot clark at ucsb dot edu
The problem doesn't exist when using checkboxes, only using selects. I'll use checkboxes for the time being. I get: [error] PHP Fatal error: Call to undefined function: processform() in /usr/local/share/pear/DB/DataObject/FormBuilder.php on line 2449 Changing the pear error handling to PEAR::setErrorHandling(PEAR_ERROR_DIE) in the script doesn't provide any additional output
 [2005-03-28 16:14 UTC] justinpatrin
Hmmmm...inetersting comment...I'm not sure I tested it with the multi-select. Could you take a look at the form element names when using checkboxes and when using the multi-select and let me know how they're different? I'm still working on getting my development environment back up.
 [2005-03-28 17:44 UTC] andrew dot clark at ucsb dot edu
Pardon the HTML cruft, but I'm not sure exactly what difference you're looking for. For a multiple select, the select element name looks like so: (I put an md5 hash on via elementNamePrefix) <select multiple="multiple" name="4eb202dba208f0b825fe60124fcc0986__crossLink_net_contact[]"> <option value="98">Blow, Joe</option> ... For checkbox, like so: <tr> <td bgcolor="lightgrey"><label for="qf_f15923">Blow, Joe</label></td> <td style="text-align: center"><input name="4eb202dba208f0b825fe60124fcc0986__crossLink_net_contact[77]" type="checkbox" value="77" id="qf_f15923" /></td> <td style="text-align: center"><select name="4eb202dba208f0b825fe60124fcc0986__crossLink_net_contact__77_role_"> <option value=""></option> <option value="primary">primary</option> <option value="secondary">secondary</option> <option value="security">security</option> <option value="financial">financial</option> </select></td>
 [2005-03-28 18:53 UTC] justinpatrin
I've committed a few changes to CVS which *may* make a difference. I realized that FB isn't checking the return of find() before calling fetch() which may return true in this situation, I'm not sure. Please test it and see what it does. Also, is that the full debug output in your original bug post?
 [2005-03-28 18:59 UTC] andrew dot clark at ucsb dot edu
I'll try cvs. The debug output in the first post is DataObject debug level 1.
 [2005-03-28 19:15 UTC] justinpatrin
Could you please do a run with debug level 3 before updating your FormBuilder?
 [2005-03-29 14:03 UTC] andrew dot clark at ucsb dot edu
Here's the debug 3 showing the delete and reinsert without crossLinkExtraFields and using a select form, instead of checkboxes. I changed the description field for the net, which deleted and reinserted the net_contact. Do you want the entire debug output (it's quite large)? <code><B>dataobjects_net_contact: CONNECT:</B> USING CACHED CONNECTION</code><BR> <code><B>dataobjects_net_contact: QUERY:</B> DELETE FROM net_contact WHERE net_contact.net_contact_id = 1097</code><BR> <code><B>dataobjects_net_contact: query:</B> QUERY DONE IN 0.018433809280396 seconds</code><BR> <code><B>dataobjects_net_contact: 1:</B> Clearing Cache for dataobjects_net_contact</code><BR> <code><B>dataobjects_net_contact: FETCH:</B> N;</code><BR> <code><B>dataobjects_net_contact: FETCH:</B> Last Data Fetch'ed after 0.00091814994812012 seconds</code><BR> <code><B>dataobjects_net_contact: CONNECT:</B> USING CACHED CONNECTION</code><BR> <code><B>dataobjects_net_contact: CONNECT:</B> USING CACHED CONNECTION</code><BR> <code><B>dataobjects_net_contact: QUERY:</B> INSERT INTO net_contact (net_id , contact_id ) VALUES ( 405 , 111 ) </code><BR> <code><B>dataobjects_net_contact: query:</B> QUERY DONE IN 0.0092880725860596 seconds</code><BR>
 [2005-03-29 14:53 UTC] justinpatrin
I'd like to know if there was a FIND run before that delete. Really I want to know if the FIND returned any results. Go ahead and try the CVS version as well and see if it's any different.
 [2005-03-29 15:20 UTC] andrew dot clark at ucsb dot edu
Indeed there was a fine. And this is sort of interesting, because although it finds two net_contacts (which is correct), it leaves the first (the one with role and id of 1) alone: <code><B>dataobjects_net_contact: CONNECT:</B> USING CACHED CONNECTION</code><BR> <code><B>dataobjects_net_contact: QUERY:</B> SELECT contact_id , net_id , net_contact_id FROM net_contact WHERE net_contact.net_id = 405 </code><BR> <code><B>dataobjects_net_contact: query:</B> QUERY DONE IN 0.00089001655578613 seconds</code><BR> <code><B>dataobjects_net_contact: __find:</B> CHECK autofetchd </code><BR> <code><B>dataobjects_net_contact: __find:</B> DONE</code><BR> <code><B>dataobjects_net_contact: FETCH:</B> a:3:{s:10:"contact_id";s:1:"1";s:6:"net_id";s:3:"405";s:14:"net_contact_id";s:4:"1098";}</code><BR> <code><B>dataobjects_net_contact: fetchrow LINE:</B> contact_id = 1</code><BR> <code><B>dataobjects_net_contact: fetchrow LINE:</B> net_id = 405</code><BR> <code><B>dataobjects_net_contact: fetchrow LINE:</B> net_contact_id = 1098</code><BR> <code><B>dataobjects_net_contact: fetchrow:</B> net_contact DONE</code><BR> <code><B>dataobjects_net_contact: FETCH:</B> a:3:{s:10:"contact_id";s:3:"111";s:6:"net_id";s:3:"405";s:14:"net_contact_id";s:4:"1097";}</code><BR> <code><B>dataobjects_net_contact: fetchrow LINE:</B> contact_id = 111</code><BR> <code><B>dataobjects_net_contact: fetchrow LINE:</B> net_id = 405</code><BR> <code><B>dataobjects_net_contact: fetchrow LINE:</B> net_contact_id = 1097</code><BR> <code><B>dataobjects_net_contact: fetchrow:</B> net_contact DONE</code><BR> <code><B>dataobjects_net_contact: CONNECT:</B> USING CACHED CONNECTION</code><BR> <code><B>dataobjects_net_contact: CONNECT:</B> USING CACHED CONNECTION</code><BR> <code><B>dataobjects_net_contact: CONNECT:</B> USING CACHED CONNECTION</code><BR> <code><B>dataobjects_net_contact: QUERY:</B> DELETE FROM net_contact WHERE net_contact.net_contact_id = 1097</code><BR> <code><B>dataobjects_net_contact: query:</B> QUERY DONE IN 0.018433809280396 seconds</code><BR> <code><B>dataobjects_net_contact: 1:</B> Clearing Cache for dataobjects_net_contact</code><BR> <code><B>dataobjects_net_contact: FETCH:</B> N;</code><BR> <code><B>dataobjects_net_contact: FETCH:</B> Last Data Fetch'ed after 0.00091814994812012 seconds</code><BR> <code><B>dataobjects_net_contact: CONNECT:</B> USING CACHED CONNECTION</code><BR> <code><B>dataobjects_net_contact: CONNECT:</B> USING CACHED CONNECTION</code><BR> <code><B>dataobjects_net_contact: QUERY:</B> INSERT INTO net_contact (net_id , contact_id ) VALUES ( 405 , 111 ) </code><BR> <code><B>dataobjects_net_contact: query:</B> QUERY DONE IN 0.0092880725860596 seconds</code><BR> I'll try CVS.
 [2005-03-29 16:10 UTC] andrew dot clark at ucsb dot edu
It's still deleting and reinserting w/o preserving the role. Here's DO debug level 3 starting at the point of the find: dataobjects_net_contact: __find: dataobjects_net_contact: CONNECT: USING CACHED CONNECTION dataobjects_net_contact: CONNECT: USING CACHED CONNECTION dataobjects_net_contact: CONNECT: USING CACHED CONNECTION dataobjects_net_contact: QUERY: SELECT contact_id , net_id , net_contact_id FROM net_contact WHERE net_contact.net_id = 405 dataobjects_net_contact: query: QUERY DONE IN 0.0042400360107422 seconds dataobjects_net_contact: __find: CHECK autofetchd dataobjects_net_contact: __find: DONE dataobjects_net_contact: FETCH: a:3:{s:10:"contact_id";s:1:"1";s:6:"net_id";s:3:"405";s:14:"net_contact_id";s:4:"1103";} dataobjects_net_contact: fetchrow LINE: contact_id = 1 dataobjects_net_contact: fetchrow LINE: net_id = 405 dataobjects_net_contact: fetchrow LINE: net_contact_id = 1103 dataobjects_net_contact: fetchrow: net_contact DONE dataobjects_net_contact: FETCH: a:3:{s:10:"contact_id";s:2:"13";s:6:"net_id";s:3:"405";s:14:"net_contact_id";s:4:"1101";} dataobjects_net_contact: fetchrow LINE: contact_id = 13 dataobjects_net_contact: fetchrow LINE: net_id = 405 dataobjects_net_contact: fetchrow LINE: net_contact_id = 1101 dataobjects_net_contact: fetchrow: net_contact DONE dataobjects_net_contact: CONNECT: USING CACHED CONNECTION dataobjects_net_contact: CONNECT: USING CACHED CONNECTION dataobjects_net_contact: CONNECT: USING CACHED CONNECTION dataobjects_net_contact: QUERY: DELETE FROM net_contact WHERE net_contact.net_contact_id = 1101 dataobjects_net_contact: query: QUERY DONE IN 0.0011110305786133 seconds dataobjects_net_contact: 1: Clearing Cache for dataobjects_net_contact dataobjects_net_contact: FETCH: a:3:{s:10:"contact_id";s:3:"111";s:6:"net_id";s:3:"405";s:14:"net_contact_id";s:4:"1102";} dataobjects_net_contact: fetchrow LINE: contact_id = 111 dataobjects_net_contact: fetchrow LINE: net_id = 405 dataobjects_net_contact: fetchrow LINE: net_contact_id = 1102 dataobjects_net_contact: fetchrow: net_contact DONE dataobjects_net_contact: CONNECT: USING CACHED CONNECTION dataobjects_net_contact: CONNECT: USING CACHED CONNECTION dataobjects_net_contact: CONNECT: USING CACHED CONNECTION dataobjects_net_contact: QUERY: DELETE FROM net_contact WHERE net_contact.net_contact_id = 1102 dataobjects_net_contact: query: QUERY DONE IN 0.0027260780334473 seconds dataobjects_net_contact: 1: Clearing Cache for dataobjects_net_contact dataobjects_net_contact: FETCH: N; dataobjects_net_contact: FETCH: Last Data Fetch'ed after 0.00092601776123047 seconds dataobjects_net_contact: CONNECT: USING CACHED CONNECTION dataobjects_net_contact: CONNECT: USING CACHED CONNECTION dataobjects_net_contact: QUERY: INSERT INTO net_contact (net_id , contact_id ) VALUES ( 405 , 13 ) dataobjects_net_contact: query: QUERY DONE IN 0.00089597702026367 seconds dataobjects_net_contact: CONNECT: USING CACHED CONNECTION dataobjects_net_contact: CONNECT: USING CACHED CONNECTION dataobjects_net_contact: QUERY: INSERT INTO net_contact (net_id , contact_id ) VALUES ( 405 , 111 ) dataobjects_net_contact: query: QUERY DONE IN 0.001162052154541 seconds
 [2005-03-30 18:47 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. Whew, I finally figured this one out. As I expected, it was a difference in how the crossLinks are named when it's a multi-select vs. checkboxes. This also helped me find and fix 2 other errors. Thanks. :-)
 [2005-03-30 19:20 UTC] andrew dot clark at ucsb dot edu
Great to hear! I'll try CVS again and verify. crossLinkExtraFields with selects would probably require hierselects, eh? Any idea how onerus implementing that would be? I would prefer multi-select to checkboxes, if possible.
 [2005-03-30 19:27 UTC] andrew dot clark at ucsb dot edu
Seems to work for me using CVS.
 [2005-03-30 23:36 UTC] justinpatrin
Well, it would require doing something like linkNewValue, i.e. using JavaScript to hide and display fields. I suppose this shouldn't be *too* hard...I'd have to invent yet another QF element to make it work correctly, though. ;-) Please put it in as a feature request so it doesn't get lost. I'll see what I can do if I have time.