Freeside:1.7:Documentation:Developer/FS/part virtual field

From Freeside
< Freeside:1.7:Documentation:Developer‎ | FS
Revision as of 17:55, 3 October 2007 by Ivan (talk | contribs) (import from POD)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

NAME

FS::part_virtual_field - Object methods for part_virtual_field records

SYNOPSIS

 use FS::part_virtual_field;

 $record = new FS::part_virtual_field \%hash;
 $record = new FS::part_virtual_field { 'column' => 'value' };

 $error = $record->insert;

 $error = $new_record->replace($old_record);

 $error = $record->delete;

 $error = $record->check;

DESCRIPTION

An FS::part_virtual_field object represents the definition of a virtual field (see the BACKGROUND section). FS::part_virtual_field contains the name and base table of the field, as well as validation rules and UI hints about the display of the field. The actual data is stored in FS::virtual_field; see its manpage for details.

FS::part_virtual_field inherits from FS::Record. The following fields are currently supported:

vfieldpart - primary key (assigned automatically); name - name of the field; dbtable - table for which this virtual field is defined; check_block - Perl code to validate/normalize data; list_source - Perl code to generate a list of values (UI hint); length - expected length of the value (UI hint); label - descriptive label for the field (UI hint); sequence - sort key (UI hint; unimplemented)

BACKGROUND

"Form is none other than emptiness, and emptiness is none other than form." -- Heart Sutra

The virtual field mechanism allows site admins to make trivial changes to the Freeside database schema without modifying the code. Specifically, the user can add custom-defined 'fields' to the set of data tracked by Freeside about objects such as customers and services. These fields are not associated with any logic in the core Freeside system, but may be referenced in peripheral code such as exports, price calculations, or alternate interfaces, or may just be stored in the database for future reference.

This system was originally devised for svc_broadband, which (by necessity) comprises such a wide range of access technologies that no static set of fields could contain all the information needed by the exports. In an appalling display of False Laziness, a parallel mechanism was implemented for the router table, to store properties such as passwords to configure routers.

The original system treated svc_broadband custom fields (sb_fields) as records in a completely separate table. Any code that accessed or manipulated these fields had to be aware that they were not fields in svc_broadband, but records in sb_field. For example, code that inserted a svc_broadband with several custom fields had to create an FS::svc_broadband object, call its insert() method, and then create several FS::sb_field objects and call their insert() methods.

This created a problem for exports. The insert method on any FS::svc_Common object (including svc_broadband) automatically triggers exports after the record has been inserted. However, at this point, the sb_fields had not yet been inserted, so the export could not rely on their presence, which was the original purpose of sb_fields.

Hence the new system. Virtual fields are appended to the field list of every record at the FS::Record level, whether the object is created ex nihilo with new() or fetched with qsearch(). The fields() method now returns a list of both real and virtual fields. The insert(), replace(), and delete() methods now update both the base table and the virtual fields, in a single transaction.

A new method is provided, virtual_fields(), which gives only the virtual fields. UI code that dynamically generates form widgets to edit virtual field data should use this to figure out what fields are defined. (See below.)

Subclasses may override virtual_fields() to restrict the set of virtual fields available. Some discipline and sanity on the part of the programmer are required; in particular, this function should probably not depend on any fields in the record other than the primary key, since the others may change after the object is instantiated. (Making it depend on virtual fields is just asking for pain.) One use of this is seen in FS::svc_Common; another possibility is field-level access control based on FS::UID::getotaker().

As a trivial case, a subclass may opt out of supporting virtual fields with the following code:

sub virtual_fields { () }

METHODS

new HASHREF
Create a new record. To add the record to the database, see "insert".
insert
Adds this record to the database. If there is an error, returns the error, otherwise returns false.
delete
Deletes this record from the database. If there is an error, returns the error, otherwise returns false.
replace OLD_RECORD
Replaces OLD_RECORD with this one in the database. If there is an error, returns the error, otherwise returns false.
check
If there is an error, returns the error, otherwise returns false. Called by the insert and replace methods.
list
Evaluates list_source.
widget UI_TYPE MODE [ VALUE ]
Generates UI code for a widget suitable for editing/viewing the field, based on list_source and length.
The only UI_TYPE currently supported is 'HTML', and the only MODE is 'view'. Others will be added later.
In HTML, all widgets are assumed to be table rows. View widgets look like <TR><TD ALIGN="right">Label</TD><TD BGCOLOR="#ffffff">Value</TD></TR>
(Most of the display style stuff, such as the colors, should probably go into a separate module specific to the UI. That can wait, though. The API for this function won't change.)
VALUE (optional) is the current value of the field.

NOTES

Semantics of check_block:

This has been changed from the sb_field implementation to make check_blocks simpler and more natural to Perl programmers who work on things other than Freeside.

The check_block is eval'd with the (proposed) new value of the field in $_, and the object to be updated in $self. Its return value is ignored. The check_block may change the value of $_ to override the proposed value, or call die() (with an appropriate error message) to reject the update entirely; the error string will be returned as the output of the check() method.

This makes check_blocks like

s/foo/bar/

do what you expect.

The check_block is expected NOT to do anything freaky to $self, like modifying other fields or calling $self->check(). You have been warned.

(FIXME: Rewrite some of the warnings from part_sb_field and insert here.)

BUGS

None. It's absolutely falwless.

SEE ALSO

FS::Record, FS::virtual_field

POD ERRORS

Hey! The above document had some coding errors, which are explained below:

Around line 186:
'=item' outside of any '=over'
Around line 264:
You forgot a '=back' before '=head1'