[Catalyst-commits] r13879 - trunk/examples/CatalystAdvent/root/2010

dhoss at dev.catalyst.perl.org dhoss at dev.catalyst.perl.org
Mon Dec 20 19:47:00 GMT 2010


Author: dhoss
Date: 2010-12-20 19:47:00 +0000 (Mon, 20 Dec 2010)
New Revision: 13879

Added:
   trunk/examples/CatalystAdvent/root/2010/21.pod
Log:
publishing 21

Added: trunk/examples/CatalystAdvent/root/2010/21.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2010/21.pod	                        (rev 0)
+++ trunk/examples/CatalystAdvent/root/2010/21.pod	2010-12-20 19:47:00 UTC (rev 13879)
@@ -0,0 +1,242 @@
+=head2 One Definition, One Place: Form::Sensible::Reflector::DBIC and Catalyst
+
+You've seen the form building frameworks.  You've been there, done that, and hated it.  But what irks you most is that you have to define your forms in multiple places, when you have a data source that has PLENTY of information to give you a quick, albeit simple, form.  Well, I share your frustration, and although I can't say it makes me like forms any more,  L<Form::Sensible::Reflector::DBIC|http://search.cpan.org/dist/Form-Sensible-Reflector-DBIC/> was written by yours truly to ease a LOT of the pain associated with creating forms.
+
+=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/>.  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 L<Form::Sensible::Reflector::DBIC|http://search.cpan.org/dist/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.
+
+We get a form like this: 
+
+=begin html
+
+<img src="/static/images/2010/form-sensible-reflector-dbic-example.png" alt="" />
+
+=end html
+
+You'll notice that I've used L<TinyMCE|http://moxiecode.com/products_tinymce.php> for the main textarea.  What's great about Form::Sensible is it creates sensible divs for your CSS to be styled, and since TinyMCE just uses the DOM, you just point it at whatever id or element you want styled as a WYSIWYG editor.
+
+
+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