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