X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Controller-DBIC-API.git;a=blobdiff_plain;f=lib%2FCatalyst%2FController%2FDBIC%2FAPI.pm;h=621015ba0a6ce2de9a2ac650d603e611858e60a4;hp=b992e9608bd9591119eec84c36eadee504fce878;hb=b421ef50a8942cda865f21820760eb12ed94c0ff;hpb=d273984026646e5b57c052deef3fcb9121122060 diff --git a/lib/Catalyst/Controller/DBIC/API.pm b/lib/Catalyst/Controller/DBIC/API.pm index b992e96..621015b 100644 --- a/lib/Catalyst/Controller/DBIC/API.pm +++ b/lib/Catalyst/Controller/DBIC/API.pm @@ -221,7 +221,7 @@ sub deserialize :ActionClass('Deserialize') { $req_params = CGI::Expand->expand_hash($c->req->params); - foreach my $param (@{[$self->search_arg, $self->count_arg, $self->page_arg, $self->ordered_by_arg, $self->grouped_by_arg, $self->prefetch_arg]}) + foreach my $param (@{[$self->search_arg, $self->count_arg, $self->page_arg, $self->offset_arg, $self->ordered_by_arg, $self->grouped_by_arg, $self->prefetch_arg]}) { # these params can also be composed of JSON # but skip if the parameter is not provided @@ -301,7 +301,7 @@ The goal of this method is to call ->search() on the current_result_set, HashRef If the L config param is defined then the hashes will contain only those columns, otherwise all columns in the object will be returned. L of course supports the function/procedure calling semantics that L. In order to have proper column names in the result, provide arguments in L (which also follows L semantics. Similarly L, L, L and L affect the maximum number of rows returned as well as the ordering and grouping. Note that if select, count, ordered_by or grouped_by request parameters are present then these will override the values set on the class with select becoming bound by the select_exposes attribute. -If not all objects in the resultset are required then it's possible to pass conditions to the method as request parameters. You can use a JSON string as the 'search' parameter for maximum flexibility or use L syntax. In the second case the request parameters are expanded into a structure and then used as the search condition. +If not all objects in the resultset are required then it's possible to pass conditions to the method as request parameters. You can use a JSON string as the 'search' parameter for maximum flexibility or use L syntax. In the second case the request parameters are expanded into a structure and then used as the search condition. For example, these request parameters: @@ -369,7 +369,7 @@ sub list_perform_search $req->_set_current_result_set($rs); $req->_set_search_total_entries($req->current_result_set->pager->total_entries) - if $req->has_search_attributes && $req->search_attributes->{page}; + if $req->has_search_attributes && (exists($req->search_attributes->{page}) && defined($req->search_attributes->{page}) && length($req->search_attributes->{page})); } catch { @@ -432,7 +432,7 @@ sub row_format_output { shift; shift; } # passthrough by default :Private -update_or_create is responsible for iterating any stored objects and performing updates or creates. Each object is first validated to ensure it meets the criteria specified in the L and L (or L) parameters of the controller config. The objects are then committed within a transaction via L. +update_or_create is responsible for iterating any stored objects and performing updates or creates. Each object is first validated to ensure it meets the criteria specified in the L and L (or L) parameters of the controller config. The objects are then committed within a transaction via L using a closure around L. =cut @@ -444,7 +444,7 @@ sub update_or_create :Private if($c->req->has_objects) { $self->validate_objects($c); - $self->transact_objects($c, \&save_objects); + $self->transact_objects($c, sub { $self->save_objects(@_) } ); } else { @@ -606,7 +606,7 @@ sub validate_object :Private -delete operates on the stored objects in the request. It first transacts the objects, deleting them in the database, and then clears the request store of objects. +delete operates on the stored objects in the request. It first transacts the objects, deleting them in the database using L and a closure around L, and then clears the request store of objects. =cut @@ -617,7 +617,7 @@ sub delete :Private if($c->req->has_objects) { - $self->transact_objects($c, \&delete_objects); + $self->transact_objects($c, sub { $self->delete_objects(@_) }); $c->req->clear_objects; } else @@ -628,54 +628,117 @@ sub delete :Private } } -=head1 HELPER FUNCTIONS +=method_protected save_objects -This functions are only helper functions and should have a void invocant. If they are called as methods, they will die. The only reason they are stored in the class is to allow for customization without rewriting the methods that make use of these helper functions. +This method is used by update_or_create to perform the actual database manipulations. It iterates each object calling L. -=head2 save_objects +=cut -This helper function is used by update_or_create to perform the actual database manipulations. +sub save_objects +{ + my ($self, $objects) = @_; -=head2 delete_objects + foreach my $obj (@$objects) + { + $self->save_object($obj); + } +} -This helper function is used by delete to perform the actual database delete of objects. +=method_protected save_object + +save_object first checks to see if the object is already in storage. If so, it calls L otherwise it calls L =cut -# NOT A METHOD -sub save_objects +sub save_object { - my ($objects) = @_; - die 'save_objects coderef had an invocant and shouldn\'t have had one' if blessed($objects); + my ($self, $obj) = @_; - foreach my $obj (@$objects) + my ($object, $params) = @$obj; + + if ($object->in_storage) { - my ($object, $params) = @$obj; - - if ($object->in_storage) { - foreach my $key (keys %{$params}) { - my $value = $params->{$key}; - if (ref($value) && !($value == JSON::Any::true || $value == JSON::Any::false)) { - my $related_params = delete $params->{$key}; - my $row = $object->find_related($key, {} , {}); - $row->update($related_params); - } - } - $object->update($params); - } else { - $object->set_columns($params); - $object->insert; + $self->update_object_from_params($object, $params); + } + else + { + $self->insert_object_from_params($object, $params); + } + +} + +=method_protected update_object_from_params + +update_object_from_params iterates through the params to see if any of them are pertinent to relations. If so it calls L with the object, and the relation parameters. Then it calls ->upbdate on the object. + +=cut + +sub update_object_from_params +{ + my ($self, $object, $params) = @_; + + foreach my $key (keys %$params) + { + my $value = $params->{$key}; + if (ref($value) && !($value == JSON::Any::true || $value == JSON::Any::false)) + { + $self->update_object_relation($object, delete $params->{$key}, $key); } } + + $object->update($params); +} + +=method_protected update_object_relation + +update_object_relation finds the relation to the object, then calls ->update with the specified parameters + +=cut + +sub update_object_relation +{ + my ($self, $object, $related_params, $relation) = @_; + my $row = $object->find_related($relation, {} , {}); + $row->update($related_params); } -# NOT A METHOD +=method_protected insert_object_from_params + +insert_object_from_params sets the columns for the object, then calls ->insert + +=cut + +sub insert_object_from_params +{ + my ($self, $object, $params) = @_; + $object->set_columns($params); + $object->insert; +} + +=method_protected delete_objects + +delete_objects iterates through each object calling L + +=cut + sub delete_objects { - my ($objects) = @_; - die 'delete_objects coderef had an invocant and shouldn\'t have had one' if blessed($objects); + my ($self, $objects) = @_; - map { $_->[0]->delete } @$objects; + map { $self->delete_object($_->[0]) } @$objects; +} + +=method_protected delete_object + +Performs the actual ->delete on the object + +=cut + +sub delete_object +{ + my ($self, $object) = @_; + + $object->delete; } =method_protected end @@ -715,7 +778,7 @@ sub end :Private { $DB::single = 1; my $returned_objects = []; - map {my %inflated = $_->[0]->get_inflated_columns; push(@$returned_objects, \%inflated) } $c->req->all_objects; + push(@$returned_objects, $self->each_object_inflate($c, $_)) for map { $_->[0] } $c->req->all_objects; $c->stash->{response}->{$self->data_root} = scalar(@$returned_objects) > 1 ? $returned_objects : $returned_objects->[0]; } @@ -723,12 +786,24 @@ sub end :Private $c->forward('serialize'); } -# from Catalyst::Action::Serialize -sub serialize :ActionClass('Serialize') { - my ($self, $c) = @_; +=method_protected each_object_inflate +each_object_inflate executes during L and allows hooking into the process of inflating the objects to return in the response. Receives, the context, and the object as arguments. + +This only executes if L if set and if there are any objects to actually return. + +=cut + +sub each_object_inflate +{ + my ($self, $c, $object) = @_; + + return { $object->get_inflated_columns }; } +# from Catalyst::Action::Serialize +sub serialize :ActionClass('Serialize') { } + =method_protected push_error push_error stores an error message into the stash to be later retrieved by L. Accepts a Dict[message => Str] parameter that defines the error message. @@ -923,7 +998,7 @@ For example if you wanted create to return the JSON for the newly created object BEGIN { extends 'MyApp::ControllerBase::DBIC::API::RPC' }; ... -It should be noted that the L attribute will produce the above result for you, free of charge. +It should be noted that the return_object attribute will produce the above result for you, free of charge. For REST the only difference besides the class names would be that create should be :Private rather than an endpoint.