package Reaction::UI::Controller::Collection::CRUD; use strict; use warnings; use base 'Reaction::UI::Controller::Collection'; use Reaction::Class; use aliased 'Reaction::UI::ViewPort::Action'; use aliased 'Reaction::UI::ViewPort::ListView'; sub _build_action_viewport_map { my $self = shift; my $map = $self->next::method(@_); $map->{list} = ListView if exists $map->{list}; #my %allowed = map { $_ => undef } # ( @{$self->default_member_actions}, @{$self->default_collection_actions} ); my @local_actions = qw/create update delete delete_all/; #$map->{$_} = Action for grep { exists $allowed{$_} } @local_actions; $map->{$_} = Action for @local_actions; return $map; } sub _build_default_member_actions { [ @{shift->next::method(@_)}, qw/update delete/ ]; } sub _build_default_collection_actions { [ @{shift->next::method(@_)}, qw/create delete_all/ ]; } sub get_model_action { my ($self, $c, $name, $target) = @_; return $target->action_for($name, ctx => $c); } sub create :Chained('base') :PathPart('create') :Args(0) { my ($self, $c) = @_; my $apply = sub { $self->after_create_callback( @_) }; my $close = sub { $self->on_create_close_callback( @_) }; my $vp_args = { target => ($c->stash->{collection} || $self->get_collection($c)), on_apply_callback => $self->make_context_closure($apply), on_close_callback => $self->make_context_closure($close), }; $self->basic_model_action( $c, $vp_args); } sub delete_all :Chained('base') :PathPart('delete_all') :Args(0) { my ($self, $c) = @_; my $close = sub { $self->on_delete_all_close_callback( @_) }; $self->basic_model_action( $c, { target => ($c->stash->{collection} || $self->get_collection($c)), on_close_callback => $self->make_context_closure($close), }); } sub on_delete_all_close_callback { my($self, $c) = @_; $self->redirect_to($c, 'list'); } sub after_create_callback { my ($self, $c, $vp, $result) = @_; return $self->redirect_to ( $c, 'update', [ @{$c->req->captures}, $result->id ] ); } sub on_create_close_callback { my($self, $c, $vp) = @_; $self->redirect_to( $c, 'list' ); } sub update :Chained('object') :Args(0) { my ($self, $c) = @_; my $close = sub { $self->on_update_close_callback( @_) }; my $vp_args = { on_close_callback => $self->make_context_closure($close), }; $self->basic_model_action( $c, $vp_args); } sub on_update_close_callback { my($self, $c) = @_; #this needs a better solution. currently thinking about it my @cap = @{$c->req->captures}; pop(@cap); # object id $self->redirect_to($c, 'list', \@cap); } sub delete :Chained('object') :Args(0) { my ($self, $c) = @_; my $close = sub { $self->on_delete_close_callback( @_) }; my $vp_args = { on_close_callback => $self->make_context_closure($close), }; $self->basic_model_action( $c, $vp_args); } sub on_delete_close_callback { my($self, $c) = @_; #this needs a better solution. currently thinking about it my @cap = @{$c->req->captures}; pop(@cap); # object id $self->redirect_to($c, 'list', \@cap); } sub basic_model_action { my ($self, $c, $vp_args) = @_; my $stash = $c->stash; my $target = delete $vp_args->{target}; $target ||= ($stash->{object} || $stash->{collection} || $self->get_collection($c)); my $action_name = join('', map{ ucfirst } split('_', $c->stack->[-1]->name)); my $model = $self->get_model_action($c, $action_name, $target); return $self->basic_page($c, { model => $model, %{$vp_args||{}} }); } 1; __END__ =head1 NAME Reaction::UI::Controller::Collection::CRUD - Basic CRUD functionality for Reaction::InterfaceModel data =head1 DESCRIPTION Controller class which extends L to provide basic Create / Update / Delete / DeleteAll actions. Building on the base of the Collection controller this controller allows you to easily create complex and highly flexible CRUD functionality for your InterfaceModel models by providing a simple way to render and process your custom InterfaceModel Actions and customize built-ins. =head1 METHODS =head2 get_model_action $c, $action_name, $target_im Get an instance of the C<$action_name> L for model C<$target> This action is suitable for passing to an C viewport =head2 after_create_callback $c, $vp, $result When a action is applied, move the user to the new object's, C page. =head2 basic_model_action $c, \%vp_args Extension to C which automatically instantiates an L with the right data target using C =head2 _build_action_viewport_map Map C, C, C and C to use the L viewport by default and have C use L by default. =head2 _build_default_member_actions Add C and C to the list of default actions. =head2 _build_default_collection_actions Add C and C to the list of default actions. =head1 ACTIONS =head2 create Chaned to C. Create a new member of the collection represented by this controller. By default it attaches the C to DWIM after apply operations. See L for more info. =head2 delete_all Chained to B, delete all the members of the B. In most cases this is very much like a C operation. See L for more info. =head2 update Chained to C, update a single object. See L for more info. =head2 delete Chained to C, delete a single object. See L for more info. =head1 SEE ALSO L, L =head1 AUTHORS See L for authors. =head1 LICENSE See L for the license. =cut