remove Base
[dbsrgits/DBIx-Class-Journal.git] / lib / DBIx / Class / Journal.pm
1 package DBIx::Class::Journal;
2
3 use base qw/DBIx::Class/;
4
5 use strict;
6 use warnings;
7
8 our $VERSION = '0.02';
9
10 ## On create/insert, add new entry to AuditLog
11
12 # sub new
13 # {
14 #     my ($class, $attrs, @rest) = @_;
15
16 #     $class->result_source->schema->_journal_schema->current_user(delete $attrs->{user_id});
17
18 #     $class->next::method($attrs, @rest);
19 # }
20
21 sub insert
22 {
23     my ($self) = @_;
24
25     return if($self->in_storage);
26     ## create new transaction here?
27     my $res = $self->next::method();
28     if($self->in_storage)
29     {
30         my $s_name = $self->result_source->source_name();
31         my $al = $self->result_source->schema->_journal_schema->resultset("${s_name}AuditLog");
32         $al->update_or_create({
33             ( map { $_ => $self->get_column($_)} $self->primary_columns ),
34             created => { changeset_id => $al->result_source->schema->current_changeset },
35         });
36     }
37
38     return $res;
39 }
40
41 ## On delete, update delete_id of AuditLog
42
43 sub delete
44 {
45     my ($self, @rest) = @_;
46     $self->next::method(@rest);
47
48     if(!$self->in_storage)
49     {
50         my $s_name = $self->result_source->source_name();
51         my $al = $self->result_source->schema->_journal_schema->resultset("${s_name}AuditLog");
52         my $alentry = $al->find_or_create({ map { $_ => $self->get_column($_)} $self->primary_columns });
53          
54         ## bulk_update doesnt do "create new item on update of rel-accessor with hashref, yet
55         my $change = $self->result_source->schema->_journal_schema->resultset('ChangeLog')->create({ changeset_id => $self->result_source->schema->_journal_schema->current_changeset });
56         $alentry->delete_id($change->id);
57         $alentry->update();
58     }
59     
60 }
61
62 ## On update, copy previous row's contents to AuditHistory
63
64 sub update 
65 {
66     my ($self, $upd, @rest) = @_;
67
68     if($self->in_storage)
69     {
70         my $s_name = $self->result_source->source_name();
71         my $ah = $self->result_source->schema->_journal_schema->resultset("${s_name}AuditHistory");
72
73         my $obj = $self->result_source->resultset->find( $self->ident_condition );
74         $ah->create({
75             $obj->get_columns
76             });
77     }
78
79     $self->next::method($upd, @rest);
80 }
81
82 =head1 NAME
83
84 DBIx::Class::Journal - auditing for tables managed by DBIx::Class
85
86 =head1 SYNOPSIS
87
88   package My::Schema;
89   use base 'DBIx::Class::Schema';
90
91   __PACKAGE__->load_components(qw/+DBIx::Class::Schema::Journal/);
92
93   __PACKAGE__->journal_connection(['dbi:SQLite:t/var/Audit.db']);
94   __PACKAGE__->journal_user(['My::Schema::User', {'foreign.userid' => 'self.user_id'}]);
95
96
97  ########
98
99   $schema->changeset_user($user->id);
100   my $new_artist = $schema->txn_do( sub {
101    return = $schema->resultset('Artist')->create({ name => 'Fred' });
102   });
103
104
105 =head1 DESCRIPTION
106
107 The purpose of this L<DBIx::Class> component module is to create an
108 audit-trail for all changes made to the data in your database (via a
109 DBIx::Class schema). It creates changesets and assigns each
110 create/update/delete operation an id. The creation and deletion date
111 of each row is stored, as well as the previous contents of any row
112 that gets changed.
113
114 All queries which want auditing should be called using
115 L<DBIx::Class::Schema/txn_do>, which is used to create changesets for
116 each transaction.
117
118 To track who did which changes, the user_id (an integer) of the
119 current user can be set, a session_id can also be set, both are
120 optional.
121
122 To access the auditing schema to look at the auditdata or revert a
123 change, use C<< $schema->_journal_schema >>.
124
125 =head2 TABLES
126
127 The journal schema contains a number of tables. 
128
129 =over
130
131 =item ChangeSet
132
133 Each changeset row has an auto-incremented ID, optional user_id and
134 session_id, and a set_date which defaults to the current datetime.
135
136 A ChangeSet has_many Changes.
137
138 =item Change
139
140 Each change/operation done in the transaction is recorded as a row in
141 the Change table. It contains an auto-incrementing ID, the
142 changeset_id and an order column for the ordering of each change in
143 the changeset.
144
145 =item AuditLog
146
147 For every table in the original database that is to be audited, an
148 AuditLog table is created. Each auditlog row has an id which will
149 contain the primary key of the table it is associated with. (NB:
150 currently only supports integer-based single column PKs). The
151 create_id and delete_id fields contain the IDs of the Changes that
152 created or deleted this row.
153
154 =item AuditHistory
155
156 For every table in the original database to be audited, an
157 AuditHistory table is created. Each row has a change_id field
158 containing the ID of the Change row. The other fields correspond to
159 all the fields from the original table. Each time a column value in
160 the original table is changed, the entire row contents before the
161 change are added as a new row in this table.
162
163 =back
164
165 =head2 METHODS
166
167 =over
168
169 =item journal_connection
170
171 =item Arguments: \@connect_info
172
173 =back
174
175 Set the connection information for the database to save your audit
176 information to. Leaving this blank assumes you want to store the audit
177 data into your current database.
178
179 =over
180
181 =item journal_sources
182
183 =item Arguments: \@source_names
184
185 =back
186
187 Set a list of source names you would like to audit, if unset, all
188 sources are used.
189
190 NOTE: Currently only sources with a single-column PK are supported, so
191 use this method if you have sources with multi-column PKs.
192
193 =over
194
195 =item journal_storage_type
196
197 =item Arguments: $storage_type
198
199 =back
200
201 Enter the special storage type of your journal schema if needed. See
202 L<DBIx::Class::Storage::DBI> for more information on storage types.
203
204 =over
205
206 =item journal_user
207
208 =item Arguments: \@relation_args
209
210 =back
211
212 The user_id column in the L</ChangeSet> will be linked to your user id
213 with a belongs_to relation, if this is set with the appropriate
214 arguments.
215
216 =over
217
218 =item changeset_user
219
220 =item Arguments: $user_id
221
222 =back
223
224 Set the user_id for the following changeset(s). This must be an integer.
225
226 =over
227
228 =item changeset_session
229
230 =item Arguments: $user_id
231
232 =back
233
234 Set the session_id for the following changeset(s). This must be an integer.
235
236 =over
237
238 =item txn_do
239
240 =iitem Arguments: $code_ref
241
242 =back
243
244 Overloaded L<DBIx::Class::Schema/txn_do>, this must be used to start a
245 new changeset to cover a group of changes. Each subsequent change to
246 an audited table will use the changeset_id created in the most recent
247 txn_do call.
248
249 =head1 SEE ALSO
250
251 L<DBIx::Class> - You'll need it to use this.
252
253 =head1 NOTES
254
255 Only single-column integer primary key'd tables are supported for auditing so far.
256
257 Updates made via L<DBIx::Class::ResultSet/update> are not yet supported.
258
259 No API for viewing or restoring changes yet.
260
261 Patches for the above welcome ;)
262
263 =head1 AUTHOR
264
265 Jess Robinson <castaway@desert-island.me.uk>
266
267 Matt S. Trout <mst@shadowcatsystems.co.uk> (ideas and prodding)
268
269 =head1 LICENCE
270
271 You may distribute this code under the same terms as Perl itself.
272
273 =cut
274
275 1;