Try supporting compound primary keys.
[catagits/Catalyst-Authentication-Store-DBIx-Class.git] / lib / Catalyst / Authentication / Store / DBIx / Class / User.pm
index 07b26e6..8a7936c 100644 (file)
@@ -2,6 +2,7 @@ package Catalyst::Authentication::Store::DBIx::Class::User;
 
 use strict;
 use warnings;
+use List::MoreUtils qw(all);
 use base qw/Catalyst::Authentication::User/;
 use base qw/Class::Accessor::Fast/;
 
@@ -22,39 +23,34 @@ sub new {
         _roles => undef,
         _user => undef
     };
-    
+
     bless $self, $class;
 
-    
+
 
     if (not $self->{'resultset'}) {
         Catalyst::Exception->throw("\$c->model('${ \$self->config->{user_model} }') did not return a resultset. Did you set user_model correctly?");
     }
 
-    ## Note to self- add handling of multiple-column primary keys.
-    if (!exists($self->config->{'id_field'})) {
-        my @pks = $self->{'resultset'}->result_source->primary_columns;
-        if ($#pks == 0) {
-            $self->config->{'id_field'} = $pks[0];
-        } else {
-            Catalyst::Exception->throw("user table does not contain a single primary key column - please specify 'id_field' in config!");
-        }
-    }
+    $self->config->{'id_field'} = [$self->{'resultset'}->result_source->primary_columns]
+        unless exists $self->config->{'id_field'};
+
+    $self->config->{'id_field'} = [$self->config->{'id_field'}]
+        unless ref $self->config->{'id_field'} eq 'ARRAY';
+
+    Catalyst::Exception->throw("id_field set to " . join(q{,} => @{ $self->config->{'id_field'} }) . " but user table has no column by that name!")
+        unless all { $self->{'resultset'}->result_source->has_column($_) } @{ $self->config->{'id_field'} };
 
-    if (!$self->{'resultset'}->result_source->has_column($self->config->{'id_field'})) {
-        Catalyst::Exception->throw("id_field set to " .  $self->config->{'id_field'} . " but user table has no column by that name!");
-    }
-    
     ## if we have lazyloading turned on - we should not query the DB unless something gets read.
     ## that's the idea anyway - still have to work out how to manage that - so for now we always force
     ## lazyload to off.
     $self->config->{lazyload} = 0;
-    
+
 #    if (!$self->config->{lazyload}) {
 #        return $self->load_user($authinfo, $c);
 #    } else {
 #        ## what do we do with a lazyload?
-#        ## presumably this is coming out of session storage.  
+#        ## presumably this is coming out of session storage.
 #        ## use $authinfo to fill in the user in that case?
 #    }
 
