Matthew Simon Cavalletto

NAME

DBIx::DBO2::Fields - Construct methods for database fields

SYNOPSIS

  package MyCDs::Disc;
  
  use DBIx::DBO2::Record '-isasubclass';
  
  use DBIx::DBO2::Fields (
    { name => 'id', field_type => 'number', required => 1 },
    { name => 'name', field_type => 'string', length => 64, required => 1 },
    { name => 'year', field_type => 'number' },
    { name => 'artist', field_type => 'number' },
    { name => 'genre', field_type => 'number' },
  );
  
  1;

DESCRIPTION

This package creates methods for DBIx::DBO2::Record objects.

It's based on Class::MakeMethods::Template.

Accessing Field Attributes

Calling ->fields() on a class or instance returns a hash of field-name => field-attribute-hash pairs.

  my %fields = BD::Customer::Account->fields();
  foreach my $fieldname ( sort keys %fields ) {
    my $field = $fields{ $fieldname };
    print "$fieldname is a $field->{meta_type} field\n";
    print "  $fieldname is required\n" if ( $field->{required} );
    print "  $fieldname max length $field->{length}\n" if ( $field->{length} );
  }

You can also pass in a field name to retrieve its attributes.

  print BD::Customer::Account->fields('public_id')->{'length'};

The results of ->fields() includes field information inherited from superclasses. To access only those fields declared within a particular class, call ->class_fields() instead.

STRING FIELDS

Field Type string

Generates methods corresponding to a SQL varchar column.

Default Interface

The general usage for a string field is:

  use DBIx::DBO2::Fields (
    string => 'x',
  );

This declaration will generate methods to support the following interface:

  • $record->x() : value

    Returns the value of field x for the given record.

  • $record->x( value )

    Sets the value of field x for the given record.

  • $record->x_invalid() : fieldname => error_message, ...

    Check for any error conditions regarding the current value of field x. See Validation, below.

Validation

String fields provide basic error-checking for required values or text that is too long to fit into the associated database column.

You may specify the length of the column and whether a field is required in your field declaration:

  use DBIx::DBO2::Fields (
    string => '-required 1 -length 64 x',
    string => '-required 0 -length 255 y',
  );

If you leave the required and length attributes undefined, an attempt will be made to detect them automatically, by checking the database table associated with the current object for a column whose name matches the field's.

  use DBIx::DBO2::Fields (
    string => 'x',
    string => 'y',
  );

  create table xyzzy ( 
    x varchar(64) not null,
    y varchar(255)
  );

The --init_and_get Interface

The string field also supports the following declaration for values which only need to be calculated once:

  use DBIx::DBO2::Fields (
    string => '--init_and_get x',
  );
  • $record->x() : value

    Returns the value of field x for the given record.

    If the value of field x is undefined, it first calls an initialization method and stores the result. The default it to call a method named init_x, but you can override this by providing a different value for the init_method attribute.

      use DBIx::DBO2::Fields (
        string => '--init_and_get -init_method find_spot x',
      );

    Or equivalently, and perhaps more readably:

      use DBIx::DBO2::Fields (
        string => [ '--init_and_get', x, { init_method => 'find_spot' } ],
      );

Field Type phone_number

Identical to the string type.

Field Type post_code

Identical to the string type.

Field Type state_province

Identical to the string type.

Field Type email_addr

Identical to the string type.

Field Type creditcardnumber

If you declare the following:

  use DBIx::DBO2::Fields (
    creditcardnumber => "ccnum",
  );

You can now use these methods:

  # Set and get raw value
  $customer->ccnum('4242424242424242');
  $customer->ccnum() eq '4242424242424242';

  # Analyze card number
  $customer->ccnum_checksum() == 1;
  $customer->ccnum_flavor() eq 'VISA card';

  # Opaque readable value for display
  $customer->ccnum_readable() eq '************4242';

  # Setting the readable value to the prior opaque value has no effect
  $customer->ccnum_readable('************4242');
  $customer->ccnum() eq '4242424242424242';

  # But setting the readable value to another value overwrites the contents
  $customer->ccnum_readable('1234-5678-9101-1213');
  $customer->ccnum() eq '1234-5678-9101-1213';

  # Recognize bogus cards by the following characteristics...
  $customer->ccnum_checksum() == 0;
  $customer->ccnum_flavor() eq 'Unrecognized';
  $customer->ccnum_readable() eq '1234-5678-9101-1213';

NUMBER FIELDS

Field Type number

Generates methods corresponding to a SQL int or float column.

The general usage for a number field is:

  use DBIx::DBO2::Fields (
    number => 'x',
  );

This declaration will generate methods to support the following interface:

  • $record->x() : value

    Returns the value of field x for the given record.

  • $record->x( value )

    Sets the value of field x for the given record.

  • $record->x_readable() : value

    Returns the value of field x for the given record formatted for display, including commas for thousands separators.

  • $record->x_readable( value )

    Sets the value of field x for the given record from a possibly formatted value.

  • $record->x_invalid() : fieldname => error_message, ...

    Check for any error conditions regarding the current value of field x. See Validation, below.

