X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2FRecord.pm;h=a04ddb9820ea376a0c3d89f0cc85d91835d56b52;hb=4e5a0655072be725acf00394186b93c96bba17ee;hp=ec326458dd2a051aec1e63f5cae7d8c94c8dc936;hpb=7b91bff5d59610bd53a8708fddc88ae279f6e662;p=freeside.git diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm index ec326458d..a04ddb982 100644 --- a/FS/FS/Record.pm +++ b/FS/FS/Record.pm @@ -9,9 +9,10 @@ use File::CounterFile; use Locale::Country; use DBIx::DBSchema 0.19; use FS::UID qw(dbh checkruid getotaker datasrc driver_name); +use FS::SearchCache; @ISA = qw(Exporter); -@EXPORT_OK = qw(dbh fields hfields qsearch qsearchs dbdef); +@EXPORT_OK = qw(dbh fields hfields qsearch qsearchs dbdef jsearch); $DEBUG = 0; @@ -135,9 +136,31 @@ sub new { } } + $self->_cache($hashref, shift) if $self->can('_cache') && @_; + $self; } +sub new_or_cached { + my $proto = shift; + my $class = ref($proto) || $proto; + my $self = {}; + bless ($self, $class); + + $self->{'Table'} = shift unless defined ( $self->table ); + + my $hashref = $self->{'Hash'} = shift; + my $cache = shift; + if ( defined( $cache->cache->{$hashref->{$cache->key}} ) ) { + my $obj = $cache->cache->{$hashref->{$cache->key}}; + $obj->_cache($hashref, $cache) if $obj->can('_cache'); + $obj; + } else { + $cache->cache->{$hashref->{$cache->key}} = $self->new($hashref, $cache); + } + +} + sub create { my $proto = shift; my $class = ref($proto) || $proto; @@ -170,15 +193,19 @@ objects. =cut sub qsearch { - my($table, $record, $select, $extra_sql ) = @_; - $table =~ /^([\w\_]+)$/ or die "Illegal table: $table"; - $table = $1; + my($stable, $record, $select, $extra_sql, $cache ) = @_; + #$stable =~ /^([\w\_]+)$/ or die "Illegal table: $table"; + #for jsearch + $stable =~ /^([\w\s\(\)\.\,\=]+)$/ or die "Illegal table: $stable"; + $stable = $1; $select ||= '*'; my $dbh = dbh; + my $table = $cache ? $cache->table : $stable; + my @fields = grep exists($record->{$_}), fields($table); - my $statement = "SELECT $select FROM $table"; + my $statement = "SELECT $select FROM $stable"; if ( @fields ) { $statement .= ' WHERE '. join(' AND ', map { if ( ! defined( $record->{$_} ) || $record->{$_} eq '' ) { @@ -200,15 +227,21 @@ sub qsearch { $sth->execute( map $record->{$_}, grep defined( $record->{$_} ) && $record->{$_} ne '', @fields - ) or croak "Error executing \"$statement\": ". $dbh->errstr; + ) or croak "Error executing \"$statement\": ". $sth->errstr; $dbh->commit or croak $dbh->errstr if $FS::UID::AutoCommit; if ( eval 'scalar(@FS::'. $table. '::ISA);' ) { if ( eval 'FS::'. $table. '->can(\'new\')' eq \&new ) { #derivied class didn't override new method, so this optimization is safe - map { - new( "FS::$table", { %{$_} } ) - } @{$sth->fetchall_arrayref( {} )}; + if ( $cache ) { + map { + new_or_cached( "FS::$table", { %{$_} }, $cache ) + } @{$sth->fetchall_arrayref( {} )}; + } else { + map { + new( "FS::$table", { %{$_} } ) + } @{$sth->fetchall_arrayref( {} )}; + } } else { warn "untested code (class FS::$table uses custom new method)"; map { @@ -224,6 +257,27 @@ sub qsearch { } +=item jsearch TABLE, HASHREF, SELECT, EXTRA_SQL, PRIMARY_TABLE, PRIMARY_KEY + +Experimental JOINed search method. Using this method, you can execute a +single SELECT spanning multiple tables, and cache the results for subsequent +method calls. Interface will almost definately change in an incompatible +fashion. + +Arguments: + +=cut + +sub jsearch { + my($table, $record, $select, $extra_sql, $ptable, $pkey ) = @_; + my $cache = FS::SearchCache->new( $ptable, $pkey ); + my %saw; + ( $cache, + grep { !$saw{$_->getfield($pkey)}++ } + qsearch($table, $record, $select, $extra_sql, $cache ) + ); +} + =item qsearchs TABLE, HASHREF Same as qsearch, except that if more than one record matches, it Bs but @@ -312,16 +366,30 @@ $record->column('value') is a synonym for $record->set('column','value'); =cut +# readable/safe +#sub AUTOLOAD { +# my($self,$value)=@_; +# my($field)=$AUTOLOAD; +# $field =~ s/.*://; +# if ( defined($value) ) { +# confess "errant AUTOLOAD $field for $self (arg $value)" +# unless $self->can('setfield'); +# $self->setfield($field,$value); +# } else { +# confess "errant AUTOLOAD $field for $self (no args)" +# unless $self->can('getfield'); +# $self->getfield($field); +# } +#} + +# efficient sub AUTOLOAD { - my($self,$value)=@_; - my($field)=$AUTOLOAD; + my $field = $AUTOLOAD; $field =~ s/.*://; - if ( defined($value) ) { - confess "errant AUTOLOAD $field for $self (arg $value)" - unless $self->can('setfield'); - $self->setfield($field,$value); + if ( defined($_[1]) ) { + $_[0]->setfield($field, $_[1]); } else { - $self->getfield($field); + $_[0]->getfield($field); } } @@ -738,7 +806,7 @@ sub ut_phonen { $phonen .= " x$4" if $4; $self->setfield($field,$phonen); } else { - warn "don't know how to check phone numbers for country $country"; + warn "warning: don't know how to check phone numbers for country $country"; return $self->ut_textn($field); } ''; @@ -970,12 +1038,12 @@ sub hfields { \%hash; } -#sub _dump { -# my($self)=@_; -# join("\n", map { -# "$_: ". $self->getfield($_). "|" -# } (fields($self->table)) ); -#} +sub _dump { + my($self)=@_; + join("\n", map { + "$_: ". $self->getfield($_). "|" + } (fields($self->table)) ); +} sub DESTROY { return; } @@ -992,10 +1060,6 @@ sub DESTROY { return; } =back -=head1 VERSION - -$Id: Record.pm,v 1.31 2001-11-02 05:11:52 ivan Exp $ - =head1 BUGS This module should probably be renamed, since much of the functionality is