Consolidate handling of "is this a literal" and "is this a value"
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / InflateColumn.pm
CommitLineData
0e5c2582 1package DBIx::Class::InflateColumn;
2
3use strict;
4use warnings;
aa562407 5
3705e3b2 6use base 'DBIx::Class::Row';
7use DBIx::Class::_Util 'is_literal_value';
8use namespace::clean;
0e5c2582 9
75d07914 10=head1 NAME
bcae85db 11
e81a6241 12DBIx::Class::InflateColumn - Automatically create references from column data
bcae85db 13
14=head1 SYNOPSIS
15
1b23a127 16 # In your table classes
17 __PACKAGE__->inflate_column('column_name', {
18 inflate => sub {
19 my ($raw_value_from_db, $result_object) = @_;
20 ...
21 },
22 deflate => sub {
23 my ($inflated_value_from_user, $result_object) = @_;
24 ...
25 },
26 });
bcae85db 27
28=head1 DESCRIPTION
29
e81a6241 30This component translates column data into references, i.e. "inflating"
31the column data. It also "deflates" references into an appropriate format
bcae85db 32for the database.
33
34It can be used, for example, to automatically convert to and from
ef7a8b67 35L<DateTime> objects for your date and time fields. There's a
48580715 36convenience component to actually do that though, try
ef7a8b67 37L<DBIx::Class::InflateColumn::DateTime>.
bcae85db 38
ef7a8b67 39It will handle all types of references except scalar references. It
40will not handle scalar values, these are ignored and thus passed
41through to L<SQL::Abstract>. This is to allow setting raw values to
42"just work". Scalar references are passed through to the database to
43deal with, to allow such settings as C< \'year + 1'> and C< \'DEFAULT' >
44to work.
45
46If you want to filter plain scalar values and replace them with
9b2c0de6 47something else, see L<DBIx::Class::FilterColumn>.
e81a6241 48
bcae85db 49=head1 METHODS
50
51=head2 inflate_column
52
75d07914 53Instruct L<DBIx::Class> to inflate the given column.
bcae85db 54
55In addition to the column name, you must provide C<inflate> and
56C<deflate> methods. The C<inflate> method is called when you access
57the field, while the C<deflate> method is called when the field needs
58to used by the database.
59
60For example, if you have a table C<events> with a timestamp field
61named C<insert_time>, you could inflate the column in the
62corresponding table class using something like:
63
64 __PACKAGE__->inflate_column('insert_time', {
1b23a127 65 inflate => sub {
66 my ($insert_time_raw_value, $event_result_object) = @_;
67 DateTime->from_epoch( epoch => $insert_time_raw_value );
68 },
69 deflate => sub {
70 my ($insert_time_dt_object, $event_result_object) = @_;
71 $insert_time_dt_object->epoch;
72 },
bcae85db 73 });
74
06fc5fc9 75The coderefs you set for inflate and deflate are called with two parameters,
1b23a127 76the first is the value of the column to be inflated/deflated, the second is
77the result object itself.
06fc5fc9 78
bcae85db 79In this example, calls to an event's C<insert_time> accessor return a
1b23a127 80L<DateTime> object. This L<DateTime> object is later "deflated" back
81to the integer epoch representation when used in the database layer.
82For a much more thorough handling of the above example, please see
83L<DBIx::Class::DateTime::Epoch>
bcae85db 84
85=cut
86
0e5c2582 87sub inflate_column {
88 my ($self, $col, $attrs) = @_;
c227b295 89
52416317 90 my $colinfo = $self->column_info($col);
91
c227b295 92 $self->throw_exception("InflateColumn does not work with FilterColumn")
93 if $self->isa('DBIx::Class::FilterColumn') &&
52416317 94 defined $colinfo->{_filter_info};
c227b295 95
bc0c9800 96 $self->throw_exception("No such column $col to inflate")
97 unless $self->has_column($col);
98 $self->throw_exception("inflate_column needs attr hashref")
99 unless ref $attrs eq 'HASH';
52416317 100 $colinfo->{_inflate_info} = $attrs;
101 my $acc = $colinfo->{accessor};
b82c8a28 102 $self->mk_group_accessors('inflated_column' => [ (defined $acc ? $acc : $col), $col]);
0e5c2582 103 return 1;
104}
105
4a07648a 106sub _inflated_column {
0e5c2582 107 my ($self, $col, $value) = @_;
9f300b1b 108 return $value unless defined $value; # NULL is NULL is NULL
3705e3b2 109
bc0c9800 110 my $info = $self->column_info($col)
111 or $self->throw_exception("No column info for $col");
3705e3b2 112
103647d5 113 return $value unless exists $info->{_inflate_info};
3705e3b2 114
103647d5 115 my $inflate = $info->{_inflate_info}{inflate};
701da8c4 116 $self->throw_exception("No inflator for $col") unless defined $inflate;
3705e3b2 117
0e5c2582 118 return $inflate->($value, $self);
119}
120
89279e9d 121sub _deflated_column {
122 my ($self, $col, $value) = @_;
3705e3b2 123
124 ## Deflate any refs except for literals, pass through plain values
125 return $value if (
126 ! length ref $value
127 or
128 is_literal_value($value)
129 );
130
89279e9d 131 my $info = $self->column_info($col) or
132 $self->throw_exception("No column info for $col");
3705e3b2 133
89279e9d 134 return $value unless exists $info->{_inflate_info};
3705e3b2 135
89279e9d 136 my $deflate = $info->{_inflate_info}{deflate};
137 $self->throw_exception("No deflator for $col") unless defined $deflate;
3705e3b2 138
89279e9d 139 return $deflate->($value, $self);
0e5c2582 140}
141
7eb4ecc8 142=head2 get_inflated_column
143
144 my $val = $obj->get_inflated_column($col);
145
146Fetch a column value in its inflated state. This is directly
147analogous to L<DBIx::Class::Row/get_column> in that it only fetches a
48580715 148column already retrieved from the database, and then inflates it.
7eb4ecc8 149Throws an exception if the column requested is not an inflated column.
150
151=cut
152
0e5c2582 153sub get_inflated_column {
154 my ($self, $col) = @_;
bc0c9800 155 $self->throw_exception("$col is not an inflated column")
156 unless exists $self->column_info($col)->{_inflate_info};
0e5c2582 157 return $self->{_inflated_column}{$col}
158 if exists $self->{_inflated_column}{$col};
f92b166e 159
160 my $val = $self->get_column($col);
3705e3b2 161
162 return $val if is_literal_value($val); #that would be a not-yet-reloaded literal update
f92b166e 163
164 return $self->{_inflated_column}{$col} = $self->_inflated_column($col, $val);
0e5c2582 165}
166
7eb4ecc8 167=head2 set_inflated_column
168
169 my $copy = $obj->set_inflated_column($col => $val);
170
171Sets a column value from an inflated value. This is directly
172analogous to L<DBIx::Class::Row/set_column>.
173
174=cut
175
0e5c2582 176sub set_inflated_column {
ad5d0ee9 177 my ($self, $col, $inflated) = @_;
178 $self->set_column($col, $self->_deflated_column($col, $inflated));
3705e3b2 179
180 if (length ref $inflated and ! is_literal_value($inflated) ) {
9b2c0de6 181 $self->{_inflated_column}{$col} = $inflated;
9f471067 182 } else {
9b2c0de6 183 delete $self->{_inflated_column}{$col};
9f471067 184 }
ad5d0ee9 185 return $inflated;
0e5c2582 186}
187
7eb4ecc8 188=head2 store_inflated_column
189
190 my $copy = $obj->store_inflated_column($col => $val);
191
192Sets a column value from an inflated value without marking the column
47c56124 193as dirty. This is directly analogous to L<DBIx::Class::Row/store_column>.
7eb4ecc8 194
195=cut
196
0e5c2582 197sub store_inflated_column {
ad5d0ee9 198 my ($self, $col, $inflated) = @_;
3705e3b2 199
200 if (is_literal_value($inflated)) {
201 delete $self->{_inflated_column}{$col};
202 $self->store_column($col => $inflated);
9f471067 203 }
3705e3b2 204 else {
205 delete $self->{_column_data}{$col};
206 $self->{_inflated_column}{$col} = $inflated;
207 }
208
209 return $inflated;
180c7679 210}
4a07648a 211
bcae85db 212=head1 SEE ALSO
213
214=over 4
215
216=item L<DBIx::Class::Core> - This component is loaded as part of the
d88ecca6 217 C<core> L<DBIx::Class> components; generally there is no need to
bcae85db 218 load it directly
219
220=back
221
222=head1 AUTHOR
223
224Matt S. Trout <mst@shadowcatsystems.co.uk>
225
226=head1 CONTRIBUTORS
227
228Daniel Westermann-Clark <danieltwc@cpan.org> (documentation)
229
e81a6241 230Jess Robinson <cpan@desert-island.demon.co.uk>
231
bcae85db 232=head1 LICENSE
233
234You may distribute this code under the same terms as Perl itself.
235
236=cut
237
0e5c2582 2381;