The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

CatalystX::RoseIntegrator - Catalyst/Rose Base Controller

WORK IN PROGRESS

WARNING: This is beta software. It works for me, not necessarily for you.

This documentation may lack information and / or be wrong. But it is a good start.

Feel free to report anything you would like to report ;-)

SYNOPSIS

    package MyApp::Controller::Books;
    use base 'CatalystX::RoseIntegrator';

    # optional config setup
    __PACKAGE__->config(
        'CatalystX::RoseIntegrator' = {
            template_type => 'TT',    # default is 'TT' (i.e. TT2), only TT supported for the moment
        }
    );

    # looks for books/edit.fb form configuration file, based on the presence of
    # the ":Form" attribute.
    sub edit : Local Form {
        my ( $self, $c, @args ) = @_;

        my $form = $self->form;

        # add email form field to fields already defined edit.fb
        $form->add_field( name => 'email', type => 'email' );

        if ( $form->was_submitted ) {
            if ( $self->has_error ) {
                $c->stash->{ERROR}          = "INVALID FORM";
            }
            else
            {
                return $c->response->body("VALID FORM");
            }
        }
    }

    # explicitedly use books/edit.fb, otherwise books/view.fb is used
    sub view : Local Form('/books/edit') {
        my ( $self, $c ) = @_;
        $c->stash->{template} = "books/edit.tt" # TT2 template;
    }

DESCRIPTION

This base controller gives the power of Rose HTML/DB Objects to Catalyst in a simple manner.

Rose::HTML/DB::Object(s)? usage within Catalyst is straightforward. Since Catalyst handles page rendering, you don't call RHTMLO's methods, as you would normally. Instead, you simply add a :Form attribute to each method that you want to associate with a form. This will give you access to a RHTMLO::Form $self->form object within that controller method:

    # An editing screen for books
    sub edit : Local Form {
        my ( $self, $c ) = @_;
        $self->form->method('post');   # set form method
    }

The out-of-the-box setup is to look for a form configuration file, usually in Config::General format but any format recognized by Config::Loader should work, named for the current action url. So, if you were serving /books/edit, this plugin would look for:

    root/forms/books/edit.conf

(The path is configurable) If no source file is found, an error is throwed.

Here is an example edit.yaml file (borrowed and adapted from you know where):

    # Form config file root/forms/books/edit.yaml
    name: books_edit
    method: post
    rdbo: Book
    fields_order: title, author, isbn, desc, submit
    fields:
        title:
            label: Book Title
            type:  text
            required: 1
            regexp: ^.{0,50}$
            message: Max 50 characters
            regexp: ^[\w\.\(\)\s-]*$
            message: Forbidden character(s)
        author:
            label: Author's Name
            type:  text
            required: 1
            regexp: ^.{0,50}$
            message: Max 50 characters
            regexp: ^[\w\.\(\)\s-]*$
            message: Forbidden character(s)
        isbn:
            label: ISBN#
            type:  text
            regexp: ^(\d{10}|\d{13})$
            message: Invalid ISBN number
            required: 1
        desc:
            label: Description
            type:  textarea
            cols:  80
            rows:  5
        submit:
            type: submit

Notice the 'rdbo' line, that specifies an optional RDBO class to tie to. With this, you don't have to specify sizes for the fields and you will be allowed to load and save content from / to the form directly from / to the DB.

To do that, use this:

    my $book = $c->model('Book')->new;
    $form->book_from_form($book);
    $book->save,

and

    my $book = $c->model('Book')->new(id => 15);
    $form->init_with_book($book);

Also notice that each validation regexp is associated with an error message. In the regexp field, instead of a real regexp, you can write -auto to check automatically the value with the type of the field (RHTMLO side). You can also enter -same-as field to check for equality (ASCII sense) with another field. This is good for password or email confirmations.

This will automatically create a complete form for you, using the specified fields. Note that the root/forms path is configurable; this path is used by default to integrate with the TTSite helper.

Within your controller, you can call any method that you would on a normal RHTMLO object on the $self->form object. To manipulate the field named desc, simply call the field() method:

    # Change our desc field dynamically
    $self->form->field(desc,
        name     => 'desc',
        label    => 'Book Description',
        required => 1
    );

To populate field options for country, you might use something like this to iterate through the database:

    # not tested!
    $self->form->field(country,
        name    => 'country',
        options =>
          [ map { [ $_->id, $_->name ] } $c->model('Country::Manager')->get_countries ],
    );

The RHTMLO methodolody is to handle both rendering and validation of the form. As such, the form will "loop back" onto the same controller method. Within your controller, you would then use the standard RHTMLO submit/validate check:

    if ( $self->form->was_submitted && !$self->has_error ) {
        $c->forward('/books/save');
    }

This would forward to /books/save if the form was submitted and passed field validation. Otherwise, it would automatically re-render the form with invalid fields highlighted, leaving the database unchanged.

To render the form in your tt2 template for example, you can use something like this to get a standard form for all your site:

    <!-- root/src/myform.tt, included in other templates -->
    [% c.localize("An asterisk (<strong>*</strong>) indicates a mandatory field.") %]<br>
    [% RINTEG.start_xhtml %]

    [% FOREACH field IN RINTEG.ordered_fields %]
        [% field.xhtml_label %] [% IF field.required %] <strong>*</strong> [% END %]
        [% IF field.error %]
            <span class="error small">
                [% field.error %]
            </span>
        [% END %]
        <br>
        [% field.xhtml_field %]
        <br><br>
    [% END %]

    [% RINTEG.end_xhtml %]

SEE ALSO

Catalyst::Controller::FormBuilder on which it is originally derived Catalyst::Manual, Catalyst::Request, Catalyst::Response Rose::HTML::Objects, Rose::DB::Object

AUTHOR

Copyright (c) 2007 Alexandre Jousset <cpan@gtmp.org>. All Rights Reserved.

Thanks to Juan Camacho for inspiration (Catalyst::Controller::FormBuilder)

This library is free software, you can redistribute it and/or modify it under the same terms as Perl itself.