Version 2.008001
[catagits/Catalyst-Controller-DBIC-API.git] / lib / Catalyst / Controller / DBIC / API.pm
index 058e790..6638e9b 100644 (file)
@@ -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',
@@ -694,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;
+                    }
                 }
             }
         }
@@ -721,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
@@ -813,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();
-}
+    $params = {%$params, %{$object->ident_condition}};
 
-=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, $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
@@ -888,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;
         }
 
@@ -908,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);
+        }
     }
 }
 
@@ -963,7 +942,7 @@ sub end : Private {
 
     if ( $c->res->status == 200 ) {
         $c->stash->{ $self->stash_key }->{success} =
-            $self->use_json_boolean ? JSON::true : 'true';
+            $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 } ) {
@@ -978,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);
 
@@ -1157,8 +1136,9 @@ $c->stash->{$self->stash_key}->{$self->item_root} and item_root default to
 =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
 
@@ -1318,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