Commit | Line | Data |
51bec050 |
1 | package DBIx::Class::FilterColumn; |
2 | |
3 | use strict; |
4 | use warnings; |
5 | |
6 | use base qw/DBIx::Class::Row/; |
7 | |
8 | sub filter_column { |
9 | my ($self, $col, $attrs) = @_; |
10 | |
11 | $self->throw_exception("No such column $col to filter") |
12 | unless $self->has_column($col); |
13 | |
14 | $self->throw_exception("filter_column needs attr hashref") |
15 | unless ref $attrs eq 'HASH'; |
16 | |
17 | $self->column_info($col)->{_filter_info} = $attrs; |
18 | my $acc = $self->column_info($col)->{accessor}; |
d7d38bef |
19 | $self->mk_group_accessors(filtered_column => [ (defined $acc ? $acc : $col), $col]); |
51bec050 |
20 | return 1; |
21 | } |
22 | |
4c0c3038 |
23 | sub _column_from_storage { |
d9ea6d6d |
24 | my ($self, $source, $col, $value) = @_; |
51bec050 |
25 | |
26 | return $value unless defined $value; |
27 | |
28 | my $info = $self->column_info($col) |
29 | or $self->throw_exception("No column info for $col"); |
30 | |
31 | return $value unless exists $info->{_filter_info}; |
32 | |
d7d38bef |
33 | my $filter = $info->{_filter_info}{filter_from_storage}; |
51bec050 |
34 | $self->throw_exception("No inflator for $col") unless defined $filter; |
35 | |
d9ea6d6d |
36 | return $source->$filter($value); |
51bec050 |
37 | } |
38 | |
4c0c3038 |
39 | sub _column_to_storage { |
d9ea6d6d |
40 | my ($self, $source, $col, $value) = @_; |
51bec050 |
41 | |
42 | my $info = $self->column_info($col) or |
43 | $self->throw_exception("No column info for $col"); |
44 | |
45 | return $value unless exists $info->{_filter_info}; |
46 | |
d7d38bef |
47 | my $unfilter = $info->{_filter_info}{filter_to_storage}; |
51bec050 |
48 | $self->throw_exception("No unfilter for $col") unless defined $unfilter; |
d9ea6d6d |
49 | return $source->$unfilter($value); |
51bec050 |
50 | } |
51 | |
d7d38bef |
52 | sub get_filtered_column { |
51bec050 |
53 | my ($self, $col) = @_; |
54 | |
55 | $self->throw_exception("$col is not a filtered column") |
56 | unless exists $self->column_info($col)->{_filter_info}; |
57 | |
58 | return $self->{_filtered_column}{$col} |
59 | if exists $self->{_filtered_column}{$col}; |
60 | |
61 | my $val = $self->get_column($col); |
62 | |
d9ea6d6d |
63 | return $self->{_filtered_column}{$col} = $self->_column_from_storage($self->result_source, $col, $val); |
51bec050 |
64 | } |
65 | |
d7d38bef |
66 | sub set_filtered_column { |
51bec050 |
67 | my ($self, $col, $filtered) = @_; |
68 | |
d9ea6d6d |
69 | $self->set_column($col, $self->_column_to_storage($self->result_source, $col, $filtered)); |
51bec050 |
70 | |
71 | delete $self->{_filtered_column}{$col}; |
72 | |
73 | return $filtered; |
74 | } |
75 | |
7b461f8a |
76 | sub update { |
77 | my ($self, $attrs, @rest) = @_; |
78 | foreach my $key (keys %{$attrs||{}}) { |
79 | if ($self->has_column($key) && |
80 | exists $self->column_info($key)->{_filter_info}) { |
81 | my $val = delete $attrs->{$key}; |
d7d38bef |
82 | $self->set_filtered_column($key, $val); |
d9ea6d6d |
83 | $attrs->{$key} = $self->_column_to_storage($self->result_source, $key, $val) |
7b461f8a |
84 | } |
85 | } |
86 | return $self->next::method($attrs, @rest); |
87 | } |
88 | |
89 | |
90 | sub new { |
91 | my ($class, $attrs, @rest) = @_; |
d9ea6d6d |
92 | my $source = delete $attrs->{-result_source} |
93 | or $class->throw_exception('Sourceless rows are not supported with DBIx::Class::FilterColumn'); |
94 | |
7b461f8a |
95 | foreach my $key (keys %{$attrs||{}}) { |
96 | if ($class->has_column($key) && |
97 | exists $class->column_info($key)->{_filter_info} ) { |
d9ea6d6d |
98 | $attrs->{$key} = $class->_column_to_storage($source, $key, delete $attrs->{$key}) |
7b461f8a |
99 | } |
100 | } |
101 | my $obj = $class->next::method($attrs, @rest); |
102 | return $obj; |
103 | } |
104 | |
51bec050 |
105 | 1; |
22d9e05a |
106 | |
107 | =head1 THE ONE TRUE WAY |
108 | |
109 | package My::Reusable::Filter; |
110 | |
111 | sub to_pennies { $_[1] * 100 } |
112 | sub from_pennies { $_[1] / 100 } |
113 | |
114 | 1; |
115 | |
116 | package My::Schema::Result::Account; |
117 | |
118 | use strict; |
119 | use warnings; |
120 | |
121 | use base 'DBIx::Class::Core'; |
122 | |
123 | __PACKAGE->load_components('FilterColumn'); |
124 | |
125 | __PACKAGE__->add_columns( |
126 | id => { |
127 | data_type => 'int', |
128 | is_auto_increment => 1, |
129 | }, |
130 | total_money => { |
131 | data_type => 'int', |
132 | }, |
133 | ); |
134 | |
135 | __PACKAGE__->set_primary_key('id'); |
136 | |
137 | __PACKAGE__->filter_column(total_money => { |
138 | filter_to_storage => 'to_pennies', |
139 | filter_from_storage => 'from_pennies', |
140 | }); |
141 | |
142 | 1; |
143 | |