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