NAME

Handel::Storage - Abstract storage layer for cart/order/item reads/writes

SYNOPSIS

use MyCustomCart;
use strict;
use warnings;
use base qw/Handel::Base/;

__PACKAGE__->storage({
    schema_source  => 'Carts',
    item_class     => 'MyCustomItem',
    constraints    => {
        id         => {'Check Id'      => \&constraint_uuid},
        shopper    => {'Check Shopper' => \&constraint_uuid},
        type       => {'Check Type'    => \&constraint_cart_type},
        name       => {'Check Name'    => \&constraint_cart_name}
    },
    default_values => {
        id         => __PACKAGE__->storage_class->can('new_uuid'),
        type       => CART_TYPE_TEMP
    }
});

1;

DESCRIPTION

Handel::Storage is used as an intermediary between Handel::Cart/Handel::Order and the schema classes used for reading/writing to the database.

CONSTRUCTOR

new

Arguments: \%options

Creates a new instance of Handel::Storage, and passes the options to "setup" on the new instance. The three examples below are the same:

my $storage = Handel::Storage-new({
    schema_source  => 'Carts',
    cart_class     => 'CustomerCart'
});

my $storage = Handel::Storage-new;
$storage->setup({
    schema_source  => 'Carts',
    cart_class     => 'CustomerCart'
});

my $storage = Handel::Storage->new;
$storage->schema_source('Carts');
$storage->cart_class('CustomCart');

The following options are available to new/setup, and take the same data as their method counterparts:

add_columns
autoupdate
cart_class
checkout_class
connection_info
constraints
constraints_class
currency_class
currency_columns
default_values_class
default_values
item_class
item_relationship
iterator_class
remove_columns
result_class
schema_class
schema_instance
schema_source
table_name
validation_class
validation_module
validation_profile

METHODS

add_columns

Arguments: @columns

Adds a list of columns to the current schema_source in the current schema_class and maps the new columns to accessors in the current class. Be careful to always use the column names, not their accessor aliases.

$storage->add_columns(qw/foo bar baz/);

You can also add columns using the DBIx::Class \%column_info syntax:

$storage->add_columns(
    foo => {data_type => 'varchar', size => 36},
    bar => {data_type => int, accessor => 'get_bar'}
);

Yes, you can even mix/match the two:

$storage->add_columns(
    'foo',
    bar => {accessor => 'get_bar', data_type => 'int'},
    'baz'
);

Before schema_instance is initialized, the columns to be added are stored internally, then added to the schema_instance when it is initialized. If a schema_instance already exists, the columns are added directly to the schema_source in the schema_instance itself.

add_constraint

Arguments: $column, $name, \&sub

Adds a named constraint for the given column to the current schema_source in the current schema_class. During insert/update operations, the constraint subs will be called upon to validation the specified columns data after and default values are set on empty columns. You can any number of constraints for each column as long as they all have different names. The constraints may or may not be called in the order in which they are added.

$storage->add_constraint('id', 'Check Id Format' => \&constraint_uuid);

Constraints can only be added before schema_instance is initialized. A Handel::Exception::Storage exception will be thrown if you try to add a constraint and schema_instance is already initialized.

Be careful to always use the column name, not its accessor alias if it has one.

add_item

Arguments: $result, \%data

Adds a new item to the specified result, returning a storage result object.

my $storage = Handel::Storage::Cart->new;
my $result = $storage->create({
    shopper => '11111111-1111-1111-1111-111111111111'
});

my $item = $storage->add_item($result, {
    sku => 'ABC123'
});

print $item->sku;

A Handel::Storage::Exception will be thrown if the specified result has no item relationship.

autoupdate

Arguments: 0|1

Gets/sets the autoupdate flag for the current schema_source. When set to 1, an update request will be made to the database for every field change. When set to 0, no updated data will be sent to the database until update is called.

$storage->autoupdate(1);

The default is 1.

cart_class

Arguments: $cart_class

Gets/sets the cart class to be used when creating orders from carts.

$storage->cart_class('CustomCart');

A Handel::Exception::Storage exception will be thrown if the specified class can not be loaded.

