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