Form Block Field IDs

Permalink
I'm trying to create a custom form block that behaves properly when multiple instances of the block are placed on a page.

If I don't give every form field a unique id, c5 can repopulate the wrong fields after one of the forms has been submitted (which is understandable); eg, putting the values from one form into the other form. So, I create unique field ids (and names) by appending a unique bID (bUID), which is taken from the block's proxyBlock if it has one. Note that bID can't be used for this because it isn't necessaily unique (eg, when a block is copied).

When this is done, c5 correctly repopulates the fields after submission.

However, this gives me a problem in the controller's action_submit() function, because it doesn't know the bUID. The controller's $this->bID is always the block's bID, even if the block's bID is ambiguous. Using getBlockObject() within the controller never seems to return a block with a proxyBlock, so it can't determine a bUID that way.

action_submit() seems to be called on two separate controller instances: once passing $bID and once passing $bUID. However, because neither controller can determine the $bUID of the block to which it corresponds, the $this->bID==$bID test means that the controller should only respond to the $bID call. This, in turn, means that the controller doesn't know the $bUID, and without that, it can't access the form fields via $this->post(), since the field names have the bUID appended.

I tried passing bUID as a hidden field. This works the first time. But the bUID field itself can't have a unique id or the controller couldn't access it (because it wouldn't know what its name was). As with the other fields, the ambigious id can result in the field being over-written by c5 after submission.

Because multiple instances of the controller are created, it isn't possible to use class variables to keep track of the block's bUID; eg, having a controller respond when $this->bID==$bID, but then use $bUID to extract the post variables.

Similarly, I tried creating a UID using uniqid() in the controller's view(), passing it to the block and saving it in the controller. However, the controllers that receive action_submit() appear to be different instances to those that execute view(), so the UID class variable is not available in action_submit(). (c5 seems to create controllers very often; I logged calls to my controller's __construct() and got dozens of entries for a single view-submit cycle.)

How can I create unique form block field IDs (and names) that can be accessed by the controller's action_submit() function? Or do I need some totally different approach?

Gondwana
 
hutman replied on at Permalink Reply
hutman
Gondwana replied on at Permalink Reply
Gondwana
Thanks for the reply. Yes, I have tried that. It seems to work fine for the form itself, but creates problems in the controller because the controller isn't aware of that bUID and so can't access the post() variables. However, I think I've now found a way to pass the bUID from the form to the controller: in the form, use
$formAction = $view->action('send').'?bUID='.$bUID;

so that the bUID gets passed to the controller as part of the URL during form submission. The controller extracts it using
$bUID = \Request::request('bUID');

and can then use it to find the form fields.

This approach has passed all my 28(!) test cases, so I'm quietly confident.

I wonder if this might be a suitable topic for a tutorial.
JohntheFish replied on at Permalink Reply
JohntheFish
You could more securely add that as a hidden form field rather than as a query string parameter.
Gondwana replied on at Permalink Reply
Gondwana
@JohntheFish: I tried that first. The problem is that, since we're dealing with two (or more) such forms on a page, there needs to be two+ such fields. Each one should have a unique id and name; otherwise, problems arise when the form is repopulated (eg, c5 puts the first block's bUID in the second block's field).

I can't then work out how the controller can know the name of the hidden field that corresponds to the form that was submitted, since c5 only passes the potentially-ambiguous $bID to the controller.

I think the security implications of tacking $bUID onto the URL are minimal. The worst thing that could happen is that the wrong form would get submitted—and only then if it had already been populated with values that pass the server-side validation checks.

$bUID is hardly a secret: it's visible in the page's HTML. The stock Form block uses this tactic to pass extra IDs from block to controller (which is where I plagiarised the idea from, of course). $bID is also passed from block to controller as an element of the URL.