1 package SL::DB::Helper::CustomVariables;
6 use List::Util qw(first);
8 use constant META_CVARS => 'cvars_config';
11 my ($class, %params) = @_;
12 my $caller_package = caller;
14 # TODO: if module is empty, module overloading needs to take effect
15 # certain stuff may have more than one overload, odr even more than one type
16 defined $caller_package or croak 'need to be included from a caller reference';
18 $params{module} ||= _calc_modules_from_overloads(%params) if $params{overloads};
19 $params{sub_module} ||= '';
20 $params{id} ||= _get_primary_key_column($caller_package);
22 $params{module} || $params{sub_module} or croak 'need param module or sub_module';
24 return unless save_meta_info($caller_package, %params);
25 make_cvar_accessor($caller_package, %params);
26 make_cvar_alias($caller_package, %params) if $params{cvars_alias};
27 make_cvar_by_configs($caller_package, %params);
28 make_cvar_by_name($caller_package, %params);
29 make_cvar_as_hashref($caller_package, %params);
33 my ($caller_package, %params) = @_;
35 my $meta = $caller_package->meta;
36 return 0 if $meta->{META_CVARS()};
38 $meta->{META_CVARS()} = \%params;
43 sub make_cvar_accessor {
44 my ($caller_package, %params) = @_;
46 my $modules = ('ARRAY' eq ref $params{module}) ?
47 join ',', @{ $params{module} } :
49 my @module_filter = $modules ?
50 ("config_id" => [ \"(SELECT custom_variable_configs.id FROM custom_variable_configs WHERE custom_variable_configs.module IN ( '$modules' ))" ]) : # " make emacs happy
53 $caller_package->meta->add_relationships(
55 type => 'one to many',
56 class => 'SL::DB::CustomVariable',
57 column_map => { $params{id} => 'trans_id' },
58 query_args => [ sub_module => $params{sub_module}, @module_filter ],
64 my ($caller_package) = @_;
66 *{ $caller_package . '::cvars' } = sub {
67 goto &{ $caller_package . '::custom_variables' };
71 # this is used for templates where you need to list every applicable config
72 # auto vivifies non existent cvar objects as necessary.
73 sub make_cvar_by_configs {
74 my ($caller_package, %params) = @_;
77 *{ $caller_package . '::cvars_by_config' } = sub {
79 @_ > 1 and croak "not an accessor";
81 my $configs = _all_configs(%params);
82 my $cvars = $self->custom_variables;
83 my %cvars_by_config = map { $_->config_id => $_ } @$cvars;
87 if ( $cvars_by_config{$_->id} ) {
88 $cvars_by_config{$_->id};
91 my $cvar = _new_cvar($self, %params, config => $_);
92 $self->add_custom_variables($cvar);
103 # this is used for print templates where you need to refer to a variable by name
104 # TODO typically these were referred as prefix_'cvar'_name
105 sub make_cvar_by_name {
106 my ($caller_package, %params) = @_;
109 *{ $caller_package . '::cvar_by_name' } = sub {
110 my ($self, $name) = @_;
112 my $configs = _all_configs(%params);
113 my $cvars = $self->custom_variables;
114 my $config = first { $_->name eq $name } @$configs;
116 croak "unknown cvar name $name" unless $config;
118 my $cvar = first { $_->config_id eq $config->id } @$cvars;
121 $cvar = _new_cvar($self, %params, config => $config);
122 $self->add_custom_variables($cvar);
129 sub make_cvar_as_hashref {
130 my ($caller_package, %params) = @_;
133 *{ $caller_package . '::cvar_as_hashref' } = sub {
135 @_ > 1 and croak "not an accessor";
137 my $cvars_by_config = $self->cvars_by_config;
140 $_->config->name => { value => $_->value_as_text, is_valid => $_->is_valid }
150 require SL::DB::CustomVariableConfig;
153 ? SL::DB::Manager::CustomVariableConfig->get_all(query => [ module => $params{module} ])
154 : SL::DB::Manager::CustomVariableConfig->get_all;
157 sub _overload_by_module {
158 my ($module, %params) = @_;
160 keys %{ $params{overloads} }; # reset each iterator
161 while (my ($fk, $def) = each %{ $params{overloads} }) {
162 return ($fk, $def->{class}) if $def->{module} eq $module;
165 croak "unknown overload, cannot resolve module $module";
169 my ($self, %params) = @_;
171 # check overloading first
172 if ($params{sub_module}) {
173 my ($fk, $class) = _overload_by_module($params{config}->module, %params);
174 my $base_cvar = $class->new(id => $self->$fk)->load->cvar_by_name($params{config}->name);
175 $inherited_value = $base_cvar->value;
178 my $cvar = SL::DB::CustomVariable->new(
179 config => $params{config},
180 trans_id => $self->${ \ $params{id} },
181 sub_module => $params{sub_module},
185 ? $cvar->value($inherited_value)
186 : $cvar->value($params{config}->default_value);
190 sub _calc_modules_from_overloads {
194 for my $def (values %{ $params{overloads} || {} }) {
195 $modules{$def->{module}} = 1;
198 return [ keys %modules ];
201 sub _get_primary_key_column {
202 my ($caller_package) = @_;
203 my $meta = $caller_package->meta;
206 $column_name = $meta->{primary_key}->{columns}->[0] if $meta->{primary_key} && (ref($meta->{primary_key}->{columns}) eq 'ARRAY') && (1 == scalar(@{ $meta->{primary_key}->{columns} }));
208 croak "Unable to retrieve primary key column name: meta information for package $caller_package not set up correctly" unless $column_name;
221 SL::DB::Helper::CustomVariables - Mixin to provide custom variables relations
225 # use in a primary class
226 use SL::DB::Helper::CustomVariables (
231 # use overloading in a secondary class
232 use SL::DB::Helper::CustomVariables (
233 sub_module => 'orderitems',
237 class => 'SL::DB::Part',
245 This module provides methods to deal with named custom variables. Two concepts are understood.
247 =head2 Primary CVar Classes
249 Primary classes are those that feature cvars for themselves. Currently those
250 are Part, Contact, Customer and Vendor. cvars for these will get saved directly
253 =head2 Secondary CVar Classes
255 Secondary classes inherit their cvars from member relationships. This is built
256 so that orders can save a copy of the cvars of their parts, customers and the
257 like to be immutable later on.
259 Secondary classes may currently not have cvars of their own.
261 =head1 INSTALLED METHODS
265 =item C<custom_variables [ CUSTOM_VARIABLES ]>
267 This is a Rose::DB::Object::Relationship accessor, generated for cvars. Use it
268 like any other OneToMany relationship.
270 =item C<cvars [ CUSTOM_VARIABLES ]>
272 Alias to C<custom_variables>. Will only be installed if C<cvars_alias> was
275 =item C<cvars_by_config>
277 Thi will return a list of CVars with the following changes over the standard accessor:
283 The list will be returned in the sorted order of the configs.
287 For every config exactly one CVar will be returned.
291 If no cvar was found for a config, a new one will be vivified, set to the
292 correct config, module etc, and registered into the object.
296 Vivified cvars for secondary classes will first try to find their base object
297 and use that value. If no such value or cvar is found the default value from
302 This is useful if you need to list every possible CVar, like in CRUD masks.
304 =item C<cvar_by_name NAME [ VALUE ]>
306 Returns the CVar object for this object which matches the given internal name.
307 Useful for print templates. If the requested cvar is not present, it will be
308 vivified with the same rules as in C<cvars_by_config>.
314 Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>