NAME

HTML::FormFu::Model::DBIC - Integrate HTML::FormFu with DBIx::Class

SYNOPSIS

Example of typical use in a Catalyst controller:

sub edit : Chained {
    my ( $self, $c ) = @_;
    
    my $form = $c->stash->{form};
    my $book = $c->stash->{book};
    
    if ( $form->submitted_and_valid ) {
        
        # update dbic row with submitted values from form
        
        $form->model->update( $book );
        
        $c->response->redirect( $c->uri_for('view', $book->id) );
        return;
    }
    elsif ( !$form->submitted ) {
        
        # use dbic row to set form's default values
        
        $form->model->default_values( $book );
    }
    
    return;
}

SETUP

For the form object to be able to access your DBIx::Class schema, it needs to be placed on the form stash, with the name schema.

This is easy if you're using Catalyst-Controller-HTML-FormFu, as you can set this up to happen in your Catalyst app's config file.

For example, if your model is named MyApp::Model::Corp, you would set this (in Config::General format):

<Controller::HTML::FormFu>
    <model_stash>
        schema Corp
    </model_stash>
</Controller::HTML::FormFu>

Or if your app's config file is in YAML format:

'Controller::HTML::FormFu':
    model_stash:
        schema: Corp

METHODS

default_values

Arguments: $dbic_row, [\%config]

Return Value: $form

$form->model->default_values( $dbic_row );

Set a form's default values from the database, to allow a user to edit them.

update

Arguments: [$dbic_row], [\%config]

Return Value: $dbic_row

$form->model->update( $dbic_row );

Update the database with the submitted form values.

create

Arguments: [\%config]

Return Value: $dbic_row

my $dbic_row = $form->model->create( {resultset => 'Book'} );

Like "update", but doesn't require a $dbic_row argument.

You need to ensure the DBIC schema is available on the form stash - see "SYNOPSIS" for an example config.

The resultset must be set either in the method arguments, or the form or block's model_config.

An example of setting the ResultSet name on a Form:

---
model_config:
  resultset: FooTable

elements:
  # [snip]

options_from_model

Populates a multi-valued field with values from the database.

This method should not be called directly, but is called for you during $form->process by fields that inherit from HTML::FormFu::Element::_Group. This includes:

HTML::FormFu::Element::Select
HTML::FormFu::Element::Checkboxgroup
HTML::FormFu::Element::Radiogroup
HTML::FormFu::Element::ComboBox

To use you must set the appropriate resultset on the element model_config:

element:
  - type: Select
    name: foo
    model_config:
      resultset: TableClass

BUILDING FORMS

single table

To edit the values in a row with no related rows, the field names simply have to correspond to the database column names.

For the following DBIx::Class schema:

package MySchema::Book;
use base 'DBIx::Class';

__PACKAGE__->load_components(qw/ Core /);

__PACKAGE__->table("book");

__PACKAGE__->add_columns(
    id     => { data_type => "INTEGER" },
    title  => { data_type => "TEXT" },
    author => { data_type => "TEXT" },
    blurb  => { data_type => "TEXT" },
);

__PACKAGE__->set_primary_key("id");

1;

A suitable form for this might be:

elements:
  - type: Text
    name: title
  
  - type: Text
    name: author
  
  - type: Textarea
    name: blurb

might_have and has_one relationships

Set field values from a related row with a might_have or has_one relationship by placing the fields within a Block (or any element that inherits from Block, such as Fieldset) with its "nested_name" in HTML::FormFu set to the relationship name.

For the following DBIx::Class schemas:

package MySchema::Book;
use base 'DBIx::Class';

__PACKAGE__->load_components(qw/ Core /);

__PACKAGE__->table("book");

__PACKAGE__->add_columns(
    id    => { data_type => "INTEGER" },
    title => { data_type => "TEXT" },
);

__PACKAGE__->set_primary_key("id");

__PACKAGE__->might_have( review => 'MySchema::Review', 'book' );

1;


package MySchema::Review;
use base 'DBIx::Class';

__PACKAGE__->load_components(qw/ Core /);

__PACKAGE__->table("review");

__PACKAGE__->add_columns(
    id          => { data_type => "INTEGER" },
    book        => { data_type => "INTEGER", is_nullable => 1 },
    review_text => { data_type => "TEXT" },
);

__PACKAGE__->set_primary_key("book");

__PACKAGE__->belongs_to( book => 'MySchema::Book' );

1;

A suitable form for this would be:

elements:
  - type: Text
    name: title
  
  - type: Block
    nested_name: review
    elements:
      - type: Textarea
        name: review_text

For might_have and has_one relationships, you generally shouldn't need to have a field for the related table's primary key, as DBIx::Class will handle retrieving the correct row automatically.

You can also set a has_one or might_have relationship using a multi value field like Select.

elements:
  - type: Text
    name: title
  
  - type: Select
    nested: review
    model_config:
      resultset: Review

