Bread:...ensible(3pm)

πŸ“¦
version 0.37 Β· 3 versions
, in debian bookworm, debian trixie, ubuntu noble, ubuntu questing

πŸ’– Bread::Board::Manual::Example::FormSensible β€” a 3pm Perl manual page, bestie πŸ’–

okay so this is one of those User Contributed Perl Documentation pages (the 3pm kind, you know the vibe), and it's literally called Bread::Board::Manual::Example::FormSensible. no cap. it's a Form::Sensible and Catalyst example and it's giving ✨architecture✨


πŸŽ€ NAME πŸŽ€

Bread::Board::Manual::Example::FormSensible β€” A Form::Sensible and Catalyst example.

like. that's it. that's the name. it does exactly what it says on the tin πŸ’…


🌸 VERSION 🌸

version 0.37


✨ SYNOPSIS ✨

okay bestie sit down, this is the whole thing, verbatim, no edits, because I would never:

  __PACKAGE__->config(
      # ... your other Catalyst configs ...

      # first put our universal
      # FormBuilder container
      # inside the config
      FormBuilder => container 'FormBuilder' => [ 'Fields' ] => as {
          service 'Form' => (
              class => 'Form::Sensible',
              block => sub {
                  my $s      = shift;
                  my $c      = $s->parent;
                  my $fields = $c->get_sub_container('Fields');
                  my $form   = Form::Sensible::Form->new( name => $s->param('name') );
                  foreach my $name ( $fields->get_service_list ) {
                      $form->add_field(
                          $fields->get_service( $name )->get
                      );
                  }

                  if ( my $state = $s->param('state') ) {
                      $form->set_values( $state );
                  }

                  $form;
              },
              parameters => {
                  name  => { isa => 'Str'                    },
                  state => { isa => 'HashRef', optional => 1 },
              }
          );
      },

      # Then we can build a set of
      # Fields for the 'foo' form
      Fields => {
          foo => container 'FooFields' => [ 'Model' ] => as {

                service 'Username' => (
                    class => 'Form::Sensible::Field::Text',
                    block => sub {
                        Form::Sensible::Field::Text->new(
                            name       => 'username',
                            validation => { regex => qr/^[0-9a-z]*$/ }
                        );
                    }
                );

                service 'Password' => (
                    class => 'Form::Sensible::Field::Text',
                    block => sub {
                        Form::Sensible::Field::Text->new(
                            name         => 'password',
                            render_hints => {
                                'HTML' => {
                                    field_type => 'password'
                                }
                            }
                        );
                    }
                );

                service 'Submit' => (
                    class => 'Form::Sensible::Field::Trigger',
                    block => sub {
                        Form::Sensible::Field::Trigger->new(
                            name => 'submit'
                        );
                    }
                );

                service 'AccessLevel' => (
                    class => 'Form::Sensible::Field::Select',
                    block => sub {
                        my $s = shift;
                        my $select = Form::Sensible::Field::Select->new(
                             name => 'access_level',
                        );
                        foreach my $access_level ( $s->param('schema')->resultset('AccessLevels')->all ) {
                            $select->add_option(
                                $access_level->id,
                                $access_level->name
                            );
                        }
                        $select;
                    },
                    dependencies => {
                        schema => depends_on('Model/schema') ,
                    },
                );
            }
      }
  );

  # later, in a
  # catalyst action ...
  sub process_foo : Local {
      my ($self, $c) = @_;
      my $Model = container 'Model' => as { service 'schema' => $c->model('DBIC') };
      my $Form  = $c->config->{FormBuilder}->create(
          Fields => $c->config->{Fields}->{foo}->create(
              Model => $Model
          )
      );

      my $f = $Form->resolve(
          service    => 'Form',
          parameters => {
              name  => 'foo',
              state => $c->req->parameters
          }
      );

      my $result = $f->validate;

      if ($result->is_valid) {
          # ...
      }
      else {
          # ...
      }
  }

no notes. iconic. πŸ”₯


πŸ¦‹ DESCRIPTION πŸ¦‹

so the lore is: this example came out of a discussion with Jay Kuri about how Bread::Board might be used in conjunction with his Form::Sensible module. collab era fr fr 🀝

the idea? create a generic form builder that's parameterized by a Fields container. and like, that could be used to store all kind of application wide behaviors. since this is in the context of Catalyst, it made sense to stuff it into the Catalyst config hash. also β€” and this is the cute part β€” they decided to use service parameters in the Form service, which lets you pass in a specific name and optionally pass in a captured state to the Form::Sensible::Form instance that is being created. parameters? slayed.

next idea: the Fields container parameter could be created for each specific form in the application. in the example above all the services are hardcoded, but (hear me out) this could be made more re-usable using the "include" keyword from Bread::Board itself, or some degree of subclassing of the Container objects. the way that's extensible?? we love to see it πŸ’…

Jay also asked about passing in the Catalyst model into the fields so that he could populate something like a select pull-down menu. again: parameterized modules. in this case they parameterized the FooFields container with a Model container which had a schema service (which was a DBIx::Class schema object). yes bestie, schema goes in, options come out πŸŽ€

from here we move into a Catalyst action to show how this might be used. we start out by wrapping the Catalyst DBIC model with a simple container, and then proceed to build our $Form object. and $Form is a Bread::Board container born of 3 levels of parameterized containers β€” which, ngl, is worth spending a little time pondering exactly what is happening there. three levels. nesting. main character energy πŸ‘‘

so once we have the $Form container, all we need to do is create an instance of our Form::Sensible::Form, passing in the name and the captured state. that's it. that's the move.

this example could likely be expanded even further to show the use of the Form::Sensible rendering as well. further creative use of parameterized containers and a couple utility methods in the Catalyst controllers could produce fairly robust and easy to use API for an application. the potential?? highkey unreal 🌈


πŸ’œ AUTHOR πŸ’œ

Stevan Little <stevan@iinteractive.com>


πŸ“ BUGS πŸ“

please report any bugs or feature requests on the bugtracker website

https://github.com/stevan/BreadBoard/issues

when submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. (do it, bestie, it makes everyone's life so much easier 🫢)


This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.


⭐ that's the whole page, no crumbs left behind. every flag, every param, every container β€” all present and accounted for. periodt. πŸ’–

okay bye bestie, this manual page was brought to you by perl v5.36.0 on 2022-12-12 πŸ¦‹βœ¨ go build something cute with parameterized containers πŸŽ€

πŸ“¦ where this page comes from