1 package SL::DB::Object;
5 use English qw(-no_match_vars);
7 use List::MoreUtils qw(any);
10 use SL::DB::Helper::Attr;
11 use SL::DB::Helper::Metadata;
12 use SL::DB::Helper::Manager;
13 use SL::DB::Object::Hooks;
15 use base qw(Rose::DB::Object);
17 my @rose_reserved_methods = qw(
18 db dbh delete DESTROY error init_db _init_db insert load meta meta_class
19 not_found save update import
24 my $self = $class->SUPER::new();
26 $self->_assign_attributes(@_) if $self;
32 my $class_or_self = shift;
33 my $class = ref($class_or_self) || $class_or_self;
34 my $type = $class =~ m/::Auth/ ? 'KIVITENDO_AUTH' : 'KIVITENDO';
36 return SL::DB::create(undef, $type);
40 return 'SL::DB::Helper::Metadata';
43 sub _get_manager_class {
44 my $class_or_self = shift;
45 my $class = ref($class_or_self) || $class_or_self;
47 return $class->meta->convention_manager->auto_manager_class_name($class);
50 my %text_column_types = (text => 1, char => 1, varchar => 1);
52 sub assign_attributes {
56 my $pk = ref($self)->meta->primary_key;
57 delete @attributes{$pk->column_names} if $pk;
58 delete @attributes{@rose_reserved_methods};
60 return $self->_assign_attributes(%attributes);
63 sub _assign_attributes {
67 my %types = map { $_->name => $_->type } ref($self)->meta->columns;
69 # Special case for *_as_man_days / *_as_man_days_string /
70 # *_as_man_days_unit: the _unit variation must always be called
71 # after the non-unit methods.
72 my @man_days_attributes = grep { m/_as_man_days(?:_string)?$/ } keys %attributes;
73 foreach my $attribute (@man_days_attributes) {
74 my $value = delete $attributes{$attribute};
75 $self->$attribute(defined($value) && ($value eq '') ? undef : $value);
78 while (my ($attribute, $value) = each %attributes) {
79 my $type = lc($types{$attribute} || 'text');
80 $value = $type eq 'boolean' ? ($value ? 't' : 'f')
81 : $text_column_types{$type} ? $value
82 : defined($value) && ($value eq '') ? undef
84 $self->$attribute($value);
90 sub update_attributes {
93 $self->assign_attributes(@_)->save;
101 return $self->$sub(@_);
109 $check = $check->($self) if ref($check) eq 'CODE';
111 return $check ? $self->$sub(@_) : $self;
114 sub get_first_conflicting {
115 my ($self, @attributes) = @_;
117 my $primary_key = ($self->meta->primary_key)[0];
118 my @where = map { ($_ => $self->$_) } @attributes;
120 push @where, ("!$primary_key" => $self->$primary_key) if $self->$primary_key;
122 return $self->_get_manager_class->get_first(where => [ and => \@where ]);
125 # These three functions cannot sit in SL::DB::Object::Hooks because
126 # mixins don't deal well with super classes (SUPER is the current
127 # package's super class, not $self's).
129 my ($self, @args) = @_;
131 SL::DB::Object::Hooks::run_hooks($self, 'before_load');
132 my $result = $self->SUPER::load(@args);
133 SL::DB::Object::Hooks::run_hooks($self, 'after_load', $result);
139 my ($self, @args) = @_;
141 my ($result, $exception);
143 $exception = $EVAL_ERROR unless eval {
144 SL::DB::Object::Hooks::run_hooks($self, 'before_save');
145 $result = $self->SUPER::save(@args);
146 SL::DB::Object::Hooks::run_hooks($self, 'after_save', $result);
153 $self->db->in_transaction ? $worker->() : $self->db->do_transaction($worker);
155 die $exception if $exception;
161 my ($self, @args) = @_;
163 my ($result, $exception);
165 $exception = $EVAL_ERROR unless eval {
166 SL::DB::Object::Hooks::run_hooks($self, 'before_delete');
167 $result = $self->SUPER::delete(@args);
168 SL::DB::Object::Hooks::run_hooks($self, 'after_delete', $result);
175 $self->db->in_transaction ? $worker->() : $self->db->do_transaction($worker);
177 die $exception if $exception;
190 SL::DB::Object: Base class for all of our model classes
194 This is the base class from which all other model classes are
195 derived. It contains functionality and settings required for all model
198 Several functions (e.g. C<make_manager_class>, C<init_db>) in this
199 class are used for setting up the classes / base classes used for all
200 model instances. They overwrite the functions from
207 =item assign_attributes %attributes
209 =item _assign_attributes %attributes
211 Assigns all elements from C<%attributes> to the columns by calling
212 their setter functions. The difference between the two functions is
213 that C<assign_attributes> protects primary key columns while
214 C<_assign_attributes> doesn't.
216 Both functions handle values that are empty strings by replacing them
217 with C<undef> for non-text columns. This allows the calling functions
218 to use data from HTML forms as the input for C<assign_attributes>
219 without having to remove empty strings themselves (think of
220 e.g. select boxes with an empty option which should be turned into
221 C<NULL> in the database).
223 =item update_attributes %attributes
225 Assigns the attributes from C<%attributes> by calling the
226 C<assign_attributes> function and saves the object afterwards. Returns
229 =item _get_manager_class
231 Returns the manager package for the object or class that it is called
232 on. Can be used from methods in this package for getting the actual
235 =item C<call_sub $name, @args>
237 Calls the sub C<$name> on C<$self> with the arguments C<@args> and
238 returns its result. This is meant for situations in which the sub's
239 name is a composite, e.g.
241 my $chart_id = $buchungsgruppe->call_sub(($is_sales ? "income" : "expense") . "_accno_id_${taxzone_id}");
243 =item C<call_sub_if $name, $check, @args>
245 Calls the sub C<$name> on C<$self> with the arguments C<@args> if
246 C<$check> is trueish. If C<$check> is a code reference then it will be
247 called with C<$self> as the only argument and its result determines
248 whether or not C<$name> is called.
250 Returns the sub's result if the check is positive and C<$self>
253 =item C<get_first_conflicting @attributes>
255 Returns the first object for which all properties listed in
256 C<@attributes> equal those in C<$self> but which is not C<$self>. Can
257 be used to check whether or not an object's columns are unique before
258 saving or during validation.
264 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>