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