Initial work on getting POD coverage testing working
[dbsrgits/DBIx-Class-Historic.git] / lib / DBIx / Class / InflateColumn.pm
CommitLineData
0e5c2582 1package DBIx::Class::InflateColumn;
2
3use strict;
4use warnings;
aa562407 5
aa562407 6
75a23b3e 7use base qw/DBIx::Class::Row/;
0e5c2582 8
75d07914 9=head1 NAME
bcae85db 10
11DBIx::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
23This component translates column data into objects, i.e. "inflating"
24the column data. It also "deflates" objects into an appropriate format
25for the database.
26
27It can be used, for example, to automatically convert to and from
75d07914 28L<DateTime> objects for your date and time fields.
bcae85db 29
30=head1 METHODS
31
32=head2 inflate_column
33
75d07914 34Instruct L<DBIx::Class> to inflate the given column.
bcae85db 35
36In addition to the column name, you must provide C<inflate> and
37C<deflate> methods. The C<inflate> method is called when you access
38the field, while the C<deflate> method is called when the field needs
39to used by the database.
40
41For example, if you have a table C<events> with a timestamp field
42named C<insert_time>, you could inflate the column in the
43corresponding 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
51database, or consider L<DateTime::Format::DBI>.)
52
06fc5fc9 53The coderefs you set for inflate and deflate are called with two parameters,
54the first is the value of the column to be inflated/deflated, the second is the
55row object itself. Thus you can call C<< ->result_source->schema->storage->dbh >> on
56it, to feed to L<DateTime::Format::DBI>.
57
bcae85db 58In this example, calls to an event's C<insert_time> accessor return a
59L<DateTime> object. This L<DateTime> object is later "deflated" when
60used in the database layer.
61
62=cut
63
0e5c2582 64sub inflate_column {
65 my ($self, $col, $attrs) = @_;
bc0c9800 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';
103647d5 70 $self->column_info($col)->{_inflate_info} = $attrs;
0e5c2582 71 $self->mk_group_accessors('inflated_column' => $col);
72 return 1;
73}
74
4a07648a 75sub _inflated_column {
0e5c2582 76 my ($self, $col, $value) = @_;
9f300b1b 77 return $value unless defined $value; # NULL is NULL is NULL
bc0c9800 78 my $info = $self->column_info($col)
79 or $self->throw_exception("No column info for $col");
103647d5 80 return $value unless exists $info->{_inflate_info};
81 my $inflate = $info->{_inflate_info}{inflate};
701da8c4 82 $self->throw_exception("No inflator for $col") unless defined $inflate;
0e5c2582 83 return $inflate->($value, $self);
84}
85
4a07648a 86sub _deflated_column {
0e5c2582 87 my ($self, $col, $value) = @_;
88 return $value unless ref $value; # If it's not an object, don't touch it
bc0c9800 89 my $info = $self->column_info($col) or
90 $self->throw_exception("No column info for $col");
103647d5 91 return $value unless exists $info->{_inflate_info};
92 my $deflate = $info->{_inflate_info}{deflate};
701da8c4 93 $self->throw_exception("No deflator for $col") unless defined $deflate;
0e5c2582 94 return $deflate->($value, $self);
95}
96
7eb4ecc8 97=head2 get_inflated_column
98
99 my $val = $obj->get_inflated_column($col);
100
101Fetch a column value in its inflated state. This is directly
102analogous to L<DBIx::Class::Row/get_column> in that it only fetches a
103column already retreived from the database, and then inflates it.
104Throws an exception if the column requested is not an inflated column.
105
106=cut
107
0e5c2582 108sub get_inflated_column {
109 my ($self, $col) = @_;
bc0c9800 110 $self->throw_exception("$col is not an inflated column")
111 unless exists $self->column_info($col)->{_inflate_info};
4a07648a 112
0e5c2582 113 return $self->{_inflated_column}{$col}
114 if exists $self->{_inflated_column}{$col};
0e5c2582 115 return $self->{_inflated_column}{$col} =
4a07648a 116 $self->_inflated_column($col, $self->get_column($col));
0e5c2582 117}
118
7eb4ecc8 119=head2 set_inflated_column
120
121 my $copy = $obj->set_inflated_column($col => $val);
122
123Sets a column value from an inflated value. This is directly
124analogous to L<DBIx::Class::Row/set_column>.
125
126=cut
127
0e5c2582 128sub set_inflated_column {
129 my ($self, $col, @rest) = @_;
47bd0267 130 my $ret = $self->_inflated_column_op('set', $col, @rest);
0e5c2582 131 return $ret;
132}
133
7eb4ecc8 134=head2 store_inflated_column
135
136 my $copy = $obj->store_inflated_column($col => $val);
137
138Sets a column value from an inflated value without marking the column
139as dirty. This is directly analogous to
140L<DBIx::Class::Row/store_column>.
141
142=cut
143
0e5c2582 144sub store_inflated_column {
47bd0267 145 my ($self, $col, @rest) = @_;
146 my $ret = $self->_inflated_column_op('store', $col, @rest);
147 return $ret;
148}
149
150sub _inflated_column_op {
151 my ($self, $op, $col, $obj) = @_;
152 my $meth = "${op}_column";
0e5c2582 153 unless (ref $obj) {
154 delete $self->{_inflated_column}{$col};
47bd0267 155 return $self->$meth($col, $obj);
0e5c2582 156 }
4a07648a 157
158 my $deflated = $self->_deflated_column($col, $obj);
9f300b1b 159 # Do this now so we don't store if it's invalid
4a07648a 160
0e5c2582 161 $self->{_inflated_column}{$col} = $obj;
47bd0267 162 $self->$meth($col, $deflated);
0e5c2582 163 return $obj;
164}
165
7eb4ecc8 166=head2 update
167
168Updates a row in the same way as L<DBIx::Class::Row/update>, handling
169inflation and deflation of columns appropriately.
170
171=cut
172
9fcda149 173sub update {
174 my ($class, $attrs, @rest) = @_;
175 $attrs ||= {};
176 foreach my $key (keys %$attrs) {
177 if (ref $attrs->{$key}
178 && exists $class->column_info($key)->{_inflate_info}) {
e9100ff7 179# $attrs->{$key} = $class->_deflated_column($key, $attrs->{$key});
180 $class->set_inflated_column ($key, delete $attrs->{$key});
9fcda149 181 }
182 }
183 return $class->next::method($attrs, @rest);
184}
185
7eb4ecc8 186=head2 new
187
188Creates a row in the same way as L<DBIx::Class::Row/new>, handling
189inflation and deflation of columns appropriately.
190
191=cut
192
0e5c2582 193sub new {
194 my ($class, $attrs, @rest) = @_;
195 $attrs ||= {};
0e5c2582 196 foreach my $key (keys %$attrs) {
103647d5 197 if (ref $attrs->{$key}
198 && exists $class->column_info($key)->{_inflate_info}) {
484c9dda 199 $attrs->{$key} = $class->_deflated_column($key, $attrs->{$key});
0e5c2582 200 }
201 }
147dd158 202 return $class->next::method($attrs, @rest);
0e5c2582 203}
204
bcae85db 205=head1 SEE ALSO
206
207=over 4
208
209=item L<DBIx::Class::Core> - This component is loaded as part of the
210 "core" L<DBIx::Class> components; generally there is no need to
211 load it directly
212
213=back
214
215=head1 AUTHOR
216
217Matt S. Trout <mst@shadowcatsystems.co.uk>
218
219=head1 CONTRIBUTORS
220
221Daniel Westermann-Clark <danieltwc@cpan.org> (documentation)
222
223=head1 LICENSE
224
225You may distribute this code under the same terms as Perl itself.
226
227=cut
228
0e5c2582 2291;