added a small amount of POD, but I need to go walk the dog now
[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 And then call C<< $schema->journal_schema_deploy >> to create all the tables
86 necessary for the journal, in your database.
87
88 Optionally set where the journal is stored, and associate a user ID with
89 each changeset.
90
91  __PACKAGE__->journal_connection(['dbi:SQLite:t/var/Audit.db']);
92  __PACKAGE__->journal_user(['My::Schema::User', {'foreign.userid' => 'self.user_id'}]);
93
94
95  #######
96
97  $schema->changeset_user($user->id);
98  my $new_artist = $schema->txn_do( sub {
99     return $schema->resultset('Artist')->create({ name => 'Fred' });
100  });
101
102
103 =head1 DESCRIPTION
104
105 The purpose of this L<DBIx::Class> component module is to create an
106 audit-trail for all changes made to the data in your database (via a
107 DBIx::Class schema). It creates changesets and assigns each
108 create/update/delete operation an id. The creation and deletion date
109 of each row is stored, as well as the previous contents of any row
110 that gets changed.
111
112 All queries which need auditing must be called using
113 L<DBIx::Class::Schema/txn_do>, which is used to create changesets for
114 each transaction.
115
116 To track who did which changes, the user_id (an integer) of the
117 current user can be set, a session_id can also be set, both are
118 optional.
119
120 To access the auditing schema to look at the auditdata or revert a
121 change, use C<< $schema->_journal_schema >>.
122
123 =head2 TABLES
124
125 The journal schema contains a number of tables.
126
127 =over
128
129 =item ChangeSet
130
131 Each changeset row has an auto-incremented ID, optional user_id and
132 session_id, and a set_date which defaults to the current datetime.
133
134 A ChangeSet has_many Changes.
135
136 =item ChangeLog
137
138 Each change/operation done in the transaction is recorded as a row in
139 the ChangeLog table. It contains an auto-incrementing ID, the
140 changeset_id and an order column for the ordering of each change in
141 the changeset.
142
143 =item AuditLog
144
145 For every table in the original database that is to be audited, an
146 AuditLog table is created. Each auditlog row has an id which will
147 contain the primary key of the table it is associated with. (NB:
148 currently only supports integer-based single column PKs). The
149 create_id and delete_id fields contain the IDs of the Changes that
150 created or deleted this row.
151
152 =item AuditHistory
153
154 For every table in the original database to be audited, an
155 AuditHistory table is created. Each row has a change_id field
156 containing the ID of the ChangeLog row. The other fields correspond to
157 all the fields from the original table. Each time a column value in
158 the original table is changed, the entire row contents before the
159 change are added as a new row in this table.
160
161 =back
162
163 =head2 METHODS
164
165 =over
166
167 =item journal_connection \@connect_info
168
169 Set the connection information for the database to save your audit
170 information to.
171
172 Leaving this blank assumes you want to store the audit data into your current
173 database. The storage object will be shared by the regular schema and the
174 journalling schema.
175
176 =item journal_components @components
177
178 If you want to add components to your journal
179 (L<DBIx::Class::Schema::Versioned> for example) pass them here.
180
181 =item journal_sources \@source_names
182
183 Set a list of source names you would like to audit, if unset, all
184 sources are used.
185
186 NOTE: Currently only sources with a single-column PK are supported, so
187 use this method if you have sources with multi-column PKs.
188
189 =item journal_storage_type $type
190
191 Enter the special storage type of your journal schema if needed. See
192 L<DBIx::Class::Storage::DBI> for more information on storage types.
193
194 =item journal_user \@rel
195
196 The user_id column in the L</ChangeSet> will be linked to your user id
197 with a belongs_to relation, if this is set with the appropriate
198 arguments.
199
200 =item journal_schema_deploy
201
202 Will use L<DBIx::Class::Schema/deploy> to set up the tables for
203 journalling in your schema. Use this method to set up your journal.
204
205 =item journal_deploy_on_connect $bool
206
207 If set to a true value will cause C<journal_schema_deploy> to be called on
208 C<connect>.
209
210 Not recommended, but present for backwards compatibility.
211
212 =item changeset_user $user_id
213
214 Set the user_id for the following changeset(s). This must be an integer.
215
216 =item changeset_session $session_id
217
218 Set the session_id for the following changeset(s). This must be an integer.
219
220 =item deploy
221
222 Overloaded L<DBIx::Class::Schema/deploy> which will deploy your primary
223 database schema and following that will deploy the journal schema.
224
225 =item txn_do $code_ref, @args
226
227 Overloaded L<DBIx::Class::Schema/txn_do>, this must be used to start a
228 new changeset to cover a group of changes. Each subsequent change to
229 an audited table will use the changeset_id created in the most recent
230 txn_do call.
231
232 Currently nested C<txn_do> calls cause a single ChangeSet object to be created.
233
234 =back
235
236 =head1 SEE ALSO
237
238 L<DBIx::Class> - You'll need it to use this.
239
240 =head1 NOTES
241
242 Only single-column integer primary key'd tables are supported for auditing so far.
243
244 Updates made via L<DBIx::Class::ResultSet/update> are not yet supported.
245
246 No API for viewing or restoring changes yet.
247
248 Patches for the above welcome ;)
249
250 =head1 AUTHOR
251
252 Jess Robinson <castaway@desert-island.me.uk>
253
254 Matt S. Trout <mst@shadowcatsystems.co.uk> (ideas and prodding)
255
256 =head1 LICENCE
257
258 You may distribute this code under the same terms as Perl itself.
259
260 =cut
261
262 1;