Maypole::Model::CDBI::DFV - Class::DBI::DFV model for Maypole.


Maypole documentation Contained in the Maypole distribution.

Index


Code Index:

NAME

Top

Maypole::Model::CDBI::DFV - Class::DBI::DFV model for Maypole.

SYNOPSIS

Top

    package Foo;
    use 'Maypole::Application';

    Foo->config->model("Maypole::Model::CDBI::DFV");
    Foo->setup([qw/ Foo::SomeTable Foo::Other::Table /]);

    # Look ma, no untainting

    sub Foo::SomeTable::SomeAction : Exported {

        . . .

    }

DESCRIPTION

Top

This module allows you to use Maypole with previously set-up Class::DBI classes that use Class::DBI::DFV;

Simply call setup with a list reference of the classes you're going to use, and Maypole will work out the tables and set up the inheritance relationships as normal.

Better still, it will also set use your DFV profile to validate input instead of CGI::Untaint. For teh win!!

METHODS

Top

setup

  This method is inherited from Maypole::Model::Base and calls setup_database,
  which uses Class::DBI::Loader to create and load Class::DBI classes from
  the given database schema.

setup_database

  This method loads the model classes for the application

class_of

  returns class for given table

adopt

This class method is passed the name of a model class that represensts a table and allows the master model class to do any set-up required.

check_params

  Checks parameters against the DFV profile for the class, returns the results
  of DFV's check.

  my $dfv_results = __PACKAGE__->check_params($r->params);

Action Methods

Top

Action methods are methods that are accessed through web (or other public) interface.

Inherited from Maypole::Model::CDBI::Base except do_edit (below)

do_edit

If there is an object in $r->objects, then it should be edited with the parameters in $r->params; otherwise, a new object should be created with those parameters, and put back into $r->objects. The template should be changed to view, or edit if there were any errors. A hash of errors will be passed to the template.

do_delete

Inherited from Maypole::Model::CDBI::Base.

This action deletes records

Inherited from Maypole::Model::CDBI::Base.

This action method searches for database records.

list

Inherited from Maypole::Model::CDBI::Base.

The list method fills $r->objects with all of the objects in the class. The results are paged using a pager.

SEE ALSO

Top

Maypole::Model::Base

Maypole::Model::CDBI::Base

AUTHOR

Top

Aaron Trevena.

LICENSE

Top

You may distribute this code under the same terms as Perl itself.


Maypole documentation Contained in the Maypole distribution.
package Maypole::Model::CDBI::DFV;
use strict;

use Data::FormValidator;
use Data::Dumper;

use Maypole::Config;
use Maypole::Model::CDBI::AsForm;

use base qw(Maypole::Model::CDBI::Base);

Maypole::Config->mk_accessors(qw(table_to_class _COLUMN_INFO));

sub setup_database {
    my ( $self, $config, $namespace, $classes ) = @_;
    $config->{classes}        = $classes;
    foreach my $class (@$classes) {
      $namespace->load_model_subclass($class);
    }
    $namespace->model_classes_loaded(1);
    $config->{table_to_class} = { map { $_->table => $_ } @$classes };
    $config->{tables}         = [ keys %{ $config->{table_to_class} } ];
}

sub class_of {
    my ( $self, $r, $table ) = @_;
    return $r->config->{table_to_class}->{$table};
}

sub adopt {
    my ( $self, $child ) = @_;
    if ( my $col = $child->stringify_column ) {
        $child->columns( Stringify => $col );
    }
}

sub check_params {
  my ($class,$params) = @_;
  return Data::FormValidator->check($params, $class->dfv_profile);
}


sub do_edit : Exported {
  my ($class, $r, $obj) = @_;

  my $config   = $r->config;
  my $table    = $r->table;

  # handle cancel button hit
  if ( $r->params->{cancel} ) {
    $r->template("list");
    $r->objects( [$class->retrieve_all] );
    return;
  }


  my $errors;
  if ($obj) {
    ($obj,$errors) = $class->_do_update($r,$obj);
  } else {
    ($obj,$errors) = $class->_do_create($r);
  }

  # handle errors, if none, proceed to view the newly created/updated object
  if (ref $errors) {
    # pass errors to template
    $r->template_args->{errors} = $errors;
    # Set it up as it was:
    $r->template_args->{cgi_params} = $r->params;
    $r->template("edit");
  } else {
    $r->template("view");
  }

  $r->objects( $obj ? [$obj] : []);
}

