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