This will load all reviews into the select field. If you select a review from that list, a current relationship to a review is removed and the new one is added. This requires that the primary key of the Review table and the foreign key do not match.

has_many and many_to_many relationships

The general principle is the same as for might_have and has_one above, except you should use a Repeatable element instead of a Block, and it needs to contain a Hidden field corresponding to the foreign key.

The Repeatable block's nested_name must be set to the name of the relationship.

The Repeable block's increment_field_names must be true (which is the default value).

The Repeable block's counter_name must be set to the name of a Hidden field, which is placed outside of the Repeatable block. This field is used to store a count of the number of repetitions of the Repeatable block were created. When the form is submitted, this value is used during $form->process to ensure the form is rebuilt with the correct number of repetitions.

For the following DBIx::Class schemas:

package MySchema::Book;
use base 'DBIx::Class';

__PACKAGE__->load_components(qw/ Core /);

__PACKAGE__->table("book");

__PACKAGE__->add_columns(
    id    => { data_type => "INTEGER" },
    title => { data_type => "TEXT" },
);

__PACKAGE__->set_primary_key("id");

__PACKAGE__->has_many( review => 'MySchema::Review', 'book' );

1;


package MySchema::Review;
use base 'DBIx::Class';

__PACKAGE__->load_components(qw/ Core /);

__PACKAGE__->table("review");

__PACKAGE__->add_columns(
    book        => { data_type => "INTEGER" },
    review_text => { data_type => "TEXT" },
);

__PACKAGE__->set_primary_key("book");

__PACKAGE__->belongs_to( book => 'MySchema::Book' );

1;

A suitable form for this would be:

elements:
  - type: Text
    name: title
  
  - type: Hidden
    name: review_count
  
  - type: Repeatable
    nested_name: review
    counter_name: review_count
    elements:
      - type: Hidden
        name: book
      
      - type: Textarea
        name: review_text

many_to_many selection

To select / deselect rows from a many_to_many relationship, you must use a multi-valued element, such as a Checkboxgroup or a Select with multiple set.

The field's name must be set to the name of the many_to_many relationship.

default_column

If you want to search / associate the related table by a column other it's primary key, set $field->model_config->{default_column}.

---
element:
    - type: Checkboxgroup
      name: authors
      model_config:
        default_column: foo

If you want to set columns on the link table you can do so if you add a link_values attribute to model_config:

---
element:
    - type: Checkboxgroup
      name: authors
      model_config:
        link_values:
          foo: bar

additive

The default implementation will first remove all related objects and set the new ones (see http://search.cpan.org/perldoc?DBIx::Class::Relationship::Base#set_$rel). If you want to add the selected objects to the current set of objects set additive in the model_config.

---
element:
    - type: Checkboxgroup
      name: authors
      model_config:
        additive: 1
        options_from_model: 0

"options_from_model" is set to 0 because it will try to fetch all objects from the result class Authors if model_config is specified without a resultset attribute.)

COMMON ARGUMENTS

The following items are supported in the optional config hash-ref argument to the methods default_values, update and create.

base

If you want the method to process a particular Block element, rather than the whole form, you can pass the element as a base argument.

$form->default_values(
    $row,
    {
        base => $formfu_element,
    },
);
nested_base

If you want the method to process a particular Block element by name, you can pass the name as an argument.

$form->default_values(
    $row,
    {
        nested_base => 'foo',
    }'
);

CONFIGURATION

Config options for fields

The following items are supported as model_config options on form fields.

accessor

If set, accessor will be used as a method-name accessor on the DBIx::Class row object, instead of using the field name.

delete_if_empty

Useful for editing a "might_have" related row containing only one field.

If the submitted value is blank, the related row is deleted.

For the following DBIx::Class schemas:

package MySchema::Book;
use base 'DBIx::Class';

__PACKAGE__->load_components(qw/ Core /);

__PACKAGE__->table("book");

__PACKAGE__->add_columns(
    id    => { data_type => "INTEGER" },
    title => { data_type => "TEXT" },
);

__PACKAGE__->set_primary_key("id");

__PACKAGE__->might_have( review => 'MySchema::Review', 'book' );

1;


package MySchema::Review;
use base 'DBIx::Class';

__PACKAGE__->load_components(qw/ Core /);

__PACKAGE__->table("review");

__PACKAGE__->add_columns(
    book        => { data_type => "INTEGER" },
    review_text => { data_type => "TEXT" },
);

__PACKAGE__->set_primary_key("book");

__PACKAGE__->belongs_to( book => 'MySchema::Book' );

1;

A suitable form for this would be:

elements:
  - type: Text
    name: title
  
  - type: Block
    nested_name: review
    elements:
      - type: Text
        name: review_text
        model_config:
          delete_if_empty: 1
label

To use a column value for a form field's label.

Config options for fields within a Repeatable block

delete_if_true

Intended for use on a Checkbox field.

