# $Id$ ## no critic (ProhibitCaptureWithoutTest) package Catalyst::Helper::Controller::Handel::Cart; use strict; use warnings; BEGIN { use Catalyst 5.7001; use Catalyst::Utils; use Path::Class; }; =head1 NAME Catalyst::Helper::Controller::Handel::Cart - Helper for Handel::Cart Controllers =head1 SYNOPSIS script/create.pl controller Handel::Cart [ ] script/create.pl controller Cart Handel::Cart Cart Checkout =head1 DESCRIPTION A Helper for creating controllers based on Handel::Cart objects. If no modelclass is specified, ::M::Cart is assumed. Both the modelclass and checkoutcontroller arguments try to do the right thing with the names given to them. For example, you can pass the shortened class name without the MyApp::M/C, or pass the fully qualified package name: MyApp::M::CartModel MyApp::Model::CartModel CartModel In all three cases everything before M{odel)|C(ontroller) will be stripped and the class CartModel will be used. B =head1 METHODS =head2 mk_compclass Makes a Handel::Cart Controller class and template files for you. =cut sub mk_compclass { my ($self, $helper, $model, $checkout) = @_; my $file = $helper->{'file'}; my $dir = dir($helper->{'base'}, 'root', $helper->{'uri'}); $model ||= 'Cart'; $checkout ||= 'Checkout'; $model =~ /^(.*::M(odel)?::)?(.*)$/i; $model = $3 ? $3 : 'Cart'; $helper->{'model'} = $model; $checkout =~ /^(.*::C(ontroller)?::)?(.*)$/i; my $couri = $3 ? lc($3) : 'checkout'; $couri =~ s/::/\//g; $helper->{'couri'} = $couri; $helper->{'action'} = Catalyst::Utils::class2prefix($helper->{'class'}); $helper->mk_dir($dir); $helper->render_file('controller', $file); $helper->render_file('default', file($dir, 'default')); $helper->render_file('list', file($dir, 'list')); $helper->render_file('errors', file($dir, 'errors')); $helper->render_file('profiles', file($dir, 'profiles.yml')); $helper->render_file('messages', file($dir, 'messages.yml')); $helper->render_file('products', file($helper->{'base'}, 'root', 'static', 'products.htm')); return 1; }; =head2 mk_comptest Makes a Handel::Cart Controller test for you. =cut sub mk_comptest { my ($self, $helper) = @_; my $test = $helper->{'test'}; $helper->render_file('test', $test); return 1; }; =head1 SEE ALSO L, L, L =head1 AUTHOR Christopher H. Laco CPAN ID: CLACO claco@chrislaco.com http://today.icantfocus.com/blog/ =cut 1; __DATA__ =begin pod_to_ignore __controller__ package [% class %]; use strict; use warnings; BEGIN { use base qw/Catalyst::Controller/; use Handel::Constants qw/:cart/; use FormValidator::Simple 0.17; use YAML 0.65; }; =head1 NAME [% class %] - Catalyst Controller =head1 DESCRIPTION Catalyst Controller. =head1 METHODS =head2 COMPONENT =cut sub COMPONENT { my $self = shift->NEXT::COMPONENT(@_); $self->{'validator'} = FormValidator::Simple->new; $self->{'validator'}->set_messages( $_[0]->path_to('root', '[% action %]', 'messages.yml') ); $self->{'profiles'} = YAML::LoadFile($_[0]->path_to('root', '[% action %]', 'profiles.yml')); return $self; }; =head2 default Default action when browsing to [% uri %]/. If no session exists, or the shopper id isn't set, no cart will be loaded. This keeps non-shoppers like Google and others from wasting sessions and cart records for no good reason. =cut sub default : Private { my ($self, $c) = @_; $c->stash->{'template'} = '[% action %]/default'; if ($c->sessionid && $c->session->{'shopper'}) { if (my $cart = $c->forward('load')) { $c->stash->{'cart'} = $cart; $c->stash->{'items'} = $cart->items; }; }; return; }; =head2 add =over =item Parameters: (See L) =back Adds an item to the current cart during POST. [% uri %]/add/ =cut sub add : Local { my ($self, $c) = @_; if ($c->req->method eq 'POST') { my $cart = $c->forward('create'); $cart->add($c->req->params); }; $c->res->redirect($c->uri_for('[% uri %]/')); }; =head2 clear Clears all items form the current shopping cart during POST. [% uri %]/clear/ =cut sub clear : Local { my ($self, $c) = @_; if ($c->req->method eq 'POST') { if (my $cart = $c->forward('load')) { $cart->clear; }; }; $c->res->redirect($c->uri_for('[% uri %]/')); }; =head2 create Creats a new temporary shopping cart or returns the existing cart, creating a new session shopper id if necessary. my $cart = $c->forward('create'); =cut sub create : Private { my ($self, $c) = @_; if (!$c->session->{'shopper'}) { $c->session->{'shopper'} = $c->model('[% model %]')->storage->new_uuid; }; if (my $cart = $c->forward('load')) { return $cart; } else { return $c->model('[% model %]')->create({ shopper => $c->session->{'shopper'}, type => CART_TYPE_TEMP }); }; return; }; =head2 delete =over =item Parameters: id =back Deletes an item from the current shopping cart during a POST. [% uri %]/delete/ =cut sub delete : Local { my ($self, $c) = @_; if ($c->req->method eq 'POST') { if ($c->forward('validate')) { if (my $cart = $c->forward('load')) { $cart->delete({ id => $c->req->params->{'id'} }); $c->res->redirect($c->uri_for('[% uri %]/')); }; } else { $c->forward('default'); }; } else { $c->res->redirect($c->uri_for('[% uri %]/')); }; return; }; =head2 destroy =over =item Parameters: id =back Deletes the specified saved cart and all of its items during a POST. [% uri %]/destroy/ =cut sub destroy : Local { my ($self, $c) = @_; if ($c->req->method eq 'POST') { if ($c->forward('validate')) { my $cart = $c->model('[% model %]')->search({ id => $c->req->params->{'id'}, shopper => $c->session->{'shopper'}, type => CART_TYPE_SAVED })->first; if ($cart) { $cart->destroy; } else { warn "not cart"; }; $c->res->redirect($c->uri_for('[% uri %]/list/')); } else { $c->forward('list'); }; } else { $c->res->redirect($c->uri_for('[% uri %]/')); }; return; }; =head2 list Displays a list of the current shoppers saved carts/wishlists. [% uri %]/list/ =cut sub list : Local { my ($self, $c) = @_; $c->stash->{'template'} = '[% action %]/list'; if ($c->sessionid && $c->session->{'shopper'}) { my $carts = $c->model('[% model %]')->search({ shopper => $c->session->{'shopper'}, type => CART_TYPE_SAVED }); $c->stash->{'carts'} = $carts; }; return; }; =head2 load Loads the shoppers current cart. my $cart = $c->forward('load'); =cut sub load : Private { my ($self, $c) = @_; if ($c->sessionid && $c->session->{'shopper'}) { return $c->model('[% model %]')->search({ shopper => $c->session->{'shopper'}, type => CART_TYPE_TEMP })->first; }; return; }; =head2 restore =over =item Parameters: id =back Restores a saved shopping cart into the shoppers current cart during a POST. [% uri %]/restore/ =cut sub restore : Local { my ($self, $c) = @_; if ($c->req->method eq 'POST') { if ($c->forward('validate')) { if (my $cart = $c->forward('create')) { $cart->restore({ id => $c->req->param('id'), shopper => $c->session->{'shopper'}, type => CART_TYPE_SAVED }, $c->req->param('mode') || CART_MODE_APPEND); $c->res->redirect($c->uri_for('[% uri %]/')); }; } else { $c->forward('list'); }; } else { $c->res->redirect($c->uri_for('[% uri %]/')); }; return; }; =head2 save =over =item Parameters: name =back Saves the current cart with the name specified. [% uri %]/save/ =cut sub save : Local { my ($self, $c) = @_; if ($c->req->method eq 'POST') { if ($c->forward('validate')) { if (my $cart = $c->forward('load')) { $cart->name($c->req->param('name') || 'My Cart'); $cart->save; $c->res->redirect($c->uri_for('[% uri %]/list/')); }; } else { $c->forward('default'); }; } else { $c->res->redirect($c->uri_for('[% uri %]/')); }; return; }; =head2 update =over =item Parameters: quantity =back Updates the specified cart item qith the quantity given. [% uri %]/update/ =cut sub update : Local { my ($self, $c) = @_; if ($c->req->method eq 'POST') { if ($c->forward('validate')) { if (my $cart = $c->forward('load')) { my $item = $cart->items({ id => $c->req->param('id') })->first; if ($item) { $item->quantity($c->req->param('quantity')); }; $c->res->redirect($c->uri_for('[% uri %]/')); }; } else { $c->forward('default'); }; } else { $c->res->redirect($c->uri_for('[% uri %]/')); }; return; }; =head2 validate Validates the current form parameters using the profile in profiles.yml that matches the current action. if ($c->forward('validate')) { }; =cut sub validate : Private { my ($self, $c) = @_; $self->{'validator'}->results->clear; my $results = $self->{'validator'}->check( $c->req, $self->{'profiles'}->{$c->action} ); if ($results->success) { return $results; } else { $c->stash->{'errors'} = $results->messages($c->action); }; return; }; =head1 AUTHOR A clever guy =head1 LICENSE This library is free software, you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1; __test__ use Test::More tests => 3; use strict; use warnings; use_ok('Catalyst::Test', '[% app %]'); use_ok('[% class %]'); ok(request('[% uri %]')->is_success, 'Request should succeed'); __default__ [% TAGS [- -] -%] [% USE HTML %]