checkout_class

Arguments: $checkout_class

Gets/sets the checkout class to be used to process the order through the CHECKOUT_PHASE_INITIALIZE phase when creating a new order and the process options is set. The default checkout class is Handel::Checkout.

$storage->checkout_class('CustomCheckout');

A Handel::Exception::Storage exception will be thrown if the specified class can not be loaded.

clone

Returns a clone of the current storage instance.

$storage->schema_source('Foo');
my $clone = $storage->clone;
$clone->schema_source('Bar');

print $storage->schema_source; # Foo
print $clone->schema_source;   # Bar

This is used mostly between sub/super classes to inherit a copy of the storage settings without having to specify options from scratch.

column_accessors

Returns a hashref containing all of the columns and their accessor names for the current storage object.

If a schema_instance already exists, the columns from schema_source in that schema_instance will be returned. If no schema_instance exists, the columns from schema_source in the current schema_class will be returned plus any columns to be added from add_columns minus and columns to be removed from remove_columns.

connection_info

Arguments: \@info

Gets/sets the connection information used when connecting to the database.

$storage->connection_info(['dbi:mysql:foo', 'user', 'pass', {PrintError=>1}]);

The info argument is an array ref that holds the following values:

$dsn

The DBI dsn to use to connect to.

$username

The username for the database you are connecting to.

$password

The password for the database you are connecting to.

\%attr

The attributes to be pass to DBI for this connection.

See DBI for more information about dsns and connection attributes.

constraints

Arguments: \%constraints

Gets/sets the constraints configuration for the current storage instance.

$storage->constraints({
    id   => {'Check Id Format' => \&constraint_uuid},
    name => {'Check Name/Type' => \%constraint_cart_type}
});

The constraints are stored in a hash where each key is the name of the column and each value is another hash reference containing the constraint name and the constraint subroutine reference.

Be careful to always use the column name, not its accessor alias if it has one.

constraints_class

Arguments: $constraint_class

Gets/sets the constraint class to be used when check column constraints. The default constraint class is Handel::Components::Constraints. The constraint class used should be subclass of Handel::Components::Constraints.

$storage->constraint_class('CustomCurrency');

A Handel::Exception::Storage exception will be thrown if the specified class can not be loaded.

copyable_item_columns

Returns a list of columns in the current item class that can be copied freely. This list is usually all columns in the item class except for the primary key columns and the foreign key columns that participate in the specified item relationship.

count_items

Arguments: $result

Returns the number of items associated with the specified result.

my $storage = Handel::Storage::Cart->new;
my $result = $storage->create({
    shopper => '11111111-1111-1111-1111-111111111111'
});

$result->add_item({
    sku => 'ABC123'
});

print $storage->count_items($result);

A Handel::Storage::Exception will be thrown if the specified result has no item relationship.

create

Arguments: \%data

Creates a new result in the current source in the current schema.

my $result = $storage->create({
    col1 => 'foo',
    col2 => 'bar'
});

This is just a convenience method that does the same thing as:

my $result = $storage->schema_instance->resultset($storage->schema_source)->create({
    col1 => 'foo',
    col2 => 'bar'
});

currency_class

Arguments: $currency_class

Gets/sets the currency class to be used when inflating currency columns. The default currency class is Handel::Currency. The currency class used should be subclass of Handel::Currency.

$storage->currency_class('CustomCurrency');

A Handel::Exception::Storage exception will be thrown if the specified class can not be loaded.

currency_columns

Arguments: @columns

Gets/sets the columns that should be inflated into currency objects.

$storage->currency_columns(qw/total price tax/);

default_values_class

Arguments: $default_values_class

Gets/sets the default values class to be used when setting default column values. The default class is Handel::Components::DefaultValues. The default values class used should be subclass of Handel::Components::DefaultValues.

$storage->default_value_class('SetDefaults');

A Handel::Exception::Storage exception will be thrown if the specified class can not be loaded.

default_values

Arguments: \%values

Gets/sets the hash containing the default values to be applied to empty columns during insert/update statements. Default values are applied to empty columns before and constraints or validation occurs.

