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;
 
 152   SL::DB::Manager::CustomVariableConfig->get_all_sorted($params{module} ? (query => [ module => $params{module} ]) : ());
 
 155 sub _overload_by_module {
 
 156   my ($module, %params) = @_;
 
 158   keys %{ $params{overloads} }; # reset each iterator
 
 159   while (my ($fk, $def) = each %{ $params{overloads} }) {
 
 160     return ($fk, $def->{class}) if $def->{module} eq $module;
 
 163   croak "unknown overload, cannot resolve module $module";
 
 167   my ($self, %params) = @_;
 
 169   # check overloading first
 
 170   if ($params{sub_module}) {
 
 171     my ($fk, $class) = _overload_by_module($params{config}->module, %params);
 
 172     my $base_cvar = $class->new(id => $self->$fk)->load->cvar_by_name($params{config}->name);
 
 173     $inherited_value = $base_cvar->value;
 
 176   my $cvar = SL::DB::CustomVariable->new(
 
 177     config     => $params{config},
 
 178     trans_id   => $self->${ \ $params{id} },
 
 179     sub_module => $params{sub_module},
 
 183    ? $cvar->value($inherited_value)
 
 184    : $cvar->value($params{config}->default_value);
 
 188 sub _calc_modules_from_overloads {
 
 192   for my $def (values %{ $params{overloads} || {} }) {
 
 193     $modules{$def->{module}} = 1;
 
 196   return [ keys %modules ];
 
 199 sub _get_primary_key_column {
 
 200   my ($caller_package) = @_;
 
 201   my $meta             = $caller_package->meta;
 
 204   $column_name = $meta->{primary_key}->{columns}->[0] if $meta->{primary_key} && (ref($meta->{primary_key}->{columns}) eq 'ARRAY') && (1 == scalar(@{ $meta->{primary_key}->{columns} }));
 
 206   croak "Unable to retrieve primary key column name: meta information for package $caller_package not set up correctly" unless $column_name;
 
 219 SL::DB::Helper::CustomVariables - Mixin to provide custom variables relations
 
 223   # use in a primary class
 
 224   use SL::DB::Helper::CustomVariables (
 
 229   # use overloading in a secondary class
 
 230   use SL::DB::Helper::CustomVariables (
 
 231     sub_module  => 'orderitems',
 
 235         class => 'SL::DB::Part',
 
 243 This module provides methods to deal with named custom variables. Two concepts are understood.
 
 245 =head2 Primary CVar Classes
 
 247 Primary classes are those that feature cvars for themselves. Currently those
 
 248 are Part, Contact, Customer and Vendor. cvars for these will get saved directly
 
 251 =head2 Secondary CVar Classes
 
 253 Secondary classes inherit their cvars from member relationships. This is built
 
 254 so that orders can save a copy of the cvars of their parts, customers and the
 
 255 like to be immutable later on.
 
 257 Secondary classes may currently not have cvars of their own.
 
 259 =head1 INSTALLED METHODS
 
 263 =item C<custom_variables [ CUSTOM_VARIABLES ]>
 
 265 This is a Rose::DB::Object::Relationship accessor, generated for cvars. Use it
 
 266 like any other OneToMany relationship.
 
 268 =item C<cvars [ CUSTOM_VARIABLES ]>
 
 270 Alias to C<custom_variables>. Will only be installed if C<cvars_alias> was
 
 273 =item C<cvars_by_config>
 
 275 Thi will return a list of CVars with the following changes over the standard accessor:
 
 281 The list will be returned in the sorted order of the configs.
 
 285 For every config exactly one CVar will be returned.
 
 289 If no cvar was found for a config, a new one will be vivified, set to the
 
 290 correct config, module etc, and registered into the object.
 
 294 Vivified cvars for secondary classes will first try to find their base object
 
 295 and use that value. If no such value or cvar is found the default value from
 
 300 This is useful if you need to list every possible CVar, like in CRUD masks.
 
 302 =item C<cvar_by_name NAME [ VALUE ]>
 
 304 Returns the CVar object for this object which matches the given internal name.
 
 305 Useful for print templates. If the requested cvar is not present, it will be
 
 306 vivified with the same rules as in C<cvars_by_config>.
 
 312 Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>