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