$storage->default_values({
    id   => \&newid,
    name => 'My New Cart'
});

The default values are stored in a hash where the key is the name of the column and the value is either a reference to a subroutine to get the value from, or an actual default value itself.

Be careful to always use the column name, not its accessor alias if it has one.

delete

Arguments: \%filter

Deletes results matching the filter in the current source in the current schema.

$storage->delete({
    id => '11111111-1111-1111-1111-111111111111'
});

This is just a convenience method that does the same thing as:

$storage->schema_instance->resultset($storage->schema_source)->search({
    id => '11111111-1111-1111-1111-111111111111'
})->delete_all;

delete_items

Arguments: $result, \%filter

Deletes items matching the filter from the specified result.

my $storage = Handel::Storage::Cart->new;
my $result = $storage->create({
    shopper => '11111111-1111-1111-1111-111111111111'
});

$result->add_item({
    sku => 'ABC123'
});

$storage->delete_items($result, {
    sku => 'ABC%'
});

A Handel::Storage::Exception will be thrown if the specified result has no item relationship.

item_class

Arguments: $item_class

Gets/sets the item class to be used when returning cart/order items.

$storage->item_class('CustomCartItem');

The class specified should be a subclass of Handel::Base, or at least provide its inflate_result and result methods.

A Handel::Exception::Storage exception will be thrown if the specified class can not be loaded.

item_relationship

Arguments: $relationship_name

Gets/sets the name of the schema relationship between carts and items. The default item relationship is 'items'.

# in your schema classes
MySchema::CustomCart->has_many(rel_items => 'MySchema::CustomItem', {'foreign.cart' => 'self.id'});

# in your storage
$storage->item_relationship('rel_items');

iterator_class

$iterator_class

Gets/sets the class used for iterative resultset operations. The default iterator is Handel::Iterator.

# in your storage
$storage->iterator_class('MyIterator');

# in your code
my $carts = Handel::Carts->load;
print ref $carts; # MyIterator

A Handel::Exception::Storage exception will be thrown if the specified class can not be loaded.

new_uuid

Returns a new uuid/guid string in the form of

xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

See DBIx::Class::UUIDColumns for more information on how uuids are generated.

process_error

This method accepts errors from DBI using $dbh->{HandelError} and converts them into Handel::Exception objects before throwing the error.

remove_columns

Arguments: @columns

Removes a list of columns from the current schema_source in the current schema_class and removes the autogenerated accessors from the current class. Be careful to always use the column names, not their accessor aliases.

$storage->remove_columns(qw/description/);

Before schema_instance is initialized, the columns to be removed are stored internally, then removed from the schema_instance when it is initialized. If a schema_instance already exists, the columns are removed directly from the schema_source in the schema_instance itself.

remove_constraint

Arguments: $column, $name

Removes a named constraint for the given column from the current schema_source in the current schema_class' constraints data structure.

$storage->remove_constraint('id', 'Check Id Format' => \&constraint_uuid);

Constraints can only be removed before schema_instance is initialized. A Handel::Exception::Storage exception will be thrown if you try to remove a constraint and schema_instance is already initialized.

Be careful to always use the column name, not its accessor alias if it has one.

remove_constraints

Arguments: $column

Removes all constraints for the given column from the current schema_source in the current schema_class' constraints data structure.

$storage->remove_constraints('id');

Constraints can only be removed before schema_instance is initialized. A Handel::Exception::Storage exception will be thrown if you try to remove a constraint and schema_instance is already initialized.

Be careful to always use the column name, not its accessor alias if it has one.

result_class

Arguments: $result_class

Gets/sets the result class to be used when returning results from create/search storage operations. The default result class is Handel::Storage::Result.

$storage->result_class('CustomStorageResult');

A Handel::Exception::Storage exception will be thrown if the specified class can not be loaded.

schema_class

Arguments: $schema_class

Gets/sets the schema class to be used for database reading/writing.

$storage->schema_class('MySchema');

A Handel::Exception::Storage exception will be thrown if the specified class can not be loaded.

schema_instance

Arguments: $schema_instance

Gets/sets the schema instance to be used for database reading/writing. If no instance exists, a new one will be created from the specified schema class.

