factor button code out and add default class
[catagits/Reaction.git] / lib / Reaction / UI / Controller / Collection / CRUD.pm
1 package Reaction::UI::Controller::Collection::CRUD;
2
3 use strict;
4 use warnings;
5 use base 'Reaction::UI::Controller::Collection';
6 use Reaction::Class;
7
8 use aliased 'Reaction::UI::ViewPort::Action';
9
10 sub _build_action_viewport_map {
11   my $map = shift->next::method(@_);
12   $map->{$_} = Action for qw/create update delete delete_all/;
13   return $map;
14 }
15
16 sub _build_action_viewport_args {
17   my $args = shift->next::method(@_);
18   $args->{list} =
19     { action_prototypes =>
20       [ { label => 'Create', action => sub {
21             [ '', 'create',    $_[1]->req->captures ] } },
22         { label => 'Delete all', action => sub {
23             [ '', 'delete_all', $_[1]->req->captures ] } },
24       ],
25       Member =>
26       { action_prototypes =>
27         [ { label => 'View', action => sub {
28               [ '', 'view', [ @{$_[1]->req->captures},   $_[0]->__id ] ] } },
29           { label => 'Edit', action => sub {
30               [ '', 'update', [ @{$_[1]->req->captures}, $_[0]->__id ] ] } },
31           { label => 'Delete', action => sub {
32               [ '', 'delete', [ @{$_[1]->req->captures}, $_[0]->__id ] ] } },
33         ],
34       },
35     };
36   return $args;
37 }
38
39 sub get_model_action {
40   my ($self, $c, $name, $target) = @_;
41
42   if ($target->can('action_for')) {
43     return $target->action_for($name, ctx => $c);
44   }
45
46   #can we please kill this already?
47   my $model_name = "Action::${name}".$self->model_name;
48   my $model = $c->model($model_name);
49   confess "no such Model $model_name" unless $model;
50   return $model->new(target_model => $target, ctx => $c);
51 }
52
53 sub create :Chained('base') :PathPart('create') :Args(0) {
54   my ($self, $c) = @_;
55   my $vp_args = {
56                  next_action => 'list',
57                  on_apply_callback => sub { $self->after_create_callback($c => @_); },
58                 };
59   $self->basic_model_action( $c, $vp_args);
60 }
61
62 sub delete_all :Chained('base') :PathPart('delete_all') :Args(0) {
63   my ($self, $c) = @_;
64   $self->basic_model_action( $c,  { next_action => 'list'});
65 }
66
67 sub after_create_callback {
68   my ($self, $c, $vp, $result) = @_;
69   return $self->redirect_to
70     ( $c, 'update', [ @{$c->req->captures}, $result->id ] );
71 }
72
73 sub update :Chained('object') :Args(0) {
74   my ($self, $c) = @_;
75   #this needs a better solution. currently thinking about it
76   my @cap = @{$c->req->captures};
77   pop(@cap); # object id
78   my $vp_args = { next_action => [ $self, 'redirect_to', 'list', \@cap ]};
79   $self->basic_model_action( $c, $vp_args);
80 }
81
82 sub delete :Chained('object') :Args(0) {
83   my ($self, $c) = @_;
84   #this needs a better solution. currently thinking about it
85   my @cap = @{$c->req->captures}; 
86   pop(@cap); # object id
87   my $vp_args = { next_action => [ $self, 'redirect_to', 'list', \@cap ]};
88   $self->basic_model_action( $c, $vp_args);
89 }
90
91 sub basic_model_action {
92   my ($self, $c, $vp_args) = @_;
93
94   my $target = exists $c->stash->{object} ?
95     $c->stash->{object} : $self->get_collection($c);
96
97   my $action_name = join('', map{ ucfirst } split('_', $c->stack->[-1]->name));
98   my $model = $self->get_model_action($c, $action_name, $target);
99   return $self->basic_page($c, { model => $model, %{$vp_args||{}} });
100 }
101
102 1;
103
104 __END__
105
106 =head1 NAME
107
108 Reaction::UI::Controller::CRUD - Basic CRUD functionality for Reaction::InterfaceModel data
109
110 =head1 DESCRIPTION
111
112 Controller class which extends L<Reaction::UI::Controller::Collection> to 
113 provide basic Create / Update / Delete / DeleteAll actions.
114
115 Building on the base of the Collection controller this controller allows you to
116 easily create complex and highly flexible CRUD functionality for your 
117 InterfaceModel models by providing a simple way to render and process your
118 custom InterfaceModel Actions and customize built-ins.
119
120 =head1 METHODS
121
122 =head2 get_model_action $c, $action_name, $target_im
123
124 Get an instance of the C<$action_name> 
125 L<InterfaceModel::Action|Reaction::InterfaceModel::Action> for model C<$target>
126 This action is suitable for passing to an 
127 C<Action|Reaction::UI::ViewPort::Action> viewport
128
129 =head2 after_create_callback $c, $vp, $result
130
131 When a <create> action is applied, move the user to the new object's,
132 C<update> page.
133
134 =head2 basic_model_action $c, \%vp_args
135
136 Extension to C<basic_page> which automatically instantiates an 
137 L<InterfaceModel::Action|Reaction::InterfaceModel::Action> with the right
138 data target using C<get_model_action>
139
140 =head2 _build_action_viewport_map
141
142 Map C<create>, C<update>, C<delete> and C<delete_all> to use the 
143 C<Action|Reaction::UI::ViewPort::Action> viewport by default.
144
145 =head2 _build_action_viewport_args
146
147 Add action_prototypes to the C<list> action so that action links render correctly in L<ListView|Rection::UI::ViewPort::Listview>.
148
149 =head1 ACTIONS
150
151 =head2 create
152
153 Chaned to C<base>. Create a new member of the collection represented by 
154 this controller. By default it attaches the C<after_create_callback> to
155 DWIM after apply operations.
156
157 See L<Create|Reaction::InterfaceModel::Action::DBIC::ResultSet::Create>
158  for more info.
159
160 =head2 delete_all
161
162 Chained to B<base>, delete all the members of the B<collection>. In most cases
163 this is very much like a C<TRUNCATE> operation.
164
165 See L<DeleteAll|Reaction::InterfaceModel::Action::DBIC::ResultSet::DeleteAll>
166  for more info.
167
168 =head2 update
169
170 Chained to C<object>, update a single object.
171
172 See L<Update|Reaction::InterfaceModel::Action::DBIC::Result::Update>
173  for more info.
174
175 =head2 delete
176
177 Chained to C<object>, deletee a single object.
178
179
180 See L<Delete|Reaction::InterfaceModel::Action::DBIC::Result::Delete>
181  for more info.
182
183 =head1 SEE ALSO
184
185 L<Reaction::UI::Controller::Collection>, L<Reaction::UI::Controller>
186
187 =head1 AUTHORS
188
189 See L<Reaction::Class> for authors.
190
191 =head1 LICENSE
192
193 See L<Reaction::Class> for the license.
194
195 =cut