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->column_info($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("No such column $col to filter")
18 unless $self->has_column($col);
20 $self->throw_exception('filter_column expects a hashref of filter specifications')
21 unless ref $attrs eq 'HASH';
23 $self->throw_exception('An invocation of filter_column() must specify either a filter_from_storage or filter_to_storage')
24 unless $attrs->{filter_from_storage} || $attrs->{filter_to_storage};
26 $colinfo->{_filter_info} = $attrs;
27 my $acc = $colinfo->{accessor};
28 $self->mk_group_accessors(filtered_column => [ (defined $acc ? $acc : $col), $col]);
32 sub _column_from_storage {
33 my ($self, $col, $value) = @_;
35 return $value if is_literal_value($value);
37 my $info = $self->result_source->column_info($col)
38 or $self->throw_exception("No column info for $col");
40 return $value unless exists $info->{_filter_info};
42 my $filter = $info->{_filter_info}{filter_from_storage};
44 return defined $filter ? $self->$filter($value) : $value;
47 sub _column_to_storage {
48 my ($self, $col, $value) = @_;
50 return $value if is_literal_value($value);
52 my $info = $self->result_source->column_info($col) or
53 $self->throw_exception("No column info for $col");
55 return $value unless exists $info->{_filter_info};
57 my $unfilter = $info->{_filter_info}{filter_to_storage};
59 return defined $unfilter ? $self->$unfilter($value) : $value;
62 sub get_filtered_column {
63 my ($self, $col) = @_;
65 $self->throw_exception("$col is not a filtered column")
66 unless exists $self->result_source->column_info($col)->{_filter_info};
68 return $self->{_filtered_column}{$col}
69 if exists $self->{_filtered_column}{$col};
71 my $val = $self->get_column($col);
73 return $self->{_filtered_column}{$col} = $self->_column_from_storage(
79 my ($self, $col) = @_;
81 if (exists $self->{_filtered_column}{$col}) {
82 return $self->{_column_data}{$col} ||= $self->_column_to_storage (
83 $col, $self->{_filtered_column}{$col}
87 return $self->next::method ($col);
90 # sadly a separate codepath in Row.pm ( used by insert() )
94 $self->{_column_data}{$_} = $self->_column_to_storage (
95 $_, $self->{_filtered_column}{$_}
97 { ! exists $self->{_column_data}{$_} }
98 keys %{$self->{_filtered_column}||{}}
101 $self->next::method (@_);
105 my ($self, $col) = (shift, @_);
108 delete $self->{_filtered_column}{$col};
110 $self->next::method(@_);
113 sub has_column_loaded {
114 my ($self, $col) = @_;
115 return 1 if exists $self->{_filtered_column}{$col};
116 return $self->next::method($col);
119 sub set_filtered_column {
120 my ($self, $col, $filtered) = @_;
122 # unlike IC, FC does not need to deal with the 'filter' abomination
123 # thus we can short-curcuit filtering entirely and never call set_column
124 # in case this is already a dirty change OR the row never touched storage
128 $self->is_column_changed($col)
130 $self->make_column_dirty($col);
131 delete $self->{_column_data}{$col};
134 $self->set_column($col, $self->_column_to_storage($col, $filtered));
137 return $self->{_filtered_column}{$col} = $filtered;
141 my ($self, $data, @rest) = @_;
143 my $colinfos = $self->result_source->columns_info;
145 foreach my $col (keys %{$data||{}}) {
146 if ( exists $colinfos->{$col}{_filter_info} ) {
147 $self->set_filtered_column($col, delete $data->{$col});
149 # FIXME update() reaches directly into the object-hash
150 # and we may *not* have a filtered value there - thus
151 # the void-ctx filter-trigger
152 $self->get_column($col) unless exists $self->{_column_data}{$col};
156 return $self->next::method($data, @rest);
160 my ($class, $data, @rest) = @_;
162 my $rsrc = $data->{-result_source}
163 or $class->throw_exception('Sourceless rows are not supported with DBIx::Class::FilterColumn');
165 my $obj = $class->next::method($data, @rest);
167 my $colinfos = $rsrc->columns_info;
169 foreach my $col (keys %{$data||{}}) {
170 if (exists $colinfos->{$col}{_filter_info} ) {
171 $obj->set_filtered_column($col, $data->{$col});
184 DBIx::Class::FilterColumn - Automatically convert column data
188 In your Schema or DB class add "FilterColumn" to the top of the component list.
190 __PACKAGE__->load_components(qw( FilterColumn ... ));
192 Set up filters for the columns you want to convert.
194 __PACKAGE__->filter_column( money => {
195 filter_to_storage => 'to_pennies',
196 filter_from_storage => 'from_pennies',
199 sub to_pennies { $_[1] * 100 }
201 sub from_pennies { $_[1] / 100 }
208 This component is meant to be a more powerful, but less DWIM-y,
209 L<DBIx::Class::InflateColumn>. One of the major issues with said component is
210 that it B<only> works with references. Generally speaking anything that can
211 be done with L<DBIx::Class::InflateColumn> can be done with this component.
217 __PACKAGE__->filter_column( colname => {
218 filter_from_storage => 'method'|\&coderef,
219 filter_to_storage => 'method'|\&coderef,
222 This is the method that you need to call to set up a filtered column. It takes
223 exactly two arguments; the first being the column name the second being a hash
224 reference with C<filter_from_storage> and C<filter_to_storage> set to either
225 a method name or a code reference. In either case the filter is invoked as:
227 $result->$filter_specification ($value_to_filter)
229 with C<$filter_specification> being chosen depending on whether the
230 C<$value_to_filter> is being retrieved from or written to permanent
233 If a specific directional filter is not specified, the original value will be
234 passed to/from storage unfiltered.
236 =head2 get_filtered_column
238 $obj->get_filtered_column('colname')
240 Returns the filtered value of the column
242 =head2 set_filtered_column
244 $obj->set_filtered_column(colname => 'new_value')
246 Sets the filtered value of the column
248 =head1 EXAMPLE OF USE
250 Some databases have restrictions on values that can be passed to
251 boolean columns, and problems can be caused by passing value that
252 perl considers to be false (such as C<undef>).
254 One solution to this is to ensure that the boolean values are set
255 to something that the database can handle - such as numeric zero
256 and one, using code like this:-
258 __PACKAGE__->filter_column(
259 my_boolean_column => {
260 filter_to_storage => sub { $_[1] ? 1 : 0 },
264 In this case the C<filter_from_storage> is not required, as just
265 passing the database value through to perl does the right thing.
267 =head1 FURTHER QUESTIONS?
269 Check the list of L<additional DBIC resources|DBIx::Class/GETTING HELP/SUPPORT>.
271 =head1 COPYRIGHT AND LICENSE
273 This module is free software L<copyright|DBIx::Class/COPYRIGHT AND LICENSE>
274 by the L<DBIx::Class (DBIC) authors|DBIx::Class/AUTHORS>. You can
275 redistribute it and/or modify it under the same terms as the
276 L<DBIx::Class library|DBIx::Class/COPYRIGHT AND LICENSE>.