Form-Processor-Model-DBIC

 view release on metacpan or  search on metacpan

lib/Form/Processor/Model/DBIC.pm  view on Meta::CPAN


With the Catalyst controller the schema is set from the model_name config
options, ($c->model($model_name)...), but it can also be set by passing 
in the schema on "new", or setting with $form->schema($schema). You can
also set a config values for whether or not to use FillInForm, and
the form namespace.


=head1 DESCRIPTION

This DBIC model will save form fields automatically to the database, will
retrieve selection lists from the database (with type => 'Select' and a 
fieldname containing a single relationship, or type => 'Multiple' and a
many_to_many pseudo-relationship), and will save the selected values (one value for 
'Select', multiple values in a mapping table for a 'Multiple' field). 

This package includes a working example using a SQLite database and a
number of forms. The templates are straightforward and unoptimized to
make it easier to see what they're doing.

=head1 METHODS

=head2 schema

The schema method is primarily intended for non-Catalyst users, so
that they can pass in their DBIx::Class schema object.

=cut

use Rose::Object::MakeMethods::Generic (
    scalar => [
        'schema'      => { interface => 'get_set_init' },
        'source_name' => {},
    ],
);

=head2 update_from_form

    my $validated = $form->update_from_form( $parameter_hash );

This is not the same as the routine called with $self->update_from_form. That
is a Catalyst plugin routine that calls this one. This routine updates or
creates the object from values in the form.

All fields that refer to columns and have changed will be updated. Field names
that are a single relationship will be updated. Any field names that are related 
to the class by "many_to_many" are assumed to have a mapping table and will be 
updated.  Validation is run unless validation has already been run.  
($form->clear might need to be called if the $form object stays in memory
between requests.)

The actual update is done in the C<update_model> method.  Your form class can
override that method (but don't forget to call SUPER) if you wish to do additional
database inserts or updates.  This is useful when a single form updates 
multiple tables, or there are secondary tables to update.

Returns false if form does not validate, otherwise returns 1.  Very likely dies on database errors.

=cut

sub update_from_form {
    my ( $self, $params ) = @_;
    return unless $self->validate($params);
    $self->schema->txn_do( sub { $self->update_model } );
    return 1;
}

=head2 model_validate

The place to put validation that requires database-specific lookups.
Subclass this method in your form.

=cut

sub model_validate {
    my ($self) = @_;
    return unless $self->validate_unique;
    return 1;
}

=head2 update_model

This is where the database row is updated. If you want to do some extra
database processing (such as updating a related table) this is the
method to subclass in your form.

This routine allows the use of non-database (non-column, non-relationship) 
accessors in your result source class. It identifies form fields as 1) column,
2) relationship, 3) other. Column and other fields are processed and update
is called on the row. Then relationships are processed.

If the row doesn't exist (no primary key or row object was passed in), then
a row is created using "create" and the fields identified as columns passed
in a hashref, followed by "other" fields and relationships.

=cut

sub update_model {
    my ($self) = @_;
    my $item   = $self->item;
    my $source = $self->source;

    # get a hash of all fields, skipping fields marked 'noupdate'
    my $prefix = $self->name_prefix;
    my %columns;
    my %multiple_m2m;
    my %other;
    my $field;
    my $value;

    # Save different flavors of fields into hashes for processing
    foreach $field ( $self->fields ) {
        next if $field->noupdate;
        my $name = $field->name;
        $name =~ s/^$prefix\.//g if $prefix;

        # If the field is flagged "clear" then set to NULL.
        $value = $field->clear ? undef : $field->value;
        if ( $source->has_relationship($name) ) {
            # If this is a relationship then may need to convert it to
            # a column name.  The common situation is where the column name
            # is "artist_id" but the belongs_to relation name is "artist"
            # where "artist" is what is used as the field name in the form.
            #
            # Another option would be to convert the $value into an object
            # as then can set the relationship like this $cd->artist_rel( $artist_object )
            ($name) = values %{ $source->relationship_info($name)->{cond} };
            $name =~ s{^self\.}{};
            $columns{$name} = $value;
        }
        elsif ( $source->has_column($name) ) {
            $columns{$name} = $value;
        }
        elsif ( $field->can('multiple') && $field->multiple == 1 ) {

            # didn't have a relationship and is multiple, so must be m2m
            $multiple_m2m{$name} = $value;
        }
        else    # neither a column nor a rel
        {
            $other{$name} = $value;
        }
    }

    # Handle database columns
    if ($item) {
        $self->updated_or_created('updated');
    }
    else {
        $item = $self->resultset->new_result( {} );
        $self->item($item);
        $self->updated_or_created('created');
    }
    for my $field_name ( keys %columns ) {
        $value = $columns{$field_name};
        my $cur = $item->$field_name;
        next unless $value || $cur;
        next if ( ( $value && $cur ) && ( $value eq $cur ) );



( run in 3.334 seconds using v1.01-cache-2.11-cpan-5623c5533a1 )