NAME
Handel::Manual::Storage - An introduction to the storage layer and how it is used by the interface layer.
DESCRIPTION
Handel::Storage is the layer of glue that binds the public interface layer API to custom storage mediums. It is used to abstract the most common storage actions away from the public classes so they are more likely to work the same whether you are storing carts in DBIx::Class schemas, or in a remote LDAP directory.
WORKFLOW
Handel::Storage provides two main sets of methods for use within the interface classes. The first set of of methods deal with creating/searching/deleting carts. The second set of methods deal with creating/searching/deleting items on the carts objects themselves. How items are associated with carts is dependent upon the type of storage being used.
When carts are created by the cart storage class, and items are created by the item storage class, both carts and items are considered to be 'first class objects'. This means that carts and items are treated as equals and will function independently of each other. This form will usually occur when you are storing carts/items in storage mediums that doesn't have an internal representation for relationships like flat test files or remote server calls.
When carts are created by the cart storage class, and the items are also created by the cart storage class, carts are considered to be 'first class objects' and items are treated as 'second class objects', or children of the cart objects. This means that one should only create items using the cart storage class and never directly using the item storage class. This form will usually occur when you are storing carts/item in storage mediums that have internal representations for relationships, like databases (foreign keys), or xml documents (item tags inside of parent cart tags), etc.
In order to accommodate both scenarios, Handel::Storage and Handel::Storage::Result provides a set of abstract methods (add_item, search_items, etc) that allow each storage class to determine the best way to act upon a carts item collection.
Items As Second Class Objects
Since this is the default method used by the default Handel::Storage::DBIC class, we'll cover it first.
In the realm of DBIx::Class schemas, all schema source classes are loaded into a schema object. After adding a cart record, one can either add an item record manually to the items table, or use the *_related helper methods provided on each cart result. Since the *_related methods take care of setting any foreign key relationship data for us, and Handel wants to know as little about your schema as possible, we will use those methods. :-)
my $cart_result = $cart_storage->create(\%data);
# These are the same
my $item_result = $cart_result->add_item(\%itemdata);
my $item_result = $cart_storage->add_item($cart_result, \%itemdata);
# in carts storage class
sub add_item {
my ($self, $cart, $data) = @_;
...
$cart->add_related('items', $data);
...
};
In the example above, the carts storage object has taken care of adding the item to the current cart. The items storage object wasn't involved at all in the process.
Items as First Class Objects
In the realm of text files, where there is no direct physical relationship between carts and items, the cart classes storage object will take care of maintaining carts, and the item classes storage will take car of maintaining items
my $cart_result = $cart_storage->create(\%data);
# These are the same
my $item_result = $cart_result->add_item(\%itemdata);
my $item_result = $cart_storage->add_item($cart_result, \%itemdata);
# in carts storage class
sub add_item {
my ($self, $cart, $data) = @_;
my $item_storage = $self->item_class->storage;
...
$item_storage->create($data);
...
};
In the example above, the carts storage object receives the call to add an item to the cart, but it actually uses the item classes storage object to do the work. This is essentially no different than doing:
my $cart_result = $cart_storage->create(\%data);
my $item_result = $item_storage->create({cart_id => $data{id}, %itemdata})
Abstract Storage Results
By default, all objects returned by the create/add/search methods above are Handel::Storage::Result objects. They are simply thin wrappers around the actual storage results:
my $storage = Handel::Storage::Cart->new;
my $result = $storage->create({name => 'My Cart'});
print ref $result; # Handel::Storage::Result
print ref $result->storage_result; # Handel::Schema::Cart
For the most part, each result simply proxies methods to its private storage result, in this case, the DBIx::Class resultset result returned from the schema.
print $result->id;
# is really just
print $result->storage_result->id;
# storage_result is a real live DBIx::Class resultset result
print ref $storage->storage_result; # Handel::Schema::Cart
The result objects also provide a few convenience methods that forward to the storage object that created them:
$result->add({sku => 'ABC-123'});
$result->items;
# is really
$storage->add_item($result, {sku => 'ABC-123'});
$storage->search_items;
Whenever possible, storage is asked to perform work on behalf of the result. This allows one to write a custom storage layer without having to also write a custom storage result, although you can do that too if you if it is necessary.
Once created, the storage results are then consumed by the interface layer which doesn't care where they came from or how they are stored.
my $cart = Handel::Cart->create_instance(
$storage->create({name => 'My Cart'})
);
print ref $cart; # Handel::Cart
print ref $cart->result # Handel::Storage::Result
print ref $cart->result->storage_result; # Handel::Schema::Cart
print $cart->id;
# is really this
print $cart->get_column('id');
# which is really just this
print $cart->result->id;
# which is really even this:
print $cart->result->storage_result->id;
SEE ALSO
Handel::Manual::Storage::DBIC, Handel::Storage, Handel::Storage::Result, Handel::Storage::DBIC
AUTHOR
Christopher H. Laco
CPAN ID: CLACO
claco@chrislaco.com
http://today.icantfocus.com/blog/