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   return unless 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;
 
  43 sub make_cvar_accessor {
 
  44   my ($caller_package, %params) = @_;
 
  46   my @module_filter = $params{module} ?
 
  47     ("config.module" => $params{module}) :
 
  50   $caller_package->meta->add_relationships(
 
  52       type         => 'one to many',
 
  53       class        => 'SL::DB::CustomVariable',
 
  54       column_map   => { ($params{id} || 'id') => 'trans_id' },
 
  55       manager_args => { with_objects => 'config' },
 
  56       query_args   => [ sub_module => $params{sub_module}, @module_filter ],
 
  62   my ($caller_package) = @_;
 
  64   *{ $caller_package . '::cvars' } =  sub {
 
  65     goto &{ $caller_package . '::custom_variables' };
 
  69 # this is used for templates where you need to list every applicable config
 
  70 # auto vivifies non existent cvar objects as necessary.
 
  71 sub make_cvar_by_configs {
 
  72   my ($caller_package, %params) = @_;
 
  75   *{ $caller_package . '::cvars_by_config' } = sub {
 
  77     @_ > 1 and croak "not an accessor";
 
  79     my $configs     = _all_configs(%params);
 
  80     my $cvars       = $self->custom_variables;
 
  81     my %cvars_by_config = map { $_->config_id => $_ } @$cvars;
 
  83     my @return  = map { $cvars_by_config{$_->id} || _new_cvar($self, %params, config => $_) } @$configs;
 
  89 # this is used for print templates where you need to refer to a variable by name
 
  90 # TODO typically these were referred as prefix_'cvar'_name
 
  91 sub make_cvar_by_name {
 
  92   my ($caller_package, %params) = @_;
 
  95   *{ $caller_package . '::cvar_by_name' } = sub {
 
  96     my ($self, $name) = @_;
 
  98     my $configs = _all_configs(%params);
 
  99     my $cvars   = $self->custom_variables;
 
 100     my $config  = first { $_->name eq $name } @$configs;
 
 102     croak "unknown cvar name $name" unless $config;
 
 104     my $cvar    = first { $_->config_id eq $config->id } @$cvars;
 
 107       $cvar = _new_cvar($self, %params, config => $config);
 
 108       $self->add_custom_variables($cvar);
 
 118     ? SL::DB::Manager::CustomVariableConfig->get_all(query => [ module => $params{module} ])
 
 119     : SL::DB::Manager::CustomVariableConfig->get_all;
 
 122 sub _overload_by_module {
 
 123   my ($module, %params) = @_;
 
 125   keys %{ $params{overloads} }; # reset each iterator
 
 126   while (my ($fk, $class) = each %{ $params{overloads} }) {
 
 127     return ($fk, $class) if $class->meta->{META_CVARS()}->{module} eq $module;
 
 130   croak "unknown overload, cannot resolve module $module";
 
 134   my ($self, %params) = @_;
 
 136   # check overloading first
 
 137   if ($params{sub_module}) {
 
 138     my ($fk, $class) = _overload_by_module($params{config}->module, %params);
 
 139     my $base_cvar = $class->new(id => $self->$fk)->load->cvar_by_name($params{config}->name);
 
 140     $inherited_value = $base_cvar->value;
 
 143   my $cvar = SL::DB::CustomVariable->new(
 
 144     config     => $params{config},
 
 145     trans_id   => $self->${ \ $params{id} },
 
 146     sub_module => $params{sub_module},
 
 150    ? $cvar->value($inherited_value)
 
 151    : $cvar->value($params{config}->default_value);
 
 155 sub _calc_modules_from_overloads {
 
 159   while (my ($fk, $class) = each %{ $params{overloads} }) {
 
 160     eval "require $class"; # make sure the class is loaded
 
 161     my $module = $class->meta->{META_CVARS()}->{module};
 
 163     $modules{$module} = 1;
 
 166   return [ keys %modules ];
 
 178 SL::DB::Helper::CustomVariables - Mixin to provide custom variables relations
 
 182   # use in a primary class
 
 183   use SL::DB::Helper::CustomVariables (
 
 188   # use overloading in a secondary class
 
 189   use SL::DB::Helper::CustomVariables (
 
 190     sub_module  => 'orderitems',
 
 193       parts_id    => 'SL::DB::Part',
 
 199 This module provides methods to deal with named custom variables. Two concepts are understood.
 
 201 =head2 Primary CVar Classes
 
 203 Primary classes are those that feature cvars for themselves. Currently those
 
 204 are Part, Contact, Customer and Vendor. cvars for these will get saved directly
 
 207 =head2 Secondary CVar Classes
 
 209 Secondary classes inherit their cvars from member relationships. This is built
 
 210 so that orders can save a copy of the cvars of their parts, customers and the
 
 211 like to be immutable later on.
 
 213 Secondary classes may currently not have cvars of their own.
 
 215 =head1 INSTALLED METHODS
 
 219 =item C<custom_variables [ CUSTOM_VARIABLES ]>
 
 221 This is a Rose::DB::Object::Relationship accessor, generated for cvars. Use it
 
 222 like any other OneToMany relationship.
 
 224 =item C<cvars [ CUSTOM_VARIABLES ]>
 
 226 Alias to C<custom_variables>. Will only be installed if C<cvars_alias> was
 
 229 =item C<cvars_by_config>
 
 231 Thi will return a list of CVars with the following changes over the standard accessor:
 
 237 The list will be returned in the sorted order of the configs.
 
 241 For every config exactly one CVar will be returned.
 
 245 If no cvar was found for a config, a new one will be vivified, set to the
 
 246 correct config, module etc, and registered into the object.
 
 250 Vivified cvars for secondary classes will first try to find their base object
 
 251 and use that value. If no such value or cvar is found the default value from
 
 256 This is useful if you need to list every possible CVar, like in CRUD masks.
 
 258 =item C<cvar_by_name NAME [ VALUE ]>
 
 260 Returns the CVar object for this object which matches the given internal name.
 
 261 Useful for print templates. If the requested cvar is not present, it will be
 
 262 vivified with the same rules as in C<cvars_by_config>.
 
 268 Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>