my $schema = MySchema->connect;

$storage->schema_instance($schema);

When a new schema instance is created or assigned, it is cloned and the clone is altered and used, leaving the original schema untouched.

See Handel::Manual::Schema for more detailed information about how the schema instance is configured.

schema_source

Arguments: $source_name

Gets/sets the result source name in the current schema class that will be used to read/write data in the schema.

$storage->schema_source('Foo');

See "source_name" in DBIx::Class::ResultSource for more information about setting the source name of schema classes. By default, this will be the short name of the schema class in DBIx::Class schemas.

By default, Handel::Storage looks for the "Carts" source when working with Handel::Cart, the "Orders" source when working with Handel::Order and the "Items" source when working with Cart/Order items.

Arguments: \%filter

Returns results in list context, or an iterator in scalar context from the current source in the current schema matching the search filter.

my $iterator = $storage->search({
    col1 => 'foo'
});

my @results = $storage->search({
    col1 => 'foo'
});

This is just a convenience method that does the same thing as:

my $resultset = $storage->schema_instance->resultset($storage->schema_source)->search({
    col1 => 'foo'
});

search_items

Arguments: $result, \%filter

Returns items matching the filter associated with the specified result.

my $storage = Handel::Storage::Cart->new;
my $result = $storage->search({
    id => '11111111-1111-1111-1111-111111111111'
});

my $iterator = $storage->search_items($result);

Returns results in list context, or an iterator in scalar context from the current source in the current schema matching the search filter.

A Handel::Storage::Exception will be thrown if the specified result has no item relationship.

setup

Arguments: \%options

Configures a storage instance with the options specified. Setup accepts the exact same options that "new" does.

package MyStorageClass;
use strict;
use warnings;
use base qw/Handel::Storage/;

__PACKAGE__->setup({
    schema_source => 'Foo'
});

# or

my $storage = Handel::Storage-new;
$storage->setup({
    schema_source  => 'Carts',
    cart_class     => 'CustomerCart'
});

This is the same as doing:

my $storage = Handel::Storage-new({
    schema_source  => 'Carts',
    cart_class     => 'CustomerCart'
});

If you call setup on a storage instance or class that has already been configured, its configuration will be reset, and it will be configured with the new options. No attempt will be made to merged the options between the two.

If you pass in a schema_instance, it will be assigned last after all of the other options have been applied.

table_name

Arguments: $table_name

Gets/sets the name of the table in the database to be used for this schema source.

validation_class

Arguments: $validation_class

Gets/sets the validation class to be used when validating column values. The default class is Handel::Components::Validation. The validation class used should be subclass of Handel::Components::Validation.

$storage->validation_class('ValidateData');

A Handel::Exception::Storage exception will be thrown if the specified class can not be loaded.

See Handel::Components::Validation and DBIx::Class::Validation for more information on to use data validation.

validation_module

Arguments: $validation_module

Gets/sets the module validation class should use to do its column data validation. The default module is FormValidator::Simple. You can use any module that is compatible with DBIx::Class::Validation.

validation_profile

Arguments: \@profile*

Gets/sets the validation profile to be used when validating column values.

$storage->validation_profile([
    param1 => ['NOT_BLANK', 'ASCII', ['LENGTH', 2, 5]],
    param2 => ['NOT_BLANK', 'INT'  ],
    mail1  => ['NOT_BLANK', 'EMAIL_LOOSE']
]);

* The default validation module is FormValidator::Simple, which expects a profile in an array reference. If you use Data::FormValidator, make sure you pass in the profile as a hash reference instead:

$storage->validation_profile({
    optional => [qw( company
                     fax 
                     country )],
    required => [qw( fullname 
                     phone 
                     email 
                     address )]
});

See Handel::Components::Validation and DBIx::Class::Validation for more information on using data validation in Handel.

SEE ALSO

Handel::Storage::Result, Handel::Manual::Storage, Handel::Storage::Cart, Handel::Storage::Order

AUTHOR

Christopher H. Laco
CPAN ID: CLACO
claco@chrislaco.com
http://today.icantfocus.com/blog/