[Catalyst-commits] r13876 -
trunk/examples/CatalystAdvent/root/2010/pen
dhoss at dev.catalyst.perl.org
dhoss at dev.catalyst.perl.org
Mon Dec 20 18:29:10 GMT 2010
Author: dhoss
Date: 2010-12-20 18:29:09 +0000 (Mon, 20 Dec 2010)
New Revision: 13876
Modified:
trunk/examples/CatalystAdvent/root/2010/pen/fs-reflector-and-cat.pod
Log:
committing a draft
Modified: trunk/examples/CatalystAdvent/root/2010/pen/fs-reflector-and-cat.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2010/pen/fs-reflector-and-cat.pod 2010-12-20 17:35:16 UTC (rev 13875)
+++ trunk/examples/CatalystAdvent/root/2010/pen/fs-reflector-and-cat.pod 2010-12-20 18:29:09 UTC (rev 13876)
@@ -4,4 +4,225 @@
=head2 Where Do I use this Magical Beast?
-Well, most of the time, I write my forms in standard, boring old HTML. This works great, and I can use L<Data::Manager|http://search.cpan.org/dist/Data-Manager/> for my data validation quite easily, but what if I have a project I want done yesterday, that has GIGANTIC forms that I don't feel like going through and creating by hand? This is a perfect scenario for L<Form::Sensible::Reflector::DBIC|http://search.cpan.org/dist/Form-Sensible-Reflector-DBIC/>.
+Well, most of the time, I write my forms in standard, boring old HTML. This works great, and I can use L<Data::Manager|http://search.cpan.org/dist/Data-Manager/> for my data validation quite easily, but what if I have a project I want done yesterday, that has GIGANTIC forms that I don't feel like going through and creating by hand? This is a perfect scenario for L<Form::Sensible::Reflector::DBIC|http://search.cpan.org/dist/Form-Sensible-Reflector-DBIC/>. In short, if you want rapid prototyping that can actually stick around and be extensible for a while, this module's for you.
+
+=head2 What you Need
+
+=over 12
+
+=item A Catalyst Application
+
+That's what you're here for, right? :-)
+
+=item DBIx::Class
+
+My favorite ORM, but Form::Sensible::Reflector provides a base reflector class you could write a reflector for just about anything.
+This is what will be used specifically, as it gave me the best options data structure-wise from database<->forms.
+
+=item Form::Sensible::Reflector::DBIC
+
+This will be doing most of the work, at least generating the form.
+
+=back
+
+=head2 Getting Started
+
+Okay. So we have a scenario where we have an existing database, an existing DBIx::Class schema, but we need a quick interface we can set up and customize very, very quickly.
+
+Let's start with our database schema (for convenience sake, we'll just use one table as an example):
+
+ package My::Form::Sensible::App::Schema::Result::Entry;
+
+ use base qw/DBIx::Class::Core/;
+
+ __PACKAGE__->load_components(qw/ InflateColumn::DateTime TimeStamp /);
+ __PACKAGE__->table('entries');
+
+ __PACKAGE__->add_columns(
+ entryid => {
+ data_type => 'integer',
+ is_nullable => 0,
+ is_auto_increment => 1,
+ },
+ title => {
+ data_type => 'varchar',
+ size => 150,
+ is_nullable => 0,
+ },
+ body => {
+ data_type => "text",
+ is_nullable => 0,
+ },
+ author => {
+ data_type => 'integer',
+ is_nullable => 0,
+ render_hints => { field_type => "hidden" },
+ },
+ published => {
+ data_type => 'bool',
+ is_nullable => 0,
+ default => 0,
+ },
+ created_at => {
+ data_type => 'datetime',
+ is_nullable => 1,
+ set_on_create => 1,
+ },
+ updated_at => {
+ data_type => 'datetime',
+ is_nullable => 1,
+ set_on_create => 1,
+ set_on_update => 1,
+ },
+ type => {
+ data_type => 'enum',
+ is_nullable => 0,
+ is_enum => 1,
+ extra => { list => [qw/post page/] },
+ },
+ parent => {
+ data_type => 'int',
+ is_nullable => 1,
+ default => 0,
+ },
+ path => {
+ data_type => 'varchar',
+ size => '100',
+ is_nullable => 1,
+ default => '1',
+ },
+
+ );
+
+ __PACKAGE__->add_unique_constraint( [qw/ title /] );
+
+ __PACKAGE__->set_primary_key('entryid');
+
+ 1;
+
+This is a typical, no frills DBIC schema representation of a table. We need to add a few things to tell FS::Reflector::DBIC how to render things:
+
+ package My::Form::Sensible::App::Schema::Result::Entry;
+
+ use base qw/DBIx::Class::Core/;
+
+ __PACKAGE__->load_components(qw/ InflateColumn::DateTime TimeStamp /);
+ __PACKAGE__->table('entries');
+
+ __PACKAGE__->add_columns(
+ entryid => {
+ data_type => 'integer',
+ is_nullable => 0,
+ is_auto_increment => 1,
+ },
+ title => {
+ data_type => 'varchar',
+ size => 150,
+ is_nullable => 0,
+ },
+ display_title => {
+ data_type => "varchar",
+ size => 250,
+ is_nullable => 0,
+ render_hints => { field_type => "hidden" },
+ },
+ body => {
+ data_type => "text",
+ is_nullable => 0,
+ },
+ author => {
+ data_type => 'integer',
+ is_nullable => 0,
+ render_hints => { field_type => "hidden" },
+ },
+ published => {
+ data_type => 'bool',
+ is_nullable => 0,
+ default => 0,
+ render_hints => { on_label => "yes", off_label => "no", on_value => 1, off_value => 0 },
+ },
+ created_at => {
+ data_type => 'datetime',
+ is_nullable => 1,
+ set_on_create => 1,
+ render_hints => { field_type => "hidden" },
+ },
+ updated_at => {
+ data_type => 'datetime',
+ is_nullable => 1,
+ set_on_create => 1,
+ set_on_update => 1,
+ render_hints => { field_type => "hidden" },
+ },
+ type => {
+ data_type => 'enum',
+ is_nullable => 0,
+ is_enum => 1,
+ extra => { list => [qw/post page/] },
+ render_hints =>
+ { options => [ { name => 'page', value => 'page' }, { name => 'post', value => 'post' } ] }
+
+ },
+ parent => {
+ data_type => 'int',
+ is_nullable => 1,
+ default => 0,
+ render_hints => { field_type => "hidden" },
+ },
+ path => {
+ data_type => 'varchar',
+ size => '100',
+ is_nullable => 1,
+ default => '1',
+ render_hints => { field_type => "hidden" },
+ },
+
+ );
+
+ __PACKAGE__->add_unique_constraint( [qw/ title /] );
+
+ __PACKAGE__->set_primary_key('entryid');
+
+ 1;
+
+So, basically, if we don't want something showing up in our form, we mark it as hidden. With things like an "enum", it's going to show up as a C<< <select> >> drop down, and we want to make sure we get the fields named correctly.
+
+So now, let's get a Catalyst controller up and going with our final reflector code:
+
+ ## this could be any action in your app, this exact one is just a for instance:
+ sub create : Chained('auth_base') PathPart('entry/new') Args(0) {
+ my ( $self, $c ) = @_;
+
+ my $reflector = Form::Sensible::Reflector::DBIC->new;
+
+ my $form =
+ $reflector->reflect_from( $c->model('Database::Entry'), { form => { name => 'entry' } } );
+ my $renderer = Form::Sensible->get_renderer('HTML');
+ $c->stash( form => $renderer->render($form), form_object => $form, post_to => '/entry/new' ); # hate this
+
+ }
+
+
+The template to display this:
+
+ [% form.complete( post_to, 'POST') %]
+
+And that's really it. Basically, I've tried to keep the SQL data types relatively intuitive. If a column is supposed to take a textfield's worth of data, it's probably a textfield. If it's got options, you get to decide what it should be. At this point, things like C<enum> are C<< <select> >> fields. Form::Sensible is very flexible in allowing you to specify what you want to look like what, so post-creation, you can even munge the fields to look like what you want.
+
+That's really it folks. Form::Sensible::Reflector::DBIC only does a few things:
+
+=over 12
+
+=item Set up a bridge between Form::Sensible and DBIx::Class
+
+=item Allow you to have form definitions in one place, as opposed to two or three, like other systems might have you do.
+
+=item Quickly display your form without much fuss.
+
+=back
+
+Define your tables, tinker, display. That's all. I hate forms, let's make it painless :-)
+
+=head2 AUTHOR
+
+Devin Austin <dhoss at cpan.org>
More information about the Catalyst-commits
mailing list