new_2_perl has asked for the wisdom of the Perl Monks concerning the following question:

Ok, I have some html forms (say 4) that I want a perl-cgi program to generate. The first form the user will enter some inforamtion and then hit a continue button and depending on what info they entered is what the next forms fields will ask. My question is what is the best way to go about doing this. Should I have a seperate perl program for every form. Can I use one perl program for all of the forms and if so how do I go about saving all the variables.
Thanks,

"Premature optimization is the root of all evil." Knuth

Replies are listed 'Best First'.
Re: generating dynamic html using perl
by oakbox (Chaplain) on Jun 14, 2001 at 21:52 UTC
    I like using one script, but the drawback is that your script quickly grows into 100's of lines of 'static' HTML. If the forms you are displaying to the user are large and you want to keep your script from being cluttered, consider storing the HTML for these in seperate text files, then pulling them into the script where appropriate.

    Keeping values across several pages:

    Try to avoid the cookie route. I get more problems than solutions with cookies, especially with sites that have a lot of AOLer's and/or WebTV users as customers.

    Here are the questions I ask myself when I have a form that spans several pages.

    - Is it important that the user not be able do see the information they entered previously?
    If you don't care if the user sees previous answers, you can stick these variables into hidden fields on the next form page. This means that you don't do any writes to your database/table until the user has completed the whole process.

    # Generate hidden fields from previous form # List all field names here @keepers = qw( first_name last_name address ); # Flip through %fields hash where you have inputs # stored, and stick them into a hidden fields foreach (@keepers){ $hidden.="<input type=\"hidden\" name=\"$_\" value=\"$fields{$_} +\"> \n"; } # Stick the $hidden variable anywhere inside the <FORM> # tags of the next page
    - Are you worried about users 'bailing out' of the form process, and want to keep the info they DID enter?
    You will need to go with some kind of external database/table that will record the results from each form completion. Then you can pass the record id back to the user as a hidden field.
    # For this kind of operation, I like # to use a fast tie hash, especially # if there is a lot of traffic use DB_File; $records = tie %stuff, 'DB_File', "entries.dat", O_CREAT|O_RDWR,066 +6;
    Be sure to clean this table up occasionally because users who 'bail' will slowly fill this up with garbage. That's purely a traffic decision though. You might get away with cleaning it once a month, or be required to clean it daily with a cron job.

    I hope this has helped more than muddied the question.

    Richard
    Oakbox

      Definitely the big question is, are you allowing partially-filled-out forms to be submitted?

      I usually am against that sort of thing. It's kind of messy, but I would have each successive form get filled up with hidden form fields containing names and values from the previous form, and wait until the final step before submitting anything.

      Also I love using static html pages, it keeps the html out of your perl and that is a good thing!

      Humble initiate,
      mr.dunstan
Re: generarting dynamic html using perl
by Sifmole (Chaplain) on Jun 14, 2001 at 21:29 UTC
    You might want to take a look at CGI::Application as support for running such an application. You might also want to take a look at HTML::Template or Template Toolkit as possible ways to support the presentation of the forms.
    These can be found on www.cpan.org by doing a search
Re: generarting dynamic html using perl
by sierrathedog04 (Hermit) on Jun 14, 2001 at 21:26 UTC
    I program Perl CGI every day. What often works for me is to use no static HTML pages at all, and to generate all HTML dynamically.

    If Form A calls Form B calls Form C, then have the form handler for Form A do its processing and then generate Form B before it exits. Similarly, the form handler for Form B should do its processing and then generate Form C before it exits.

    I would not combine the form handlers for two dissimilar pages in the same script.

    However, if two pages are similar then I call them both using the same form handler. I use CGI.pm's referer() (sic, only three r's in the CGI.pm spelling) function to see which page called the form, and then have the form modify its processing accordingly.

      Thanks guys this help
