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