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(
75 my ($self, $col) = @_;
77 if (exists $self->{_filtered_column}{$col}) {
78 return $self->{_column_data}{$col} ||= $self->_column_to_storage (
79 $col, $self->{_filtered_column}{$col}
83 return $self->next::method ($col);
86 # sadly a separate codepath in Row.pm ( used by insert() )
90 $self->{_column_data}{$_} = $self->_column_to_storage (
91 $_, $self->{_filtered_column}{$_}
93 { ! exists $self->{_column_data}{$_} }
94 keys %{$self->{_filtered_column}||{}}
97 $self->next::method (@_);
101 my ($self, $col) = (shift, @_);
104 delete $self->{_filtered_column}{$col};
106 $self->next::method(@_);
109 sub has_column_loaded {
110 my ($self, $col) = @_;
111 return 1 if exists $self->{_filtered_column}{$col};
112 return $self->next::method($col);
115 sub set_filtered_column {
116 my ($self, $col, $filtered) = @_;
118 # unlike IC, FC does not need to deal with the 'filter' abomination
119 # thus we can short-curcuit filtering entirely and never call set_column
120 # in case this is already a dirty change OR the row never touched storage
124 $self->is_column_changed($col)
126 $self->make_column_dirty($col);
127 delete $self->{_column_data}{$col};
130 $self->set_column($col, $self->_column_to_storage($col, $filtered));
133 return $self->{_filtered_column}{$col} = $filtered;
137 my ($self, $data, @rest) = @_;
139 foreach my $col (keys %{$data||{}}) {
141 $self->has_column($col)
143 exists $self->column_info($col)->{_filter_info}
145 $self->set_filtered_column($col, delete $data->{$col});
147 # FIXME update() reaches directly into the object-hash
148 # and we may *not* have a filtered value there - thus
149 # the void-ctx filter-trigger
150 $self->get_column($col) unless exists $self->{_column_data}{$col};
154 return $self->next::method($data, @rest);
158 my ($class, $data, @rest) = @_;
159 my $source = $data->{-result_source}
160 or $class->throw_exception('Sourceless rows are not supported with DBIx::Class::FilterColumn');
162 my $obj = $class->next::method($data, @rest);
164 foreach my $col (keys %{$data||{}}) {
165 if ($obj->has_column($col) &&
166 exists $obj->column_info($col)->{_filter_info} ) {
167 $obj->set_filtered_column($col, $data->{$col});
178 DBIx::Class::FilterColumn - Automatically convert column data
182 In your Schema or DB class add "FilterColumn" to the top of the component list.
184 __PACKAGE__->load_components(qw( FilterColumn ... ));
186 Set up filters for the columns you want to convert.
188 __PACKAGE__->filter_column( money => {
189 filter_to_storage => 'to_pennies',
190 filter_from_storage => 'from_pennies',
193 sub to_pennies { $_[1] * 100 }
195 sub from_pennies { $_[1] / 100 }
202 This component is meant to be a more powerful, but less DWIM-y,
203 L<DBIx::Class::InflateColumn>. One of the major issues with said component is
204 that it B<only> works with references. Generally speaking anything that can
205 be done with L<DBIx::Class::InflateColumn> can be done with this component.
211 __PACKAGE__->filter_column( colname => {
212 filter_from_storage => 'method'|\&coderef,
213 filter_to_storage => 'method'|\&coderef,
216 This is the method that you need to call to set up a filtered column. It takes
217 exactly two arguments; the first being the column name the second being a hash
218 reference with C<filter_from_storage> and C<filter_to_storage> set to either
219 a method name or a code reference. In either case the filter is invoked as:
221 $result->$filter_specification ($value_to_filter)
223 with C<$filter_specification> being chosen depending on whether the
224 C<$value_to_filter> is being retrieved from or written to permanent
227 If a specific directional filter is not specified, the original value will be
228 passed to/from storage unfiltered.
230 =head2 get_filtered_column
232 $obj->get_filtered_column('colname')
234 Returns the filtered value of the column
236 =head2 set_filtered_column
238 $obj->set_filtered_column(colname => 'new_value')
240 Sets the filtered value of the column
242 =head1 EXAMPLE OF USE
244 Some databases have restrictions on values that can be passed to
245 boolean columns, and problems can be caused by passing value that
246 perl considers to be false (such as C<undef>).
248 One solution to this is to ensure that the boolean values are set
249 to something that the database can handle - such as numeric zero
250 and one, using code like this:-
252 __PACKAGE__->filter_column(
253 my_boolean_column => {
254 filter_to_storage => sub { $_[1] ? 1 : 0 },
258 In this case the C<filter_from_storage> is not required, as just
259 passing the database value through to perl does the right thing.