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',
],
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,
);
? [ $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;
+ }
}
}
}
}
# 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
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
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;
}
$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);
+ }
}
}
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;
}
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);
=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
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