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