226913a555383d643cf1cad3e861a7df3a995426
[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       $class->set_inflated_column ($key, delete $attrs->{$key});
135     }
136   }
137   return $class->next::method($attrs, @rest);
138 }
139
140 sub new {
141   my ($class, $attrs, @rest) = @_;
142   $attrs ||= {};
143   foreach my $key (keys %$attrs) {
144     if (ref $attrs->{$key}
145           && exists $class->column_info($key)->{_inflate_info}) {
146       $attrs->{$key} = $class->_deflated_column($key, $attrs->{$key});
147     }
148   }
149   return $class->next::method($attrs, @rest);
150 }
151
152 =head1 SEE ALSO
153
154 =over 4
155
156 =item L<DBIx::Class::Core> - This component is loaded as part of the
157       "core" L<DBIx::Class> components; generally there is no need to
158       load it directly
159
160 =back
161
162 =head1 AUTHOR
163
164 Matt S. Trout <mst@shadowcatsystems.co.uk>
165
166 =head1 CONTRIBUTORS
167
168 Daniel Westermann-Clark <danieltwc@cpan.org> (documentation)
169
170 =head1 LICENSE
171
172 You may distribute this code under the same terms as Perl itself.
173
174 =cut
175
176 1;