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) = @_;
76 if (exists $self->{_filtered_column}{$col}) {
77 return $self->{_column_data}{$col} ||= $self->_column_to_storage (
78 $col, $self->{_filtered_column}{$col}
82 return $self->next::method ($col);
85 # sadly a separate codepath in Row.pm ( used by insert() )
89 foreach my $col (keys %{$self->{_filtered_column}||{}}) {
90 $self->{_column_data}{$col} ||= $self->_column_to_storage (
91 $col, $self->{_filtered_column}{$col}
92 ) if exists $self->{_filtered_column}{$col};
95 $self->next::method (@_);
99 my ($self, $col) = (shift, @_);
102 delete $self->{_filtered_column}{$col};
104 $self->next::method(@_);
107 sub set_filtered_column {
108 my ($self, $col, $filtered) = @_;
110 # do not blow up the cache via set_column unless necessary
111 # (filtering may be expensive!)
112 if (exists $self->{_filtered_column}{$col}) {
114 if ($self->_eq_column_values ($col, $filtered, $self->{_filtered_column}{$col} ) );
116 $self->make_column_dirty ($col); # so the comparison won't run again
119 $self->set_column($col, $self->_column_to_storage($col, $filtered));
121 return $self->{_filtered_column}{$col} = $filtered;
125 my ($self, $data, @rest) = @_;
127 foreach my $col (keys %{$data||{}}) {
129 $self->has_column($col)
131 exists $self->column_info($col)->{_filter_info}
133 $self->set_filtered_column($col, delete $data->{$col});
135 # FIXME update() reaches directly into the object-hash
136 # and we may *not* have a filtered value there - thus
137 # the void-ctx filter-trigger
138 $self->get_column($col) unless exists $self->{_column_data}{$col};
142 return $self->next::method($data, @rest);
146 my ($class, $data, @rest) = @_;
147 my $source = $data->{-result_source}
148 or $class->throw_exception('Sourceless rows are not supported with DBIx::Class::FilterColumn');
150 my $obj = $class->next::method($data, @rest);
152 foreach my $col (keys %{$data||{}}) {
153 if ($obj->has_column($col) &&
154 exists $obj->column_info($col)->{_filter_info} ) {
155 $obj->set_filtered_column($col, $data->{$col});
166 DBIx::Class::FilterColumn - Automatically convert column data
170 In your Schema or DB class add "FilterColumn" to the top of the component list.
172 __PACKAGE__->load_components(qw( FilterColumn ... ));
174 Set up filters for the columns you want to convert.
176 __PACKAGE__->filter_column( money => {
177 filter_to_storage => 'to_pennies',
178 filter_from_storage => 'from_pennies',
181 sub to_pennies { $_[1] * 100 }
183 sub from_pennies { $_[1] / 100 }
190 This component is meant to be a more powerful, but less DWIM-y,
191 L<DBIx::Class::InflateColumn>. One of the major issues with said component is
192 that it B<only> works with references. Generally speaking anything that can
193 be done with L<DBIx::Class::InflateColumn> can be done with this component.
199 __PACKAGE__->filter_column( colname => {
200 filter_from_storage => 'method'|\&coderef,
201 filter_to_storage => 'method'|\&coderef,
204 This is the method that you need to call to set up a filtered column. It takes
205 exactly two arguments; the first being the column name the second being a hash
206 reference with C<filter_from_storage> and C<filter_to_storage> set to either
207 a method name or a code reference. In either case the filter is invoked as:
209 $result->$filter_specification ($value_to_filter)
211 with C<$filter_specification> being chosen depending on whether the
212 C<$value_to_filter> is being retrieved from or written to permanent
215 If a specific directional filter is not specified, the original value will be
216 passed to/from storage unfiltered.
218 =head2 get_filtered_column
220 $obj->get_filtered_column('colname')
222 Returns the filtered value of the column
224 =head2 set_filtered_column
226 $obj->set_filtered_column(colname => 'new_value')
228 Sets the filtered value of the column
230 =head1 EXAMPLE OF USE
232 Some databases have restrictions on values that can be passed to
233 boolean columns, and problems can be caused by passing value that
234 perl considers to be false (such as C<undef>).
236 One solution to this is to ensure that the boolean values are set
237 to something that the database can handle - such as numeric zero
238 and one, using code like this:-
240 __PACKAGE__->filter_column(
241 my_boolean_column => {
242 filter_to_storage => sub { $_[1] ? 1 : 0 },
246 In this case the C<filter_from_storage> is not required, as just
247 passing the database value through to perl does the right thing.