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