HTML-Tested-ClassDBI
view release on metacpan or search on metacpan
lib/HTML/Tested/ClassDBI.pm view on Meta::CPAN
=head1 NAME
HTML::Tested::ClassDBI - Enhances HTML::Tested to work with Class::DBI
=head1 SYNOPSIS
package MyClass;
use base 'HTML::Tested::ClassDBI';
__PACKAGE__->ht_add_widget('HTML::Tested::Value'
, id => cdbi_bind => "Primary");
__PACKAGE__->ht_add_widget('HTML::Tested::Value'
, x => cdbi_bind => "");
__PACKAGE__->ht_add_widget('HTML::Tested::Value::Upload'
, x => cdbi_upload => "largeobjectoid");
__PACKAGE__->bind_to_class_dbi('MyClassDBI');
# And later somewhere ...
# Query and load underlying Class::DBI:
my $list = MyClass->query_class_dbi(search => x => 15);
# or sync it to the database:
$obj->cdbi_create_or_update;
=head1 DESCRIPTION
This class provides mapping between Class::DBI and HTML::Tested objects.
It inherits from HTML::Tested. Widgets created with C<ht_add_widget> can have
additional C<cdbi_bind> property.
After calling C<bind_to_class_dbi> you would be able to automatically
synchronize between HTML::Tested::ClassDBI instance and underlying Class::DBI.
=cut
use strict;
use warnings FATAL => 'all';
package HTML::Tested::ClassDBI;
use base 'HTML::Tested';
use Carp;
use HTML::Tested::ClassDBI::Field;
use Data::Dumper;
my @_cdata = qw(_CDBI_Class _PrimaryFields _Field_Handlers _PrimaryKey);
__PACKAGE__->mk_classdata($_) for @_cdata;
our $VERSION = '0.23';
sub class_dbi_object { shift()->class_dbi_object_gr('_CDBIM_', @_); }
sub class_dbi_object_gr {
my ($self, $gr, $val) = @_;
return $self->{_class_dbi_objects}->{$gr} if @_ == 2;
$self->{_class_dbi_objects}->{$gr} = $val;
}
sub cdbi_bind_from_fields {
my ($class, $gr) = @_;
for my $v (@{ $class->Widgets_List }) {
my $wgr = $v->options->{cdbi_group} || '_CDBIM_';
$v->options->{cdbi_group} = $wgr;
next unless $wgr eq $gr;
my $f = HTML::Tested::ClassDBI::Field->new($class, $v, $gr)
or next;
$class->_Field_Handlers->{ $v->options->{cdbi_group} }
->{$v->name} = $f;
}
}
sub CDBI_Class { return shift()->_CDBI_Class->{_CDBIM_} }
sub PrimaryFields { return shift()->_PrimaryFields->{_CDBIM_} }
sub Field_Handlers { return shift()->_Field_Handlers->{_CDBIM_} }
sub PrimaryKey { return shift()->_PrimaryKey->{_CDBIM_} }
=head1 METHODS
=head2 $class->bind_to_class_dbi($cdbi_class)
Binds $class to $cdbi_class, by going over all fields declared with C<cdbi_bind>
or C<cdbi_upload> option.
C<cdbi_bind> option value could be one of the following:
name of the column, empty string for the column named the same as field or for
array of columns.
C<cdbi_upload> can be used to upload file into the database. Uploaded file is
stored as PostgreSQL's large object. Its OID is assigned to the bound column.
C<cdbi_upload_with_mime> uploads the file and prepends its mime type as a
header. Use HTML::Tested::ClassDBI::Upload->strip_mime_header to pull it from
the data.
C<cdbi_readonly> boolean option can be used to make its widget readonly thus
skipping its value during update. Read only widgets will not be validated.
C<cdbi_primary> boolean option is used to make an unique column behave as
primary key. C<cdbi_load> will use this field while retrieving the object from
the database.
=cut
sub bind_to_class_dbi { shift()->bind_to_class_dbi_gr('_CDBIM_', @_); }
=head2 $class->bind_to_class_dbi_gr($group, $cdbi_class)
Binds $class to $cdbi class in group $group. Special group _CDBIM_ is used
as the default group.
=cut
sub bind_to_class_dbi_gr {
my ($class, $gr, $dbi_class, $opts) = @_;
$class->$_({}) for grep { !$class->$_ } @_cdata;
$class->_CDBI_Class->{$gr} = $dbi_class;
$class->_Field_Handlers->{$gr} = {};
$class->_PrimaryFields->{$gr} = {};
$class->cdbi_bind_from_fields($gr);
$class->_load_db_info($gr);
my $pk = $opts ? $opts->{PrimaryKey} : undef;
$class->_PrimaryKey->{$gr} = $pk if $pk;
confess "# No Primary fields given\n"
unless ($pk || %{ $class->_PrimaryFields->{$gr} });
}
sub _get_cdbi_pk_for_retrieve {
my ($self, $gr) = @_;
my $pk = $self->_PrimaryKey->{$gr} or goto PFIELDS;
my %pkh;
for my $f (@$pk) {
my $v = $self->$f;
goto PFIELDS unless defined($v);
my $h = $self->_Field_Handlers->{$gr}->{$f};
$pkh{ $h ? $h->column_name : $f } = $v;
}
return \%pkh if %pkh;
PFIELDS:
my $res = {};
my %pf = %{ $self->_PrimaryFields->{$gr} };
my ($pv, $pc);
while (my ($k, $v) = each %pf) {
$pv = $self->$k;
next unless defined $pv;
$pc = $v;
last;
}
return undef unless defined($pv);
my @vals = split('_', $pv);
for (my $i = 0; $i < @$pc; $i++) {
$res->{ $pc->[$i] } = $vals[$i];
}
return $res;
}
sub _fill_in_from_class_dbi {
my ($self, $gr, $is_update) = @_;
my $fhs = $self->_Field_Handlers->{$gr};
my $cdbi = $self->class_dbi_object_gr($gr);
while (my ($f, $h) = each %$fhs) {
next if ($is_update && defined $self->{$f});
$self->$f($h->get_column_value($cdbi));
}
}
sub cdbi_retrieve { shift()->_call_for_all('cdbi_retrieve_gr', @_); }
sub cdbi_retrieve_gr {
my ($self, $gr) = @_;
my $pk = $self->_get_cdbi_pk_for_retrieve($gr);
return unless defined($pk);
( run in 0.613 second using v1.01-cache-2.11-cpan-5a3173703d6 )