use strict
[kivitendo-erp.git] / SL / DB / Helper / LinkedRecords.pm
1 package SL::DB::Helpers::LinkedRecords;
2
3 use strict;
4
5 require Exporter;
6 our @ISA    = qw(Exporter);
7 our @EXPORT = qw(linked_records link_to_record);
8
9 use Carp;
10
11 use SL::DB::Helpers::Mappings;
12 use SL::DB::RecordLink;
13
14 sub linked_records {
15   my $self     = shift;
16   my %params   = @_;
17
18   my $wanted   = $params{direction} || croak("Missing parameter `direction'");
19   my $myself   = $wanted eq 'from' ? 'to' : $wanted eq 'to' ? 'from' : croak("Invalid parameter `direction'");
20
21   my $my_table = SL::DB::Helpers::Mappings::get_table_for_package(ref($self));
22
23   my @query    = ( "${myself}_table" => $my_table,
24                    "${myself}_id"    => $self->id );
25
26   if ($params{$wanted}) {
27     my $wanted_classes = ref($params{$wanted}) eq 'ARRAY' ? $params{$wanted} : [ $params{$wanted} ];
28     my $wanted_tables  = [ map { SL::DB::Helpers::Mappings::get_table_for_package($_) || croak("Invalid parameter `${wanted}'") } @{ $wanted_classes } ];
29     push @query, ("${wanted}_table" => $wanted_tables);
30   }
31
32   my $links            = SL::DB::Manager::RecordLink->get_all(query => [ and => \@query ]);
33
34   my $sub_wanted_table = "${wanted}_table";
35   my $sub_wanted_id    = "${wanted}_id";
36
37   my $records          = [];
38   @query               = ref($params{query}) eq 'ARRAY' ? @{ $params{query} } : ();
39
40   foreach my $link (@{ $links }) {
41     my $class = SL::DB::Helpers::Mappings::get_manager_package_for_table($link->$sub_wanted_table);
42     push @{ $records }, @{ $class->get_all(query => [ id => $link->$sub_wanted_id, @query ]) };
43   }
44
45   return $records;
46 }
47
48 sub link_to_record {
49   my $self   = shift;
50   my $other  = shift;
51
52   croak "self has no id"  unless $self->id;
53   croak "other has no id" unless $other->id;
54
55   my %params = ( from_table => SL::DB::Helpers::Mappings::get_table_for_package(ref($self)),
56                  from_id    => $self->id,
57                  to_table   => SL::DB::Helpers::Mappings::get_table_for_package(ref($other)),
58                  to_id      => $other->id,
59                );
60
61   my $link = SL::DB::Manager::RecordLink->find_by(and => [ %params ]);
62   return $link ? $link : SL::DB::RecordLink->new(%params)->save;
63 }
64
65 1;
66
67 __END__
68
69 =encoding utf8
70
71 =head1 NAME
72
73 SL::DB::Helpers::LinkedRecords - Mixin for retrieving linked records via the table C<record_links>
74
75 =head1 FUNCTIONS
76
77 =over 4
78
79 =item C<linked_records %params>
80
81 Retrieves records linked from or to C<$self> via the table
82 C<record_links>. The mandatory parameter C<direction> (either C<from>
83 or C<to>) determines whether the function retrieves records that link
84 to C<$self> (for C<direction> = C<to>) or that are linked from
85 C<$self> (for C<direction> = C<from>).
86
87 The optional parameter C<from> or C<to> (same as C<direction>)
88 contains the package names of Rose models for table limitation. It can
89 be a single model name as a single scalar or multiple model names in
90 an array reference in which case all links matching any of the model
91 names will be returned.
92
93 If you only need invoices created from an order C<$order> then the
94 call could look like this:
95
96   my $invoices = $order->linked_records(direction => 'to',
97                                         to        => 'SL::DB::Invoice');
98
99 The optional parameter C<query> can be used to limit the records
100 returned. The following call limits the earlier example to invoices
101 created today:
102
103   my $invoices = $order->linked_records(direction => 'to',
104                                         to        => 'SL::DB::Invoice',
105                                         query     => [ transdate => DateTime->today_local ]);
106
107 Returns an array reference.
108
109 =item C<link_to_record $record>
110
111 Will create an entry in the table C<record_links> with the C<from>
112 side being C<$self> and the C<to> side being C<$record>. Will only
113 insert a new entry if such a link does not already exist.
114
115 Returns either the existing link or the newly created one as an
116 instance of C<SL::DB::RecordLink>.
117
118 =back
119
120 =head1 BUGS
121
122 Nothing here yet.
123
124 =head1 AUTHOR
125
126 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
127
128 =cut