@@ -64,26 +60,26 @@ sub new {
 
 sub load {
     my ($self, $authinfo, $c) = @_;
-    
+
     my $dbix_class_config = 0;
-    
+
     if (exists($authinfo->{'dbix_class'})) {
         $authinfo = $authinfo->{'dbix_class'};
         $dbix_class_config = 1;
     }
-    
+
     ## User can provide an arrayref containing the arguments to search on the user class.
     ## or even provide a prepared resultset, allowing maximum flexibility for user retreival.
-    ## these options are only available when using the dbix_class authinfo hash. 
+    ## these options are only available when using the dbix_class authinfo hash.
     if ($dbix_class_config && exists($authinfo->{'resultset'})) {
         $self->_user($authinfo->{'resultset'}->first);
     } elsif ($dbix_class_config && exists($authinfo->{'searchargs'})) {
-        $self->_user($self->resultset->search(@{$authinfo->{'searchargs'}})->first);    
+        $self->_user($self->resultset->search(@{$authinfo->{'searchargs'}})->first);
     } else {
         ## merge the ignore fields array into a hash - so we can do an easy check while building the query
-        my %ignorefields = map { $_ => 1} @{$self->config->{'ignore_fields_in_find'}};                                    
+        my %ignorefields = map { $_ => 1} @{$self->config->{'ignore_fields_in_find'}};
         my $searchargs = {};
-        
+
         # now we walk all the fields passed in, and build up a search hash.
         foreach my $key (grep {!$ignorefields{$_}} keys %{$authinfo}) {
             if ($self->resultset->result_source->has_column($key)) {
@@ -123,11 +119,11 @@ sub roles {
     if (ref $self->_roles eq 'ARRAY') {
         return(@{$self->_roles});
     }
-    
+
     my @roles = ();
     if (exists($self->config->{'role_column'})) {
         my $role_data = $self->get($self->config->{'role_column'});
-        if ($role_data) { 
+        if ($role_data) {
             @roles = split /[\s,\|]+/, $self->get($self->config->{'role_column'});
         }
         $self->_roles(\@roles);
@@ -148,49 +144,49 @@ sub roles {
 
 sub for_session {
     my $self = shift;
-    
+
     #return $self->get($self->config->{'id_field'});
-    
+
     #my $frozenuser = $self->_user->result_source->schema->freeze( $self->_user );
     #return $frozenuser;
-    
+
     my %userdata = $self->_user->get_columns();
     return \%userdata;
 }
 
 sub from_session {
     my ($self, $frozenuser, $c) = @_;
-    
+
     #my $obj = $self->resultset->result_source->schema->thaw( $frozenuser );
     #$self->_user($obj);
-    
+
     #if (!exists($self->config->{'use_userdata_from_session'}) || $self->config->{'use_userdata_from_session'} == 0) {
 #        $self->_user->discard_changes();
 #    }
-#    
+#
 #    return $self;
-#    
+#
 ## if use_userdata_from_session is defined in the config, we fill in the user data from the session.
-    if (exists($self->config->{'use_userdata_from_session'}) && $self->config->{'use_userdata_from_session'} != 0)
-    {
+    if (exists($self->config->{'use_userdata_from_session'}) && $self->config->{'use_userdata_from_session'} != 0) {
         my $obj = $self->resultset->new_result({ %$frozenuser });
         $obj->in_storage(1);
         $self->_user($obj);
         return $self;
-    } else {
-        my $id;
-        if (ref($frozenuser) eq 'HASH') {
-            $id = $frozenuser->{$self->config->{'id_field'}};
-        } else {
-            $id = $frozenuser;
-        }
-        return $self->load( { $self->config->{'id_field'} => $id }, $c);
     }
+
+    if (ref $frozenuser eq 'HASH') {
+        return $self->load({
+            map { ($_ => $frozenuser->{$_}) }
+            @{ $self->config->{id_field} }
+        });
+    }
+
+    return $self->load( { $self->config->{'id_field'} => $frozenuser }, $c);
 }
 
 sub get {
     my ($self, $field) = @_;
-    
+
     if ($self->_user->can($field)) {
         return $self->_user->$field;
     } else {
@@ -200,7 +196,7 @@ sub get {
 
 sub get_object {
     my ($self, $force) = @_;
-    
+
     if ($force) {
         $self->_user->discard_changes;
     }
@@ -210,7 +206,7 @@ sub get_object {
 
 sub obj {
     my ($self, $force) = @_;
-    
+
     return $self->get_object($force);
 }
 
@@ -253,7 +249,7 @@ L<Catalyst::Authentication::Store::DBIx::Class> for details on how to
 use this module. If you need more information than is present there, read the
 source.
 
-                
+
 
 =head1 DESCRIPTION
 
@@ -262,11 +258,11 @@ connected to an underlying DBIx::Class schema object.
 
 =head1 SUBROUTINES / METHODS
 
-=head2 new 
+=head2 new
 
 Constructor.
 
-=head2 load ( $authinfo, $c ) 
+=head2 load ( $authinfo, $c )
 
 Retrieves a user from storage using the information provided in $authinfo.
 
@@ -280,18 +276,18 @@ Returns an array of roles associated with this user, if roles are configured for
 
 =head2 for_session
 
-Returns a serialized user for storage in the session.  
+Returns a serialized user for storage in the session.
 
 =head2 from_session
 
-Revives a serialized user from storage in the session. 
+Revives a serialized user from storage in the session.
 
 =head2 get ( $fieldname )
 
-Returns the value of $fieldname for the user in question.  Roughly translates to a call to 
+Returns the value of $fieldname for the user in question.  Roughly translates to a call to
 the DBIx::Class::Row's get_column( $fieldname ) routine.
 
-=head2 get_object 
+=head2 get_object
 
 Retrieves the DBIx::Class object that corresponds to this user
 
@@ -301,8 +297,8 @@ Synonym for get_object
 
 =head2 auto_create
 
-This is called when the auto_create_user option is turned on in 
-Catalyst::Plugin::Authentication and a user matching the authinfo provided is not found. 
+This is called when the auto_create_user option is turned on in
+Catalyst::Plugin::Authentication and a user matching the authinfo provided is not found.
 By default, this will call the C<auto_create()> method of the resultset associated
 with this object. It is up to you to implement that method.