package MyApp::Controller::Books; use strict; use warnings; use base 'Catalyst::Controller'; use FormElementContainer; =head1 NAME MyApp::Controller::Books - Catalyst Controller =head1 DESCRIPTION Catalyst Controller. =head1 METHODS =cut =head2 index =cut sub index : Private { my ( $self, $c ) = @_; $c->response->body('Matched MyApp::Controller::Books in Books.'); } =head2 list Fetch all book objects and pass to books/list.tt2 in stash to be displayed =cut sub list : Local { # Retrieve the usual perl OO '$self' for this object. $c is the Catalyst # 'Context' that's used to 'glue together' the various components # that make up the application my ($self, $c) = @_; # Retrieve all of the book records as book model objects and store in the # stash where they can be accessed by the TT template $c->stash->{books} = [$c->model('MyAppDB::Book')->all]; # Set the TT template to use. You will almost always want to do this # in your action methods (actions methods respond to user input in # your controllers). $c->stash->{template} = 'books/list.tt2'; } =head2 url_create Create a book with the supplied title and rating, with manual authorization =cut sub url_create : Local { # In addition to self & context, get the title, rating & author_id args # from the URL. Note that Catalyst automatically puts extra information # after the "//check_user_roles('admin')) { # Call create() on the book model object. Pass the table # columns/field values we want to set as hash values my $book = $c->model('MyAppDB::Book')->create({ title => $title, rating => $rating }); # Add a record to the join table for this book, mapping to # appropriate author $book->add_to_book_authors({author_id => $author_id}); # Note: Above is a shortcut for this: # $book->create_related('book_authors', {author_id => $author_id}); # Assign the Book object to the stash for display in the view $c->stash->{book} = $book; # This is a hack to disable XSUB processing in Data::Dumper # (it's used in the view). This is a work-around for a bug in # the interaction of some versions or Perl, Data::Dumper & DBIC. # You won't need this if you aren't using Data::Dumper (or if # you are running DBIC 0.06001 or greater), but adding it doesn't # hurt anything either. $Data::Dumper::Useperl = 1; # Set the TT template to use $c->stash->{template} = 'books/create_done.tt2'; } else { # Provide very simple feedback to the user $c->response->body('Unauthorized!'); } } =head2 form_create Display form to collect information for book to create =cut sub form_create : Local { my ($self, $c) = @_; # Set the TT template to use $c->stash->{template} = 'books/form_create.tt2'; } =head2 form_create_do Take information from form and add to database =cut sub form_create_do : Local { my ($self, $c) = @_; # Retrieve the values from the form my $title = $c->request->params->{title} || 'N/A'; my $rating = $c->request->params->{rating} || 'N/A'; my $author_id = $c->request->params->{author_id} || '1'; # Create the book my $book = $c->model('MyAppDB::Book')->create({ title => $title, rating => $rating, }); # Handle relationship with author $book->add_to_book_authors({author_id => $author_id}); # Store new model object in stash $c->stash->{book} = $book; # Avoid Data::Dumper issue mentioned earlier # You can probably omit this $Data::Dumper::Useperl = 1; # Set the TT template to use $c->stash->{template} = 'books/create_done.tt2'; } =head2 delete Delete a book =cut sub delete : Local { # $id = primary key of book to delete my ($self, $c, $id) = @_; # Search for the book and then delete it $c->model('MyAppDB::Book')->search({id => $id})->delete_all; # Use 'flash' to save information across requests util it's read $c->flash->{status_msg} = "Book deleted"; # Redirect the user back to the list page with status msg as an arg $c->response->redirect($c->uri_for('/books/list')); } =head2 access_denied Handle Catalyst::Plugin::Authorization::ACL access denied exceptions =cut sub access_denied : Private { my ($self, $c) = @_; # Set the error message $c->stash->{error_msg} = 'Unauthorized!'; # Display the list $c->forward('list'); } =head2 make_book_widget Build an HTML::Widget form for book creation and updates =cut sub make_book_widget { my ($self, $c) = @_; # Create an HTML::Widget to build the form my $w = $c->widget('book_form')->method('post'); # ***New: Use custom class to render each element in the form $w->element_container_class('FormElementContainer'); # Get authors my @authorObjs = $c->model("MyAppDB::Author")->all(); my @authors = map {$_->id => $_->last_name } sort {$a->last_name cmp $b->last_name} @authorObjs; # Create the form feilds $w->element('Textfield', 'title' )->label('Title')->size(60); $w->element('Textfield', 'rating' )->label('Rating')->size(1); # Convert to multi-select list $w->element('Select', 'authors')->label('Authors') ->options(@authors)->multiple(1)->size(3); $w->element('Submit', 'submit' )->value('submit'); # Set constraints $w->constraint(All => qw/title rating authors/) ->message('Required. '); $w->constraint(Integer => qw/rating/) ->message('Must be an integer. '); $w->constraint(Range => qw/rating/)->min(1)->max(5) ->message('Must be a number between 1 and 5. '); $w->constraint(Length => qw/title/)->min(5)->max(50) ->message('Must be between 5 and 50 characters. '); # Set filters for my $column (qw/title rating authors/) { $w->filter( HTMLEscape => $column ); $w->filter( TrimEdges => $column ); } # Return the widget return $w; } =head2 hw_create Build an HTML::Widget form for book creation and updates =cut sub hw_create : Local { my ($self, $c) = @_; # Create the widget and set the action for the form my $w = $self->make_book_widget($c); $w->action($c->uri_for('hw_create_do')); # Write form to stash variable for use in template $c->stash->{widget_result} = $w->result; # Set the template $c->stash->{template} = 'books/hw_form.tt2'; } =head2 hw_create_do Build an HTML::Widget form for book creation and updates =cut =head2 hw_create_do Build an HTML::Widget form for book creation and updates =cut sub hw_create_do : Local { my ($self, $c) = @_; # Create the widget and set the action for the form my $w = $self->make_book_widget($c); $w->action($c->uri_for('hw_create_do')); # Validate the form parameters my $result = $w->process($c->req); # Write form (including validation error messages) to # stash variable for use in template $c->stash->{widget_result} = $result; # Were their validation errors? if ($result->has_errors) { # Warn the user at the top of the form that there were errors. # Note that there will also be per-field feedback on # validation errors because of '$w->process($c->req)' above. $c->stash->{error_msg} = 'Validation errors!'; } else { my $book = $c->model('MyAppDB::Book')->new({}); $book->populate_from_widget($result); # Add a record to the join table for this book, mapping to # appropriate author. Note that $authors will be 1 author as # a scalar or ref to list of authors depending on how many the # user selected; the 'ref $authors ?...' handles both cases my $authors = $c->request->params->{authors}; foreach my $author (ref $authors ? @$authors : $authors) { $book->add_to_book_authors({author_id => $author}); } # Set a status message for the user $c->stash->{status_msg} = 'Book created'; # Redisplay an empty form for another $c->stash->{widget_result} = $w->result; } # Set the template $c->stash->{template} = 'books/hw_form.tt2'; } =head1 AUTHOR root =head1 LICENSE This library is free software, you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1;