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=6638e9b1a6af0d603569673edf715277ae63c544;hp=f90891ae2e3386e51d243dc5a48c7ebcd0b0e871;hb=HEAD;hpb=4cb8623abade7b9f100b3892df93ddcfb1168e01 diff --git a/lib/Catalyst/Controller/DBIC/API.pm b/lib/Catalyst/Controller/DBIC/API.pm index f90891a..6638e9b 100644 --- a/lib/Catalyst/Controller/DBIC/API.pm +++ b/lib/Catalyst/Controller/DBIC/API.pm @@ -6,25 +6,26 @@ BEGIN { extends 'Catalyst::Controller'; } use CGI::Expand (); use DBIx::Class::ResultClass::HashRefInflator; -use JSON (); +use JSON::MaybeXS (); use Test::Deep::NoTest('eq_deeply'); use MooseX::Types::Moose(':all'); use Moose::Util; use Scalar::Util( 'blessed', 'reftype' ); use Try::Tiny; use Catalyst::Controller::DBIC::API::Request; +use DBIx::Class::ResultSet::RecursiveUpdate; use namespace::autoclean; has '_json' => ( is => 'ro', - isa => 'JSON', + isa => JSON::MaybeXS::JSON(), lazy_build => 1, ); sub _build__json { # no ->utf8 here because the request params get decoded by Catalyst - return JSON->new; + return JSON::MaybeXS->new; } with 'Catalyst::Controller::DBIC::API::StoredResultSource', @@ -63,7 +64,8 @@ __PACKAGE__->config(); ], ordered_by => ['age'], search_exposes => ['age', 'nickname', { cds => ['title', 'year'] }], - data_root => 'data', + data_root => 'list', + item_root => 'data', use_json_boolean => 1, return_object => 1, ); @@ -693,14 +695,32 @@ sub validate_object { ? [ $related_source->columns ] : $allowed_fields; - foreach my $related_col ( @{$allowed_related_cols} ) { - if (defined( - my $related_col_value = - $related_params->{$related_col} - ) - ) - { - $values{$key}{$related_col} = $related_col_value; + if (ref($related_params) && reftype($related_params) eq 'ARRAY') { + my @related_data; + for my $related_param (@$related_params) { + my %data; + foreach my $related_col ( @{$allowed_related_cols} ) { + if (defined( + my $related_col_value = + $related_param->{$related_col} + ) + ) { + $data{$related_col} = $related_col_value; + } + } + push @related_data, \%data; + } + $values{$key} = \@related_data; + } + else { + foreach my $related_col ( @{$allowed_related_cols} ) { + if (defined( + my $related_col_value = + $related_params->{$related_col} + ) + ) { + $values{$key}{$related_col} = $related_col_value; + } } } } @@ -720,7 +740,7 @@ sub validate_object { } # check for multiple values - if ( ref($value) && !( reftype($value) eq reftype(JSON::true) ) ) + if ( ref($value) && !( reftype($value) eq reftype(JSON::MaybeXS::true) ) ) { require Data::Dumper; die @@ -812,66 +832,19 @@ object, and the relation parameters. Then it calls ->update on the object. sub update_object_from_params { my ( $self, $c, $object, $params ) = @_; - foreach my $key ( keys %$params ) { - my $value = $params->{$key}; - if ( ref($value) && !( reftype($value) eq reftype(JSON::true) ) ) { - $self->update_object_relation( $c, $object, - delete $params->{$key}, $key ); - } - - # accessor = colname - elsif ( $object->can($key) ) { - $object->$key($value); - } - - # accessor != colname - else { - my $accessor = - $object->result_source->column_info($key)->{accessor}; - $object->$accessor($value); - } - } - - $object->update(); -} - -=method_protected update_object_relation + $params = {%$params, %{$object->ident_condition}}; -update_object_relation finds the relation to the object, then calls ->update -with the specified parameters. - -=cut - -sub update_object_relation { - my ( $self, $c, $object, $related_params, $relation ) = @_; - my $row = $object->find_related( $relation, {}, {} ); - - if ($row) { - foreach my $key ( keys %$related_params ) { - my $value = $related_params->{$key}; - if ( ref($value) && !( reftype($value) eq reftype(JSON::true) ) ) - { - $self->update_object_relation( $c, $row, - delete $related_params->{$key}, $key ); - } - - # accessor = colname - elsif ( $row->can($key) ) { - $row->$key($value); - } + my $updated_object = + DBIx::Class::ResultSet::RecursiveUpdate::Functions::recursive_update( + resultset => $c->req->current_result_set, + # unknown_params_ok => 1, + updates => $params, + ); - # accessor != colname - else { - my $accessor = - $row->result_source->column_info($key)->{accessor}; - $row->$accessor($value); - } - } - $row->update(); - } - else { - $object->create_related( $relation, $related_params ); - } + # replace request object with updated one for response + my $vals = $c->req->get_object(0)->[1]; + $c->req->clear_objects; + $c->req->add_object( [ $updated_object, $vals ] ); } =method_protected insert_object_from_params @@ -887,7 +860,7 @@ sub insert_object_from_params { my %rels; while ( my ( $key, $value ) = each %{$params} ) { - if ( ref($value) && !( reftype($value) eq reftype(JSON::true) ) ) { + if ( ref($value) && !( reftype($value) eq reftype(JSON::MaybeXS::true) ) ) { $rels{$key} = $value; } @@ -907,7 +880,14 @@ sub insert_object_from_params { $object->insert; while ( my ( $k, $v ) = each %rels ) { - $object->create_related( $k, $v ); + if (reftype($v) eq 'ARRAY') { + foreach my $next_v ( @$v ) { + $object->create_related($k, $next_v); + } + } + else { + $object->create_related($k => $v); + } } } @@ -962,8 +942,10 @@ sub end : Private { if ( $c->res->status == 200 ) { $c->stash->{ $self->stash_key }->{success} = - $self->use_json_boolean ? JSON::true : 'true'; - if ( $self->return_object && $c->req->has_objects ) { + $self->use_json_boolean ? JSON::MaybeXS::true : 'true'; + if ( $self->return_object + && $c->req->has_objects + && ! exists $c->stash->{ $self->stash_key }->{ $self->data_root } ) { my $returned_objects = []; push( @$returned_objects, $self->each_object_inflate( $c, $_ ) ) for map { $_->[0] } $c->req->all_objects; @@ -975,7 +957,7 @@ sub end : Private { } else { $c->stash->{ $self->stash_key }->{success} = - $self->use_json_boolean ? JSON::false : 'false'; + $self->use_json_boolean ? JSON::MaybeXS::false : 'false'; $c->stash->{ $self->stash_key }->{messages} = $self->get_errors($c) if $self->has_errors($c); @@ -1140,16 +1122,23 @@ Controls where in stash request_data should be stored, and defaults to 'response =head3 data_root -By default, the response data is serialized into +By default, the response data of multiple item actions is serialized into $c->stash->{$self->stash_key}->{$self->data_root} and data_root defaults to 'list' to preserve backwards compatibility. This is now configuable to meet the needs of the consuming client. +=head3 item_root + +By default, the response data of single item actions is serialized into +$c->stash->{$self->stash_key}->{$self->item_root} and item_root default to +'data'. + =head3 use_json_boolean By default, the response success status is set to a string value of "true" or -"false". If this attribute is true, JSON's true() and false() will be used -instead. Note, this does not effect other internal processing of boolean values. +"false". If this attribute is true, JSON::MaybeXS's true() and false() will be +used instead. Note, this does not effect other internal processing of boolean +values. =head3 count_arg, page_arg, select_arg, search_arg, grouped_by_arg, ordered_by_arg, prefetch_arg, as_arg, total_entries_arg @@ -1309,9 +1298,10 @@ status quo. The internals were revamped to use more modern tools such as Moose and its role system to refactor functionality out into self-contained roles. To this end, internally, this module now understands JSON boolean values (as -represented by the JSON module) and will Do The Right Thing in handling those -values. This means you can have ColumnInflators installed that can covert -between JSON booleans and whatever your database wants for boolean values. +represented by the JSON::MaybeXS module) and will Do The Right Thing in +handling those values. This means you can have ColumnInflators installed that +can covert between JSON booleans and whatever your database wants for boolean +values. Validation for various *_allows or *_exposes is now accomplished via Data::DPath::Validator with a lightly simplified, via a subclass of