1 package DBIx::Class::FilterColumn;
5 use base 'DBIx::Class::Row';
6 use SQL::Abstract 'is_literal_value';
10 my ($self, $col, $attrs) = @_;
12 my $colinfo = $self->result_source->columns_info([$col])->{$col};
14 $self->throw_exception("FilterColumn can not be used on a column with a declared InflateColumn inflator")
15 if defined $colinfo->{_inflate_info} and $self->isa('DBIx::Class::InflateColumn');
17 $self->throw_exception('filter_column expects a hashref of filter specifications')
18 unless ref $attrs eq 'HASH';
20 $self->throw_exception('An invocation of filter_column() must specify either a filter_from_storage or filter_to_storage')
21 unless $attrs->{filter_from_storage} || $attrs->{filter_to_storage};
23 $colinfo->{_filter_info} = $attrs;
24 my $acc = $colinfo->{accessor};
25 $self->mk_group_accessors(filtered_column => [ (defined $acc ? $acc : $col), $col]);
29 sub _column_from_storage {
30 my ($self, $col, $value) = @_;
32 return $value if is_literal_value($value);
34 my $info = $self->result_source->columns_info([$col])->{$col};
36 return $value unless exists $info->{_filter_info};
38 my $filter = $info->{_filter_info}{filter_from_storage};
40 return defined $filter ? $self->$filter($value) : $value;
43 sub _column_to_storage {
44 my ($self, $col, $value) = @_;
46 return $value if is_literal_value($value);
48 my $info = $self->result_source->columns_info([$col])->{$col};
50 return $value unless exists $info->{_filter_info};
52 my $unfilter = $info->{_filter_info}{filter_to_storage};
54 return defined $unfilter ? $self->$unfilter($value) : $value;
57 sub get_filtered_column {
58 my ($self, $col) = @_;
60 $self->throw_exception("$col is not a filtered column")
61 unless exists $self->result_source->columns_info->{$col}{_filter_info};
63 return $self->{_filtered_column}{$col}
64 if exists $self->{_filtered_column}{$col};
66 my $val = $self->get_column($col);
68 return $self->{_filtered_column}{$col} = $self->_column_from_storage(
74 my ($self, $col) = @_;
76 ! exists $self->{_column_data}{$col}
78 exists $self->{_filtered_column}{$col}
80 $self->{_column_data}{$col} = $self->_column_to_storage (
81 $col, $self->{_filtered_column}{$col}
84 return $self->next::method ($col);
87 # sadly a separate codepath in Row.pm ( used by insert() )
91 $self->{_column_data}{$_} = $self->_column_to_storage (
92 $_, $self->{_filtered_column}{$_}
94 { ! exists $self->{_column_data}{$_} }
95 keys %{$self->{_filtered_column}||{}}
98 $self->next::method (@_);
101 # and *another* separate codepath, argh!
102 sub get_dirty_columns {
105 $self->{_dirty_columns}{$_}
107 ! exists $self->{_column_data}{$_}
109 $self->{_column_data}{$_} = $self->_column_to_storage (
110 $_, $self->{_filtered_column}{$_}
112 for keys %{$self->{_filtered_column}||{}};
114 $self->next::method(@_);
118 my ($self, $col) = (shift, @_);
121 delete $self->{_filtered_column}{$col};
123 $self->next::method(@_);
126 sub has_column_loaded {
127 my ($self, $col) = @_;
128 return 1 if exists $self->{_filtered_column}{$col};
129 return $self->next::method($col);
132 sub set_filtered_column {
133 my ($self, $col, $filtered) = @_;
135 # unlike IC, FC does not need to deal with the 'filter' abomination
136 # thus we can short-curcuit filtering entirely and never call set_column
137 # in case this is already a dirty change OR the row never touched storage
141 $self->is_column_changed($col)
143 $self->make_column_dirty($col);
144 delete $self->{_column_data}{$col};
147 $self->set_column($col, $self->_column_to_storage($col, $filtered));
150 return $self->{_filtered_column}{$col} = $filtered;
154 my ($self, $data, @rest) = @_;
156 my $colinfos = $self->result_source->columns_info;
158 foreach my $col (keys %{$data||{}}) {
159 if ( exists $colinfos->{$col}{_filter_info} ) {
160 $self->set_filtered_column($col, delete $data->{$col});
162 # FIXME update() reaches directly into the object-hash
163 # and we may *not* have a filtered value there - thus
164 # the void-ctx filter-trigger
165 $self->get_column($col) unless exists $self->{_column_data}{$col};
169 return $self->next::method($data, @rest);
173 my ($class, $data, @rest) = @_;
175 my $rsrc = $data->{-result_source}
176 or $class->throw_exception('Sourceless rows are not supported with DBIx::Class::FilterColumn');
178 my $obj = $class->next::method($data, @rest);
180 my $colinfos = $rsrc->columns_info;
182 foreach my $col (keys %{$data||{}}) {
183 if (exists $colinfos->{$col}{_filter_info} ) {
184 $obj->set_filtered_column($col, $data->{$col});
197 DBIx::Class::FilterColumn - Automatically convert column data
201 In your Schema or DB class add "FilterColumn" to the top of the component list.
203 __PACKAGE__->load_components(qw( FilterColumn ... ));
205 Set up filters for the columns you want to convert.
207 __PACKAGE__->filter_column( money => {
208 filter_to_storage => 'to_pennies',
209 filter_from_storage => 'from_pennies',
212 sub to_pennies { $_[1] * 100 }
214 sub from_pennies { $_[1] / 100 }
221 This component is meant to be a more powerful, but less DWIM-y,
222 L<DBIx::Class::InflateColumn>. One of the major issues with said component is
223 that it B<only> works with references. Generally speaking anything that can
224 be done with L<DBIx::Class::InflateColumn> can be done with this component.
230 __PACKAGE__->filter_column( colname => {
231 filter_from_storage => 'method'|\&coderef,
232 filter_to_storage => 'method'|\&coderef,
235 This is the method that you need to call to set up a filtered column. It takes
236 exactly two arguments; the first being the column name the second being a hash
237 reference with C<filter_from_storage> and C<filter_to_storage> set to either
238 a method name or a code reference. In either case the filter is invoked as:
240 $result->$filter_specification ($value_to_filter)
242 with C<$filter_specification> being chosen depending on whether the
243 C<$value_to_filter> is being retrieved from or written to permanent
246 If a specific directional filter is not specified, the original value will be
247 passed to/from storage unfiltered.
249 =head2 get_filtered_column
251 $obj->get_filtered_column('colname')
253 Returns the filtered value of the column
255 =head2 set_filtered_column
257 $obj->set_filtered_column(colname => 'new_value')
259 Sets the filtered value of the column
261 =head1 EXAMPLE OF USE
263 Some databases have restrictions on values that can be passed to
264 boolean columns, and problems can be caused by passing value that
265 perl considers to be false (such as C<undef>).
267 One solution to this is to ensure that the boolean values are set
268 to something that the database can handle - such as numeric zero
269 and one, using code like this:-
271 __PACKAGE__->filter_column(
272 my_boolean_column => {
273 filter_to_storage => sub { $_[1] ? 1 : 0 },
277 In this case the C<filter_from_storage> is not required, as just
278 passing the database value through to perl does the right thing.
280 =head1 FURTHER QUESTIONS?
282 Check the list of L<additional DBIC resources|DBIx::Class/GETTING HELP/SUPPORT>.
284 =head1 COPYRIGHT AND LICENSE
286 This module is free software L<copyright|DBIx::Class/COPYRIGHT AND LICENSE>
287 by the L<DBIx::Class (DBIC) authors|DBIx::Class/AUTHORS>. You can
288 redistribute it and/or modify it under the same terms as the
289 L<DBIx::Class library|DBIx::Class/COPYRIGHT AND LICENSE>.