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 sequential

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 31 possible characters, a length of 2 gives almost a thousand chocies, 4 gives almost a million, 6 gives almost a billion, and 8 gives 852 billion, or almost a trillion possible choices.

Note that you'll want to have many more choices than you are actually going to use, both to avoid conflicts and to prevent guessing.

RELATIONAL FIELDS

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.

restrict_delete Interface

Identical to the default interface except as follows: an ok_delete hook is installed to check for the existance of any related records, in which case the deletion is cancelled. This prevents you from deleting the "parent" record for a number of related records.

cascade_delete Interface

Identical to the default interface except as follows: a post_delete hook is installed to delete all of the related records after the parent record is deleted.

nullify_delete Interface

Identical to the default interface except as follows: a post_delete hook is installed to change all of the related records to have a default value.

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 delegate

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
    delegate => [ 
        [ '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.