Validation

Number fields provide error-checking for required values or values which are not numeric.

You may specify whether a field is required or allow this to be detected based on whether the corresponding database column allows null values.

The -init_and_get Interface

The number field also supports the -init_and_get provided by the string field type.

Field Type timestamp

Generates methods corresponding to a SQL int column storing a date and time in Unix seconds-since-1970 format.

Default Interface

The general usage for a timestamp field is:

  use DBIx::DBO2::Fields (
    timestamp => 'x',
  );

This declaration will generate methods to support the following interface:

  • $record->x() : value

    Returns the raw numeric value of field x for the given record.

  • $record->x( value )

    $record->x( readable_value )

    Sets the value of field x for the given record. You may provide either a raw numeric value or a human-entered formatted value.

  • $record->touch_x()

    Sets the value of field x to the current date and time.

  • $record->x_readable() : readable_value

    Returns the value of field x formatted for display.

  • $record->x_readable(format_string) : readable_value

    Returns the value of field x formatted in particular way. (See Data::Quantity::Time::Timestamp for supported formats.)

  • $record->x_obj() : quantity_object

    Gets the value of field x as a Data::Quantity::Time::Timestamp object.

  • $record->x_invalid() : fieldname => error_message, ...

    Inherited from the number field.

Field Type julian_day

Generates methods corresponding to a SQL int column storing a date in the Julian days-since-the-invention-of-fire format.

Default Interface

The general usage for a julian_day field is:

  use DBIx::DBO2::Fields (
    julian_day => 'x',
  );

This declaration will generate methods to support the following interface:

  • $record->x() : value

    Returns the raw numeric value of field x for the given record.

  • $record->x( value )

    $record->x( readable_value )

    Sets the value of field x for the given record. You may provide either a raw numeric value or a human-entered formatted value.

  • $record->touch_x()

    Sets the value of field x to the current date.

  • $record->x_readable() : readable_value

    Returns the value of field x formatted for display.

  • $record->x_readable(format_string) : readable_value

    Returns the value of field x formatted in particular way. (See Data::Quantity::Time::Date for supported formats.)

  • $record->x_obj() : quantity_object

    Gets the value of field x as a Data::Quantity::Time::Date object.

  • $record->x_invalid() : fieldname => error_message, ...

    Inherited from the number field.

Field Type currency_uspennies

Generates methods corresponding to a SQL int column storing a US currency value in pennies.

Default Interface

The general usage for a currency_uspennies field is:

  use DBIx::DBO2::Fields (
    currency_uspennies => 'x',
  );

This declaration will generate methods to support the following interface:

  • $record->x() : value

    Returns the raw numeric value of field x for the given record.

  • $record->x( value )

    Sets the raw numeric value of field x for the given record.

  • $record->x_readable() : readable_value

    Returns the value of field x formatted for display.

  • $record->x_readable(readable_value)

    Set the value of x based on a human-entered value

  • $record->x_invalid() : fieldname => error_message, ...

    Inherited from the number field.

Field Type saved_total

Used to store numeric values which only have to be calculated when the related records change.

A declaration of

  use DBIx::DBO2::Fields (
    saved_total => 'x',
  );

Is equivalent to the following method definitions:

  # Recalculate if status_is_cart; else return previously stored value
  sub x { 
    my $self = shift; 
    if ( $self->status_is_cart() ) {
      $self->{x} = $self->init_x();
    } else {
      $self->{x};
    }
  }
  
  # Recalculate and store the value.
  sub reset_x { 
    my $self = shift; 
    $self->{x} = $self->init_x();
  }
  
  # Is our stored value out of synch with current calculations?
  sub x_difference { 
    my $self = shift; 
    $self->init_x() - $self->{x};
  }

You are expected to provide an 'init_x' method which calculates and returns the value, but does not save it.

Field Type saved_total_uspennies

Like saved_total, but also has a read-only *_readable method that provides US Currency formatting.

DATABASE-ORIENTED FIELDS

Field Type unique_code

Used to generate and store a unique code for this object.

The identifiers generally look like 'QX3P6N' or the like -- a mix of the digits from 0 to 9 and upper case consonants (skipping the vowels to avoid confusion between 0/O and 1/I, and to avoid constructing real words). The size is controlled by the "length" meta-method attribute.

Here's a sample declaration:

  package Acme::Order::Order;
  use DBIx::DBO2::Fields ( 
      "unique_code --length 6 => 'public_id',
  );

This field is automatically assigned and confirmed to be unique when the record is inserted.

Here's how you retrieve a specific row:

  my $pubid = 'QX3P6N';
  $order = Acme::Order::Order->fetch_public_id( $pubid );

With 41 possible characters, a length of 3 gives 68,921 choices, 4 gives 2,825,761, 6 gives 4,750,104,241, and 8 gives 7,984,925,229,121.

Field Type foreign_key

Generates methods corresponding to a SQL int or varchar column storing a value which corresponds to the primary key of a related record from another table.

Default Interface

The general usage for a foreign_key field is:

  use DBIx::DBO2::Fields (
    foreign_key => 'x',
  );

