1 package SL::DB::Helper::CustomVariables;
6 use List::Util qw(first);
7 use SL::DB::CustomVariableConfig;
9 use constant META_CVARS => 'cvars_config';
12 my ($class, %params) = @_;
13 my $caller_package = caller;
15 # TODO: if module is empty, module overloading needs to take effect
16 # certain stuff may have more than one overload, odr even more than one type
17 defined $caller_package or croak 'need to be included from a caller reference';
19 $params{module} ||= _calc_modules_from_overloads(%params) if $params{overloads};
20 $params{sub_module} ||= '';
23 $params{module} || $params{sub_module} or croak 'need param module or sub_module';
25 save_meta_info($caller_package, %params);
26 make_cvar_accessor($caller_package, %params);
27 make_cvar_alias($caller_package, %params) if $params{cvars_alias};
28 make_cvar_by_configs($caller_package, %params);
29 make_cvar_by_name($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;
41 sub make_cvar_accessor {
42 my ($caller_package, %params) = @_;
44 my @module_filter = $params{module} ?
45 ("config.module" => $params{module}) :
48 $caller_package->meta->add_relationships(
50 type => 'one to many',
51 class => 'SL::DB::CustomVariable',
52 column_map => { ($params{id} || 'id') => 'trans_id' },
53 manager_args => { with_objects => 'config' },
54 query_args => [ sub_module => $params{sub_module}, @module_filter ],
60 my ($caller_package) = @_;
62 *{ $caller_package . '::cvars' } = sub {
63 goto &{ $caller_package . '::custom_variables' };
67 # this is used for templates where you need to list every applicable config
68 # auto vivifies non existant cvar objects as necessary.
69 sub make_cvar_by_configs {
70 my ($caller_package, %params) = @_;
73 *{ $caller_package . '::cvars_by_config' } = sub {
75 @_ > 1 and croak "not an accessor";
77 my $configs = _all_configs(%params);
78 my $cvars = $self->custom_variables;
79 my %cvars_by_config = map { $_->config_id => $_ } @$cvars;
81 my @return = map { $cvars_by_config{$_->id} || _new_cvar($self, %params, config => $_) } @$configs;
87 # this is used for print templates where you need to refer to a variable by name
88 # TODO typically these were referred as prefix_'cvar'_name
89 sub make_cvar_by_name {
90 my ($caller_package, %params) = @_;
93 *{ $caller_package . '::cvar_by_name' } = sub {
94 my ($self, $name) = @_;
96 my $configs = _all_configs(%params);
97 my $cvars = $self->custom_variables;
98 my $config = first { $_->name eq $name } @$configs;
100 croak "unknown cvar name $name" unless $config;
102 my $cvar = first { $_->config_id eq $config->id } @$cvars;
105 $cvar = _new_cvar($self, %params, config => $config);
106 $self->add_custom_variables($cvar);
116 ? SL::DB::Manager::CustomVariableConfig->get_all(query => [ module => $params{module} ])
117 : SL::DB::Manager::CustomVariableConfig->get_all;
120 sub _overload_by_module {
121 my ($module, %params) = @_;
123 while (my ($fk, $class) = each %{ $params{overloads} }) {
124 return ($fk, $class) if $class->meta->{META_CVARS()}->{module} eq $module;
127 croak "unknown overload, cannot resolve module $module";
131 my ($self, %params) = @_;
133 # check overloading first
134 if ($params{sub_module}) {
135 my ($fk, $class) = _overload_by_module($params{config}->module, %params);
136 my $base_cvar = $class->new(id => $self->$fk)->load->cvar_by_name($params{config}->name);
137 $inherited_value = $base_cvar->value;
140 my $cvar = SL::DB::CustomVariable->new(
141 config => $params{config},
142 trans_id => $self->${ \ $params{id} },
143 sub_module => $params{sub_module},
147 ? $cvar->value($inherited_value)
148 : $cvar->value($params{config}->default_value);
152 sub _calc_modules_from_overloads {
156 while (my ($fk, $class) = each %{ $params{overloads} }) {
157 eval "require $class"; # make sure the class is loaded
158 my $module = $class->meta->{META_CVARS()}->{module};
160 $modules{$module} = 1;
163 return [ keys %modules ];
175 SL::DB::Helper::CustomVariables - Mixin to provide custom variables relations
179 # use in a primary class
180 use SL::DB::Helper::CustomVariables (
185 # use overloading in a secondary class
186 use SL::DB::Helper::CustomVariables (
187 sub_module => 'orderitems',
190 parts_id => 'SL::DB::Part',
196 This module provides methods to deal with named custom variables. Two concepts are understood.
198 =head2 Primary CVar Classes
200 Primary classes are those that feature cvars for themselves. Currently those
201 are Part, Contact, Customer and Vendor. cvars for these will get saved directly
204 =head2 Secondary CVar Classes
206 Secondary classes inherit their cvars from member relationships. This is built
207 so that orders can save a copy of the cvars of their parts, customers and the
208 like to be immutable later on.
210 Secondary classes may currently not have cvars of their own.
212 =head1 INSTALLED METHODS
216 =item C<custom_variables [ CUSTOM_VARIABLES ]>
218 This is a Rose::DB::Object::Relationship accessor, generated for cvars. Use it
219 like any other OneToMany relationship.
221 =item C<cvars [ CUSTOM_VARIABLES ]>
223 Alias to C<custom_variables>. Will only be installed if C<cvars_alias> was
226 =item C<cvars_by_config>
228 Thi will return a list of CVars with the following changes over the standard accessor:
234 The list will be returned in the sorted order of the configs.
238 For every config exactly one CVar will be returned.
242 If no cvar was found for a config, a new one will be vivified, set to the
243 correct config, module etc, and registered into the object.
247 Vivified cvars for secondary classes will first try to find their base object
248 and use that value. If no such value or cvar is found the default value from
253 This is useful if you need to list every possible CVar, like in CRUD masks.
255 =item C<cvar_by_name NAME [ VALUE ]>
257 Returns the CVar object for this object which matches the given internal name.
258 Useful for print templates. If the requested cvar is not present, it will be
259 vivified with the same rules as in C<cvars_by_config>.
265 Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>