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