Commit | Line | Data |
0e5c2582 |
1 | package DBIx::Class::InflateColumn; |
2 | |
3 | use strict; |
4 | use warnings; |
aa562407 |
5 | |
6 | use Carp qw/croak/; |
7 | |
75a23b3e |
8 | use base qw/DBIx::Class::Row/; |
0e5c2582 |
9 | |
bcae85db |
10 | =head1 NAME |
11 | |
12 | DBIx::Class::InflateColumn - Automatically create objects from column data |
13 | |
14 | =head1 SYNOPSIS |
15 | |
16 | # In your table classes |
17 | __PACKAGE__->inflate_column('column_name', { |
18 | inflate => sub { ... }, |
19 | deflate => sub { ... }, |
20 | }); |
21 | |
22 | =head1 DESCRIPTION |
23 | |
24 | This component translates column data into objects, i.e. "inflating" |
25 | the column data. It also "deflates" objects into an appropriate format |
26 | for the database. |
27 | |
28 | It can be used, for example, to automatically convert to and from |
29 | L<DateTime> objects for your date and time fields. |
30 | |
31 | =head1 METHODS |
32 | |
33 | =head2 inflate_column |
34 | |
35 | Instruct L<DBIx::Class> to inflate the given column. |
36 | |
37 | In addition to the column name, you must provide C<inflate> and |
38 | C<deflate> methods. The C<inflate> method is called when you access |
39 | the field, while the C<deflate> method is called when the field needs |
40 | to used by the database. |
41 | |
42 | For example, if you have a table C<events> with a timestamp field |
43 | named C<insert_time>, you could inflate the column in the |
44 | corresponding table class using something like: |
45 | |
46 | __PACKAGE__->inflate_column('insert_time', { |
47 | inflate => sub { DateTime::Format::Pg->parse_datetime(shift); }, |
48 | deflate => sub { DateTime::Format::Pg->format_datetime(shift); }, |
49 | }); |
50 | |
51 | (Replace L<DateTime::Format::Pg> with the appropriate module for your |
52 | database, or consider L<DateTime::Format::DBI>.) |
53 | |
54 | In this example, calls to an event's C<insert_time> accessor return a |
55 | L<DateTime> object. This L<DateTime> object is later "deflated" when |
56 | used in the database layer. |
57 | |
58 | =cut |
59 | |
0e5c2582 |
60 | sub inflate_column { |
61 | my ($self, $col, $attrs) = @_; |
aa562407 |
62 | croak "No such column $col to inflate" unless $self->has_column($col); |
63 | croak "inflate_column needs attr hashref" unless ref $attrs eq 'HASH'; |
103647d5 |
64 | $self->column_info($col)->{_inflate_info} = $attrs; |
0e5c2582 |
65 | $self->mk_group_accessors('inflated_column' => $col); |
66 | return 1; |
67 | } |
68 | |
4a07648a |
69 | sub _inflated_column { |
0e5c2582 |
70 | my ($self, $col, $value) = @_; |
9f300b1b |
71 | return $value unless defined $value; # NULL is NULL is NULL |
aa562407 |
72 | my $info = $self->column_info($col) || croak "No column info for $col"; |
103647d5 |
73 | return $value unless exists $info->{_inflate_info}; |
74 | my $inflate = $info->{_inflate_info}{inflate}; |
aa562407 |
75 | croak "No inflator for $col" unless defined $inflate; |
0e5c2582 |
76 | return $inflate->($value, $self); |
77 | } |
78 | |
4a07648a |
79 | sub _deflated_column { |
0e5c2582 |
80 | my ($self, $col, $value) = @_; |
81 | return $value unless ref $value; # If it's not an object, don't touch it |
aa562407 |
82 | my $info = $self->column_info($col) || croak "No column info for $col"; |
103647d5 |
83 | return $value unless exists $info->{_inflate_info}; |
84 | my $deflate = $info->{_inflate_info}{deflate}; |
aa562407 |
85 | croak "No deflator for $col" unless defined $deflate; |
0e5c2582 |
86 | return $deflate->($value, $self); |
87 | } |
88 | |
89 | sub get_inflated_column { |
90 | my ($self, $col) = @_; |
91 | $self->throw("$col is not an inflated column") unless |
103647d5 |
92 | exists $self->column_info($col)->{_inflate_info}; |
4a07648a |
93 | |
0e5c2582 |
94 | return $self->{_inflated_column}{$col} |
95 | if exists $self->{_inflated_column}{$col}; |
0e5c2582 |
96 | return $self->{_inflated_column}{$col} = |
4a07648a |
97 | $self->_inflated_column($col, $self->get_column($col)); |
0e5c2582 |
98 | } |
99 | |
100 | sub set_inflated_column { |
101 | my ($self, $col, @rest) = @_; |
47bd0267 |
102 | my $ret = $self->_inflated_column_op('set', $col, @rest); |
0e5c2582 |
103 | return $ret; |
104 | } |
105 | |
106 | sub store_inflated_column { |
47bd0267 |
107 | my ($self, $col, @rest) = @_; |
108 | my $ret = $self->_inflated_column_op('store', $col, @rest); |
109 | return $ret; |
110 | } |
111 | |
112 | sub _inflated_column_op { |
113 | my ($self, $op, $col, $obj) = @_; |
114 | my $meth = "${op}_column"; |
0e5c2582 |
115 | unless (ref $obj) { |
116 | delete $self->{_inflated_column}{$col}; |
47bd0267 |
117 | return $self->$meth($col, $obj); |
0e5c2582 |
118 | } |
4a07648a |
119 | |
120 | my $deflated = $self->_deflated_column($col, $obj); |
9f300b1b |
121 | # Do this now so we don't store if it's invalid |
4a07648a |
122 | |
0e5c2582 |
123 | $self->{_inflated_column}{$col} = $obj; |
47bd0267 |
124 | $self->$meth($col, $deflated); |
0e5c2582 |
125 | return $obj; |
126 | } |
127 | |
128 | sub new { |
129 | my ($class, $attrs, @rest) = @_; |
130 | $attrs ||= {}; |
0e5c2582 |
131 | foreach my $key (keys %$attrs) { |
103647d5 |
132 | if (ref $attrs->{$key} |
133 | && exists $class->column_info($key)->{_inflate_info}) { |
484c9dda |
134 | $attrs->{$key} = $class->_deflated_column($key, $attrs->{$key}); |
0e5c2582 |
135 | } |
136 | } |
147dd158 |
137 | return $class->next::method($attrs, @rest); |
0e5c2582 |
138 | } |
139 | |
bcae85db |
140 | =head1 SEE ALSO |
141 | |
142 | =over 4 |
143 | |
144 | =item L<DBIx::Class::Core> - This component is loaded as part of the |
145 | "core" L<DBIx::Class> components; generally there is no need to |
146 | load it directly |
147 | |
148 | =back |
149 | |
150 | =head1 AUTHOR |
151 | |
152 | Matt S. Trout <mst@shadowcatsystems.co.uk> |
153 | |
154 | =head1 CONTRIBUTORS |
155 | |
156 | Daniel Westermann-Clark <danieltwc@cpan.org> (documentation) |
157 | |
158 | =head1 LICENSE |
159 | |
160 | You may distribute this code under the same terms as Perl itself. |
161 | |
162 | =cut |
163 | |
0e5c2582 |
164 | 1; |