1 package DBIx::Class::FilterColumn;
5 use base qw/DBIx::Class::Row/;
8 my ($self, $col, $attrs) = @_;
10 my $colinfo = $self->column_info($col);
12 $self->throw_exception("FilterColumn can not be used on a column with a declared InflateColumn inflator")
13 if defined $colinfo->{_inflate_info} and $self->isa('DBIx::Class::InflateColumn');
15 $self->throw_exception("No such column $col to filter")
16 unless $self->has_column($col);
18 $self->throw_exception('filter_column expects a hashref of filter specifications')
19 unless ref $attrs eq 'HASH';
21 $self->throw_exception('An invocation of filter_column() must specify either a filter_from_storage or filter_to_storage')
22 unless $attrs->{filter_from_storage} || $attrs->{filter_to_storage};
24 $colinfo->{_filter_info} = $attrs;
25 my $acc = $colinfo->{accessor};
26 $self->mk_group_accessors(filtered_column => [ (defined $acc ? $acc : $col), $col]);
30 sub _column_from_storage {
31 my ($self, $col, $value) = @_;
33 return $value unless defined $value;
35 my $info = $self->column_info($col)
36 or $self->throw_exception("No column info for $col");
38 return $value unless exists $info->{_filter_info};
40 my $filter = $info->{_filter_info}{filter_from_storage};
42 return defined $filter ? $self->$filter($value) : $value;
45 sub _column_to_storage {
46 my ($self, $col, $value) = @_;
48 my $info = $self->column_info($col) or
49 $self->throw_exception("No column info for $col");
51 return $value unless exists $info->{_filter_info};
53 my $unfilter = $info->{_filter_info}{filter_to_storage};
55 return defined $unfilter ? $self->$unfilter($value) : $value;
58 sub get_filtered_column {
59 my ($self, $col) = @_;
61 $self->throw_exception("$col is not a filtered column")
62 unless exists $self->column_info($col)->{_filter_info};
64 return $self->{_filtered_column}{$col}
65 if exists $self->{_filtered_column}{$col};
67 my $val = $self->get_column($col);
69 return $self->{_filtered_column}{$col} = $self->_column_from_storage($col, $val);
73 my ($self, $col) = @_;
74 if (exists $self->{_filtered_column}{$col}) {
75 return $self->{_column_data}{$col} ||= $self->_column_to_storage ($col, $self->{_filtered_column}{$col});
78 return $self->next::method ($col);
81 # sadly a separate codepath in Row.pm ( used by insert() )
85 foreach my $col (keys %{$self->{_filtered_column}||{}}) {
86 $self->{_column_data}{$col} ||= $self->_column_to_storage ($col, $self->{_filtered_column}{$col})
87 if exists $self->{_filtered_column}{$col};
90 $self->next::method (@_);
94 my ($self, $col) = (shift, @_);
97 delete $self->{_filtered_column}{$col};
99 $self->next::method(@_);
102 sub set_filtered_column {
103 my ($self, $col, $filtered) = @_;
105 # do not blow up the cache via set_column unless necessary
106 # (filtering may be expensive!)
107 if (exists $self->{_filtered_column}{$col}) {
109 if ($self->_eq_column_values ($col, $filtered, $self->{_filtered_column}{$col} ) );
111 $self->make_column_dirty ($col); # so the comparison won't run again
114 $self->set_column($col, $self->_column_to_storage($col, $filtered));
116 return $self->{_filtered_column}{$col} = $filtered;
120 my ($self, $attrs, @rest) = @_;
122 foreach my $key (keys %{$attrs||{}}) {
124 $self->has_column($key)
126 exists $self->column_info($key)->{_filter_info}
128 $self->set_filtered_column($key, delete $attrs->{$key});
130 # FIXME update() reaches directly into the object-hash
131 # and we may *not* have a filtered value there - thus
132 # the void-ctx filter-trigger
133 $self->get_column($key) unless exists $self->{_column_data}{$key};
137 return $self->next::method($attrs, @rest);
141 my ($class, $attrs, @rest) = @_;
142 my $source = $attrs->{-result_source}
143 or $class->throw_exception('Sourceless rows are not supported with DBIx::Class::FilterColumn');
145 my $obj = $class->next::method($attrs, @rest);
146 foreach my $key (keys %{$attrs||{}}) {
147 if ($obj->has_column($key) &&
148 exists $obj->column_info($key)->{_filter_info} ) {
149 $obj->set_filtered_column($key, $attrs->{$key});
160 DBIx::Class::FilterColumn - Automatically convert column data
164 In your Schema or DB class add "FilterColumn" to the top of the component list.
166 __PACKAGE__->load_components(qw( FilterColumn ... ));
168 Set up filters for the columns you want to convert.
170 __PACKAGE__->filter_column( money => {
171 filter_to_storage => 'to_pennies',
172 filter_from_storage => 'from_pennies',
175 sub to_pennies { $_[1] * 100 }
177 sub from_pennies { $_[1] / 100 }
184 This component is meant to be a more powerful, but less DWIM-y,
185 L<DBIx::Class::InflateColumn>. One of the major issues with said component is
186 that it B<only> works with references. Generally speaking anything that can
187 be done with L<DBIx::Class::InflateColumn> can be done with this component.
193 __PACKAGE__->filter_column( colname => {
194 filter_from_storage => 'method'|\&coderef,
195 filter_to_storage => 'method'|\&coderef,
198 This is the method that you need to call to set up a filtered column. It takes
199 exactly two arguments; the first being the column name the second being a hash
200 reference with C<filter_from_storage> and C<filter_to_storage> set to either
201 a method name or a code reference. In either case the filter is invoked as:
203 $result->$filter_specification ($value_to_filter)
205 with C<$filter_specification> being chosen depending on whether the
206 C<$value_to_filter> is being retrieved from or written to permanent
209 If a specific directional filter is not specified, the original value will be
210 passed to/from storage unfiltered.
212 =head2 get_filtered_column
214 $obj->get_filtered_column('colname')
216 Returns the filtered value of the column
218 =head2 set_filtered_column
220 $obj->set_filtered_column(colname => 'new_value')
222 Sets the filtered value of the column
224 =head1 EXAMPLE OF USE
226 Some databases have restrictions on values that can be passed to
227 boolean columns, and problems can be caused by passing value that
228 perl considers to be false (such as C<undef>).
230 One solution to this is to ensure that the boolean values are set
231 to something that the database can handle - such as numeric zero
232 and one, using code like this:-
234 __PACKAGE__->filter_column(
235 my_boolean_column => {
236 filter_to_storage => sub { $_[1] ? 1 : 0 },
240 In this case the C<filter_from_storage> is not required, as just
241 passing the database value through to perl does the right thing.