d9817feb7ae167df1880328f3eb5cf88b0fa9467
[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 The coderefs you set for inflate and deflate are called with two parameters,
54 the first is the value of the column to be inflated/deflated, the second is the
55 row object itself. Thus you can call C<< ->result_source->schema->storage->dbh >> on
56 it, to feed to L<DateTime::Format::DBI>.
57
58 In this example, calls to an event's C<insert_time> accessor return a
59 L<DateTime> object. This L<DateTime> object is later "deflated" when
60 used in the database layer.
61
62 =cut
63
64 sub inflate_column {
65   my ($self, $col, $attrs) = @_;
66   $self->throw_exception("No such column $col to inflate")
67     unless $self->has_column($col);
68   $self->throw_exception("inflate_column needs attr hashref")
69     unless ref $attrs eq 'HASH';
70   $self->column_info($col)->{_inflate_info} = $attrs;
71   $self->mk_group_accessors('inflated_column' => $col);
72   return 1;
73 }
74
75 sub _inflated_column {
76   my ($self, $col, $value) = @_;
77   return $value unless defined $value; # NULL is NULL is NULL
78   my $info = $self->column_info($col)
79     or $self->throw_exception("No column info for $col");
80   return $value unless exists $info->{_inflate_info};
81   my $inflate = $info->{_inflate_info}{inflate};
82   $self->throw_exception("No inflator for $col") unless defined $inflate;
83   return $inflate->($value, $self);
84 }
85
86 sub _deflated_column {
87   my ($self, $col, $value) = @_;
88   return $value unless ref $value; # If it's not an object, don't touch it
89   my $info = $self->column_info($col) or
90     $self->throw_exception("No column info for $col");
91   return $value unless exists $info->{_inflate_info};
92   my $deflate = $info->{_inflate_info}{deflate};
93   $self->throw_exception("No deflator for $col") unless defined $deflate;
94   return $deflate->($value, $self);
95 }
96
97 sub get_inflated_column {
98   my ($self, $col) = @_;
99   $self->throw_exception("$col is not an inflated column")
100     unless exists $self->column_info($col)->{_inflate_info};
101
102   return $self->{_inflated_column}{$col}
103     if exists $self->{_inflated_column}{$col};
104   return $self->{_inflated_column}{$col} =
105            $self->_inflated_column($col, $self->get_column($col));
106 }
107
108 sub set_inflated_column {
109   my ($self, $col, @rest) = @_;
110   my $ret = $self->_inflated_column_op('set', $col, @rest);
111   return $ret;
112 }
113
114 sub store_inflated_column {
115   my ($self, $col, @rest) = @_;
116   my $ret = $self->_inflated_column_op('store', $col, @rest);
117   return $ret;
118 }
119
120 sub _inflated_column_op {
121   my ($self, $op, $col, $obj) = @_;
122   my $meth = "${op}_column";
123   unless (ref $obj) {
124     delete $self->{_inflated_column}{$col};
125     return $self->$meth($col, $obj);
126   }
127
128   my $deflated = $self->_deflated_column($col, $obj);
129            # Do this now so we don't store if it's invalid
130
131   $self->{_inflated_column}{$col} = $obj;
132   $self->$meth($col, $deflated);
133   return $obj;
134 }
135
136 sub update {
137   my ($class, $attrs, @rest) = @_;
138   $attrs ||= {};
139   foreach my $key (keys %$attrs) {
140     if (ref $attrs->{$key}
141           && exists $class->column_info($key)->{_inflate_info}) {
142 #      $attrs->{$key} = $class->_deflated_column($key, $attrs->{$key});
143       $class->set_inflated_column ($key, delete $attrs->{$key});
144     }
145   }
146   return $class->next::method($attrs, @rest);
147 }
148
149 sub new {
150   my ($class, $attrs, @rest) = @_;
151   $attrs ||= {};
152   foreach my $key (keys %$attrs) {
153     if (ref $attrs->{$key}
154           && exists $class->column_info($key)->{_inflate_info}) {
155       $attrs->{$key} = $class->_deflated_column($key, $attrs->{$key});
156     }
157   }
158   return $class->next::method($attrs, @rest);
159 }
160
161 =head1 SEE ALSO
162
163 =over 4
164
165 =item L<DBIx::Class::Core> - This component is loaded as part of the
166       "core" L<DBIx::Class> components; generally there is no need to
167       load it directly
168
169 =back
170
171 =head1 AUTHOR
172
173 Matt S. Trout <mst@shadowcatsystems.co.uk>
174
175 =head1 CONTRIBUTORS
176
177 Daniel Westermann-Clark <danieltwc@cpan.org> (documentation)
178
179 =head1 LICENSE
180
181 You may distribute this code under the same terms as Perl itself.
182
183 =cut
184
185 1;