If the checkbox is checked, the following occurs: for a has-many relationship, the related row is deleted; for a many-to-many relationship, the relationship link is removed.

An example of use might be:

elements:
  - type: Text
    name: title
  
  - type: Hidden
    name: review_count
  
  - type: Repeatable
    nested_name: review
    counter_name: review_count
    elements:
      - type: Hidden
        name: book
      
      - type: Textarea
        name: review_text
      
      - type: Checkbox
        name: delete_review
        label: 'Delete Review?'
        model_config:
          delete_if_true: 1

Note: make sure the name of this field does not clash with one of your DBIx::Class::Row method names (e.g. "delete") - see "CAVEATS".

Config options for Repeatable blocks

empty_rows

For a Repeatable block corresponding to a has-many or many-to-many relationship, to allow the user to insert new rows, set empty_rows to the number of extra repetitions you wish added to the end of the Repeatable block.

new_rows_max

Set to the maximum number of new rows that a Repeatable block is allowed to add.

If not set, it will fallback to the value of empty_rows.

Config options for options_from_model

The column used for the element values is set with the model_config value id_column - or if not set, the table's primary column is used.

element:
  - type: Select
    name: foo
    model_config:
      resultset: TableClass
      id_column: pk_col

The column used for the element labels is set with the model_config value label_column - or if not set, the first text/varchar column found in the table is used - or if one is not found, the id_column is used instead.

element:
  - type: Select
    name: foo
    model_config:
      resultset: TableClass
      label_column: label_col

To pass the database label values via the form's localization object, set localize_label

element:
  - type: Select
    name: foo
    model_config:
      localize_label: 1

You can set a condition, which will be passed as the 1st argument to "search" in DBIx::Class::ResultSet.

element:
  - type: Select
    name: foo
    model_config:
      resultset: TableClass
      condition:
        type: is_foo

You can set a condition_from_stash, which will be passed as the 1st argument to "search" in DBIx::Class::ResultSet.

key is the column-name to be passed to search, and stash_key is the name of a key on the form stash from which the value to be passed to search is found.

element:
  - type: Select
    name: foo
    model_config:
      resultset: TableClass
      condition_from_stash:
        key: stash_key

Is comparable to:

$form->element({
    type => 'Select',
    name => 'foo',
    model_config => {
        resultset => 'TableClass',
        condition => {
            key => $form->stash->{stash_key}
        }
    }
})

You can set attributes, which will be passed as the 2nd argument to "search" in DBIx::Class::ResultSet.

FAQ

Add extra values not in the form

To update values to the database which weren't submitted to the form, you can first add them to the form with add_valid.

my $passwd = generate_passwd();

$form->add_valid( passwd => $passwd );

$form->model->update( $row );

add_valid works for fieldnames that don't exist in the form.

Set a field read only

You can make a field read only. The value of such fields cannot be changed by the user even if they submit a value for it.

$field->model_config->{read_only} = 1;

- Name: field
  model_config:
    read_only: 1

See HTML::FormFu::Element::Label.

CAVEATS

To ensure your column's inflators and deflators are called, we have to get / set values using their named methods, and not with get_column / set_column.

Because of this, beware of having column names which clash with DBIx::Class built-in method-names, such as delete. - It will have obviously undesirable results!

REMOVED METHODS

new_empty_row

See empty_rows in "Config options for Repeatable blocks" instead.

new_empty_row_multi

See new_rows_max in "Config options for Repeatable blocks" instead.

Range constraint

See empty_rows in "Config options for Repeatable blocks" instead.

SUPPORT

Project Page:

http://code.google.com/p/html-formfu/

Mailing list:

http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/html-formfu

Mailing list archives:

http://lists.scsys.co.uk/pipermail/html-formfu/

BUGS

Please submit bugs / feature requests to http://code.google.com/p/html-formfu/issues/list (preferred) or http://rt.perl.org.

SUBVERSION REPOSITORY

The publicly viewable subversion code repository is at http://html-formfu.googlecode.com/svn/trunk/HTML-FormFu-Model-DBIC.

If you wish to contribute, you'll need a GMAIL email address. Then just ask on the mailing list for commit access.

If you wish to contribute but for some reason really don't want to sign up for a GMAIL account, please post patches to the mailing list (although you'll have to wait for someone to commit them).

If you have commit permissions, use the HTTPS repository url: https://html-formfu.googlecode.com/svn/trunk/HTML-FormFu-Model-DBIC

SEE ALSO

HTML::FormFu, DBIx::Class, Catalyst::Controller::HTML::FormFu

AUTHOR

Carl Franks

CONTRIBUTORS

Based on the code of DBIx::Class::HTML::FormFu, which was contributed to by:

Adam Herzog

Daisuke Maki

Mario Minati

COPYRIGHT AND LICENSE

Copyright (C) 2007 by Carl Franks

Based on the original source code of DBIx::Class::HTMLWidget, copyright Thomas Klausner.

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.8 or, at your option, any later version of Perl 5 you may have available.