This declaration will generate methods to support the following interface:

  • $record->x_id() : value

    Returns the raw numeric value of field x_id for the given record.

  • $record->x_id( value )

    Sets the raw numeric value of field x_id for the given record.

  • $record->x() : related_object

    Fetches and returns the related record.

    If the x_id value is empty, or if there is not a record with the corresponding value in the related table, returns undef.

  • $record->x( related_object )

    Sets the raw numeric value of field x_id based on the corresponding field in the related object.

  • $record->x_required() : related_object

    Fetches and returns the related record, in a case where your code depends on it existing, generally because it calls additional methods without checking the result.

    If the x_id value is empty, or if there is not a record with the corresponding value in the related table, croaks with a fatal exception. This makes it easier to spot the problem then Perl's generic "can't call method on undefined value" message.

  • $record->x_invalid() : fieldname => error_message, ...

    If the field is marked required (or the underlying column is defined as not null), reports an error for missing values.

    If a value is provided, attempts to fetch the associated record and reports an error if can not be found.

Attributes

  • hash_key - defaults to *_id

  • related_class

  • related_id_method

Example

  package EBiz::Order::Order;
  use DBIx::DBO2::Fields ( 
      foreign_key => { name=>'account',  related_class => 'Account' },
  );
  ...
  $order->account_id( 27 );
  print $order->required_account->email();

Field Type line_items

Generates methods to retrieve records from another table which have a foreign_key relationship to the current record. Depends on there being a primary key column, but does not require a separate database column of its own.

Default Interface

The general usage for a line_items field is:

  package Y;
  use DBIx::DBO2::Fields (
    line_items => { name=>'x', 'related_field'=>'y_id', related_class=>'X' },
  );

This declaration will generate methods to support the following interface:

  • $record->x() : related_objects

    Fetches the related records. Returns a RecordSet.

  • $record->x( rel_col => rel_value, ...) : related_objects

    Fetches a subset of the related records which also meet the indicated criteria. Returns a RecordSet.

  • $record->count_x()

    Returns the number or related records.

  • $record->new_x() : related_object

    Creates and returns a new related record, setting its foreign key field to refer to our record's ID.

    (Note that the record is created but not inserted; you need to call ->save() yourself.)

  • $record->delete_x()

    Deletes all of the related records.

You can also specify an array-ref value for the default_criteria attribute; if present, it is treated as a list of fieldname/value pairs to be passed to the fetch and new methods of the related class.

CODE-ORIENTED FIELDS

The below types are for internal use and do not correspond to SQL columns.

Field Type alias

  use DBIx::DBO2::Fields (
    alias => [ 'x' => 'y' ],
  );

This declares a method ->x() that simply calls method ->y() and passes along all of its arguments.

Field Type forward

Local alias for the Universal:forward_methods method generator.

Creates a method which delegates to an object provided by another method.

Example:

  use DBIx::DBO2::Fields
    forward => [ 
        [ 'w' ], { target=> 'whistle' }, 
        [ 'x', 'y' ], { target=> 'xylophone' }, 
        { name=>'z', target=>'zither', target_args=>[123], method_name=>do_zed },
      ];

Example: The above defines that method w will be handled by the calling w on the object returned by whistle, whilst methods x and y will be handled by xylophone, and method z will be handled by calling do_zed on the object returned by calling zither(123).

Attributes: The following additional attributes are supported:

target

Required. The name of the method that will provide the object that will handle the operation.

target_args

Optional ref to an array of arguments to be passed to the target method.

method_name

The name of the method to call on the handling object. Defaults to the name of the meta-method being created.

TO DO

  • Resolve differing approaches to setting values from human-entered formatted values. Current interface is:

    -

    julian_day: $record->x( readable_value )

    -

    timestamp: $record->x( readable_value )

    -

    currency_uspennies: $record->x_readable(readable_value)

    -

    creditcardnumber: $record->x_readable(readable_value)

CHANGES

2002-01-17 Simon: Update of Fields to use new version of Class::MakeMethods.

2001-04-09 Simon: Added line_items attrib: default_criteria=>[field=>value,...]

2001-02-07 Simon: Completed fields() method, and improved column attr detection.

2001-01-30 Simon: Added _readable method for all number fields (for ',000's).

2001-01-29 Simon: Filled in missing chunks of documentation.

2001-01-29 Simon: Added *_invalid methods and column-info detection.

2001-01-20 Simon: Added saved_total_uspennies

2001-01-16 Simon: Added saved_total

2000-12 Simon: Added currency_uspennies, timestamp, and julian_day types

2000-12 Simon: Added foreign_key and line_items types

2000-08-04 Simon: Moved package into EBiz::Database.

2000-03-30 Simon: Julian day readable now calls method to access value.

2000-03-10 Simon: Added get_and_set, get_set_filter.

2000-03-06 Simon: Added get_set_alias

2000-02-29 Simon: Created.

COPYRIGHT

Copyright 2000, 2001 Evolution Online Systems, Inc.

You may use, modify, and distribute this software under the same terms as Perl.