Fix to update(\%args) with inflation from test case by Peter Rabbitson
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / InflateColumn.pm
1 package DBIx::Class::InflateColumn;
2
3 use strict;
4 use warnings;
5
6
7 use base qw/DBIx::Class::Row/;
8
9 =head1 NAME 
10
11 DBIx::Class::InflateColumn - Automatically create objects from column data
12
13 =head1 SYNOPSIS
14
15     # In your table classes
16     __PACKAGE__->inflate_column('column_name', {
17         inflate => sub { ... },
18         deflate => sub { ... },
19     });
20
21 =head1 DESCRIPTION
22
23 This component translates column data into objects, i.e. "inflating"
24 the column data. It also "deflates" objects into an appropriate format
25 for the database.
26
27 It can be used, for example, to automatically convert to and from
28 L<DateTime> objects for your date and time fields. 
29
30 =head1 METHODS
31
32 =head2 inflate_column
33
34 Instruct L<DBIx::Class> to inflate the given column. 
35
36 In addition to the column name, you must provide C<inflate> and
37 C<deflate> methods. The C<inflate> method is called when you access
38 the field, while the C<deflate> method is called when the field needs
39 to used by the database.
40
41 For example, if you have a table C<events> with a timestamp field
42 named C<insert_time>, you could inflate the column in the
43 corresponding table class using something like:
44
45     __PACKAGE__->inflate_column('insert_time', {
46         inflate => sub { DateTime::Format::Pg->parse_datetime(shift); },
47         deflate => sub { DateTime::Format::Pg->format_datetime(shift); },
48     });
49
50 (Replace L<DateTime::Format::Pg> with the appropriate module for your
51 database, or consider L<DateTime::Format::DBI>.)
52
53 In this example, calls to an event's C<insert_time> accessor return a
54 L<DateTime> object. This L<DateTime> object is later "deflated" when
55 used in the database layer.
56
57 =cut
58
59 sub inflate_column {
60   my ($self, $col, $attrs) = @_;
61   $self->throw_exception("No such column $col to inflate") unless $self->has_column($col);
62   $self->throw_exception("inflate_column needs attr hashref") unless ref $attrs eq 'HASH';
63   $self->column_info($col)->{_inflate_info} = $attrs;
64   $self->mk_group_accessors('inflated_column' => $col);
65   return 1;
66 }
67
68 sub _inflated_column {
69   my ($self, $col, $value) = @_;
70   return $value unless defined $value; # NULL is NULL is NULL
71   my $info = $self->column_info($col) || $self->throw_exception("No column info for $col");
72   return $value unless exists $info->{_inflate_info};
73   my $inflate = $info->{_inflate_info}{inflate};
74   $self->throw_exception("No inflator for $col") unless defined $inflate;
75   return $inflate->($value, $self);
76 }
77
78 sub _deflated_column {
79   my ($self, $col, $value) = @_;
80   return $value unless ref $value; # If it's not an object, don't touch it
81   my $info = $self->column_info($col) || $self->throw_exception("No column info for $col");
82   return $value unless exists $info->{_inflate_info};
83   my $deflate = $info->{_inflate_info}{deflate};
84   $self->throw_exception("No deflator for $col") unless defined $deflate;
85   return $deflate->($value, $self);
86 }
87
88 sub get_inflated_column {
89   my ($self, $col) = @_;
90   $self->throw_exception("$col is not an inflated column") unless
91     exists $self->column_info($col)->{_inflate_info};
92
93   return $self->{_inflated_column}{$col}
94     if exists $self->{_inflated_column}{$col};
95   return $self->{_inflated_column}{$col} =
96            $self->_inflated_column($col, $self->get_column($col));
97 }
98
99 sub set_inflated_column {
100   my ($self, $col, @rest) = @_;
101   my $ret = $self->_inflated_column_op('set', $col, @rest);
102   return $ret;
103 }
104
105 sub store_inflated_column {
106   my ($self, $col, @rest) = @_;
107   my $ret = $self->_inflated_column_op('store', $col, @rest);
108   return $ret;
109 }
110
111 sub _inflated_column_op {
112   my ($self, $op, $col, $obj) = @_;
113   my $meth = "${op}_column";
114   unless (ref $obj) {
115     delete $self->{_inflated_column}{$col};
116     return $self->$meth($col, $obj);
117   }
118
119   my $deflated = $self->_deflated_column($col, $obj);
120            # Do this now so we don't store if it's invalid
121
122   $self->{_inflated_column}{$col} = $obj;
123   $self->$meth($col, $deflated);
124   return $obj;
125 }
126
127 sub update {
128   my ($class, $attrs, @rest) = @_;
129   $attrs ||= {};
130   foreach my $key (keys %$attrs) {
131     if (ref $attrs->{$key}
132           && exists $class->column_info($key)->{_inflate_info}) {
133       $attrs->{$key} = $class->_deflated_column($key, $attrs->{$key});
134     }
135   }
136   return $class->next::method($attrs, @rest);
137 }
138
139 sub new {
140   my ($class, $attrs, @rest) = @_;
141   $attrs ||= {};
142   foreach my $key (keys %$attrs) {
143     if (ref $attrs->{$key}
144           && exists $class->column_info($key)->{_inflate_info}) {
145       $attrs->{$key} = $class->_deflated_column($key, $attrs->{$key});
146     }
147   }
148   return $class->next::method($attrs, @rest);
149 }
150
151 =head1 SEE ALSO
152
153 =over 4
154
155 =item L<DBIx::Class::Core> - This component is loaded as part of the
156       "core" L<DBIx::Class> components; generally there is no need to
157       load it directly
158
159 =back
160
161 =head1 AUTHOR
162
163 Matt S. Trout <mst@shadowcatsystems.co.uk>
164
165 =head1 CONTRIBUTORS
166
167 Daniel Westermann-Clark <danieltwc@cpan.org> (documentation)
168
169 =head1 LICENSE
170
171 You may distribute this code under the same terms as Perl itself.
172
173 =cut
174
175 1;