Re: generarting dynamic html using perl
by dimmesdale (Friar) on Jun 14, 2001 at 21:11 UTC
    I would say it would be best to have just one perl program to make it easier to save the state of the variables across the form; you could have a function that would find the next question by a call like this: ask_question(@answers_so_far). The @answers_so_far variable would just be an array of the current answers, in any format suitable for your program(a suggestion, though I don't know the input, questions, etc., would be for the element to be like this "1:a" for question one, answer a, to make an easy split, though you could have parallel arrays, or even a hash for something similar, TIMTOWTDI). You asked, aslo:
    if so how do I go about saving all the variables

    Well, in the one-script example, you could just have a global array(or whatever you are storing the answers in), and other variables global as mandated by the problem. However, if you mean that you want to save them for each user, consider using cookies, or a variety of other methods(described in O'Rielly's CGI/PERL book, and across countless nodes, I suspect, on this site).

    I hope I've answered the question sufficiently, if not just ask again.

    UPDATE: I forgot to mention(though as sifmole said) check out some of the modules on CPAN, and read CGI.pm's docs for form generation tips, etc.

Re: generarting dynamic html using perl
by BMaximus (Chaplain) on Jun 15, 2001 at 08:21 UTC
    I like using one program. I think Sifmole's idea of using CGI::Application is well suited for a script like this. Reason being is that it will help you to keep things organized. As far as the HTML goes, use HTML::Template or Template::ToolKit to fill it out and keep the HTML away from the perl side of things. For validating form input use HTML::FormValidator, it will allow you to make rules for each field very easily.

    For saving the information of a multiple page form I would use MySQL if you have it avalable. If you don't, then use IPC::Shareable and keep track of the session by using a session id placed in a cookie (encrypt it if you can - see Crypt::CBC). Don't forget to timestamp the session as well as keep the session id available somewhere so that if somone bails out in the middle, you can clean it up later. For more ways to keep state I would suggest you get a hold of Writing Apache Modules with Perl and C also known as "The Eagle Book". I know you may be writing this in regular CGI but still it is a useful book for ideas on how to do things. You might also like to get a hold of Writing CGI Applications with Perl as well.

    Good luck,

    Brother BMaximus

    update:Fixed some lousy grammatical errors and linked IPC::Shareable to CPAN
Re: generarting dynamic html using perl
by hsweet (Pilgrim) on Jun 15, 2001 at 06:21 UTC
    A book I found very helpful with this type of stuff was Sams Teach youself perl in 24 hours. I got it last year when I was getting my school's website off the ground and was a perl virgin. It has a very good (I think) explaination of how to do exactly that sort of thing. You can do it all in one script by using hidden html fields to store information from the previous page, or cookies. Ignorance with Confidence
Re: generarting dynamic html using perl
by mattr (Curate) on Jun 15, 2001 at 16:30 UTC
    The modules mentioned above like Template::Toolkit and CGI::Application sound good. If you would like to roll your own, you could go about it in this manner:

    If you set a hidden form variable differently in each html file you can create a state machine. Each form could include two lines like this:

    <input name="mode" value="form1"> <input name="history" value="HISTORYBUF">
    inside the its form. The forms would all post to the same Perl program. The program would identify which form has called it with something like

    use strict; use CGI; use MIME::Base64; my $q = new CGI; my $mode = $q->param("mode"); if ($mode eq "form1") { &validateform1; } elsif ...

    The history variable would contain a string resulting from packing a hash of the previous page's form variables. You unpack this hash, add the current page's variables to it, and repack.

    When you are done processing the form, read the next html template file from disk and replace the phrase HISTORYBUF with the history string. This should accumulate all the pages' form variables, that is if you guarantee they all use unique names.

    It is possible to do away with packing and unpacking by just putting the bare word HISTORYBUF in without a surrounding input tag, and replacing that keyword with a whole list of input tags you generate on the spot.

    But if you are going to pack a hash into a variable (called serializing), you could use the popular Storable module which lets you freeze structures for transport and then thaw them out. You also probably want to make sure the variable doesn't include any nasty characters by using MIME Base 64 encoding.

    use Storable; # unpack history from form, and add latest form's settings my %formhash = %{ thaw( decode_base64( $q->param("history")) ) }; foreach $p ($q->params) { $formhash{$p}=$q->param($p}; } # meat of program # pack back up. $template contains template file contents. my $frozen = encode_base64( freeze \%formhash ); # someone check this +pls $template =~ s/HISTORYBUF/$frozen/; print $template; # do a "print $q->header" at top

    When I've used this method I haven't serialized the data, but it should work.

    Anyway there are many ways to do it as usual in Perl. You could also make a series of Perl programs with embedded html and which just load the previous page's form variables and don't worry about serialization of the data.

Re: generarting dynamic html using perl
by Anonymous Monk on Jun 15, 2001 at 21:43 UTC
    If it were me I would follow the first two virtues of a programer and use mason and embperl, they are easy... will save you time and effort and that means you can put it off untill it's almost due :)