sub _do_update {
  my ($class,$r,$obj) = @_;
  my $errors;
  my $dfv_results = Data::FormValidator->check($r->{params}, $class->dfv_profile);

  # handle dfv errors
  if ( $dfv_results->has_missing ) {   # missing fields
    foreach my $field ( $dfv_results->missing ) {
      $errors->{$field} = "$field is required";
    }
  }
  if ( $dfv_results->has_invalid ) {   # Print the name of invalid fields
    foreach my $field ( $dfv_results->invalid ) {
      $errors->{$field} =  "$field is invalid: " . $dfv_results->invalid( $field );
    }
  }


  my $this_class_params = {};


  # NG changes start here.
  # Code below fails to handle multi col PKs
  my @pks = $class->columns('Primary');

  foreach my $param ( $class->columns ) {
    # next if ($param eq $class->columns('Primary'));
    next if grep {/^${param}$/} @pks;

    my $value = $r->params->{$param};
    next unless (defined $value);
    $this_class_params->{$param} = ( $value eq '' ) ?  undef : $value;
  }

  # update or make other related (must_have, might_have, has_many  etc )
  unless ($errors) {
    foreach my $accssr ( grep ( !(exists $this_class_params->{$_}) , keys %{$r->{params}} ) ) {
      # get related object if it exists
      my $rel_meta = $class->related_meta('r',$accssr);
      if (!$rel_meta) {
	$r->warn("[_do_update] No relationship for $accssr in " . ref($class));
	next;
      }

      my $rel_type  = $rel_meta->{name};
      my $fclass    = $rel_meta->{foreign_class};
      my ($rel_obj,$errs);
      $rel_obj = $fclass->retrieve($r->params->{$accssr});
      # update or create related object
      ($rel_obj, $errs) = ($rel_obj)
	? $fclass->_do_update($r, $rel_obj)
	  : $obj->_create_related($accssr, $r->params);
      $errors->{$accssr} = $errs if ($errs);
    }
  }

  unless ($errors) {
    $obj->set( %$this_class_params );
    $obj->update;
  }

  return ($obj,$errors);
}

sub _do_create {
  my ($class,$r) = @_;
  my $errors;

  my $this_class_params = {};
  foreach my $param ( $class->columns ) {
    next if ($param eq $class->columns('Primary'));
    my $value = $r->params->{$param};
    next unless (defined $value);
    $this_class_params->{$param} = ( $value eq '' ) ?  undef : $value;
  }

  my $obj;

  my $dfv_results = Data::FormValidator->check($r->{params}, $class->dfv_profile);
  if ($dfv_results->success) {
    $obj = $class->create($this_class_params);
  } else {
    # handle dfv errors
    if ( $dfv_results->has_missing ) {   # missing fields
      foreach my $field ( $dfv_results->missing ) {
	$errors->{$field} = "$field is required";
      }
    }
    if ( $dfv_results->has_invalid ) {   # Print the name of invalid fields
      foreach my $field ( $dfv_results->invalid ) {
	$errors->{$field} =  "$field is invalid: " . $dfv_results->invalid( $field );
      }
    }
  }

  # Make other related (must_have, might_have, has_many  etc )
  unless ($errors) {
    foreach my $accssr ( grep ( !(exists $this_class_params->{$_}) , keys %{$r->{params}} ) ) {
      my ($rel_obj, $errs) = $obj->_create_related($accssr, $r->{params}{$accssr});
      $errors->{$accssr} = $errs if ($errs);
    }
  }
  return ($obj,$errors);
}


sub _create_related {
  # self is object or class, accssr is accssr to relationship, params are
  # data for relobject, and created is the array ref to store objs
  my ( $self, $accssr, $params )  = @_;
  $self->_croak ("Can't make related object without a parent $self object") unless (ref $self);
  my $created = [];
  my $rel_meta = $self->related_meta('r',$accssr);
  if (!$rel_meta) {
    $self->_carp("[_create_related] No relationship for $accssr in " . ref($self));
    return;
  }

  my $rel_type  = $rel_meta->{name};
  my $fclass    = $rel_meta->{foreign_class};

  my ($rel, $errs);

  # Set up params for might_have, has_many, etc
  if ($rel_type ne 'has_own' and $rel_type ne 'has_a') {
    # Foreign Key meta data not very standardized in CDBI
    my $fkey= $rel_meta->{args}{foreign_key} || $rel_meta->{foreign_column};
    unless ($fkey) { die " Could not determine foreign key for $fclass"; }
    my %data = (%$params, $fkey => $self->id);
    %data = ( %data, %{$rel_meta->{args}->{constraint} || {}} );
    ($rel, $errs) =  $fclass->_do_create(\%data);
  }
  else {
    ($rel, $errs) =  $fclass->_do_create($params);
    unless ($errs) {
      $self->$accssr($rel->id);
      $self->update;
    }
  }
  return ($rel, $errs);
}


sub _column_info {
  my $class = shift;

  # get COLUMN INFO from DB
  $class->SUPER::_column_info() unless (ref $class->COLUMN_INFO);

  # update with required columns from DFV Profile
  my $profile = $class->dfv_profile;
  $class->required_columns($profile->{required});

  return $class->COLUMN_INFO;
}



1;