Patch from abraxxa to make set_primary_key barf if called with non-existant columns
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / PK.pm
1 package DBIx::Class::PK;
2
3 use strict;
4 use warnings;
5 use Tie::IxHash;
6
7 use base qw/DBIx::Class::Row/;
8
9 __PACKAGE__->mk_classdata('_primaries' => {});
10
11 =head1 NAME 
12
13 DBIx::Class::PK - Primary Key class
14
15 =head1 SYNOPSIS
16
17 =head1 DESCRIPTION
18
19 This class represents methods handling primary keys
20 and depending on them.
21
22 =head1 METHODS
23
24 =over 4
25
26 =cut
27
28 sub _ident_cond {
29   my ($class) = @_;
30   return join(" AND ", map { "$_ = ?" } keys %{$class->_primaries});
31 }
32
33 sub _ident_values {
34   my ($self) = @_;
35   return (map { $self->{_column_data}{$_} } keys %{$self->_primaries});
36 }
37
38 =item set_primary_key <@cols>
39
40 define one or more columns as primary key for this class
41
42 =cut
43
44 sub set_primary_key {
45   my ($class, @cols) = @_;
46   # check if primary key columns are valid columns
47   for (@cols) {
48     $class->throw( "Column $_ can't be used as primary key because it isn't defined in $class" )
49       unless $class->has_column($_);
50   }
51   my %pri;
52   tie %pri, 'Tie::IxHash', map { $_ => {} } @cols;
53   $class->_primaries(\%pri);
54 }
55
56 =item find
57
58 Finds columns based on the primary key(s).
59
60 =cut
61
62 sub find {
63   my ($class, @vals) = @_;
64   my $attrs = (@vals > 1 && ref $vals[$#vals] eq 'HASH' ? pop(@vals) : {});
65   my @pk = keys %{$class->_primaries};
66   $class->throw( "Can't find unless primary columns are defined" ) 
67     unless @pk;
68   my $query;
69   if (ref $vals[0] eq 'HASH') {
70     $query = $vals[0];
71   } elsif (@pk == @vals) {
72     $query = {};
73     @{$query}{@pk} = @vals;
74     #my $ret = ($class->search_literal($class->_ident_cond, @vals, $attrs))[0];
75     #warn "$class: ".join(', ', %{$ret->{_column_data}});
76     #return $ret;
77   } else {
78     $query = {@vals};
79   }
80   $class->throw( "Can't find unless all primary keys are specified" )
81     unless (keys %$query >= @pk); # If we check 'em we run afoul of uc/lc
82                                   # column names etc. Not sure what to do yet
83   #return $class->search($query)->next;
84   my @cols = $class->_select_columns;
85   my @row = $class->storage->select_single($class->_table_name, \@cols, $query);
86   return (@row ? $class->_row_to_object(\@cols, \@row) : ());
87 }
88
89 =item discard_changes
90
91 Roll back changes that hasn't been comitted to the database.
92
93 =cut
94
95 sub discard_changes {
96   my ($self) = @_;
97   delete $self->{_dirty_columns};
98   return unless $self->in_storage; # Don't reload if we aren't real!
99   my ($reload) = $self->find($self->id);
100   unless ($reload) { # If we got deleted in the mean-time
101     $self->in_storage(0);
102     return $self;
103   }
104   delete @{$self}{keys %$self};
105   @{$self}{keys %$reload} = values %$reload;
106   return $self;
107 }
108
109 =item id
110
111 returns the primary key(s) for the current row. Can't be called as
112 a class method.
113
114 =cut
115
116 sub id {
117   my ($self) = @_;
118   $self->throw( "Can't call id() as a class method" ) unless ref $self;
119   my @pk = $self->_ident_values;
120   return (wantarray ? @pk : $pk[0]);
121 }
122
123 =item  primary_columns
124
125 read-only accessor which returns a list of primary keys.
126
127 =cut
128
129 sub primary_columns {
130   return keys %{shift->_primaries};
131 }
132
133 sub ID {
134   my ($self) = @_;
135   $self->throw( "Can't call ID() as a class method" ) unless ref $self;
136   return undef unless $self->in_storage;
137   return $self->_create_ID(map { $_ => $self->{_column_data}{$_} } keys %{$self->_primaries});
138 }
139
140 sub _create_ID {
141   my ($class,%vals) = @_;
142   return undef unless 0 == grep { !defined } values %vals;
143   $class = ref $class || $class;
144   return join '|', $class, map { $_ . '=' . $vals{$_} } sort keys %vals;    
145 }
146
147 sub ident_condition {
148   my ($self) = @_;
149   my %cond;
150   $cond{$_} = $self->get_column($_) for $self->primary_columns;
151   return \%cond;
152 }
153
154 1;
155
156 =back
157
158 =head1 AUTHORS
159
160 Matt S. Trout <mst@shadowcatsystems.co.uk>
161
162 =head1 LICENSE
163
164 You may distribute this code under the same terms as Perl itself.
165
166 =cut
167