This commit was generated by cvs2svn to compensate for changes in r6252,
[freeside.git] / FS / FS / m2m_Common.pm
1 package FS::m2m_Common;
2
3 use strict;
4 use vars qw( @ISA $DEBUG );
5 use FS::Schema qw( dbdef );
6 use FS::Record qw( qsearch qsearchs dbh );
7
8 #hmm.  well.  we seem to be used as a mixin.
9 #@ISA = qw( FS::Record );
10
11 $DEBUG = 0;
12
13 =head1 NAME
14
15 FS::m2m_Common - Mixin class for classes in a many-to-many relationship
16
17 =head1 SYNOPSIS
18
19 use FS::m2m_Common;
20
21 @ISA = qw( FS::m2m_Common FS::Record );
22
23 =head1 DESCRIPTION
24
25 FS::m2m_Common is intended as a mixin class for classes which have a
26 many-to-many relationship with another table (via a linking table).
27
28 Note: It is currently assumed that the link table contains two fields
29 named the same as the primary keys of ths base and target tables.
30
31 =head1 METHODS
32
33 =over 4
34
35 =item process_m2m OPTION => VALUE, ...
36
37 Available options:
38
39 link_table (required) - 
40
41 target_table (required) - 
42
43 params (required) - hashref; keys are primary key values in target_table (values are boolean).  For convenience, keys may optionally be prefixed with the name
44 of the primary key, as in agentnum54 instead of 54, or passed as an arrayref
45 of values.
46
47 =cut
48
49 sub process_m2m {
50   my( $self, %opt ) = @_;
51
52   my $self_pkey = $self->dbdef_table->primary_key;
53   my %hash = ( $self_pkey => $self->$self_pkey() );
54
55   my $link_table = $self->_load_table($opt{'link_table'});
56
57   my $target_table = $self->_load_table($opt{'target_table'});
58   my $target_pkey = dbdef->table($target_table)->primary_key;
59
60   if ( ref($opt{'params'}) eq 'ARRAY' ) {
61     $opt{'params'} = { map { $_=>1 } @{$opt{'params'}} };
62   }
63
64   local $SIG{HUP} = 'IGNORE';
65   local $SIG{INT} = 'IGNORE';
66   local $SIG{QUIT} = 'IGNORE';
67   local $SIG{TERM} = 'IGNORE';
68   local $SIG{TSTP} = 'IGNORE';
69   local $SIG{PIPE} = 'IGNORE';
70
71   my $oldAutoCommit = $FS::UID::AutoCommit;
72   local $FS::UID::AutoCommit = 0;
73   my $dbh = dbh;
74
75   foreach my $del_obj (
76     grep { 
77            my $targetnum = $_->$target_pkey();
78            (    ! $opt{'params'}->{$targetnum}
79              && ! $opt{'params'}->{"$target_pkey$targetnum"}
80            );
81          }
82          qsearch( $link_table, \%hash )
83   ) {
84     my $error = $del_obj->delete;
85     if ( $error ) {
86       $dbh->rollback if $oldAutoCommit;
87       return $error;
88     }
89   }
90
91   foreach my $add_targetnum (
92     grep { ! qsearchs( $link_table, { %hash, $target_pkey => $_ } ) }
93     map  { /^($target_pkey)?(\d+)$/; $2; }
94     grep { /^($target_pkey)?(\d+)$/ }
95     grep { $opt{'params'}->{$_} }
96     keys %{ $opt{'params'} }
97   ) {
98
99     my $add_obj = "FS::$link_table"->new( {
100       %hash, 
101       $target_pkey => $add_targetnum,
102     });
103     my $error = $add_obj->insert;
104     if ( $error ) {
105       $dbh->rollback if $oldAutoCommit;
106       return $error;
107     }
108   }
109
110   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
111   '';
112 }
113
114 sub _load_table {
115   my( $self, $table ) = @_;
116   eval "use FS::$table";
117   die $@ if $@;
118   $table;
119 }
120
121 #=item target_table
122 #
123 #=cut
124 #
125 #sub target_table {
126 #  my $self = shift;
127 #  my $target_table = $self->_target_table;
128 #  eval "use FS::$target_table";
129 #  die $@ if $@;
130 #  $target_table;
131 #}
132
133 =back
134
135 =head1 BUGS
136
137 =head1 SEE ALSO
138
139 L<FS::Record>
140
141 =cut
142
143 1;
144