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