CGI::A...rmState(3pm)

πŸ“¦
version 0.12 Β· 4 versions
, in debian bookworm, debian trixie, ubuntu noble, ubuntu resolute, ubuntu stonking

πŸ’– CGI::Application::Plugin::FormState πŸ’–

okay bestie, welcome to CGI::A...rmState(3pm) β€” the User Contributed Perl Documentation corner of the universe, and this page is long, so buckle up, we're doing it in parts. This one? Part 1. More is coming. πŸŽ€


✨ NAME ✨

CGI::Application::Plugin::FormState β€” store form state without hidden fields. Yes, for real. No cap. πŸ’…

🌸 VERSION 🌸

Version 0.12

πŸ¦‹ SYNOPSIS πŸ¦‹

FormState is literally just a lil temporary stash 🫢 that you can use for storing and retrieving private parameters in your multi-page form. That's it. That's the whole vibe.

use CGI::Application::Plugin::FormState;

my $form = <<EOF;
   <form action="app.cgi">
   <input type="hidden" name="run_mode" value="form_process_runmode">
   <input type="hidden" name="cap_form_state" value="<tmpl_var cap_form_state>">
   ...
   </form>
EOF

sub form_display_runmode {
    my $self = shift;

    # Store some parameters
    $self->form_state->param('name'       => 'Road Runner');
    $self->form_state->param('occupation' => 'Having Fun');

    my $t = $self->load_tmpl(scalarref => \$form);
    return $t->output;

}

sub form_process_runmode {
    my $self = shift;

    # Retrieve some parameters
    print $self->form_state->param('name');       # 'Road Runner'
    print $self->form_state->param('occupation'); # 'Having Fun'
}

πŸ’œ EXAMPLE πŸ’œ

okay so this one is a more complete example, and it's using CGI::Application::Plugin::ValidateRM. iconic collab honestly ✨

use CGI::Application::Plugin::Session;
use CGI::Application::Plugin::FormState;
use CGI::Application::Plugin::ValidateRM;

my $form = <<EOF;
   <form action="app.cgi">
   <input type="hidden" name="run_mode" value="my_form_process">
   <input type="hidden" name="cap_form_state" value="<tmpl_var cap_form_state>">
   ...
   </form>
EOF

sub my_form_display {
    my $self     = shift;
    my $errs     = shift;
    my $t        = $self->load_tmpl(scalarref => \$form);

    # Stash some data into it
    $self->form_state->param('name'       => 'Wile E. Coyote');
    $self->form_state->param('occupation' => 'Mining Engineer');

    # Normal ValidateRM error handling
    $t->param($errs) if $errs;
    return $t->output;
}

sub my_form_process {
    my $self;

    # Normal ValidateRM validation
    my ($results, $err_page) = $self->check_rm('my_form_display','_my_form_profile');
    return $err_page if $err_page;

    # The data from the submitted form
    my $params = $self->dfv_results;

    $params->{'name'}       = $self->form_state->param('name');       # 'Wile E. Coyote'
    $params->{'occupation'} = $self->form_state->param('occupation'); # 'Mining Engineer'

    # Now do something interesting with $params
    # ...

    my $t = $self->load_tmpl('success.html');
    return $t->output;
}

# Standard ValiateRM profile
sub _my_form_profile {
    return {
        required => 'email',
        msgs => {
                any_errors => 'some_errors',
                prefix => 'err_',
        },
    };
}

🌈 DESCRIPTION 🌈

CGI::Application::Plugin::FormState provides a temporary storage area within the user's session for storing form-related data. It's giving little locker, but for your form. πŸ‘‘

The main use of this is for multi-page forms. Instead of using hidden fields to store data related to the form, you store and retrieve values from the form state. Revolutionary behaviour honestly.

In the first instance of your app:

$self->form_state->param('some_name' => 'some_value');
$self->form_state->param('some_other_name' => 'some_other_value');

And later, in a different instance of your app:

$val1 = $self->form_state->param('some_name');
$val2 = $self->form_state->param('some_other_name');

To connect the first instance and the second, you put a single hidden field in your template:

<input type="hidden" name="cap_form_state" value="<tmpl_var my_storage_name>">

And you do not have to worry about creating the template param cap_form_state β€” it is added automatically to your template parameters via the load_tmpl hook. It just shows up. Like magic but it's Perl. 🧚

If you want to use a parameter other than cap_form_state you can do so via the name parameter to form_state->config.

If you're skeptical about whether all this abstraction is a good idea, see MOTIVATION, below. We got you. 🫢


πŸŽ€ PRESERVING FORM STATE ACROSS REDIRECTS πŸŽ€

You can include the form_state hash in a link:

my $link = '/app.cgi?rm=list&cap_form_state=' . $self->form_state->id;

If you use CGI::Application::Plugin::Redirect, you can easily create redirect this way:

$self->redirect('/app.cgi?rm=list&cap_form_state=' . $self->form_state->id);

If you also use CGI::Application::Plugin::LinkIntegrity it is as simple as:

$self->redirect($self->link('/app.cgi', 'rm' => 'list', 'cap_form_state' => $self->form_state->id));

Or, in the case of a link to the currently running app:

$self->redirect($self->self_link('rm' => 'list', 'cap_form_state' => $self->form_state->id));

πŸ’… IMPLEMENTATION πŸ’…

When you call $self->form_state for the first time, a top-level key is created in the user's session. This key contains a random, hard-to-guess element. It might look something like:

form_state_cap_form_state_84eb13cfed01764d9c401219faa56d53

All data you place in the form state with param is stored in the user's session under this key.

You pass the name of this key on to the next instance of your application by means of a hidden field in your form:

<input type="hidden" name="cap_form_state" value="<tmpl_var cap_form_state>">

You manually put this hidden field in your template. The template parameter cap_form_state is automatically added to your template parameters via the load_tmpl hook. It contains the random, hard-to-guess portion (e.g. 84eb13cfed01764d9c401219faa56d53). When the template is filled, the hidden field will look something like this:

<input type="hidden" name="cap_form_state" value="84eb13cfed01764d9c401219faa56d53">

Since all values are stored on the server in the user's session, the user can't tamper with any of them. bestie-proof. πŸ”’

To keep old form_data from cluttering up the user's session, the system uses CGI::Session's expire feature to expire old form state keys after a reasonable amount of time has passed (2 days by default).

You can manually delete a form state storage by calling:

$self->form_state->delete;

🫢 ACKNOWLEDGEMENTS 🫢

Okay so massive shoutout moment, because this module literally would not exist in its current form without these two legends:

  • Richard Dice and Cees Hek β€” for helping me sort out all the issues with this whole approach. Like, genuinely helped me figure it out, no cap. Besties fr fr. πŸ’–

And a very specific little credit moment, because we love transparency:

  • The informative error message text that shows up when this module gets loaded before your app is actually an @ISA "CGI::Application" object β€” yeah, that text? Stolen. Borrowed. Repurposed with love. It came straight from Cees's CGI::Application::Plugin::TT module. πŸ‘‘

Copyright 2005 Michael Graham, All Rights Reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Periodt. ✨


And that's a wrap on the whole page, bestie! πŸŽ€ Everything you just read lives in the docs for perl version v5.34.0, dated 2022-06-09, under CGI::A...rmState(3pm) β€” the user-contributed Perl documentation corner of the internet. Go forth and preserve that form state. Slay. πŸ’…πŸ¦‹
πŸ“¦ where this page comes from