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->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->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->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 foreach my $col (keys %{$data||{}}) {
145 $self->has_column($col)
147 exists $self->column_info($col)->{_filter_info}
149 $self->set_filtered_column($col, delete $data->{$col});
151 # FIXME update() reaches directly into the object-hash
152 # and we may *not* have a filtered value there - thus
153 # the void-ctx filter-trigger
154 $self->get_column($col) unless exists $self->{_column_data}{$col};
158 return $self->next::method($data, @rest);
162 my ($class, $data, @rest) = @_;
163 my $source = $data->{-result_source}
164 or $class->throw_exception('Sourceless rows are not supported with DBIx::Class::FilterColumn');
166 my $obj = $class->next::method($data, @rest);
168 foreach my $col (keys %{$data||{}}) {
169 if ($obj->has_column($col) &&
170 exists $obj->column_info($col)->{_filter_info} ) {
171 $obj->set_filtered_column($col, $data->{$col});
182 DBIx::Class::FilterColumn - Automatically convert column data
186 In your Schema or DB class add "FilterColumn" to the top of the component list.
188 __PACKAGE__->load_components(qw( FilterColumn ... ));
190 Set up filters for the columns you want to convert.
192 __PACKAGE__->filter_column( money => {
193 filter_to_storage => 'to_pennies',
194 filter_from_storage => 'from_pennies',
197 sub to_pennies { $_[1] * 100 }
199 sub from_pennies { $_[1] / 100 }
206 This component is meant to be a more powerful, but less DWIM-y,
207 L<DBIx::Class::InflateColumn>. One of the major issues with said component is
208 that it B<only> works with references. Generally speaking anything that can
209 be done with L<DBIx::Class::InflateColumn> can be done with this component.
215 __PACKAGE__->filter_column( colname => {
216 filter_from_storage => 'method'|\&coderef,
217 filter_to_storage => 'method'|\&coderef,
220 This is the method that you need to call to set up a filtered column. It takes
221 exactly two arguments; the first being the column name the second being a hash
222 reference with C<filter_from_storage> and C<filter_to_storage> set to either
223 a method name or a code reference. In either case the filter is invoked as:
225 $result->$filter_specification ($value_to_filter)
227 with C<$filter_specification> being chosen depending on whether the
228 C<$value_to_filter> is being retrieved from or written to permanent
231 If a specific directional filter is not specified, the original value will be
232 passed to/from storage unfiltered.
234 =head2 get_filtered_column
236 $obj->get_filtered_column('colname')
238 Returns the filtered value of the column
240 =head2 set_filtered_column
242 $obj->set_filtered_column(colname => 'new_value')
244 Sets the filtered value of the column
246 =head1 EXAMPLE OF USE
248 Some databases have restrictions on values that can be passed to
249 boolean columns, and problems can be caused by passing value that
250 perl considers to be false (such as C<undef>).
252 One solution to this is to ensure that the boolean values are set
253 to something that the database can handle - such as numeric zero
254 and one, using code like this:-
256 __PACKAGE__->filter_column(
257 my_boolean_column => {
258 filter_to_storage => sub { $_[1] ? 1 : 0 },
262 In this case the C<filter_from_storage> is not required, as just
263 passing the database value through to perl does the right thing.