Your Shopping Cart

[% INCLUDE [- action -]/errors %] [% IF items.count %] [% WHILE (item = items.next) %] [% END %]
SKU Description Price Quantity Total
[% HTML.escape(item.sku) %] [% HTML.escape(item.description) %] [% HTML.escape(item.price.as_string('FMT_SYMBOL')) %] [% HTML.escape(item.total.as_string('FMT_SYMBOL')) %]
Subtotal: [% HTML.escape(cart.subtotal.as_string('FMT_SYMBOL')) %]
[% ELSE %]

Your shopping cart is empty.

[% END %] __list__ [% TAGS [- -] -%] [% USE HTML %]

Your Saved Shopping Carts

[% INCLUDE [- action -]/errors %] [% IF carts.count %] [% WHILE (cart = carts.next) %] [% END %]
Name Restore Mode
[% HTML.escape(cart.name) %]
[% ELSE %]

You have no saved shopping carts.

[% END %] __errors__ [% TAGS [- -] -%] [% IF errors %]
    [% FOREACH error IN errors %]
  • [% HTML.escape(error) %]
  • [% END %]
[% END %] __messages__ [% action %]/save: name: NOT_BLANK: The name field cannot be empty. LENGTH: The name field must be between 1 and 50 characters. [% action %]/update: id: REGEX: The id field is in the wrong format. quantity: NOT_BLANK: The quantity field cannot be empty. UINT: The quantity field must be a positive number. [% action %]/delete: id: REGEX: The id field is in the wrong format. [% action %]/destroy: id: REGEX: The id field is in the wrong format. [% action %]/restore: id: REGEX: The id field is in the wrong format. mode: BETWEEN: The mode field must be between 1 and 3. __profiles__ [% action %]/save: - name - [ [NOT_BLANK], [LENGTH, 1, 50] ] [% action %]/delete: - id - - - REGEX - !!perl/regexp (?i-xsm:^[a-f0-9]{8}-([a-f0-9]{4}-){3}[a-f0-9]{12}$) [% action %]/destroy: - id - - - REGEX - !!perl/regexp (?i-xsm:^[a-f0-9]{8}-([a-f0-9]{4}-){3}[a-f0-9]{12}$) [% action %]/update: - id - - - REGEX - !!perl/regexp (?i-xsm:^[a-f0-9]{8}-([a-f0-9]{4}-){3}[a-f0-9]{12}$) - quantity - [ [NOT_BLANK], [UINT] ] [% action %]/restore: - id - - - REGEX - !!perl/regexp (?i-xsm:^[a-f0-9]{8}-([a-f0-9]{4}-){3}[a-f0-9]{12}$) - mode - [ [BETWEEN, 1, 3] ] __products__ Nifty New Products

Nifty New Products

View Cart |

Mendlefarg 3000

It slices. It dices. It MVCs!

Flimblebot 98

The most advanced flimble-based bot response software ever!

__END__