1 package SL::DB::Object;
 
   6 use English qw(-no_match_vars);
 
   8 use Rose::DB::Object::Constants qw();
 
   9 use List::MoreUtils qw(any pairwise);
 
  12 use SL::DB::Helper::Attr;
 
  13 use SL::DB::Helper::Metadata;
 
  14 use SL::DB::Helper::Manager;
 
  15 use SL::DB::Object::Hooks;
 
  17 use base qw(Rose::DB::Object);
 
  19 my @rose_reserved_methods = qw(
 
  20   db dbh delete DESTROY error init_db _init_db insert load meta meta_class
 
  21   not_found save update import
 
  26   my $self  = $class->SUPER::new();
 
  28   $self->_assign_attributes(@_) if $self;
 
  34   my $class_or_self = shift;
 
  35   my $class         = ref($class_or_self) || $class_or_self;
 
  36   my $type          = $class =~ m/::Auth/ ? 'KIVITENDO_AUTH' : 'KIVITENDO';
 
  38   return SL::DB::create(undef, $type);
 
  42   return 'SL::DB::Helper::Metadata';
 
  45 sub _get_manager_class {
 
  46   my $class_or_self = shift;
 
  47   my $class         = ref($class_or_self) || $class_or_self;
 
  49   return $class->meta->convention_manager->auto_manager_class_name($class);
 
  52 my %text_column_types = (text => 1, char => 1, varchar => 1);
 
  54 sub assign_attributes {
 
  58   my $pk         = ref($self)->meta->primary_key;
 
  59   delete @attributes{$pk->column_names} if $pk;
 
  60   delete @attributes{@rose_reserved_methods};
 
  62   return $self->_assign_attributes(%attributes);
 
  65 sub _assign_attributes {
 
  69   my %types      = map { $_->name => $_->type } ref($self)->meta->columns;
 
  71   # Special case for *_as_man_days / *_as_man_days_string /
 
  72   # *_as_man_days_unit: the _unit variation must always be called
 
  73   # after the non-unit methods.
 
  74   my @man_days_attributes = grep { m/_as_man_days(?:_string)?$/ } keys %attributes;
 
  75   foreach my $attribute (@man_days_attributes) {
 
  76     my $value = delete $attributes{$attribute};
 
  77     $self->$attribute(defined($value) && ($value eq '') ? undef : $value);
 
  80   while (my ($attribute, $value) = each %attributes) {
 
  81     my $type = lc($types{$attribute} || 'text');
 
  82     $value   = $type eq 'boolean'                ? ($value ? 't' : 'f')
 
  83              : $text_column_types{$type}         ? $value
 
  84              : defined($value) && ($value eq '') ? undef
 
  86     $self->$attribute($value);
 
  92 sub update_attributes {
 
  95   $self->assign_attributes(@_)->save;
 
 103   return $self->$sub(@_);
 
 111   $check    = $check->($self) if ref($check) eq 'CODE';
 
 113   return $check ? $self->$sub(@_) : $self;
 
 116 sub get_first_conflicting {
 
 117   my ($self, @attributes) = @_;
 
 119   my $primary_key         = ($self->meta->primary_key)[0];
 
 120   my @where               = map { ($_ => $self->$_) } @attributes;
 
 122   push @where, ("!$primary_key" => $self->$primary_key) if $self->$primary_key;
 
 124   return $self->_get_manager_class->get_first(where => [ and => \@where ]);
 
 127 # These three functions cannot sit in SL::DB::Object::Hooks because
 
 128 # mixins don't deal well with super classes (SUPER is the current
 
 129 # package's super class, not $self's).
 
 131   my ($self, @args) = @_;
 
 133   SL::DB::Object::Hooks::run_hooks($self, 'before_load');
 
 134   my $result = $self->SUPER::load(@args);
 
 135   SL::DB::Object::Hooks::run_hooks($self, 'after_load', $result);
 
 141   my ($self, @args) = @_;
 
 143   my ($result, $exception);
 
 145     $exception = $EVAL_ERROR unless eval {
 
 146       SL::DB::Object::Hooks::run_hooks($self, 'before_save');
 
 147       $result = $self->SUPER::save(@args);
 
 148       SL::DB::Object::Hooks::run_hooks($self, 'after_save', $result);
 
 155   $self->db->in_transaction ? $worker->() : $self->db->do_transaction($worker);
 
 157   die $exception if $exception;
 
 163   my ($self, @args) = @_;
 
 165   my ($result, $exception);
 
 167     $exception = $EVAL_ERROR unless eval {
 
 168       SL::DB::Object::Hooks::run_hooks($self, 'before_delete');
 
 169       $result = $self->SUPER::delete(@args);
 
 170       SL::DB::Object::Hooks::run_hooks($self, 'after_delete', $result);
 
 177   $self->db->in_transaction ? $worker->() : $self->db->do_transaction($worker);
 
 179   die $exception if $exception;
 
 185   my $class_or_self = shift;
 
 187   my $class         = ref($class_or_self) || $class_or_self;
 
 188   my $cache         = $::request->cache("::SL::DB::Object::object_cache::${class}");
 
 190   croak "Missing ID" unless @ids;
 
 192   my @missing_ids = grep { !exists $cache->{$_} } @ids;
 
 194   return $cache->{$ids[0]} if !@missing_ids;
 
 196   croak "Caching can only be used with classes with exactly one primary key column" if 1 != scalar(@{ $class->meta->primary_key_columns });
 
 198   my $primary_key = $class->meta->primary_key_columns->[0]->name;
 
 199   my $objects     = $class->_get_manager_class->get_all(where => [ $primary_key => \@missing_ids ]);
 
 201   $cache->{$_->$primary_key} = $_ for @{ $objects};
 
 203   return $cache->{$ids[0]};
 
 206 sub invalidate_cached {
 
 207   my ($class_or_self, @ids) = @_;
 
 208   my $class                 = ref($class_or_self) || $class_or_self;
 
 210   if (ref($class_or_self) && !@ids) {
 
 211     croak "Caching can only be used with classes with exactly one primary key column" if 1 != scalar(@{ $class->meta->primary_key_columns });
 
 213     my $primary_key = $class->meta->primary_key_columns->[0]->name;
 
 214     @ids            = ($class_or_self->$primary_key);
 
 217   delete @{ $::request->cache("::SL::DB::Object::object_cache::${class}") }{ @ids };
 
 219   return $class_or_self;
 
 222 my %_skip_fields_when_cloning = map { ($_ => 1) } qw(itime mtime);
 
 224 sub clone_and_reset {
 
 226   my $class               = ref $self;
 
 227   my $cloning             = Rose::DB::Object::Constants::STATE_CLONING();
 
 228   local $self->{$cloning} = 1;
 
 230   my $meta                = $class->meta;
 
 231   my @accessors           = $meta->column_accessor_method_names;
 
 232   my @mutators            = $meta->column_mutator_method_names;
 
 234     grep     { $_->[0] && $_->[1] && !$_skip_fields_when_cloning{ $_->[0] } }
 
 235     pairwise { no warnings qw(once); [ $a, $b] } @accessors, @mutators;
 
 237   my $clone = $class->new(map { my $method = $_->[0]; ($_->[1] => $self->$method) } @column_names);
 
 239   # Blank all primary and unique key columns
 
 241     $meta->primary_key_column_mutator_names,
 
 242     map { my $uk = $_; map { $meta->column_mutator_method_name($_) } ($uk->columns) } ($meta->unique_keys)
 
 245   $clone->$_(undef) for @keys;
 
 247   # Also copy db object, if any
 
 248   $clone->db($self->{db}) if $self->{db};
 
 263 SL::DB::Object: Base class for all of our model classes
 
 267 This is the base class from which all other model classes are
 
 268 derived. It contains functionality and settings required for all model
 
 271 Several functions (e.g. C<make_manager_class>, C<init_db>) in this
 
 272 class are used for setting up the classes / base classes used for all
 
 273 model instances. They overwrite the functions from
 
 280 =item assign_attributes %attributes
 
 282 =item _assign_attributes %attributes
 
 284 Assigns all elements from C<%attributes> to the columns by calling
 
 285 their setter functions. The difference between the two functions is
 
 286 that C<assign_attributes> protects primary key columns while
 
 287 C<_assign_attributes> doesn't.
 
 289 Both functions handle values that are empty strings by replacing them
 
 290 with C<undef> for non-text columns. This allows the calling functions
 
 291 to use data from HTML forms as the input for C<assign_attributes>
 
 292 without having to remove empty strings themselves (think of
 
 293 e.g. select boxes with an empty option which should be turned into
 
 294 C<NULL> in the database).
 
 296 =item update_attributes %attributes
 
 298 Assigns the attributes from C<%attributes> by calling the
 
 299 C<assign_attributes> function and saves the object afterwards. Returns
 
 302 =item _get_manager_class
 
 304 Returns the manager package for the object or class that it is called
 
 305 on. Can be used from methods in this package for getting the actual
 
 308 =item C<call_sub $name, @args>
 
 310 Calls the sub C<$name> on C<$self> with the arguments C<@args> and
 
 311 returns its result. This is meant for situations in which the sub's
 
 312 name is a composite, e.g.
 
 314   my $chart_id = $buchungsgruppe->call_sub(($is_sales ? "income" : "expense") . "_accno_id_${taxzone_id}");
 
 316 =item C<call_sub_if $name, $check, @args>
 
 318 Calls the sub C<$name> on C<$self> with the arguments C<@args> if
 
 319 C<$check> is trueish. If C<$check> is a code reference then it will be
 
 320 called with C<$self> as the only argument and its result determines
 
 321 whether or not C<$name> is called.
 
 323 Returns the sub's result if the check is positive and C<$self>
 
 326 =item C<get_first_conflicting @attributes>
 
 328 Returns the first object for which all properties listed in
 
 329 C<@attributes> equal those in C<$self> but which is not C<$self>. Can
 
 330 be used to check whether or not an object's columns are unique before
 
 331 saving or during validation.
 
 333 =item C<load_cached @ids>
 
 335 Loads objects from the database which haven't been cached before and
 
 336 caches them for the duration of the current request (see
 
 337 L<SL::Request/cache>).
 
 339 This method can be called both as an instance method and a class
 
 340 method. It loads objects for the corresponding class (e.g. both
 
 341 C<SL::DB::Part-E<gt>load_cached(…)> and
 
 342 C<$some_part-E<gt>load_cached(…)> will load parts).
 
 344 Currently only classes with a single primary key column are supported.
 
 346 Returns the cached object for the first ID.
 
 348 =item C<invalidate_cached @ids>
 
 350 Deletes all cached instances of this class (see L</load_cached>) for
 
 353 If called as an instance method without further arguments then the
 
 356 Returns the object/class it was called on.
 
 358 =item C<clone_and_reset>
 
 360 This works similar to L<Rose::DB::Object::Helpers/clone_and_reset>: it
 
 361 returns a cloned instance of C<$self>. All primary and unique key
 
 362 fields have been reset.
 
 364 The difference between Rose's and this function is that this function
 
 365 will also skip setting the following fields if such columns exist for
 
 366 C<$self>: C<itime>, C<mtime>.
 
 372 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>