Back on the submit of forms - with use case code
Moderator: General Moderators
Re: Back on the submit of forms - with use case code
ZF already handles this elegantly, viewscript form decorator. Uses view scripts for high level formatting, renders elements via helpers so you don't duplicate HTML. Element abstraction is necessary at the micro level, unless you consider it efficient to deal with collections directly when generating things like <select>s. I believe you can use view scripts for generating each element, I usually implement that thru subForms ( Zend_Form implements composite pattern ) which uses array notation and provides for a more flexible form ( turning single fields into "multi" fields is a matter of configuring a few things and using a jquery plugin i wrote )
- inghamn
- Forum Contributor
- Posts: 174
- Joined: Mon Apr 16, 2007 10:33 am
- Location: Bloomington, IN, USA
Re: Back on the submit of forms - with use case code
You know....I really do believe it's easier and more effecient to deal with the collections directly, instead of using ZF's views and decorators. I believe the views should look like HTML. That way, they can be edited by designers with minimal PHP experience. And if you're reusing the views throughout the application, there's really not any major repetition.
Code: Select all
<?php
/**
* @param Media $this->media
* @param string $this->return_url
*/
?>
<form method="post" action="<?php echo $_SERVER['SCRIPT_NAME']; ?>">
<fieldset><legend>Media</legend>
<input name="media_id" type="hidden" value="<?php echo $this->media->getId(); ?>" />
<input name="return_url" type="hidden" value="<?php echo View::escape($this->return_url); ?>" />
<table>
<tr><td><label for="media-title">Title</label></td>
<td><input name="media[title]" id="media-title" value="<?php echo View::escape($this->media->getTitle(); ?>" />
</td>
</tr>
<tr><td colspan="2">
<div><label for="media-description">Description</label></div>
<textarea name="media[description]" id="media-description" rows="3" cols="60"><?php echo View::escape($this->media->getDescription()); ?></textarea>
</td>
</tr>
<tr><td><label for="media-roll_id">Roll</label></td>
<td><select name="media[roll_id]" id="media-roll_id">
<?php
$rolls = new RollList();
$rolls->find();
foreach ($rolls as $roll) {
$selected = $roll->getId()==$this->media->getRoll_id()
? ' selected="selected"' : '';
echo "<option value=\"{$roll->getId()}\"$selected>{$roll}</option>";
}
?>
</select>
</td>
</tr>
</table>
<button type="submit" class="save">Save</button>
<button type="button" class="cancel" onclick="document.location.href='<?php echo $this->return_url; ?>';">
Cancel
</button>
</fieldset>
</form>
- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
Re: Back on the submit of forms - with use case code
So one controller acts as a Controller and the other controller acts as a View ... sounds good ...PCSpectra wrote:In my own applications there are two controllers, one which builds the view,
josh wrote:ZF already handles this elegantly, viewscript form decorator. Uses view scripts for high level formatting, renders elements via helpers so you don't duplicate HTML.
Apparently there is a difference of opinion on this subject.inghamn wrote:You know....I really do believe it's easier and more effecient to deal with the collections directly, instead of using ZF's views and decorators. I believe the views should look like HTML.
(#10850)
- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
Re: Back on the submit of forms - with use case code
The problem with the RequestHandler idea is that is ends up being something that is never used. A FormHandler solves a very specific problem. Forms are just not like regular requests because of they must be resubmitted until valid.Chris Corbyn wrote:I was thinking that myself too. It's more of a RequestHandler than a FormHandler. The only things that make my code closer to forms than other request inputs are the fact I have isSubmitted() and isCancelled() methods. The View helper is very much tied to forms, but that's since it's view level stuff.
(#10850)
- Chris Corbyn
- Breakbeat Nuttzer
- Posts: 13098
- Joined: Wed Mar 24, 2004 7:57 am
- Location: Melbourne, Australia
Re: Back on the submit of forms - with use case code
I got this working nicely myself. My view is HTML that I write myself. If I want re-usable fragments I'll do that in the normal way I'd do it for any other view fragments.
The form processing code is more or less what I proposed... it's more about request data validation than anything else. It would need some work to be considered full-featured but I'm happy to move on for now. The only disadvantage I can note at this point is that the lack of marriage between form handling and HTML generation means there's more chance for inconsistencies in validation error messages compared with form <label> elements. For example <label>Confirm Password</label> in the view, but "Repeat Password" being used in the controller. Something to think about, but really it's a minor caveat.
(Not yet refactored...):
And the view:
It's so incredibly non-over-engineered and there's really no exciting magic happening here at all. I'm still not 100% happy with it but generally it's the way I see things working. I quite like the idea of breaking the FormHandler away into it's own class though...
The form processing code is more or less what I proposed... it's more about request data validation than anything else. It would need some work to be considered full-featured but I'm happy to move on for now. The only disadvantage I can note at this point is that the lack of marriage between form handling and HTML generation means there's more chance for inconsistencies in validation error messages compared with form <label> elements. For example <label>Confirm Password</label> in the view, but "Repeat Password" being used in the controller. Something to think about, but really it's a minor caveat.
(Not yet refactored...):
Code: Select all
$form = $this->_getForm( //Convenience method for the FormHandler creation
array('filename', 'source'),
array(
'filename' => $this->_getCurrentDownload()->getName(),
'source' => $this->_getCurrentDownload()->getSource()
)
);
$this->view->assign(array(
'title' => 'Download Manager',
'user' => $this->_getAuthenticator()->getIdentity(),
'downloads' => $this->_getAllDownloads(),
'formValues' => $form->getValues()
));
if (!$form->isSubmitted())
{
return;
}
$validator = $this->_getValidator(array(
new Swift_Website_Validation_Rules_RequiredRule('filename', 'Filename'),
new Swift_Website_Validation_Rules_RequiredRule('source', 'Download Location'),
new Swift_Website_Validation_Rules_EnumRule(
'source', array('sourceforge', 'googlecode', 'github'), 'Download Location'
)
));
if ($validator->isValid($form->getValues()))
{
$values = $form->getValues();
if (Doctrine::getTable('Download')->findAvailableByName($values['filename']))
{
$validator->addValidationError(
'This file already exists. Perhaps you need to revoke it?'
);
}
else
{
$download = new Download();
try
{
Zend_Registry::getInstance()->get('download_factory')
->createDownload($values['filename'], $values['source'], $download)
;
$download->setStable(false);
$download->save();
Zend_Registry::getInstance()->get('notification_queue')
->addNotification(
'The download was created but is marked as unstable. You may edit it.'
)
;
return $this->_helper->getHelper('Redirector')
->gotoRoute(
array(
'action'=>'edit-download',
'controller'=>'admin',
'downloadid' => $download->getDownloadId()
),
'edit-download'
);
}
catch (Swift_Website_DownloadNotFoundException $e)
{
$validator->addValidationError(
'This file could not be found at the remote location'
);
}
}
}
$this->view->assign(array(
'validationErrors' => $validator->getValidationErrors()
));Code: Select all
<h1>Download Manager</h1>
<?php require('fragments/_notifications.phtml'); ?>
<p>
From here you can view the list of downloads added for public consumption. You
can revoke these downloads (in the event of say a really critical bug) and you
can add to them.
</p>
<p>
In order to add a download it must first exist at either SourceForge, Google Code
or GitHub. This Download Manager will lookup the details of the package from
the remote resource.
</p>
<h2>Add a new download</h2>
<?php require('fragments/_validation-errors.phtml'); ?>
<form id="adddownloadform" action="" method="post">
<fieldset class="downloadfields">
<label for="field_filename">
<strong>Package Filename</strong>
<span class="instruction">
You need to specify the full name of the file that is available for download
</span>
</label>
<input type="text" class="text" id="field_filename" <?php echo $this->formAttributes('input', 'filename'); ?> />
<label for="field_sources">
<strong>Download Location</strong>
<span class="instruction">
Downloads must be hosted at a third-party site. The download manager will determine
the correct link for the remote file.
</span>
</label>
<fieldset id="field_sources" class="radios">
<label for="field_source_sourceforge">
<input type="radio" class="radio" id="field_source_sourceforge" <?php echo $this->formAttributes('radio', 'source', 'sourceforge'); ?> />
SourceForge
</label>
<label for="field_source_googlecode">
<input type="radio" class="radio" id="field_source_googlecode" <?php echo $this->formAttributes('radio', 'source', 'googlecode'); ?> />
Google Code
</label>
<label for="field_source_github">
<input type="radio" class="radio" id="field_source_github" <?php echo $this->formAttributes('radio', 'source', 'github'); ?> />
GitHub
</label>
</fieldset>
</fieldset>
<fieldset class="buttons">
<input type="submit" class="primary button" name="add" value="Add Download" />
</fieldset>
</form>
<!-- ... SNIP ... -->
Re: Back on the submit of forms - with use case code
require != partial helper... I know its more comfortable to do things the old way, but the view helpers really do do a lot of heavy lifting for you. The biggest benefit with the view helpers I see, is lets say a field needed to be changed to an entirely different type, now you don't need a designer at all! I can't really think of any example where I'd need to generate the HTML by hand especially since you can inject custom attributes, but I guess thats just a matter of opinion like what was already said, just wanting to clear up a few misconceptions
- allspiritseve
- DevNet Resident
- Posts: 1174
- Joined: Thu Mar 06, 2008 8:23 am
- Location: Ann Arbor, MI (USA)
Re: Back on the submit of forms - with use case code
Chris, have you improved on this code at all?Chris Corbyn wrote:I got this working nicely myself... It would need some work to be considered full-featured but I'm happy to move on for now.
Just a quick thought, but would it work to have error messages in the html already, just hidden until they're needed?Chris Corbyn wrote:The only disadvantage I can note at this point is that the lack of marriage between form handling and HTML generation means there's more chance for inconsistencies in validation error messages compared with form <label> elements. For example <label>Confirm Password</label> in the view, but "Repeat Password" being used in the controller. Something to think about, but really it's a minor caveat.
- Chris Corbyn
- Breakbeat Nuttzer
- Posts: 13098
- Joined: Wed Mar 24, 2004 7:57 am
- Location: Melbourne, Australia
Re: Back on the submit of forms - with use case code
Not really, I haven't worked on it since putting the site online.allspiritseve wrote:Chris, have you improved on this code at all?
Do you mean with CSS/JavaScript? I'm really not a fan of that sort of thing since it's not really accessible (screen readers will always report the errors) and how would you predict each possible combination of errors before the form has been submitted?Just a quick thought, but would it work to have error messages in the html already, just hidden until they're needed?
Re: Back on the submit of forms - with use case code
I don't know if the following structure have been discussed before, at least I haven't seen it:
So the basic idea is that the form itself is a data model.
Code: Select all
class LoginForm extends BaseFormModel {
var $username;
var $password;
var $password_again;
protected function getRules() {
//validators, sample syntax
return array('username' => 'required',
'password' => 'required',
'password_again' => array('required',
array('match' => array('password', 'password_again'))));
}
}
... controller
function postLoginForm() {
$form = new LoginForm();
if(isPostBack) {
$form->setAttribute($_POST);
if($form->isValid()) {
//do something with it
}
}
$this->render('login' => $form);
}
Re: Back on the submit of forms - with use case code
We have been discussing this concept for a while now in the skeleton framework. The idea was that you would have:wei wrote:So the basic idea is that the form itself is a data model.
- models with rules and filters (domain logic stuff)
- form models which can ask a model (which it is handling the form for) for the rules and filters,
- form models can have additional rules and filters
A good example would be a register form (like you show). The form would ask for a username, email and password and an additional field to repeat the password. That additional password field must have a validation rule as well (it must match the first password). But you don't want to put that extra rule in the User model itself. The User model only cares about getting a valid username+email+password. The repeat-password is a rule for the (model)form itself.
Re: Back on the submit of forms - with use case code
composition or inheritance for domain model + form model?
there are a few nice features for this approach:
1. the default values can be the values of the attributes declared in the form class.
2. the form model can be tested, although it is usually not complex
3. the form model is separate to: rendering, validation and persistence
there are however a few problems:
1. validation error message become naturally coupled with the form model, not such a big deal
2. how to deal with transient state? Think wizard forms, i.e. only persist in the final step of the wizard, or saved forms.
3. updating the form model usually requires changes to the form rendering/layout, which is probably ok,
4. who handles data formats? e.g. date input (could be localized) -> timestamp, could be form model or domain model
there are a few nice features for this approach:
1. the default values can be the values of the attributes declared in the form class.
2. the form model can be tested, although it is usually not complex
3. the form model is separate to: rendering, validation and persistence
there are however a few problems:
1. validation error message become naturally coupled with the form model, not such a big deal
2. how to deal with transient state? Think wizard forms, i.e. only persist in the final step of the wizard, or saved forms.
3. updating the form model usually requires changes to the form rendering/layout, which is probably ok,
4. who handles data formats? e.g. date input (could be localized) -> timestamp, could be form model or domain model