package FS::Record; use strict; use vars qw( $dbdef_file $dbdef $setup_hack $AUTOLOAD @ISA @EXPORT_OK $DEBUG $me %dbdef_cache %virtual_fields_cache ); use subs qw(reload_dbdef); use Exporter; use Carp qw(carp cluck croak confess); use File::CounterFile; use Locale::Country; use DBI qw(:sql_types); use DBIx::DBSchema 0.23; use FS::UID qw(dbh getotaker datasrc driver_name); use FS::SearchCache; use FS::Msgcat qw(gettext); use FS::part_virtual_field; use Tie::IxHash; @ISA = qw(Exporter); @EXPORT_OK = qw(dbh fields hfields qsearch qsearchs dbdef jsearch); $DEBUG = 0; $me = '[FS::Record]'; #ask FS::UID to run this stuff for us later $FS::UID::callback{'FS::Record'} = sub { $File::CounterFile::DEFAULT_DIR = "/usr/local/etc/freeside/counters.". datasrc; $dbdef_file = "/usr/local/etc/freeside/dbdef.". datasrc; &reload_dbdef unless $setup_hack; #$setup_hack needed now? }; =head1 NAME FS::Record - Database record objects =head1 SYNOPSIS use FS::Record; use FS::Record qw(dbh fields qsearch qsearchs dbdef); $record = new FS::Record 'table', \%hash; $record = new FS::Record 'table', { 'column' => 'value', ... }; $record = qsearchs FS::Record 'table', \%hash; $record = qsearchs FS::Record 'table', { 'column' => 'value', ... }; @records = qsearch FS::Record 'table', \%hash; @records = qsearch FS::Record 'table', { 'column' => 'value', ... }; $table = $record->table; $dbdef_table = $record->dbdef_table; $value = $record->get('column'); $value = $record->getfield('column'); $value = $record->column; $record->set( 'column' => 'value' ); $record->setfield( 'column' => 'value' ); $record->column('value'); %hash = $record->hash; $hashref = $record->hashref; $error = $record->insert; $error = $record->delete; $error = $new_record->replace($old_record); # external use deprecated - handled by the database (at least for Pg, mysql) $value = $record->unique('column'); $error = $record->ut_float('column'); $error = $record->ut_number('column'); $error = $record->ut_numbern('column'); $error = $record->ut_money('column'); $error = $record->ut_text('column'); $error = $record->ut_textn('column'); $error = $record->ut_alpha('column'); $error = $record->ut_alphan('column'); $error = $record->ut_phonen('column'); $error = $record->ut_anything('column'); $error = $record->ut_name('column'); $dbdef = reload_dbdef; $dbdef = reload_dbdef "/non/standard/filename"; $dbdef = dbdef; $quoted_value = _quote($value,'table','field'); #deprecated $fields = hfields('table'); if ( $fields->{Field} ) { # etc. @fields = fields 'table'; #as a subroutine @fields = $record->fields; #as a method call =head1 DESCRIPTION (Mostly) object-oriented interface to database records. Records are currently implemented on top of DBI. FS::Record is intended as a base class for table-specific classes to inherit from, i.e. FS::cust_main. =head1 CONSTRUCTORS =over 4 =item new [ TABLE, ] HASHREF Creates a new record. It doesn't store it in the database, though. See L<"insert"> for that. Note that the object stores this hash reference, not a distinct copy of the hash it points to. You can ask the object for a copy with the I method. TABLE can only be omitted when a dervived class overrides the table method. =cut sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = {}; bless ($self, $class); unless ( defined ( $self->table ) ) { $self->{'Table'} = shift; carp "warning: FS::Record::new called with table name ". $self->{'Table'}; } my $hashref = $self->{'Hash'} = shift; foreach my $field ( grep !defined($hashref->{$_}), $self->fields ) { $hashref->{$field}=''; } $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; my $self = {}; bless ($self, $class); if ( defined $self->table ) { cluck "create constructor is deprecated, use new!"; $self->new(@_); } else { croak "FS::Record::create called (not from a subclass)!"; } } =item qsearch TABLE, HASHREF, SELECT, EXTRA_SQL, CACHE_OBJ Searches the database for all records matching (at least) the key/value pairs in HASHREF. Returns all the records found as `FS::TABLE' objects if that module is loaded (i.e. via `use FS::cust_main;'), otherwise returns FS::Record objects. ###oops, argh, FS::Record::new only lets us create database fields. #Normal behaviour if SELECT is not specified is `*', as in #C