NAME

XAO::DO::FS::Hash - Core data class for XAO::FS

SYNOPSIS

my $data=XAO::Objects->new(objname => 'FS::Hash');

$data->put(aaa => 123);

my $aaa=$data->get('aaa');

DESCRIPTION

This is a core data class for XAO Foundation Server (see XAO::FS). All data classes are based on FS::Hash and for that matter FS::Hash can be considered pure virtual class which you would never want to use directly.

A data object can contain arbitrary number of named single-value parameters and arbitrary number of list objects. Whenever you need more then one count of something you will have to create a list object to store those things. For example if you only have one shipping address per customer - you can store it as a couple of properties in Customer object, but if you want a customer to have an address book - create list object named Addresses and store addresses inside of it.

Any data object at any given time can be in either attached state (stored in some List and connected to the database) or in detached state (when it is not connected to the database layer all manipulations on object's properties only change memory).

Detached data object can be at any time re-attached to a container or stored under different ID. That allows a developer to have increased performance when required by detaching an object and then using in-memory copy.

When an object is attached all data manipulations are done directly on database and never cached. Reasonable measures should be taken by developer to ensure that it is safe to re-attach an object to the database because the content of the object would replace current database content. Object server does not try to map any changes that you make to the database back to the detached object. Once detached the object is on its own.

Here is the entire API that is available in all objects based on the FS::Hash class. It is not recommended to override or extend any of these methods unless stated otherwise in method description. Methods are listed in alphabetical order.

add_placeholder (%)

Modifies database scheme by adding new possible parameter name into the object. After you call this method you can call put() and get() on the name you have added.

The operation can take some time especially if you have a lot of objects of that type in the database because it have to modify database tables and add new field.

Arguments are (alphabetically):

class

Only makes sense for 'list' type - sets the name of class for the objects that would be contained in the list. That class have to exist and be available for object loader at the time of the call.

connector

Optional name of the key that would refer objects in the list to the object they are contained in. Default is 'parent_unique_id', you can change it to something more meaningfull so that it would make sense for somebody looking at the plain SQL tables.

default

Sets default values that would be returned from get() when no content is available. Valid only for data types. Defaults to 0 or minvalue if zero is not in the range for 'real' and 'integer' types and empty string for 'text'.

Maximum length of the default text is limited to 30 characters regardless of the 'maxlength'.

index

Optional parameter that when set to non-zero value suggests that you are going to use this field in searches a lot. This works especially good on `real' and `integer' fields when you later search on them using ranges or equivalence.

For text searches it is usually better to make a field of type `words' then making an index on it. Indexing text fields works great mostly on 'eq' and 'ne' search operators, not 'ws' or 'wq'.

Regardless of your use of indexes searches are guaranteed to produc equal results. Indexing can only improve performance or decrease it when used incorrectly.

key

Name of the key that would identify objects in the list. Required if you add a 'list' placeholder.

When possible it is recommended to name the key as the class name or some derivative from it with '_id' suffix. If you add Orders list placeholder it is recommended to call the key 'order_id'.

maxlength

Maximum length for 'text' type in characters. If later on you will try to store longer text string into that field an error will be thrown. Default is 100.

maxvalue

Maximum possible value for `integer' and `real' properties, inclusive. Default is 2147483647 for integer property if minvalue is negative and 4294967295 if minvalue is zero or positive.

Depending on database driver used you may inmprove performance when you use minimal possible ranges for your values. This is true for MySQL at least.

minvalue

Minimum possible value for `integer' and `real' properties, inclusive. Default is -2147483648 for integer property. If you use positive value or zero for integer property you're converting that property to unsigned integer value effectively.

name

Placeholder name -- this is the name you would then use in put() and get() to access that property.

It is recommended to name single-value properties in all-lowercase with underscores to separate words (like "middle_name") and name lists in capitalized words (like "Addresses"). Names have to start from letter and can consist of only letters, digits and underscore symbol.

table

Optional table name for 'list' type placeholder. If not defined it would be created from class name (something like 'osCustomer_Address' for 'Customer::Address' class).

type

Placeholder type, one of 'text', 'words', 'integer', 'unsigned', 'real' or 'list'.

unique

If set to non-zero value then this property will have to be filled with values that are unique troughout the entire class. Placeholder with that modifier can only be added when there are no objects of that class in the database.

An example of adding new single-value property placeholder:

$customer->add_placeholder(name      => 'middle_name',
                           type      => 'text',
                           maxlength => 10);

$customer->put(middlename => 'Jr.');

An example of adding new list of properties placeholder:

$customer->add_placeholder(name  => 'Addresses',
                           class => 'Customer::Address',
                           key   => 'address_id');

my $adlist=$customer->get('Addresses');

$adlist->put('addr001' => $address);

NOTE: A placeholder added to an object in fact adds it to all objects of the same class regardless of their status - attached or detached.

build_structure (%)

Convenience method that checks object structure and adds specified placeholders if they do not currently exist.

Upon return object will have at least the given fields. It can have some extra fields that were on the object before, but given fields guaranteed to exist and have the same descriptions as provided.

If any existing field has different description than supplied build_structure() method will throw an error.

Example:

$customer->build_structure(
    Orders => {
        type => 'list',
        class => 'Data::Order',
        key => 'order_id',
        structure => {
            total => {
                type => 'integer',
                minvalue => 0,
            },
        },
    },
    first_name => {
        type => 'text',
        maxlength => 100
    },
    last_name => {
        type => 'text',
        maxlength => 100
    });
container_key ()

If current object is not on top level returns key that refers to the current object in the container object that contains current object.

Would return undef if current object was created with "new" and had never been stored anywhere.

container_object ()

If current object is not on top level returns container object that contains current object. For Global object will return `undef'.

Will throw an error if current object was created with get_new() method or something similar.

Example:

my $orders_list=$order->container_object();
defined ($)

Obsolete since version 1.03 when the concept of existing, but undefined value was eliminated for simplicity. Values are always defined -- therefore this method will either return true or throw an error if the name is not correct.

delete ($)

Deletes content of the given property. If you use get() on the deleted property you will get empty List for `list' properties or `undef' for value properties.

NOTE: If the name you gave refers to a contained object then destroy() method would be called on that object. List object would then unlink all its contained object and if that was the only place they were linked into then these object would be destroyed too. Be careful with delete() method.

Delete() method would not alter database structure. It can leave some tables empty, but it would not change relations scheme.

describe ($)

Returns a hash reference that contains description of the given field. The format is exactly like you would pass to add_placeholder() method.

Can be used to check limitations and field types. Will return `undef' on non-existing fields.

Example:

my $description=$customer->describe('first_name');
print "Type: $description->{type}\n";
print "Maximum length: $description->{maxlength}\n";
destroy ()

Deletes everything inside the current object -- an alias for the following code:

foreach my $key ($customer->keys) {
    $customer->delete($key);
}
detach ()

Detaches current object from its container and from database. Detaching an object leads to detaching every object it contains to the deepest possible level.

Once an object is detached it "remembers" a place where it was attached and can be re-attached later.

No changes in the database data would be propagated to the detached object and no changes in the detached object would ever change the database. An exception is add_placeholder() and drop_placeholder() methods that operate on all objects of the same type regardless of their status -- detached or attached.

NOT IMPLEMENTED YET. Almost all supporting infrastructure is in place, but currently the only way to get a detached object is to call get_new() on List object.

It is safe to call detach() though. You should do that in places where you think this is appropriate. Like that:

##
# Printing all properties. Detach() will load all the values into
# memory and allow speedy prints. The code will work in exactly the
# same way with or without detach() but once detach() is implemented
# there would be speed up.
#
my $obj=$customers->get($customer_id);
$obj->detach();
foreach my $key ($obj->keys) {
    my $value=$obj->get($key);
    if(ref($value)) {
        print "obj{$key}=List (" . $value->uri . ")\n";
    }
    else {
        print "obj{$key}='$value'\n";
    }
}
drop_placeholder ($)

Deletes placeholder and all values stored in the field being deleted in all Hash objects. Be careful!

It might take considerable amount of time to finish if you have large database.

Example:

$customer->drop_placeholder('Orders');

There is currently no way to rename a placeholder. You can create new one, copy data from the old one to the new one and then drop old one.

If you drop a list placeholder it will effectively chop off entire branch that starts at that placeholder and drop all related tables in the SQL database.

exists ($)

Checks if there is a placeholder for the given key. Here is an example:

if(! $customer->exists('middle_name')) {
    print "No placeholder for 'middle_name' exists in the database\n";
}

Not the same as defined() which just checks if given property has value or not. Property can have placeholder and still be undefined.

fetch ($)

Takes URI style path and returns an object or property referenced by that path. If path starts with slash (/) then it goes from the top, otherwise - from the current object. Counting objects from top would always give you connected object or undef.

Examples:

my $zip001=$customer->fetch('Addresses/addr001/zipcode');

my $customers=$address->fetch('/Customers');

Currently is only implemented on Glue, relative URI are not supported.

fill (%)

Takes a hash or another object of the same type and merges all the data from it into the current object.

NOT IMPLEMENTED.

get ($)

Retrieves data field or a list reference from the object.

Example:

my $addresses_list=$customer->get('Addresses');

my $first_name=$customer->get('first_name');

As a convenience (and an optimisation, because database driver would be able to optimise that into just one query into database in most cases) you can pass more then one property name into the get() method. In that case it will return you an array of values in the same order that you passed property names.

my ($name,$phone,$fax)=$customer->get('name','phone','fax');
is_attached ()

Boolean method that returns true if the current object is attached or false otherwise.

keys ()

Returns list of field names and list names stored in that object. Excludes connectors and unique_id.

Example:

foreach my $key ($object->keys) {
   my $value=$object->get($key);
   print "object.$key=$value\n";
}
new (%)

Creates new instance of a Hash object. Would not work if called directly, XAO::Objects' new() method should always be used instead.

Example:

my $obj=XAO::Objects->new(objname => 'Data::Customer',
                          glue => $glue);

One required argument is 'glue', it must contain a reference to XAO::DO::FS::Glue object.

As a convenience it is recommended to call get_new() method on the list object to get new empty detached objects of the class that list can store. If you do so first you will not forget to pass `glue' as get_new() will do that for you and second if later on the name of the class will change you will not have to worry about that.

Example:

my $customer=$customers_list->get_new();
objtype ()

For all objects based on Hash returns 'Hash' string.

put ($$)

Stores new value into the Hash object. Values can currently only be strings and numbers, you cannot store a list.

On attached objects your changes go directly into the database. On detached objects changes accumulate in memory and only get stored into the database when you put() that object into an attached list.

Example:

$customer->put(full_name => 'John Silver');

Name must correspond to a previously defined placeholder otherwise an error will be thrown.

Value must meet constrains set for the placeholder, otherwise error will be thrown and no changes will be made.

reattach ()

The same as to call put() method on upper level container. Works only for detached objects and does nothing if the object is already attached.

Note: entire content of an object currently stored in the database would be replaced with current object recursively. Be careful!

All attached objects that refer to the same piece of data would immediately start returning updated values on calls to get() method.

Not implemented yet. Call put() on the list instead to attach the object.

upper_class (;$)

Returns class name for the hash that contained the list that contained current hash object.

values ()

Returns a list of all property values for the current object including its contained Lists if any.

Note: the order of values is the same as the order of keys returned by keys() method. At least until you modify the object directly on indirectly. It is not recommended to use values() method for the reason of pure predictability.

uri ($)

Returns complete URI to either the object itself (if no argument is given) or to a property with the given name.

That URI can then be used to retrieve a property or object using $odb->fetch($uri). Be aware, that fetch() is a relatively slow method and should not be abused.

Example:

my $uri=$customer->uri;
print "URI of that customer is: $uri\n";

AUTHORS

Xao, Inc. (c) 2001. This module was developed by Andrew Maltsev <am@xao.com> with the help and valuable comments from other team members.

SEE ALSO

Further reading: XAO::OS, XAO::DO::FS::List (aka FS::List), XAO::DO::FS::Glue (aka FS::Glue).