Commit | Line | Data |
89b70ba7 |
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 | |
c8fbb8ad |
8 | use aliased 'Reaction::UI::ViewPort::Action'; |
89b70ba7 |
9 | |
10 | sub _build_action_viewport_map { |
11 | my $map = shift->next::method(@_); |
f3c72687 |
12 | $map->{$_} = Action for qw/create update delete delete_all/; |
89b70ba7 |
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 | ], |
c8fbb8ad |
25 | Member => |
89b70ba7 |
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 | }; |
5d86015d |
59 | $self->basic_model_action( $c, $vp_args); |
89b70ba7 |
60 | } |
61 | |
62 | sub delete_all :Chained('base') :PathPart('delete_all') :Args(0) { |
63 | my ($self, $c) = @_; |
b68d6a35 |
64 | $self->basic_model_action( $c, { next_action => 'list'}); |
89b70ba7 |
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 ]}; |
b68d6a35 |
79 | $self->basic_model_action( $c, $vp_args); |
89b70ba7 |
80 | } |
81 | |
82 | sub delete :Chained('object') :Args(0) { |
83 | my ($self, $c) = @_; |
84 | #this needs a better solution. currently thinking about it |
7d3fe0d2 |
85 | my @cap = @{$c->req->captures}; |
89b70ba7 |
86 | pop(@cap); # object id |
87 | my $vp_args = { next_action => [ $self, 'redirect_to', 'list', \@cap ]}; |
b68d6a35 |
88 | $self->basic_model_action( $c, $vp_args); |
89b70ba7 |
89 | } |
90 | |
aee256be |
91 | sub basic_model_action { |
89b70ba7 |
92 | my ($self, $c, $vp_args) = @_; |
93 | |
94 | my $target = exists $c->stash->{object} ? |
95 | $c->stash->{object} : $self->get_collection($c); |
96 | |
7d3fe0d2 |
97 | my $action_name = join('', map{ ucfirst } split('_', $c->stack->[-1]->name)); |
98 | my $model = $self->get_model_action($c, $action_name, $target); |
5d86015d |
99 | return $self->basic_page($c, { model => $model, %{$vp_args||{}} }); |
89b70ba7 |
100 | } |
101 | |
102 | 1; |
7d3fe0d2 |
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 |